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 
172 
173 /// A function. Each of these describes only one overload.
174 struct Function {
175     string moduleName;
176     int overloadIndex;
177     string identifier;
178     Type returnType;
179     Parameter[] parameters;
180     // TODO: @safe, pure, nothrow, etc.
181     // TODO: UDAs
182 
183 
184     string importMixin() @safe pure nothrow const {
185         return `static import ` ~ moduleName ~ `;`;
186     }
187 
188     string callMixin(A...)(auto ref A args) {
189         import std.conv: text;
190         import std.array: join;
191         import std.algorithm: map;
192 
193         string[] argTexts;
194 
195         static foreach(arg; args) {
196             argTexts ~= arg.text;
197         }
198 
199         return text(
200             moduleName, `.`, identifier, `(`,
201             argTexts.map!text.join(`, `),
202             `)`);
203     }
204 
205     string fullyQualifiedName() @safe pure nothrow const {
206         return moduleName ~ "." ~ identifier;
207     }
208 
209     string pointerMixin() @safe pure nothrow const {
210         import std.conv: text;
211         return text(`&__traits(getOverloads, `, moduleName, `, "`, identifier, `")[`, overloadIndex, `]`);
212     }
213 }
214 
215 /**
216    Returns a pointer to the function described
217    by `function_`.
218  */
219 auto pointer(Function function_)() {
220     mixin(`static import `, function_.moduleName, `;`);
221 
222     alias overloads = __traits(
223         getOverloads,
224         mixin(function_.moduleName),
225         function_.identifier
226     );
227 
228     return &overloads[function_.overloadIndex];
229 }
230 
231 
232 /// A function parameter
233 struct Parameter {
234     import std.traits: ParameterStorageClass;
235 
236     Type type;
237     string identifier;
238     string default_;  /// default value, if any
239     ParameterStorageClass storageClass;
240 }