1 /**
2    This module provides the template metaprogramming variant of compile-time
3    reflection, allowing client code to do type-level computations on the
4    contents of a D module.
5  */
6 module mirror.meta.reflection;
7 
8 
9 import mirror.trait_enums: Protection, toProtection, Linkage, toLinkage;
10 import std.meta: Alias;
11 
12 /**
13    Compile-time information on a D module.
14  */
15 template Module(string moduleName) {
16 
17     import mirror.meta.traits: RecursiveTypeTree, RecursiveFieldTypes, FundamentalType, PublicMembers,
18         MemberFunctionsByOverload;
19     import std.meta: Alias, NoDuplicates, Filter, staticMap, templateNot;
20 
21     mixin(`import `, moduleName, `;`);
22     private alias mod = Alias!(mixin(moduleName));
23 
24     private alias publicMembers = PublicMembers!mod;
25 
26     /// User-defined structs/classes
27     alias Aggregates = aggregates!publicMembers;
28     private enum isEnum(T) = is(T == enum);
29     private alias memberFunctions =
30         staticMap!(MemberFunctionsByOverload,
31                    Filter!(templateNot!isEnum, Aggregates));
32 
33     private template isAggregate(T) {
34         alias U = FundamentalType!T;
35         enum isAggregate = is(U == enum) || is(U == struct) || is(U == class) || is(U == interface) || is(U == union);
36     }
37 
38     /// User-defined structs/classes and all types contained in them
39     alias AggregatesTree = Filter!(isAggregate, RecursiveTypeTree!Aggregates);
40 
41     /// Global variables/enums
42     alias Variables = variables!publicMembers;
43 
44     /// List of functions by symbol - contains overloads for each entry
45     alias FunctionsBySymbol = functionsBySymbol!(mod, publicMembers);
46 
47     /// List of functions by overload - each overload is a separate entry
48     alias FunctionsByOverload = functionsByOverload!(mod, publicMembers);
49 
50     alias AllFunctionReturnTypes = NoDuplicates!(returnTypes!FunctionsByOverload, returnTypes!memberFunctions);
51     alias AllFunctionReturnTypesTree = RecursiveTypeTree!AllFunctionReturnTypes;
52 
53     alias AllFunctionParameterTypes = NoDuplicates!(parameterTypes!FunctionsByOverload, parameterTypes!memberFunctions);
54     alias AllFunctionParameterTypesTree = RecursiveTypeTree!AllFunctionParameterTypes;
55 
56     /**
57        All aggregates, including explicitly defined and appearing in
58        function signatures
59     */
60     alias AllAggregates =
61         NoDuplicates!(
62             staticMap!(FundamentalType,
63                        Filter!(isAggregate,
64                                AggregatesTree, AllFunctionReturnTypesTree, AllFunctionParameterTypesTree)
65         )
66     );
67 }
68 
69 
70 private template returnTypes(functions...) {
71 
72     import mirror.meta.traits: FundamentalType;
73     import std.traits: ReturnType;
74     import std.meta: staticMap, NoDuplicates;
75 
76     private template symbol(alias F) {
77         import std.traits: isSomeFunction;
78         static if(isSomeFunction!F)
79             alias symbol = F;
80         else
81             alias symbol = F.symbol;
82     }
83 
84     alias returnTypes =
85         NoDuplicates!(staticMap!(FundamentalType,
86                                  staticMap!(ReturnType,
87                                             staticMap!(symbol, functions))));
88 }
89 
90 private template parameterTypes(functions...) {
91 
92     import mirror.meta.traits: FundamentalType;
93     import std.traits: Parameters;
94     import std.meta: staticMap, NoDuplicates;
95 
96     private template symbol(alias F) {
97         import std.traits: isSomeFunction;
98         static if(isSomeFunction!F)
99             alias symbol = F;
100         else
101             alias symbol = F.symbol;
102     }
103 
104     alias parameterTypes =
105         NoDuplicates!(staticMap!(FundamentalType,
106                                  staticMap!(Parameters,
107                                             staticMap!(symbol, functions))));
108 }
109 
110 
111 // User-defined types
112 package template aggregates(publicMembers...) {
113     import std.meta: staticMap, Filter;
114 
115     private template memberIsType(alias member) {
116         import std.traits: isType;
117         enum memberIsType = isType!(member.symbol);
118     }
119 
120     private alias symbolOf(alias member) = member.symbol;
121 
122     alias aggregates = staticMap!(symbolOf, Filter!(memberIsType, publicMembers));
123 }
124 
125 
126 // Global variables
127 private template variables(publicMembers...) {
128     import mirror.meta.traits: isMutableSymbol, isVariable;
129     import std.meta: staticMap, Filter;
130 
131     private template toVariable(alias member) {
132         alias T = member.Type;
133         enum id = member.identifier;
134 
135         static if(__traits(compiles, Variable!(T, id, member.symbol, !isMutableSymbol!(member.symbol))))
136             alias toVariable = Variable!(T, id, member.symbol, !isMutableSymbol!(member.symbol));
137         else
138             alias toVariable = Variable!(T, id, T.init, !isMutableSymbol!(member.symbol));
139     }
140 
141     alias variables = staticMap!(toVariable, Filter!(isVariable, publicMembers));
142 }
143 
144 /**
145    A global variable.
146  */
147 template Variable(T, string N, alias V, bool C) {
148     alias Type = T;
149     enum identifier = N;
150     enum value = V;
151     enum isConstant = C;
152 }
153 
154 
155 private template functionsBySymbol(alias parent, publicMembers...) {
156 
157     import mirror.meta.traits: memberIsRegularFunction;
158     import std.meta: Filter, staticMap;
159 
160     private alias functionMembers = Filter!(memberIsRegularFunction, publicMembers);
161 
162     private alias toFunction(alias member) = FunctionSymbol!(
163         member.symbol,
164         __traits(getProtection, member.symbol).toProtection,
165         __traits(getLinkage, member.symbol).toLinkage,
166         member.identifier,
167         parent,
168         );
169 
170     alias functionsBySymbol = staticMap!(toFunction, functionMembers);
171 }
172 
173 
174 /**
175    A function symbol with nested overloads.
176  */
177 template FunctionSymbol(
178     alias F,
179     Protection P = __traits(getProtection, F).toProtection,
180     Linkage L = __traits(getLinkage, F).toLinkage,
181     string I = __traits(identifier, F),
182     alias Parent = Alias!(__traits(parent, F)),
183 )
184 {
185     import std.meta: staticMap;
186 
187     alias symbol = F;
188     enum identifier = I;
189     alias parent = Parent;
190 
191     private alias toOverload(alias symbol) = FunctionOverload!(
192         symbol,
193         __traits(getProtection, symbol).toProtection,
194         __traits(getLinkage, symbol).toLinkage,
195         identifier,
196         parent,
197     );
198 
199     alias overloads = staticMap!(toOverload, __traits(getOverloads, parent, identifier));
200 
201     string toString() @safe pure {
202         import std.conv: text;
203         return text(`Function(`, overloads.stringof, ")");
204     }
205 }
206 
207 
208 package template functionsByOverload(alias parent, publicMembers...) {
209 
210     import mirror.meta.traits: memberIsRegularFunction;
211     import std.meta: Filter, staticMap;
212 
213     private alias functionMembers = Filter!(memberIsRegularFunction, publicMembers);
214 
215     private template overload(alias S, string I, size_t Idx) {
216         alias symbol = S;
217         enum identifier = I;
218         enum index = Idx;
219     }
220 
221     template symbolsWithIndex(A...) {
222         import std.range: iota;
223         import std.meta: aliasSeqOf, staticMap;
224 
225         template Result(alias S, size_t I) {
226             alias symbol = S;
227             enum index = I;
228         }
229 
230         alias toResult(size_t I) = Result!(A[I], I);
231 
232         alias symbolsWithIndex = staticMap!(toResult, aliasSeqOf!(A.length.iota));
233     }
234 
235     private template memberToOverloads(alias member) {
236         private template isPublic(alias S) {
237             enum isPublic =  __traits(getProtection, S.symbol) == "public"
238                 || __traits(getProtection, S.symbol) == "export";
239         }
240         alias overloadsWithIndex = symbolsWithIndex!(__traits(getOverloads, parent, member.identifier));
241         // the reason we need to filter here is that some of the overloads might be private
242         private alias overloadSymbols = Filter!(isPublic, overloadsWithIndex);
243         private alias toOverload(alias symbol) = overload!(symbol.symbol, member.identifier, symbol.index);
244         alias memberToOverloads = staticMap!(toOverload, overloadSymbols);
245     }
246 
247     private alias toFunction(alias overload) = FunctionOverload!(
248         overload.symbol,
249         __traits(getProtection, overload.symbol).toProtection,
250         __traits(getLinkage, overload.symbol).toLinkage,
251         overload.identifier,
252         parent,
253         overload.index,
254     );
255 
256     alias functionsByOverload = staticMap!(toFunction, staticMap!(memberToOverloads, functionMembers));
257 }
258 
259 
260 /**
261    A specific overload of a function.  In most cases it will be
262    synonymous with the function symbol since most functions aren't
263    overloaded.
264  */
265 template FunctionOverload(
266     alias F,
267     Protection P = __traits(getProtection, F).toProtection,
268     Linkage L = __traits(getLinkage, F).toLinkage,
269     string I = __traits(identifier, F),
270     alias Parent = Alias!(__traits(parent, F)),
271     size_t Idx = 0,
272 )
273 {
274     import mirror.meta.traits: Parameters;
275     import std.traits: RT = ReturnType;
276 
277     alias symbol = F;
278     alias protection = P;
279     alias linkage = L;
280     enum identifier = I;
281     alias parent = Parent;
282     enum index = Idx;
283 
284     alias ReturnType = RT!symbol;
285     alias parameters = Parameters!F;
286 
287     string toString() @safe pure {
288         import std.conv: text;
289         import std.traits: fullyQualifiedName;
290         return text(`Function(`, fullyQualifiedName!symbol, ", ", protection, ", ", linkage, ")");
291     }
292 }