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 }