1 /**
2    This module provides the CTFE variant of compile-time reflection,
3    allowing client code to use regular D functions (as opposed to
4    template metaprogramming) to operate on the contents of a D module
5    using string mixins.
6  */
7 module mirror.ctfe.reflection;
8 
9 
10 /**
11    Returns compile-time reflection information about a D module.
12  */
13 Module module_(string moduleName)() {
14     import mirror.meta.reflection: ModuleTemplate = Module;
15     import mirror.meta.traits: Fields;
16     import std.meta: staticMap;
17     import std.traits: fullyQualifiedName;
18 
19     Module ret;
20     ret.identifier = moduleName;
21 
22     alias module_ = ModuleTemplate!moduleName;
23 
24     template toKind(T) {
25         import mirror.meta.traits: FundamentalType;
26         alias U = FundamentalType!T;
27         static if(is(U == enum))
28             enum toKind = Aggregate.Kind.enum_;
29         else static if(is(U == struct))
30             enum toKind = Aggregate.Kind.struct_;
31         else static if(is(U == class))
32             enum toKind = Aggregate.Kind.class_;
33         else static if(is(U == interface))
34             enum toKind = Aggregate.Kind.interface_;
35         else static if(is(U == union))
36             enum toKind = Aggregate.Kind.union_;
37         else
38             static assert(false, "Unknown kind " ~ fullyQualifiedName!T);
39     }
40 
41     enum toVariable(alias V) = Variable(fullyQualifiedName!(V.Type), V.identifier);
42     ret.variables = [ staticMap!(toVariable, module_.Variables) ];
43 
44     enum toAggregate(T) = Aggregate(
45         fullyQualifiedName!T,
46         toKind!T,
47         [ staticMap!(toVariable, Fields!T)],
48     );
49 
50     ret.aggregates    = [ staticMap!(toAggregate, module_.Aggregates)    ];
51     ret.allAggregates = [ staticMap!(toAggregate, module_.AllAggregates) ];
52 
53 
54     template toFunction(alias F) {
55         import mirror.meta.traits: Parameters;
56         import std.traits: ReturnType;
57 
58         template toDefault(alias Default) {
59             static if(is(Default == void))
60                 enum toDefault = "";
61             else
62                 enum toDefault = Default.stringof;
63         }
64 
65         enum toParameter(alias P) = Parameter(
66             type!(P.Type),
67             P.identifier,
68             toDefault!(P.Default),
69             P.storageClass
70         );
71 
72         enum toFunction = Function(
73             moduleName,
74             F.index,
75             F.identifier,
76             type!(ReturnType!(F.symbol)),
77             [ staticMap!(toParameter, Parameters!(F.symbol)) ],
78         );
79     }
80 
81     ret.functionsByOverload = [ staticMap!(toFunction, module_.FunctionsByOverload) ];
82 
83     template withIndex(A...) {
84         import std.range: iota;
85         import std.meta: aliasSeqOf;
86 
87         template overload(alias F, size_t I) {
88             alias symbol = F.symbol;
89             enum identifier = F.identifier;
90             enum index = I;
91         }
92 
93         alias toOverload(size_t I) = overload!(A[I], I);
94 
95         alias withIndex = staticMap!(toOverload, aliasSeqOf!(A.length.iota));
96     }
97 
98     template toOverloaded(alias F) {
99         enum toOverloaded = OverloadSet(
100             F.identifier,
101             [ staticMap!(toFunction, withIndex!(F.overloads)) ]
102         );
103     }
104 
105     ret.functionsBySymbol = [ staticMap!(toOverloaded, module_.FunctionsBySymbol) ];
106 
107     return ret;
108 }
109 
110 
111 /**
112    A D module.
113  */
114 struct Module {
115     string identifier;
116     Aggregate[] aggregates;
117     Aggregate[] allAggregates;  /// includes all function return types
118     Variable[] variables;
119     Function[] functionsByOverload;
120     OverloadSet[] functionsBySymbol;
121 }
122 
123 
124 /**
125    A user-defined type (struct, class, or enum).
126  */
127 struct Aggregate {
128 
129     enum Kind {
130         enum_,
131         struct_,
132         class_,
133         interface_,
134         union_,
135     }
136 
137     string identifier;
138     Kind kind;
139     Variable[] fields;
140     Function[] functions;  // TODO
141     // TODO: attributes
142 }
143 
144 
145 Type type(T)() {
146     import std.traits: fullyQualifiedName;
147     return Type(fullyQualifiedName!T, T.sizeof);
148 }
149 
150 
151 struct Type {
152     string name;
153     size_t size;
154     // UDAs?
155 }
156 
157 
158 /// A variable
159 struct Variable {
160     string type;
161     string identifier;
162     // UDAs?
163 }
164 
165 
166 /// A set of function overloads
167 struct OverloadSet {
168     string identifier;
169     Function[] overloads;
170 
171     invariant { assert(overloads.length > 0); }
172 
173     string importMixin() @safe pure nothrow const scope {
174         return overloads[0].importMixin;
175     }
176 
177     string fullyQualifiedName() @safe pure nothrow const scope {
178         return overloads[0].fullyQualifiedName;
179     }
180 }
181 
182 
183 /// A function. Each of these describes only one overload.
184 struct Function {
185     string moduleName;
186     int overloadIndex;
187     string identifier;
188     Type returnType;
189     Parameter[] parameters;
190     // TODO: @safe, pure, nothrow, etc.
191     // TODO: UDAs
192 
193 
194     string importMixin() @safe pure nothrow const {
195         return `static import ` ~ moduleName ~ `;`;
196     }
197 
198     string callMixin(A...)(auto ref A args) {
199         import std.conv: text;
200         import std.array: join;
201         import std.algorithm: map;
202 
203         string[] argTexts;
204 
205         static foreach(arg; args) {
206             argTexts ~= arg.text;
207         }
208 
209         return text(
210             moduleName, `.`, identifier, `(`,
211             argTexts.map!text.join(`, `),
212             `)`);
213     }
214 
215     string fullyQualifiedName() @safe pure nothrow const {
216         return moduleName ~ "." ~ identifier;
217     }
218 
219     string pointerMixin() @safe pure nothrow const {
220         import std.conv: text;
221         return text(`&__traits(getOverloads, `, moduleName, `, "`, identifier, `")[`, overloadIndex, `]`);
222     }
223 }
224 
225 /**
226    Returns a pointer to the function described
227    by `function_`.
228  */
229 auto pointer(Function function_)() {
230     mixin(`static import `, function_.moduleName, `;`);
231 
232     alias overloads = __traits(
233         getOverloads,
234         mixin(function_.moduleName),
235         function_.identifier
236     );
237 
238     return &overloads[function_.overloadIndex];
239 }
240 
241 
242 /// A function parameter
243 struct Parameter {
244     import std.traits: ParameterStorageClass;
245 
246     Type type;
247     string identifier;
248     string default_;  /// default value, if any
249     ParameterStorageClass storageClass;
250 }