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 }