├── .gitignore ├── LICENSE ├── README.md ├── dub.sdl └── source └── witchcraft ├── aggregates.d ├── attributes.d ├── classes.d ├── constructors.d ├── fields.d ├── impl ├── attributes.d ├── classes.d ├── constructors.d ├── fields.d ├── inspect.d ├── interfaces.d ├── methods.d ├── modules.d ├── package.d └── structs.d ├── interfaces.d ├── invocable.d ├── members.d ├── methods.d ├── mixins ├── base.d ├── classes.d ├── constructors.d ├── fields.d ├── interfaces.d ├── methods.d ├── package.d └── structs.d ├── modules.d ├── package.d ├── structs.d ├── types.d └── unittests ├── accessible.d ├── base.d ├── inheritance.d ├── noMixin.d └── structs.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | *.lib 7 | *.dll 8 | *.exe 9 | *.selections.json 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mihail K 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Witchcraft - Runtime Reflection is Magic [![DUB](https://img.shields.io/dub/v/witchcraft.svg)](http://code.dlang.org/packages/witchcraft) [![DUB](https://img.shields.io/dub/l/witchcraft.svg)](http://code.dlang.org/packages/witchcraft) 2 | Extensions to runtime reflection in D. 3 | 4 | Witchcraft provides enhanced runtime reflection facilities for D classes. It provides a means to interact with fields, functions, constructors, classes, and user-defined attributes at runtime (features D is sadly lacking). All of this happens through a simple and familiar API inspired by Java's `java.reflect` package. 5 | 6 | ## Mix in some Witchcraft 7 | 8 | Start with a simple class, mix in some Witchcraft, 9 | 10 | ```d 11 | import witchcraft; 12 | 13 | class User 14 | { 15 | mixin Witchcraft; 16 | 17 | string username; 18 | string password; 19 | 20 | this(string username, string password) 21 | { 22 | this.username = username; 23 | this.password = password; 24 | } 25 | } 26 | ``` 27 | 28 | And now you have some amazing new runtime reflection powers to play with. 29 | 30 | ```d 31 | void main() 32 | { 33 | Class c = User.metaof; 34 | // ... or new User().getMetaType; 35 | 36 | // Create a new user object, 37 | User user = cast(User) c.create; 38 | 39 | // Iterate over fields . . . 40 | foreach(field; c.getFields) 41 | { 42 | // Get value on instance 'user' 43 | field.get(user).writeln; 44 | } 45 | } 46 | ``` 47 | 48 | #### Aggressive Mode! 49 | 50 | Witchcraft also comes with an option to enable 'Aggressive Mode'. Normally, only types that have `mixin Witchcraft;` can be reflected on, and any types that these may reference (ie. A method return type) must also have Witchcraft in to support reflection. 51 | 52 | In Aggressive Mode, Witchcraft will generate meta information for _all_ types that are referenced reflectively (and any types that those types may reference, and so on...). However, for any types that don't mixin Witchcraft, only public members will be accessible. You can test if an element is accessible with the `isAccessible` property. 53 | 54 | ### Fields, Methods, and Constructors 55 | 56 | Witchcraft grants runtime access to all fields, methods, and constructors of any type it is mixed into. Fields and methods alike are easily accessible en-masse (returned as an array), or individually given by name. Methods and constructors are both also accessible based on their overloads. 57 | 58 | ```d 59 | // Access to fields and methods. 60 | Field[] fields = c.getFields; 61 | Method[] method = c.getMethods; 62 | 63 | // Access to a field by name. 64 | Field field = c.getField("username"); 65 | 66 | // Access all overloads of a method by name. 67 | Method[] overloads = c.getMethods("updatePassword"); 68 | 69 | // Access the updateEmail(string) method. 70 | Method method = c.getMethod!(string)("updatePassword"); 71 | ``` 72 | 73 | Once you have your fields and methods, you are free to read, write, and call them. They also provide some useful properties like their types, attributes, parameters types, declaring types, etc. This includes useful properties like `isStatic`, to check if a method is static or bound to and instance of a class. 74 | 75 | ```d 76 | // Fetch a variant holding the value of user.email 77 | Variant email = c.getField("email").get(user); 78 | 79 | // Or... Use a template argument to convert the result. 80 | string email = as c.getField("email").get!(string)(user); 81 | 82 | // And now call user.updateEmail(email) 83 | c.getMethod!(string)("updatePassword").invoke(user, email); 84 | 85 | // Or... Use a TypeInfo object instead of template argument. 86 | c.getMethod("updatePassword", typeid(string)).invoke(user, email); 87 | ``` 88 | 89 | #### Templates not Required 90 | 91 | Witchcraft provides templated methods for convenience for every method that might accept or return a `Variant`, or any method that accepts D's `TypeInfo` or Witchcraft's `Type` objects. For example, if you were looking for a constructor that might accept behave like `this(string, string)`, 92 | 93 | ```d 94 | // You'd have the option using templates, 95 | Constructor ctor = c.getConstructor!(string, string); 96 | 97 | // Or you can find it with runtime parameters. 98 | Constructor ctor = c.getConstructor(typeid(string), typeid(string)); 99 | ``` 100 | 101 | Similarly, for returned values, you could do, 102 | 103 | ```d 104 | // With templates, you can get a User object, 105 | User user = ctor.create!(User)("John Smith", "secret"); 106 | 107 | // Without templates, Variants go in, Variants come out. 108 | Variant user = ctor.create(Variant("John Smith"), Variant("secret")); 109 | ``` 110 | 111 | ### Attributes 112 | 113 | Witchcraft can also do some neat magic with User-Defined Attributes. You can inspect and access UDAs on any field, method, constructor, or type that has reflective capabilities. (That is to say, any type that has `mixin Witchcraft;`, or, keep reading for 'Aggressive Mode'!) Consider, 114 | 115 | ```d 116 | struct Column 117 | { 118 | string name; 119 | } 120 | 121 | struct User 122 | { 123 | mixin Witchcraft; 124 | 125 | @Column 126 | string username; 127 | 128 | @Column("pass_word") 129 | string password; 130 | } 131 | ``` 132 | 133 | Witchcraft breaks up attributes into 2 types; Type attributes and Expression attributes. Expression attributes are any attributes that can produce a value at runtime, whereas Type attributes are attributes that cannot. Essentially, 134 | 135 | ```d 136 | Struct s = User.metaof; 137 | 138 | // @Column on username cannot produce a value; it's a Type attribute. 139 | Attribute uAttr = s.getField("username").getAttributes!(Column)[0]; 140 | assert(uAttr.isType == true); 141 | 142 | // @Column("pass_word") produces a value at runtime; it's an Expression attribute. 143 | Attribute pAttr = s.getField("password").getAttributes!(Column)[0]; 144 | assert(pAttr.isExpression == true); 145 | ``` 146 | 147 | `getAttributes` may also be called without a parameter to get all attributes on an element, or it can be given either a D `TypeInfo` or Witchcraft `Type` object in place of a template argument. 148 | 149 | ## Caveats 150 | 151 | - Every class that needs reflective capabilities must mixin `Witchcraft`. This includes children of classes that already do! 152 | - Witchcraft reflection completely ignores protection attributes. Private fields and functions are accessible from everywhere through reflection. (although this may be a benefit in some cases!) 153 | - Aggressive Mode is very aggressive! Compile times can take quite a while since meta information will be constructed for every reachable type through any type that is referenced reflectively. (Fields, return types, parameter types, attributes, etc.) 154 | 155 | ## License 156 | 157 | MIT 158 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "witchcraft" 2 | description "Extensions to runtime reflection in D." 3 | copyright "Copyright © 2016, Mihail K" 4 | authors "Mihail-K" 5 | targetType "library" 6 | license "MIT" 7 | 8 | configuration "library" { 9 | } 10 | 11 | configuration "aggressive" { 12 | versions "aggressive" 13 | } 14 | -------------------------------------------------------------------------------- /source/witchcraft/aggregates.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.aggregates; 3 | 4 | import witchcraft; 5 | 6 | import std.algorithm; 7 | import std.array; 8 | import std.range; 9 | 10 | abstract class Aggregate : Type 11 | { 12 | /++ 13 | + Looks up and returns a constructor with a parameter list that exactly 14 | + matches the given array of types. 15 | + 16 | + Params: 17 | + parameterTypes = A parameter list the constructor must exactly match. 18 | + 19 | + Returns: 20 | + The constructor object, or null if no such constructor exists. 21 | ++/ 22 | final const(Constructor) getConstructor(Type[] parameterTypes...) const 23 | { 24 | // Iterate up the inheritance tree. 25 | return getConstructors.retro 26 | .filter!(c => c.getParameterTypes == parameterTypes) 27 | .takeOne 28 | .chain(null.only) 29 | .front; 30 | } 31 | 32 | /++ 33 | + Looks up and returns a constructor with a parameter list that exactly 34 | + matches the given array of types. 35 | + 36 | + Params: 37 | + parameterTypeInfos = A parameter list the constructor must exactly match. 38 | + 39 | + Returns: 40 | + The constructor object, or null if no such constructor exists. 41 | ++/ 42 | final const(Constructor) getConstructor(TypeInfo[] parameterTypeInfos) const 43 | { 44 | // Iterate up the inheritance tree. 45 | return getConstructors.retro 46 | .filter!(c => c.getParameterTypeInfos == parameterTypeInfos) 47 | .takeOne 48 | .chain(null.only) 49 | .front; 50 | } 51 | 52 | /++ 53 | + Ditto, but accepts types given by variadic template arguments. 54 | + 55 | + Params: 56 | + TList = A list of types the constructor must exactly match. 57 | + 58 | + Returns: 59 | + The constructor object, or null if no such constructor exists. 60 | ++/ 61 | final const(Constructor) getConstructor(TList...)() const 62 | { 63 | auto types = new TypeInfo[TList.length]; 64 | 65 | foreach(index, type; TList) 66 | { 67 | types[index] = typeid(type); 68 | } 69 | 70 | return this.getConstructor(types); 71 | } 72 | 73 | /++ 74 | + Returns an array of all constructors defined by this type. 75 | + This does not include the default constructor. 76 | + 77 | + If a type declares no constructors, this method will return an empty 78 | + array. 79 | + 80 | + Returns: 81 | + And array of all constructors on the aggregate type. 82 | ++/ 83 | abstract const(Constructor)[] getConstructors() const; 84 | 85 | @property 86 | final override bool isAggregate() const 87 | { 88 | return true; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /source/witchcraft/attributes.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.attributes; 3 | 4 | import witchcraft; 5 | 6 | import std.conv; 7 | import std.variant; 8 | 9 | /++ 10 | + Represents a single attribute attached to some element. 11 | ++/ 12 | abstract class Attribute 13 | { 14 | /++ 15 | + Reads the runtime value of the attribute if it caqn produce one. The 16 | + result is returned as a `Variant` object. 17 | + 18 | + Returns: 19 | + A `Variant` that constains the runtime value of the attribute. 20 | ++/ 21 | @property 22 | abstract Variant get() const; 23 | 24 | /++ 25 | + Ditto, but also accepts a template parameter which the result is 26 | + converted to (if the conversion is possible). 27 | + 28 | + Params: 29 | + T = The conversion type. 30 | + 31 | + Returns: 32 | + The converted attribute value. 33 | ++/ 34 | @property 35 | T get(T)() const 36 | { 37 | return this.get.get!T; 38 | } 39 | 40 | @property 41 | abstract const(Type) getAttributeType() const; 42 | 43 | @property 44 | abstract const(TypeInfo) getAttributeTypeInfo() const; 45 | 46 | /++ 47 | + Checks if this attribute is expression. Any attribute that can produce 48 | + a value at runtime is considered an expression attribute. 49 | + 50 | + Returns: 51 | + `true` if the attribute produces a runtime value. 52 | + 53 | + See_Also: 54 | + isType 55 | ++/ 56 | @property 57 | abstract bool isExpression() const; 58 | 59 | /++ 60 | + Checks if this attribute is a type (or some other non-expression). 61 | + 62 | + Returns: 63 | + `true` if the attribute does not produce a runtime value. 64 | ++/ 65 | @property 66 | abstract bool isType() const; 67 | 68 | override string toString() const 69 | { 70 | if(isExpression) 71 | { 72 | return "@(" ~ get.text ~ ")"; 73 | } 74 | else 75 | { 76 | return "@(" ~ getAttributeType.text ~ ")"; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /source/witchcraft/classes.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.classes; 3 | 4 | import witchcraft; 5 | 6 | import std.algorithm; 7 | import std.array; 8 | import std.range; 9 | import std.traits; 10 | 11 | /++ 12 | + Represents and grants access to a class's attributes, fields, methods, 13 | + and constructors. 14 | ++/ 15 | abstract class Class : Aggregate 16 | { 17 | /++ 18 | + Creates a new instance of the class, provided it has a default or 19 | + zero-argument constructor. 20 | + 21 | + Returns: 22 | + A new instance of the class. 23 | ++/ 24 | @property 25 | abstract Object create() const; 26 | 27 | /++ 28 | + Ditto, but also casts the result to a type given by template parameter. 29 | + 30 | + Params: 31 | + T = An Object type to which the result is cast. 32 | + 33 | + Returns: 34 | + A new instance of the class. 35 | ++/ 36 | @property 37 | T create(T : Object)() const 38 | { 39 | return cast(T) this.create; 40 | } 41 | 42 | /++ 43 | + Returns an array of all constructors defined by this class. 44 | + This does not include any constructors inherited from a base class, 45 | + nor the default constructor. 46 | + 47 | + If a class declares no constructors, this method will return an empty 48 | + array. To construct an Object of such a type, the `create()` method 49 | + defined by `Class` should be used. 50 | + 51 | + Returns: 52 | + And array of all constructors on the class. 53 | + 54 | + See_Also: 55 | + create 56 | ++/ 57 | abstract override const(Constructor)[] getConstructors() const; 58 | 59 | /++ 60 | + Looks up a field by name, searching this class, and each of its super 61 | + classes in turn. 62 | + 63 | + Params: 64 | + name = The name of the field. 65 | + 66 | + Returns: 67 | + The field object, or null if no such field exists. 68 | ++/ 69 | override const(Field) getField(string name) const 70 | { 71 | auto field = getLocalField(name); 72 | 73 | if(field !is null) 74 | { 75 | return field; 76 | } 77 | else if(getSuperClass !is null) 78 | { 79 | return getSuperClass.getField(name); 80 | } 81 | else 82 | { 83 | return null; 84 | } 85 | } 86 | 87 | /++ 88 | + Returns an array of all fields defined by this class and classes that 89 | + it inherits from. This only extends to super classes that also use 90 | + `Witchcraft`. 91 | + 92 | + Returns: 93 | + An array of all known field objects belonging to this class. 94 | ++/ 95 | override const(Field)[] getFields() const 96 | { 97 | if(getSuperClass !is null) 98 | { 99 | return getSuperClass.getFields ~ getLocalFields; 100 | } 101 | else 102 | { 103 | return getLocalFields; 104 | } 105 | } 106 | 107 | /++ 108 | + Returns an array of all interfaces that are declared directly on this 109 | + class. Interfaces that appear multiple times on the class are only 110 | + present once in the array. 111 | + 112 | + Returns: 113 | + An array of interfaces on this class. 114 | ++/ 115 | abstract const(InterfaceType)[] getInterfaces() const; 116 | 117 | const(Field) getLocalField(string name) const 118 | { 119 | return super.getField(name); 120 | } 121 | 122 | string[] getLocalFieldNames() const 123 | { 124 | return getLocalFields 125 | .map!"a.getName" 126 | .array; 127 | } 128 | 129 | const(Field)[] getLocalFields() const 130 | { 131 | return super.getFields; 132 | } 133 | 134 | const(Method) getLocalMethod(string name, TypeInfo[] parameterTypes...) const 135 | { 136 | return getLocalMethods(name) 137 | .filter!(m => m.getParameterTypes == parameterTypes) 138 | .takeOne 139 | .chain(null.only) 140 | .front; 141 | } 142 | 143 | const(Method) getLocalMethod(TList...)(string name) const 144 | { 145 | auto types = new TypeInfo[TList.length]; 146 | 147 | foreach(index, type; TList) 148 | { 149 | types[index] = typeid(type); 150 | } 151 | 152 | return this.getLocalMethod(name, types); 153 | } 154 | 155 | string[] getLocalMethodNames() const 156 | { 157 | return getLocalMethods 158 | .map!"a.getName" 159 | .array; 160 | } 161 | 162 | const(Method)[] getLocalMethods() const 163 | { 164 | return super.getMethods; 165 | } 166 | 167 | const(Method)[] getLocalMethods(string name) const 168 | { 169 | return super.getMethods(name); 170 | } 171 | 172 | /++ 173 | + Returns all methods declared on this class, and non-private methods in 174 | + classes that it inherits from. This is restricted to classes for which 175 | + reflective information is present. 176 | + 177 | + Methods that are declared by a base class and are overriden by another 178 | + class appear in the array for every class that delcares them. 179 | + 180 | + Returns: 181 | + All methods that are available to this class. 182 | + 183 | + See_Also: 184 | + getDeclaringType 185 | ++/ 186 | override const(Method)[] getMethods() const 187 | { 188 | if(getSuperClass !is null) 189 | { 190 | return getSuperClass.getMethods 191 | .filter!`a.getProtection != "private"` 192 | .chain(getLocalMethods) 193 | .array; 194 | } 195 | else 196 | { 197 | return getLocalMethods; 198 | } 199 | } 200 | 201 | /++ 202 | + Ditto, but returns only methods that match the given name. 203 | + 204 | + Params: 205 | + name = The name of the method to look for. 206 | + 207 | + Returns: 208 | + All methods that match the given name. 209 | ++/ 210 | override const(Method)[] getMethods(string name) const 211 | { 212 | if(getSuperClass !is null) 213 | { 214 | return getSuperClass.getMethods(name) 215 | .filter!`a.getProtection != "private"` 216 | .chain(getLocalMethods(name)) 217 | .array; 218 | } 219 | else 220 | { 221 | return getLocalMethods(name); 222 | } 223 | } 224 | 225 | /++ 226 | + Returns the parent of this class. If the class doesn't declare a super 227 | + class, the super class is `Object`, unless the class itself is `Object`. 228 | + For `Object`, this method always returns `null`. 229 | + 230 | + Returns: 231 | + This class's super class. 232 | ++/ 233 | abstract const(Class) getSuperClass() const; 234 | 235 | /++ 236 | + Ditto, but a `TypeInfo` object is returned instead. 237 | + 238 | + Returns: 239 | + This class's super class. 240 | ++/ 241 | abstract const(TypeInfo) getSuperTypeInfo() const; 242 | 243 | /++ 244 | + Checks if this class is abstract. 245 | + 246 | + Returns: 247 | + `true` if the class is abstract. 248 | ++/ 249 | @property 250 | abstract bool isAbstract() const; 251 | 252 | /++ 253 | + Checks if this type is a class. For children of `Class`, this always 254 | + returns `true`. 255 | + 256 | + Returns: 257 | + `true` if the type is a class. 258 | ++/ 259 | @property 260 | final override bool isClass() const 261 | { 262 | return true; 263 | } 264 | 265 | /++ 266 | + Checks if this class is final. 267 | + 268 | + Returns: 269 | + `true` if the type is final. 270 | ++/ 271 | @property 272 | abstract bool isFinal() const; 273 | 274 | abstract bool isSubClassOf(const Class other) const; 275 | 276 | abstract bool isSuperClassOf(const Class other) const; 277 | } 278 | -------------------------------------------------------------------------------- /source/witchcraft/constructors.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.constructors; 3 | 4 | import witchcraft; 5 | 6 | import std.string; 7 | import std.variant; 8 | 9 | /++ 10 | + Represents and grants access to a single constructor defined in some 11 | + aggregate type. 12 | ++/ 13 | abstract class Constructor : Invocable 14 | { 15 | /++ 16 | + Invokes the constructor, passing in arguments as variant types. 17 | + The result is returned as a `Variant` type. 18 | + 19 | + Returns: 20 | + The result of the construct call, as a `Variant`. 21 | ++/ 22 | final Variant create(Variant[] arguments...) const 23 | { 24 | return this.invoke(null, arguments); 25 | } 26 | 27 | /++ 28 | + Ditto, but accepts arguments of any type, and permits the result to also 29 | + be cast to a type specified by template argument. 30 | + 31 | + Params: 32 | + T = The conversion type. If Variant, no conversion is performed. 33 | + 34 | + Returns: 35 | + The result of the construct call, as the templated type. 36 | ++/ 37 | final T create(T, TList...)(TList arguments) const 38 | { 39 | auto result = this.invoke(null, arguments); 40 | 41 | static if(is(T == Variant)) 42 | { 43 | return result; 44 | } 45 | else 46 | { 47 | return result.get!T; 48 | } 49 | } 50 | 51 | /++ 52 | + Returns `__ctor`, which is the name of all constructors. 53 | + 54 | + Returns: 55 | + The name of this constructor. 56 | ++/ 57 | final override string getName() const 58 | { 59 | return "__ctor"; 60 | } 61 | 62 | override string toString() const 63 | { 64 | return "%s(%(%s, %))".format(getName, getParameterTypeInfos); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /source/witchcraft/fields.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.fields; 3 | 4 | import witchcraft; 5 | 6 | import std.algorithm; 7 | import std.array; 8 | import std.variant; 9 | 10 | /++ 11 | + Represents and grants access to a single field defined on a type. 12 | ++/ 13 | abstract class Field : Member 14 | { 15 | abstract Variant get(Variant instance) const; 16 | 17 | T get(T = Variant, O)(O instance) const 18 | { 19 | auto result = this.get(Variant(instance)); 20 | 21 | static if(is(T == Variant)) 22 | { 23 | return result; 24 | } 25 | else 26 | { 27 | return result.get!T; 28 | } 29 | } 30 | 31 | @property 32 | abstract const(Type) getValueType() const; 33 | 34 | @property 35 | abstract const(TypeInfo) getValueTypeInfo() const; 36 | 37 | /++ 38 | + Checks if the field is declared as static. This returns true if the 39 | + is directly accessible on the type that is declared on (rather that on 40 | + an instance of that type). 41 | + 42 | + Returns: 43 | + `true` if this field is static. 44 | ++/ 45 | @property 46 | abstract bool isStatic() const; 47 | 48 | /++ 49 | + Checks if the field can be written to. 50 | + 51 | + Returns: 52 | + `true` if the value of the field can be set. 53 | ++/ 54 | @property 55 | abstract bool isWritable() const; 56 | 57 | abstract void set(Variant instance, Variant value) const; 58 | 59 | void set(T, O)(O instance, T value) const 60 | { 61 | this.set(Variant(instance), Variant(value)); 62 | } 63 | 64 | override string toString() const 65 | { 66 | return getValueTypeInfo.toString ~ " " ~ getName; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /source/witchcraft/impl/attributes.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.attributes; 3 | 4 | import witchcraft; 5 | 6 | import std.variant; 7 | 8 | class AttributeImpl(alias attribute) : Attribute 9 | { 10 | override Variant get() const 11 | { 12 | static if(is(typeof(attribute))) 13 | { 14 | return Variant(attribute); 15 | } 16 | else 17 | { 18 | return Variant(null); 19 | } 20 | } 21 | 22 | override const(Type) getAttributeType() const 23 | { 24 | static if(is(typeof(attribute))) 25 | { 26 | static if(__traits(compiles, typeof(attribute).metaof)) 27 | { 28 | return typeof(attribute).metaof; 29 | } 30 | else 31 | { 32 | return null; 33 | } 34 | } 35 | else 36 | { 37 | static if(__traits(compiles, attribute.metaof)) 38 | { 39 | return attribute.metaof; 40 | } 41 | else 42 | { 43 | return null; 44 | } 45 | } 46 | } 47 | 48 | override const(TypeInfo) getAttributeTypeInfo() const 49 | { 50 | static if(is(typeof(attribute))) 51 | { 52 | return typeid(typeof(attribute)); 53 | } 54 | else 55 | { 56 | return typeid(attribute); 57 | } 58 | } 59 | 60 | override bool isExpression() const 61 | { 62 | return is(typeof(attribute)); 63 | } 64 | 65 | override bool isType() const 66 | { 67 | return !isExpression; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /source/witchcraft/impl/classes.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.classes; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | template ClassImpl(T) 9 | if(is(T == class)) 10 | { 11 | // TODO : Determine this more reliably. 12 | static if(__traits(getProtection, T) == "public") 13 | { 14 | // In this context, use field-impl. 15 | template FieldMixin(T, string name) 16 | { 17 | alias FieldMixin = FieldImpl!(T, name); 18 | } 19 | 20 | // In this context, use method-impl. 21 | template MethodMixin(T, string name, size_t overload) 22 | { 23 | alias MethodMixin = MethodImpl!(T, name, overload); 24 | } 25 | 26 | // In this context, use constructor-impl. 27 | template ConstructorMixin(T, size_t overload) 28 | { 29 | alias ConstructorMixin = ConstructorImpl!(T, overload); 30 | } 31 | 32 | mixin WitchcraftClass; 33 | 34 | alias ClassImpl = ClassMixin!T; 35 | } 36 | else 37 | { 38 | class ClassImpl : Class 39 | { 40 | @property 41 | override Object create() const 42 | { 43 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 44 | } 45 | 46 | const(Attribute)[] getAttributes() const 47 | { 48 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 49 | } 50 | 51 | override const(Constructor)[] getConstructors() const 52 | { 53 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 54 | } 55 | 56 | const(Type) getDeclaringType() const 57 | { 58 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 59 | } 60 | 61 | const(TypeInfo) getDeclaringTypeInfo() const 62 | { 63 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 64 | } 65 | 66 | override const(InterfaceType)[] getInterfaces() const 67 | { 68 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 69 | } 70 | 71 | string getFullName() const 72 | { 73 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 74 | } 75 | 76 | string getName() const 77 | { 78 | return T.stringof; 79 | } 80 | 81 | string getProtection() const 82 | { 83 | return __traits(getProtection, T); 84 | } 85 | 86 | override const(Class) getSuperClass() const 87 | { 88 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 89 | } 90 | 91 | override const(TypeInfo) getSuperTypeInfo() const 92 | { 93 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 94 | } 95 | 96 | override const(TypeInfo) getTypeInfo() const 97 | { 98 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 99 | } 100 | 101 | @property 102 | override bool isAbstract() const 103 | { 104 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 105 | } 106 | 107 | @property 108 | override bool isFinal() const 109 | { 110 | assert(0, "Class " ~ T.stringof ~ " is inaccessible."); 111 | } 112 | 113 | @property 114 | final bool isAccessible() const 115 | { 116 | return false; 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /source/witchcraft/impl/constructors.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.constructors; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | import std.variant; 9 | 10 | template ConstructorImpl(alias T, size_t overload) 11 | { 12 | // TODO : Determine this more reliably. 13 | static if(__traits(getProtection, __traits(getOverloads, T, "__ctor")[overload]) == "public") 14 | { 15 | mixin WitchcraftConstructor; 16 | 17 | alias ConstructorImpl = ConstructorMixin!(T, overload); 18 | } 19 | else 20 | { 21 | class ConstructorImpl : Constructor 22 | { 23 | const(Attribute)[] getAttributes() const 24 | { 25 | assert(0, "Constructor is not accessible."); 26 | } 27 | 28 | const(Type) getDeclaringType() const 29 | { 30 | assert(0, "Constructor is not accessible."); 31 | } 32 | 33 | const(TypeInfo) getDeclaringTypeInfo() const 34 | { 35 | assert(0, "Constructor is not accessible."); 36 | } 37 | 38 | string getFullName() const 39 | { 40 | assert(0, "Constructor is not accessible."); 41 | } 42 | 43 | const(Type)[] getParameterTypes() const 44 | { 45 | assert(0, "Constructor is not accessible."); 46 | } 47 | 48 | const(TypeInfo)[] getParameterTypeInfos() const 49 | { 50 | assert(0, "Constructor is not accessible."); 51 | } 52 | 53 | string getProtection() const 54 | { 55 | return __traits(getProtection, __traits(getOverloads, T, "__ctor")[overload]); 56 | } 57 | 58 | const(Type) getReturnType() const 59 | { 60 | assert(0, "Constructor is not accessible."); 61 | } 62 | 63 | @property 64 | const(TypeInfo) getReturnTypeInfo() const 65 | { 66 | assert(0, "Constructor is not accessible."); 67 | } 68 | 69 | Variant invoke(Variant instance, Variant[] arguments...) const 70 | { 71 | assert(0, "Constructor is not accessible."); 72 | } 73 | 74 | @property 75 | final bool isAccessible() const 76 | { 77 | return false; 78 | } 79 | 80 | @property 81 | bool isVarArgs() const 82 | { 83 | assert(0, "Constructor is not accessible."); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /source/witchcraft/impl/fields.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.fields; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | import std.variant; 9 | 10 | template FieldImpl(alias T, string name) 11 | { 12 | // TODO : Determine this more reliably. 13 | static if(__traits(getProtection, __traits(getMember, T, name)) == "public") 14 | { 15 | mixin WitchcraftField; 16 | 17 | alias FieldImpl = FieldMixin!(T, name); 18 | } 19 | else 20 | { 21 | class FieldImpl : Field 22 | { 23 | override Variant get(Variant instance) const 24 | { 25 | assert(0, "Field " ~ name ~ " is inaccessible."); 26 | } 27 | 28 | const(Attribute)[] getAttributes() const 29 | { 30 | assert(0, "Field " ~ name ~ " is inaccessible."); 31 | } 32 | 33 | const(Type) getDeclaringType() const 34 | { 35 | assert(0, "Field " ~ name ~ " is inaccessible."); 36 | } 37 | 38 | const(TypeInfo) getDeclaringTypeInfo() const 39 | { 40 | assert(0, "Field " ~ name ~ " is inaccessible."); 41 | } 42 | 43 | string getFullName() const 44 | { 45 | assert(0, "Field " ~ name ~ " is inaccessible."); 46 | } 47 | 48 | @property 49 | string getName() const 50 | { 51 | return name; 52 | } 53 | 54 | string getProtection() const 55 | { 56 | return __traits(getProtection, __traits(getMember, T, name)); 57 | } 58 | 59 | override const(Type) getValueType() const 60 | { 61 | assert(0, "Field " ~ name ~ " is inaccessible."); 62 | } 63 | 64 | override const(TypeInfo) getValueTypeInfo() const 65 | { 66 | assert(0, "Field " ~ name ~ " is inaccessible."); 67 | } 68 | 69 | @property 70 | override bool isStatic() const 71 | { 72 | assert(0, "Field " ~ name ~ " is inaccessible."); 73 | } 74 | 75 | @property 76 | bool isAccessible() const 77 | { 78 | return __traits(hasMember, typeof(this), "member"); 79 | } 80 | 81 | @property 82 | override bool isWritable() const 83 | { 84 | return __traits(compiles, { 85 | T instance = void; 86 | typeof(member) value = void; 87 | 88 | __traits(getMember, instance, name) = value; 89 | }); 90 | } 91 | 92 | override void set(Variant instance, Variant value) const 93 | { 94 | assert(0, "Field " ~ name ~ " is inaccessible."); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /source/witchcraft/impl/inspect.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.inspect; 3 | 4 | import witchcraft; 5 | 6 | import std.meta; 7 | 8 | /++ 9 | + Inspects a type given by template argument using the `metaof` property if 10 | + the type defines one. Otherwise, a `null` is returned. 11 | + 12 | + If aggressive version of `Witchcraft` is used, this method behaves 13 | + differently and returns a generic aggressive meta type. 14 | + 15 | + Returns: 16 | + The meta type of the type given by a parameter. 17 | ++/ 18 | @property 19 | Type inspect(TList...)() 20 | if(TList.length == 1) 21 | { 22 | alias T = TList[0]; 23 | 24 | static if(__traits(hasMember, T, "metaof")) 25 | { 26 | return T.metaof; 27 | } 28 | else 29 | { 30 | version(aggressive) 31 | { 32 | static if(is(T == class)) 33 | { 34 | return new ClassImpl!T; 35 | } 36 | else static if(is(T == struct)) 37 | { 38 | return new StructImpl!T; 39 | } 40 | else static if(is(T == interface)) 41 | { 42 | return new InterfaceTypeImpl!T; 43 | } 44 | else static if(!is(T)) 45 | { 46 | return new ModuleImpl!T; 47 | } 48 | else 49 | { 50 | return null; 51 | } 52 | } 53 | else 54 | { 55 | return null; 56 | } 57 | } 58 | } 59 | 60 | /++ 61 | + Inspects a value, using the `getMetaType` method if present. Otherwise, 62 | + the value's type is inspected by the generic `inspect` function type. 63 | + 64 | + Params: 65 | + value = The parameter value to be inspected. 66 | + 67 | + Returns: 68 | + The meta type of the value given by a parameter. 69 | + 70 | + See_Also: 71 | + inspect() 72 | ++/ 73 | @property 74 | Type inspect(T)(T value) 75 | { 76 | static if(__traits(hasMember, T, "getMetaType")) 77 | { 78 | return value ? value.getMetaType : null; 79 | } 80 | else 81 | { 82 | return inspect!T; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /source/witchcraft/impl/interfaces.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.interfaces; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | template InterfaceTypeImpl(T) 9 | { 10 | // TODO : Determine this more reliably. 11 | static if(__traits(getProtection, T) == "public") 12 | { 13 | // In this context, use field-impl. 14 | template FieldMixin(T, string name) 15 | { 16 | alias FieldMixin = FieldImpl!(T, name); 17 | } 18 | 19 | // In this context, use method-impl. 20 | template MethodMixin(T, string name, size_t overload) 21 | { 22 | alias MethodMixin = MethodImpl!(T, name, overload); 23 | } 24 | 25 | mixin WitchcraftInterface; 26 | 27 | alias InterfaceTypeImpl = InterfaceTypeMixin!T; 28 | } 29 | else 30 | { 31 | class InterfaceTypeImpl : InterfaceType 32 | { 33 | const(Attribute)[] getAttributes() const 34 | { 35 | assert(0, "Interface " ~ T.stringof ~ " is inaccessible."); 36 | } 37 | 38 | const(Type) getDeclaringType() const 39 | { 40 | assert(0, "Interface " ~ T.stringof ~ " is inaccessible."); 41 | } 42 | 43 | const(TypeInfo) getDeclaringTypeInfo() const 44 | { 45 | assert(0, "Interface " ~ T.stringof ~ " is inaccessible."); 46 | } 47 | 48 | string getFullName() const 49 | { 50 | assert(0, "Interface " ~ T.stringof ~ " is inaccessible."); 51 | } 52 | 53 | string getName() const 54 | { 55 | return T.stringof; 56 | } 57 | 58 | string getProtection() const 59 | { 60 | return __traits(getProtection, T); 61 | } 62 | 63 | override const(TypeInfo) getTypeInfo() const 64 | { 65 | assert(0, "Interface " ~ T.stringof ~ " is inaccessible."); 66 | } 67 | 68 | @property 69 | final bool isAccessible() const 70 | { 71 | return false; 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/witchcraft/impl/methods.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.methods; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | import std.variant; 9 | 10 | template MethodImpl(alias T, string name, size_t overload) 11 | { 12 | // TODO : Determine this more reliably. 13 | static if(__traits(getProtection, __traits(getOverloads, T, name)[overload]) == "public") 14 | { 15 | mixin WitchcraftMethod; 16 | 17 | alias MethodImpl = MethodMixin!(T, name, overload); 18 | } 19 | else 20 | { 21 | class MethodImpl : Method 22 | { 23 | const(Attribute)[] getAttributes() const 24 | { 25 | assert(0, "Method " ~ name ~ " is inaccessible."); 26 | } 27 | 28 | const(Type) getDeclaringType() const 29 | { 30 | assert(0, "Method " ~ name ~ " is inaccessible."); 31 | } 32 | 33 | const(TypeInfo) getDeclaringTypeInfo() const 34 | { 35 | assert(0, "Method " ~ name ~ " is inaccessible."); 36 | } 37 | 38 | string getFullName() const 39 | { 40 | assert(0, "Method " ~ name ~ " is inaccessible."); 41 | } 42 | 43 | string getName() const 44 | { 45 | return name; 46 | } 47 | 48 | const(Type)[] getParameterTypes() const 49 | { 50 | assert(0, "Method " ~ name ~ " is inaccessible."); 51 | } 52 | 53 | const(TypeInfo)[] getParameterTypeInfos() const 54 | { 55 | assert(0, "Method " ~ name ~ " is inaccessible."); 56 | } 57 | 58 | string getProtection() const 59 | { 60 | return __traits(getProtection, __traits(getOverloads, T, name)[overload]); 61 | } 62 | 63 | const(Type) getReturnType() const 64 | { 65 | assert(0, "Method " ~ name ~ " is inaccessible."); 66 | } 67 | 68 | @property 69 | const(TypeInfo) getReturnTypeInfo() const 70 | { 71 | assert(0, "Method " ~ name ~ " is inaccessible."); 72 | } 73 | 74 | Variant invoke(Variant instance, Variant[] arguments...) const 75 | { 76 | assert(0, "Method " ~ name ~ " is inaccessible."); 77 | } 78 | 79 | @property 80 | final bool isAccessible() const 81 | { 82 | return false; 83 | } 84 | 85 | @property 86 | override bool isFinal() const 87 | { 88 | assert(0, "Method " ~ name ~ " is inaccessible."); 89 | } 90 | 91 | @property 92 | override bool isOverride() const 93 | { 94 | assert(0, "Method " ~ name ~ " is inaccessible."); 95 | } 96 | 97 | @property 98 | override bool isStatic() const 99 | { 100 | assert(0, "Method " ~ name ~ " is inaccessible."); 101 | } 102 | 103 | @property 104 | bool isVarArgs() const 105 | { 106 | assert(0, "Method " ~ name ~ " is inaccessible."); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /source/witchcraft/impl/modules.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.modules; 3 | 4 | import witchcraft; 5 | 6 | import std.traits; 7 | 8 | class ModuleImpl(alias M) : Module 9 | { 10 | mixin WitchcraftField; 11 | mixin WitchcraftMethod; 12 | 13 | this() 14 | { 15 | foreach(name; __traits(derivedMembers, M)) 16 | { 17 | // HACK: There's no good way to determine if something is a field. 18 | static if(is(typeof(__traits(getMember, M, name))) && 19 | !is(typeof(__traits(getMember, M, name)) == class) && 20 | !is(typeof(__traits(getMember, M, name)) == enum) && 21 | !is(typeof(__traits(getMember, M, name)) == function) && 22 | !is(typeof(__traits(getMember, M, name)) == struct) && 23 | !is(typeof(__traits(getMember, M, name)) == void) && 24 | !is(typeof(__traits(getMember, M, name)) == union)) 25 | { 26 | _fields[name] = new FieldMixin!(M, name); 27 | } 28 | } 29 | 30 | foreach(name; __traits(derivedMembers, M)) 31 | { 32 | static if(is(typeof(__traits(getMember, M, name)) == function)) 33 | { 34 | // HACK: For some reason, object.isnan causes a linking error on Windows. 35 | static if(fullyQualifiedName!(__traits(getMember, M, name)) != "object.isnan") 36 | { 37 | foreach(index, overload; __traits(getOverloads, M, name)) 38 | { 39 | _methods[name] ~= new MethodMixin!(M, name, index); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | string getFullName() const 47 | { 48 | return fullyQualifiedName!M; 49 | } 50 | 51 | string getName() const 52 | { 53 | return M.stringof; 54 | } 55 | 56 | string getProtection() const 57 | { 58 | return __traits(getProtection, M); 59 | } 60 | 61 | @property 62 | final bool isAccessible() const 63 | { 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /source/witchcraft/impl/package.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl; 3 | 4 | public import witchcraft.impl.attributes; 5 | public import witchcraft.impl.classes; 6 | public import witchcraft.impl.constructors; 7 | public import witchcraft.impl.fields; 8 | public import witchcraft.impl.inspect; 9 | public import witchcraft.impl.interfaces; 10 | public import witchcraft.impl.methods; 11 | public import witchcraft.impl.modules; 12 | public import witchcraft.impl.structs; 13 | -------------------------------------------------------------------------------- /source/witchcraft/impl/structs.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.impl.structs; 3 | 4 | version(aggressive): 5 | 6 | import witchcraft; 7 | 8 | template StructImpl(T) 9 | { 10 | // TODO : Determine this more reliably. 11 | static if(__traits(getProtection, T) == "public") 12 | { 13 | // In this context, use field-impl. 14 | template FieldMixin(T, string name) 15 | { 16 | alias FieldMixin = FieldImpl!(T, name); 17 | } 18 | 19 | // In this context, use method-impl. 20 | template MethodMixin(T, string name, size_t overload) 21 | { 22 | alias MethodMixin = MethodImpl!(T, name, overload); 23 | } 24 | 25 | // In this context, use constructor-impl. 26 | template ConstructorMixin(T, size_t overload) 27 | { 28 | alias ConstructorMixin = ConstructorImpl!(T, overload); 29 | } 30 | 31 | mixin WitchcraftStruct; 32 | 33 | alias StructImpl = StructMixin!T; 34 | } 35 | else 36 | { 37 | class StructImpl : Struct 38 | { 39 | const(Attribute)[] getAttributes() const 40 | { 41 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 42 | } 43 | 44 | override const(Constructor)[] getConstructors() const 45 | { 46 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 47 | } 48 | 49 | const(Type) getDeclaringType() const 50 | { 51 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 52 | } 53 | 54 | const(TypeInfo) getDeclaringTypeInfo() const 55 | { 56 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 57 | } 58 | 59 | string getFullName() const 60 | { 61 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 62 | } 63 | 64 | string getName() const 65 | { 66 | return T.stringof; 67 | } 68 | 69 | string getProtection() const 70 | { 71 | return __traits(getProtection, T); 72 | } 73 | 74 | override const(TypeInfo) getTypeInfo() const 75 | { 76 | assert(0, "Struct " ~ T.stringof ~ " is inaccessible."); 77 | } 78 | 79 | @property 80 | final bool isAccessible() const 81 | { 82 | return false; 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/witchcraft/interfaces.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.interfaces; 3 | 4 | import witchcraft; 5 | 6 | abstract class InterfaceType : Aggregate 7 | { 8 | final override const(Constructor)[] getConstructors() const 9 | { 10 | return [ ]; 11 | } 12 | 13 | /++ 14 | + Checks if this type is an interface. For children of `Interfalse`, this 15 | + always returns `true`. 16 | + 17 | + Returns: 18 | + `true` if the type is an interface. 19 | ++/ 20 | @property 21 | final override bool isInterface() const 22 | { 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/witchcraft/invocable.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.invocable; 3 | 4 | import witchcraft; 5 | 6 | import std.variant; 7 | 8 | interface Invocable : Member 9 | { 10 | /++ 11 | + Returns an array of `Type` objects representing this element's 12 | + parameter types. If a parameter does not have an associated reflective 13 | + type, its value is `null`. 14 | + 15 | + Returns: 16 | + This element's parameter types. 17 | + 18 | + See_Also: 19 | + getParameterTypes 20 | ++/ 21 | const(Type)[] getParameterTypes() const; 22 | 23 | /++ 24 | + Returns an array representing the element's parameter types. 25 | + 26 | + Returns: 27 | + This method's parameter types. 28 | ++/ 29 | const(TypeInfo)[] getParameterTypeInfos() const; 30 | 31 | /++ 32 | + A `Type` object that represent's this element's return type, if type 33 | + type has reflective support. Null is returned otherwise. 34 | + 35 | + Returns: 36 | + This element's return type. 37 | ++/ 38 | @property 39 | const(Type) getReturnType() const; 40 | 41 | /++ 42 | + This element's return `TypeInfo` object. 43 | + 44 | + Returns: 45 | + This element's return type. 46 | ++/ 47 | @property 48 | const(TypeInfo) getReturnTypeInfo() const; 49 | 50 | /++ 51 | + Invokes this element on the given instance of class using arguments given 52 | + as an array of `Variant` values. For static elements, the value of the 53 | + instance object may be null. 54 | + The result is returned as a `Variant`. If the call would return void, 55 | + a null value is returned instead. 56 | + 57 | + Params: 58 | + instance = The object instance to invoke the method as. 59 | + arguments = The arguments to be passed along to the method. 60 | + 61 | + Returns: 62 | + The result of the invocation. 63 | ++/ 64 | Variant invoke(Variant instance, Variant[] arguments...) const; 65 | 66 | /++ 67 | + Ditto, but arguments are taken as variadic arguments. 68 | + A template argument may be specified to covert the result of the call. 69 | + 70 | + Params: 71 | + T = The type to convert the result into. 72 | + instance = The object instance to invoke the method as. 73 | + arguments = The arguments to be passed along to the method. 74 | + 75 | + Returns: 76 | + The result of the invocation. 77 | ++/ 78 | final T invoke(T = Variant, O, TList...)(O instance, TList arguments) const 79 | { 80 | auto values = new Variant[TList.length]; 81 | 82 | foreach(index, argument; arguments) 83 | { 84 | values[index] = Variant(argument); 85 | } 86 | 87 | auto result = this.invoke(Variant(instance), values); 88 | 89 | static if(is(T == Variant)) 90 | { 91 | return result; 92 | } 93 | else 94 | { 95 | return result.get!T; 96 | } 97 | } 98 | 99 | /++ 100 | + Checks if this element accepts variable arguments. 101 | + 102 | + Returns: 103 | + `true` if the element accepts varargs. 104 | ++/ 105 | @property 106 | bool isVarArgs() const; 107 | } 108 | -------------------------------------------------------------------------------- /source/witchcraft/members.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.members; 3 | 4 | import witchcraft; 5 | 6 | import std.algorithm; 7 | import std.array; 8 | 9 | /++ 10 | + Represents a set of behaviours common to reflective elements. 11 | ++/ 12 | interface Member 13 | { 14 | /++ 15 | + Returns an array of attributes that are attached to this element. 16 | + 17 | + Returns: 18 | + An array of attributes. 19 | ++/ 20 | const(Attribute)[] getAttributes() const; 21 | 22 | /++ 23 | + Returns an array of attributes of the given type that are attched to 24 | + this element. If no such attributes exist, an empty array is returned. 25 | + 26 | + Params: 27 | + attributeType = The type of attribute to filter by. 28 | + 29 | + Returns: 30 | + An array of attributes. 31 | ++/ 32 | final const(Attribute)[] getAttributes(Type attributeType) const 33 | { 34 | return getAttributes 35 | .filter!(a => a.getAttributeType == attributeType) 36 | .array; 37 | } 38 | 39 | /++ 40 | + Returns an array of attributes of the given type that are attched to 41 | + this element. If no such attributes exist, an empty array is returned. 42 | + 43 | + Params: 44 | + attributeTypeInfo = The type of attribute to filter by. 45 | + 46 | + Returns: 47 | + An array of attributes. 48 | ++/ 49 | final const(Attribute)[] getAttributes(TypeInfo attributeTypeInfo) const 50 | { 51 | return getAttributes 52 | .filter!(a => a.getAttributeTypeInfo == attributeTypeInfo) 53 | .array; 54 | } 55 | 56 | /++ 57 | + Ditto, but the type is given by template parameter. 58 | + 59 | + Params: 60 | + T = The type of attribute to filter by. 61 | + 62 | + Returns: 63 | + An array of attributes. 64 | ++/ 65 | final const(Attribute)[] getAttributes(T)() const 66 | { 67 | return getAttributes(typeid(T)); 68 | } 69 | 70 | /++ 71 | + Returns the type that encapsulates this one. Null is returned if this 72 | + is the topmost element, or if the outer type lacks reflective meta 73 | + information. 74 | + 75 | + Returns: 76 | + The declaring element's type. 77 | ++/ 78 | const(Type) getDeclaringType() const; 79 | 80 | /++ 81 | + Returns a `TypeInfo` object for the declaring element. 82 | + 83 | + Returns: 84 | + The type of the declaring element. 85 | + 86 | + See_Also: 87 | + getDeclaringType 88 | ++/ 89 | const(TypeInfo) getDeclaringTypeInfo() const; 90 | 91 | /++ 92 | + The name of the element. 93 | + 94 | + Returns: 95 | + The name of this element. 96 | ++/ 97 | string getName() const; 98 | 99 | /++ 100 | + Returns the fully-qualified name of the element, including the package 101 | + and module name, and any types that might enclose it. 102 | + 103 | + Returns: 104 | + The fully-qualified name of this element. 105 | ++/ 106 | string getFullName() const; 107 | 108 | /++ 109 | + Returns a string that represents this element's declared protection. 110 | + 111 | + Returns: 112 | + This element's protection. 113 | ++/ 114 | string getProtection() const; 115 | 116 | @property 117 | bool isAccessible() const; 118 | } 119 | -------------------------------------------------------------------------------- /source/witchcraft/methods.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.methods; 3 | 4 | import witchcraft; 5 | 6 | import std.string; 7 | 8 | /++ 9 | + Represents and grants access to a single method defined on a type. 10 | ++/ 11 | abstract class Method : Invocable 12 | { 13 | /++ 14 | + Checks if this method is declared to be final. 15 | + 16 | + Returns: 17 | + `true` if the method is final. 18 | ++/ 19 | @property 20 | abstract bool isFinal() const; 21 | 22 | /++ 23 | + Checks if this method is declared as an override. 24 | + 25 | + Returns: 26 | + `true` if the method is an override. 27 | ++/ 28 | @property 29 | abstract bool isOverride() const; 30 | 31 | /++ 32 | + Checks if this method is declared to be static. 33 | + 34 | + Returns: 35 | + `true` if the method is static. 36 | ++/ 37 | @property 38 | abstract bool isStatic() const; 39 | 40 | /++ 41 | + Checks if this method has entry in the object's vtable. 42 | + 43 | + Returns: 44 | + `true` if the method is virtual. 45 | + 46 | + See_Also: 47 | + isFinal, isStatic 48 | ++/ 49 | @property 50 | bool isVirtual() const 51 | { 52 | return !isFinal && !isStatic; 53 | } 54 | 55 | override string toString() const 56 | { 57 | return "%s %s(%(%s, %))".format( 58 | getReturnTypeInfo, getName, getParameterTypeInfos 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/base.d: -------------------------------------------------------------------------------- 1 | module witchcraft.mixins.base; 2 | 3 | import std.meta; 4 | 5 | template TypeOfMeta(T) 6 | { 7 | import witchcraft; 8 | 9 | static if(is(T == class)) 10 | { 11 | alias TypeOfMeta = Class; 12 | } 13 | else static if(is(T == struct)) 14 | { 15 | alias TypeOfMeta = Struct; 16 | } 17 | else static if(is(T == interface)) 18 | { 19 | alias TypeOfMeta = InterfaceType; 20 | } 21 | else static if(!is(T)) 22 | { 23 | alias TypeOfMeta = Module; 24 | } 25 | else 26 | { 27 | static assert(false); //todo: proper error 28 | } 29 | } 30 | 31 | template ImplTypeOfMeta(T) 32 | { 33 | import witchcraft; 34 | 35 | mixin WitchcraftClass; 36 | mixin WitchcraftConstructor; 37 | mixin WitchcraftField; 38 | mixin WitchcraftInterface; 39 | mixin WitchcraftMethod; 40 | mixin WitchcraftStruct; 41 | 42 | static if(is(T == class)) 43 | { 44 | alias ImplTypeOfMeta = ClassMixin!(T); 45 | } 46 | else static if(is(T == struct)) 47 | { 48 | alias ImplTypeOfMeta = StructMixin!(T); 49 | } 50 | else static if(is(T == interface)) 51 | { 52 | alias ImplTypeOfMeta = InterfaceTypeMixin!(T); 53 | } 54 | else static if(!is(T)) 55 | { 56 | alias ImplTypeOfMeta = ModuleImpl!(__traits(parent, T)); 57 | } 58 | else 59 | { 60 | static assert(false); //todo: proper error 61 | } 62 | } 63 | 64 | TypeOfMeta!(T) getMeta(T)() 65 | { 66 | return new ImplTypeOfMeta!(T); 67 | } 68 | 69 | mixin template Witchcraft() 70 | { 71 | import witchcraft; 72 | 73 | private static TypeOfMeta!(typeof(this)) __typeinfoext; 74 | 75 | @property 76 | static typeof(__typeinfoext) metaof() 77 | { 78 | if(__typeinfoext is null) 79 | { 80 | __typeinfoext = getMeta!(typeof(this))(); 81 | } 82 | 83 | return __typeinfoext; 84 | } 85 | 86 | static if(__traits(compiles, typeof(super).metaof)) 87 | { 88 | override typeof(__typeinfoext) getMetaType() 89 | { 90 | return typeof(this).metaof; 91 | } 92 | } 93 | else static if(is(typeof(this))) 94 | { 95 | typeof(__typeinfoext) getMetaType() 96 | { 97 | return typeof(this).metaof; 98 | } 99 | } 100 | else 101 | { 102 | typeof(__typeinfoext) getMetaType() 103 | { 104 | return metaof; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/classes.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.classes; 3 | 4 | mixin template WitchcraftClass() 5 | { 6 | import witchcraft; 7 | 8 | import std.meta; 9 | import std.traits; 10 | 11 | static class ClassMixin(T) : Class 12 | if(is(T == class)) 13 | { 14 | this() 15 | { 16 | foreach(name; FieldNameTuple!T) 17 | { 18 | _fields[name] = new FieldMixin!(T, name); 19 | } 20 | 21 | foreach(name; __traits(derivedMembers, T)) 22 | { 23 | static if(is(typeof(__traits(getMember, T, name)) == function)) 24 | { 25 | static if(name != "__ctor" && name != "__dtor") 26 | { 27 | foreach(index, overload; __traits(getOverloads, T, name)) 28 | { 29 | _methods[name] ~= new MethodMixin!(T, name, index); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | @property 37 | override Object create() const 38 | { 39 | return T.classinfo.create; 40 | } 41 | 42 | const(Attribute)[] getAttributes() const 43 | { 44 | alias attributes = AliasSeq!(__traits(getAttributes, T)); 45 | auto values = new Attribute[attributes.length]; 46 | 47 | foreach(index, attribute; attributes) 48 | { 49 | values[index] = new AttributeImpl!attribute; 50 | } 51 | 52 | return values; 53 | } 54 | 55 | override const(Constructor)[] getConstructors() const 56 | { 57 | static if(__traits(hasMember, T, "__ctor")) 58 | { 59 | alias constructors = AliasSeq!(__traits(getOverloads, T, "__ctor")); 60 | auto values = new Constructor[constructors.length]; 61 | 62 | foreach(index, constructor; constructors) 63 | { 64 | values[index] = new ConstructorMixin!(T, index); 65 | } 66 | 67 | return values; 68 | } 69 | else 70 | { 71 | return [ ]; 72 | } 73 | } 74 | 75 | const(Type) getDeclaringType() const 76 | { 77 | alias Parent = Alias!(__traits(parent, T)); 78 | 79 | return inspect!Parent; 80 | } 81 | 82 | const(TypeInfo) getDeclaringTypeInfo() const 83 | { 84 | alias Parent = Alias!(__traits(parent, T)); 85 | 86 | static if(__traits(compiles, typeid(Parent))) 87 | { 88 | return typeid(Parent); 89 | } 90 | else 91 | { 92 | return null; 93 | } 94 | } 95 | 96 | override const(InterfaceType)[] getInterfaces() const 97 | { 98 | alias Interfaces = InterfacesTuple!T; 99 | auto values = new InterfaceType[Interfaces.length]; 100 | 101 | foreach(index, IFace; Interfaces) 102 | { 103 | values[index] = cast(InterfaceType) inspect!IFace; 104 | } 105 | 106 | return values; 107 | } 108 | 109 | string getFullName() const 110 | { 111 | return fullyQualifiedName!T; 112 | } 113 | 114 | string getName() const 115 | { 116 | return T.stringof; 117 | } 118 | 119 | string getProtection() const 120 | { 121 | return __traits(getProtection, T); 122 | } 123 | 124 | override const(Class) getSuperClass() const 125 | { 126 | static if(is(Unqual!T == Object)) 127 | { 128 | return null; 129 | } 130 | else 131 | { 132 | alias Bases = BaseClassesTuple!T; 133 | 134 | static if(Bases.length > 0) 135 | { 136 | return cast(const(Class)) inspect!(Bases[0]); 137 | } 138 | else 139 | { 140 | return null; 141 | } 142 | } 143 | } 144 | 145 | override const(TypeInfo) getSuperTypeInfo() const 146 | { 147 | static if(is(Unqual!T == Object)) 148 | { 149 | return null; 150 | } 151 | else 152 | { 153 | alias Bases = BaseClassesTuple!T; 154 | 155 | static if(Bases.length > 0) 156 | { 157 | return typeid(Bases[0]); 158 | } 159 | else 160 | { 161 | return null; 162 | } 163 | } 164 | } 165 | 166 | override const(TypeInfo) getTypeInfo() const 167 | { 168 | return T.classinfo; 169 | } 170 | 171 | @property 172 | override bool isAbstract() const 173 | { 174 | return __traits(isAbstractClass, T); 175 | } 176 | 177 | @property 178 | final bool isAccessible() const 179 | { 180 | return true; 181 | } 182 | 183 | @property 184 | override bool isFinal() const 185 | { 186 | return __traits(isFinalClass, T); 187 | } 188 | 189 | override bool isSubClassOf(const Class other) const 190 | { 191 | return _d_isbaseof(T.classinfo, cast(ClassInfo) other.getTypeInfo); 192 | } 193 | 194 | override bool isSuperClassOf(const Class other) const 195 | { 196 | return _d_isbaseof(cast(ClassInfo) other.getTypeInfo, T.classinfo); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/constructors.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.constructors; 3 | 4 | mixin template WitchcraftConstructor() 5 | { 6 | import witchcraft; 7 | 8 | import std.algorithm; 9 | import std.conv; 10 | import std.meta; 11 | import std.range; 12 | import std.string; 13 | import std.traits; 14 | import std.variant; 15 | 16 | static class ConstructorMixin(alias T, size_t overload) : Constructor 17 | { 18 | private: 19 | alias method = Alias!(__traits(getOverloads, T, "__ctor")[overload]); 20 | alias Return = ReturnType!method; 21 | 22 | public: 23 | const(Attribute)[] getAttributes() const 24 | { 25 | alias attributes = AliasSeq!(__traits(getAttributes, method)); 26 | auto values = new Attribute[attributes.length]; 27 | 28 | foreach(index, attribute; attributes) 29 | { 30 | values[index] = new AttributeImpl!attribute; 31 | } 32 | 33 | return values; 34 | } 35 | 36 | const(Type) getDeclaringType() const 37 | { 38 | alias Parent = Alias!(__traits(parent, method)); 39 | 40 | return inspect!Parent; 41 | } 42 | 43 | const(TypeInfo) getDeclaringTypeInfo() const 44 | { 45 | alias Parent = Alias!(__traits(parent, method)); 46 | 47 | static if(__traits(compiles, typeid(Parent))) 48 | { 49 | return typeid(Parent); 50 | } 51 | else 52 | { 53 | return null; 54 | } 55 | } 56 | 57 | string getFullName() const 58 | { 59 | return fullyQualifiedName!method; 60 | } 61 | 62 | const(Type)[] getParameterTypes() const 63 | { 64 | auto parameterTypes = new Type[Parameters!method.length]; 65 | 66 | foreach(index, Parameter; Parameters!method) 67 | { 68 | parameterTypes[index] = inspect!Parameter; 69 | } 70 | 71 | return parameterTypes; 72 | } 73 | 74 | const(TypeInfo)[] getParameterTypeInfos() const 75 | { 76 | auto parameterTypeInfos = new TypeInfo[Parameters!method.length]; 77 | 78 | foreach(index, Parameter; Parameters!method) 79 | { 80 | static if(__traits(compiles, typeid(Parameter))) 81 | { 82 | parameterTypeInfos[index] = typeid(Parameter); 83 | } 84 | } 85 | 86 | return parameterTypeInfos; 87 | } 88 | 89 | string getProtection() const 90 | { 91 | return __traits(getProtection, method); 92 | } 93 | 94 | const(Type) getReturnType() const 95 | { 96 | return inspect!Return; 97 | } 98 | 99 | @property 100 | const(TypeInfo) getReturnTypeInfo() const 101 | { 102 | static if(__traits(compiles, typeid(Return))) 103 | { 104 | return typeid(Return); 105 | } 106 | else 107 | { 108 | return null; 109 | } 110 | } 111 | 112 | static if(isAbstractClass!T) 113 | { 114 | Variant invoke(Variant instance, Variant[] arguments...) const 115 | { 116 | assert(0, T.stringof ~ " is abstract."); 117 | } 118 | } 119 | else 120 | { 121 | Variant invoke(Variant instance, Variant[] arguments...) const 122 | { 123 | import std.algorithm, std.conv, std.range, std.string; 124 | 125 | alias Params = Parameters!method; 126 | 127 | enum variables = iota(0, Params.length) 128 | .map!(i => "auto v%1$s = arguments[%1$s].get!(Params[%1$s]);".format(i)) 129 | .joiner.text; 130 | 131 | enum invokeString = iota(0, Params.length) 132 | .map!(i => "v%s".format(i)) 133 | .joiner(", ").text; 134 | 135 | mixin(variables); 136 | mixin("Params args = AliasSeq!(" ~ invokeString ~ ");"); 137 | 138 | static if(is(T == class)) 139 | { 140 | return Variant(new T(args)); 141 | } 142 | else 143 | { 144 | return Variant(T(args)); 145 | } 146 | } 147 | } 148 | 149 | @property 150 | final bool isAccessible() const 151 | { 152 | return true; 153 | } 154 | 155 | @property 156 | bool isVarArgs() const 157 | { 158 | return variadicFunctionStyle!method != Variadic.no; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/fields.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.fields; 3 | 4 | mixin template WitchcraftField() 5 | { 6 | import witchcraft; 7 | 8 | import std.meta; 9 | import std.traits; 10 | import std.variant; 11 | 12 | static class FieldMixin(alias T, string name) : Field 13 | { 14 | private: 15 | alias member = Alias!(__traits(getMember, T, name)); 16 | 17 | enum bool writable = __traits(compiles, { 18 | T instance = void; 19 | typeof(member) value = void; 20 | 21 | __traits(getMember, instance, name) = value; 22 | }); 23 | 24 | public: 25 | override Variant get(Variant instance) const 26 | { 27 | static if(is(T == class)) 28 | { 29 | auto this_ = cast(ClassInfo) typeid(T); 30 | auto other = cast(ClassInfo) instance.type; 31 | 32 | // Ensure both types exist and can be converted. 33 | if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_))) 34 | { 35 | assert(0, "Instance isn't type of `" ~ T.stringof ~ "`."); 36 | } 37 | 38 | auto obj = instance.coerce!T; 39 | } 40 | else static if(is(T)) 41 | { 42 | auto obj = instance.get!T; 43 | } 44 | else 45 | { 46 | alias obj = T; 47 | } 48 | 49 | return Variant(__traits(getMember, obj, name)); 50 | } 51 | 52 | const(Attribute)[] getAttributes() const 53 | { 54 | alias attributes = AliasSeq!(__traits(getAttributes, member)); 55 | auto values = new Attribute[attributes.length]; 56 | 57 | foreach(index, attribute; attributes) 58 | { 59 | values[index] = new AttributeImpl!attribute; 60 | } 61 | 62 | return values; 63 | } 64 | 65 | const(Type) getDeclaringType() const 66 | { 67 | alias Parent = Alias!(__traits(parent, member)); 68 | 69 | return inspect!Parent; 70 | } 71 | 72 | const(TypeInfo) getDeclaringTypeInfo() const 73 | { 74 | alias Parent = Alias!(__traits(parent, member)); 75 | 76 | static if(__traits(compiles, typeid(Parent))) 77 | { 78 | return typeid(Parent); 79 | } 80 | else 81 | { 82 | return null; 83 | } 84 | } 85 | 86 | @property 87 | string getName() const 88 | { 89 | return name; 90 | } 91 | 92 | string getFullName() const 93 | { 94 | return fullyQualifiedName!member; 95 | } 96 | 97 | string getProtection() const 98 | { 99 | return __traits(getProtection, __traits(getMember, T, name)); 100 | } 101 | 102 | override const(Type) getValueType() const 103 | { 104 | return inspect!(typeof(member)); 105 | } 106 | 107 | override const(TypeInfo) getValueTypeInfo() const 108 | { 109 | return typeid(typeof(member)); 110 | } 111 | 112 | @property 113 | final bool isAccessible() const 114 | { 115 | return true; 116 | } 117 | 118 | @property 119 | override bool isStatic() const 120 | { 121 | return __traits(compiles, { 122 | auto value = __traits(getMember, T, name); 123 | }); 124 | } 125 | 126 | @property 127 | override bool isWritable() const 128 | { 129 | return writable; 130 | } 131 | 132 | override void set(Variant instance, Variant value) const 133 | { 134 | static if(is(T == class)) 135 | { 136 | auto this_ = cast(ClassInfo) typeid(T); 137 | auto other = cast(ClassInfo) instance.type; 138 | 139 | // Ensure both types exist and can be converted. 140 | if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_))) 141 | { 142 | assert(0, "Instance isn't type of `" ~ T.stringof ~ "`."); 143 | } 144 | 145 | auto obj = instance.coerce!T; 146 | } 147 | else static if(is(T)) 148 | { 149 | auto obj = instance.get!T; 150 | } 151 | else 152 | { 153 | alias obj = T; 154 | } 155 | 156 | static if(writable) 157 | { 158 | __traits(getMember, obj, name) = value.get!(typeof(member)); 159 | } 160 | else 161 | { 162 | assert("Field " ~ name ~ " is not writable."); 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/interfaces.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.interfaces; 3 | 4 | mixin template WitchcraftInterface() 5 | { 6 | import witchcraft; 7 | 8 | import std.meta; 9 | import std.traits; 10 | 11 | static class InterfaceTypeMixin(T) : InterfaceType 12 | if(is(T == interface)) 13 | { 14 | this() 15 | { 16 | foreach(name; __traits(derivedMembers, T)) 17 | { 18 | static if(is(typeof(__traits(getMember, T, name)) == function)) 19 | { 20 | static if(name != "__ctor" && name != "__dtor") 21 | { 22 | foreach(index, overload; __traits(getOverloads, T, name)) 23 | { 24 | _methods[name] ~= new MethodMixin!(T, name, index); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | const(Attribute)[] getAttributes() const 32 | { 33 | alias attributes = AliasSeq!(__traits(getAttributes, T)); 34 | auto values = new Attribute[attributes.length]; 35 | 36 | foreach(index, attribute; attributes) 37 | { 38 | values[index] = new AttributeImpl!attribute; 39 | } 40 | 41 | return values; 42 | } 43 | 44 | const(Type) getDeclaringType() const 45 | { 46 | alias Parent = Alias!(__traits(parent, T)); 47 | 48 | return inspect!Parent; 49 | } 50 | 51 | const(TypeInfo) getDeclaringTypeInfo() const 52 | { 53 | alias Parent = Alias!(__traits(parent, T)); 54 | 55 | static if(__traits(compiles, typeid(Parent))) 56 | { 57 | return typeid(Parent); 58 | } 59 | else 60 | { 61 | return null; 62 | } 63 | } 64 | 65 | override const(Field) getField(string name) const 66 | { 67 | auto ptr = name in _fields; 68 | return ptr ? *ptr : null; 69 | } 70 | 71 | override const(Field)[] getFields() const 72 | { 73 | return _fields.values; 74 | } 75 | 76 | string getFullName() const 77 | { 78 | return fullyQualifiedName!T; 79 | } 80 | 81 | override const(Method)[] getMethods(string name) const 82 | { 83 | auto ptr = name in _methods; 84 | return ptr ? *ptr : null; 85 | } 86 | 87 | override const(Method)[] getMethods() const 88 | { 89 | const(Method)[] methods; 90 | 91 | foreach(overloads; _methods.values) 92 | { 93 | methods ~= overloads; 94 | } 95 | 96 | return methods; 97 | } 98 | 99 | string getName() const 100 | { 101 | return T.stringof; 102 | } 103 | 104 | string getProtection() const 105 | { 106 | return __traits(getProtection, T); 107 | } 108 | 109 | override const(TypeInfo) getTypeInfo() const 110 | { 111 | return typeid(T); 112 | } 113 | 114 | @property 115 | final bool isAccessible() const 116 | { 117 | return true; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/methods.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.methods; 3 | 4 | mixin template WitchcraftMethod() 5 | { 6 | import witchcraft; 7 | 8 | import std.meta; 9 | import std.traits; 10 | import std.variant; 11 | 12 | static class MethodMixin(alias T, string name, size_t overload) : Method 13 | { 14 | private: 15 | alias method = Alias!(__traits(getOverloads, T, name)[overload]); 16 | alias Return = ReturnType!method; 17 | 18 | public: 19 | const(Attribute)[] getAttributes() const 20 | { 21 | alias attributes = AliasSeq!(__traits(getAttributes, method)); 22 | auto values = new Attribute[attributes.length]; 23 | 24 | foreach(index, attribute; attributes) 25 | { 26 | values[index] = new AttributeImpl!attribute; 27 | } 28 | 29 | return values; 30 | } 31 | 32 | const(Type) getDeclaringType() const 33 | { 34 | alias Parent = Alias!(__traits(parent, method)); 35 | 36 | return inspect!Parent; 37 | } 38 | 39 | const(TypeInfo) getDeclaringTypeInfo() const 40 | { 41 | alias Parent = Alias!(__traits(parent, method)); 42 | 43 | static if(__traits(compiles, typeid(Parent))) 44 | { 45 | return typeid(Parent); 46 | } 47 | else 48 | { 49 | return null; 50 | } 51 | } 52 | 53 | string getName() const 54 | { 55 | return name; 56 | } 57 | 58 | string getFullName() const 59 | { 60 | return fullyQualifiedName!method; 61 | } 62 | 63 | const(Type)[] getParameterTypes() const 64 | { 65 | auto parameterTypes = new Type[Parameters!method.length]; 66 | 67 | foreach(index, Parameter; Parameters!method) 68 | { 69 | parameterTypes[index] = inspect!Parameter; 70 | } 71 | 72 | return parameterTypes; 73 | } 74 | 75 | const(TypeInfo)[] getParameterTypeInfos() const 76 | { 77 | auto parameterTypeInfos = new TypeInfo[Parameters!method.length]; 78 | 79 | foreach(index, Parameter; Parameters!method) 80 | { 81 | static if(__traits(compiles, typeid(Parameter))) 82 | { 83 | parameterTypeInfos[index] = typeid(Parameter); 84 | } 85 | } 86 | 87 | return parameterTypeInfos; 88 | } 89 | 90 | string getProtection() const 91 | { 92 | return __traits(getProtection, method); 93 | } 94 | 95 | const(Type) getReturnType() const 96 | { 97 | return inspect!Return; 98 | } 99 | 100 | @property 101 | const(TypeInfo) getReturnTypeInfo() const 102 | { 103 | static if(__traits(compiles, typeid(Return))) 104 | { 105 | return typeid(Return); 106 | } 107 | else 108 | { 109 | return null; 110 | } 111 | } 112 | 113 | Variant invoke(Variant instance, Variant[] arguments...) const 114 | { 115 | import std.algorithm, std.conv, std.range, std.string; 116 | 117 | alias Params = Parameters!method; 118 | 119 | template NormalizeType(T) 120 | { 121 | static if(is(T == InoutOf!T)) 122 | { 123 | // HACK: There's no good way to remove inout-ness. 124 | alias NormalizeType = void *; 125 | } 126 | else 127 | { 128 | alias NormalizeType = T; 129 | } 130 | } 131 | 132 | enum variables = iota(0, Params.length) 133 | .map!(i => "auto v%1$s = arguments[%1$s].get!(NormalizeType!(Params[%1$s]));".format(i)) 134 | .joiner 135 | .text; 136 | 137 | enum invokeString = iota(0, Params.length) 138 | .map!(i => "v%1$s".format(i)) 139 | .joiner(", ") 140 | .text; 141 | 142 | mixin(variables); 143 | 144 | static if(is(T == class)) 145 | { 146 | auto this_ = cast(ClassInfo) typeid(T); 147 | auto other = cast(ClassInfo) instance.type; 148 | 149 | // Ensure both types exist and can be converted. 150 | if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_))) 151 | { 152 | assert(0, "Instance isn't type of `" ~ T.stringof ~ "`."); 153 | } 154 | 155 | Unqual!T obj = instance.coerce!(Unqual!T); 156 | } 157 | else static if(is(T)) 158 | { 159 | Unqual!T obj = instance.get!(Unqual!T); 160 | } 161 | else 162 | { 163 | alias obj = T; 164 | } 165 | 166 | static if(!is(ReturnType!method == void)) 167 | { 168 | mixin("auto result = __traits(getOverloads, obj, name)[overload](" ~ invokeString ~ ");"); 169 | 170 | return Variant(result); 171 | } 172 | else 173 | { 174 | mixin("__traits(getOverloads, obj, name)[overload](" ~ invokeString ~ ");"); 175 | 176 | return Variant(null); 177 | } 178 | } 179 | 180 | @property 181 | final bool isAccessible() const 182 | { 183 | return true; 184 | } 185 | 186 | @property 187 | override bool isFinal() const 188 | { 189 | return __traits(isFinalFunction, method); 190 | } 191 | 192 | @property 193 | override bool isOverride() const 194 | { 195 | return __traits(isOverrideFunction, method); 196 | } 197 | 198 | @property 199 | override bool isStatic() const 200 | { 201 | return __traits(isStaticFunction, method); 202 | } 203 | 204 | @property 205 | bool isVarArgs() const 206 | { 207 | return variadicFunctionStyle!method != Variadic.no; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/package.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins; 3 | 4 | public import witchcraft.mixins.base; 5 | public import witchcraft.mixins.classes; 6 | public import witchcraft.mixins.constructors; 7 | public import witchcraft.mixins.fields; 8 | public import witchcraft.mixins.interfaces; 9 | public import witchcraft.mixins.methods; 10 | public import witchcraft.mixins.structs; 11 | -------------------------------------------------------------------------------- /source/witchcraft/mixins/structs.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.mixins.structs; 3 | 4 | mixin template WitchcraftStruct() 5 | { 6 | import witchcraft; 7 | 8 | import std.meta; 9 | import std.traits; 10 | 11 | static class StructMixin(T) : Struct 12 | if(is(T == struct)) 13 | { 14 | this() 15 | { 16 | foreach(name; FieldNameTuple!T) 17 | { 18 | _fields[name] = new FieldMixin!(T, name); 19 | } 20 | 21 | foreach(name; __traits(derivedMembers, T)) 22 | { 23 | static if(is(typeof(__traits(getMember, T, name)) == function)) 24 | { 25 | static if(name != "__ctor" && name != "__dtor") 26 | { 27 | foreach(index, overload; __traits(getOverloads, T, name)) 28 | { 29 | _methods[name] ~= new MethodMixin!(T, name, index); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | const(Attribute)[] getAttributes() const 37 | { 38 | alias attributes = AliasSeq!(__traits(getAttributes, T)); 39 | auto values = new Attribute[attributes.length]; 40 | 41 | foreach(index, attribute; attributes) 42 | { 43 | values[index] = new AttributeImpl!attribute; 44 | } 45 | 46 | return values; 47 | } 48 | 49 | override const(Constructor)[] getConstructors() const 50 | { 51 | static if(__traits(hasMember, T, "__ctor")) 52 | { 53 | alias constructors = AliasSeq!(__traits(getOverloads, T, "__ctor")); 54 | auto values = new Constructor[constructors.length]; 55 | 56 | foreach(index, constructor; constructors) 57 | { 58 | values[index] = new ConstructorMixin!(T, index); 59 | } 60 | 61 | return values; 62 | } 63 | else 64 | { 65 | return [ ]; 66 | } 67 | } 68 | 69 | const(Type) getDeclaringType() const 70 | { 71 | alias Parent = Alias!(__traits(parent, T)); 72 | 73 | return inspect!Parent; 74 | } 75 | 76 | const(TypeInfo) getDeclaringTypeInfo() const 77 | { 78 | alias Parent = Alias!(__traits(parent, T)); 79 | 80 | static if(__traits(compiles, typeid(Parent))) 81 | { 82 | return typeid(Parent); 83 | } 84 | else 85 | { 86 | return null; 87 | } 88 | } 89 | 90 | string getFullName() const 91 | { 92 | return fullyQualifiedName!T; 93 | } 94 | 95 | string getName() const 96 | { 97 | return T.stringof; 98 | } 99 | 100 | string getProtection() const 101 | { 102 | return __traits(getProtection, T); 103 | } 104 | 105 | override const(TypeInfo) getTypeInfo() const 106 | { 107 | return typeid(T); 108 | } 109 | 110 | @property 111 | final bool isAccessible() const 112 | { 113 | return true; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /source/witchcraft/modules.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.modules; 3 | 4 | import witchcraft; 5 | 6 | abstract class Module : Type 7 | { 8 | final const(Attribute)[] getAttributes() const 9 | { 10 | return [ ]; 11 | } 12 | 13 | final const(Type) getDeclaringType() const 14 | { 15 | return null; 16 | } 17 | 18 | final const(TypeInfo) getDeclaringTypeInfo() const 19 | { 20 | return null; 21 | } 22 | 23 | final override const(TypeInfo) getTypeInfo() const 24 | { 25 | return null; 26 | } 27 | 28 | @property 29 | final override bool isModule() const 30 | { 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/witchcraft/package.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft; 3 | 4 | public import witchcraft.aggregates; 5 | public import witchcraft.attributes; 6 | public import witchcraft.classes; 7 | public import witchcraft.constructors; 8 | public import witchcraft.fields; 9 | public import witchcraft.impl; 10 | public import witchcraft.interfaces; 11 | public import witchcraft.invocable; 12 | public import witchcraft.members; 13 | public import witchcraft.methods; 14 | public import witchcraft.mixins; 15 | public import witchcraft.modules; 16 | public import witchcraft.structs; 17 | public import witchcraft.types; 18 | 19 | extern(C) bool _d_isbaseof(ClassInfo child, ClassInfo parent); 20 | -------------------------------------------------------------------------------- /source/witchcraft/structs.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.structs; 3 | 4 | import witchcraft; 5 | 6 | abstract class Struct : Aggregate 7 | { 8 | /++ 9 | + Checks if this type is a struct. For children of `Struct`, this always 10 | + returns `true`. 11 | + 12 | + Returns: 13 | + `true` if the type is a struct. 14 | ++/ 15 | @property 16 | final override bool isStruct() const 17 | { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/witchcraft/types.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.types; 3 | 4 | import witchcraft; 5 | 6 | import std.algorithm; 7 | import std.array; 8 | import std.range; 9 | 10 | abstract class Type : Member 11 | { 12 | protected: 13 | Field[string] _fields; 14 | Method[][string] _methods; 15 | 16 | public: 17 | /++ 18 | + Looks up a field by name. 19 | + 20 | + Params: 21 | + name = The name of the field. 22 | + 23 | + Returns: 24 | + The field object, or null if no such field exists. 25 | ++/ 26 | const(Field) getField(string name) const 27 | { 28 | auto ptr = name in _fields; 29 | return ptr ? *ptr : null; 30 | } 31 | 32 | final string[] getFieldNames() const 33 | { 34 | return getFields 35 | .map!"a.getName" 36 | .array; 37 | } 38 | 39 | /++ 40 | + Returns an array of all fields defined by this type. 41 | + 42 | + Returns: 43 | + All fields objects on this type. 44 | ++/ 45 | const(Field)[] getFields() const 46 | { 47 | return _fields.values; 48 | } 49 | 50 | final const(Method) getMethod(string name, Type[] parameterTypes...) const 51 | { 52 | // Iterate up the inheritance tree. 53 | return this.getMethods(name).retro 54 | .filter!(m => m.getParameterTypes == parameterTypes) 55 | .takeOne 56 | .chain(null.only) 57 | .front; 58 | } 59 | 60 | final const(Method) getMethod(string name, TypeInfo[] parameterTypeInfos) const 61 | { 62 | // Iterate up the inheritance tree. 63 | return this.getMethods(name).retro 64 | .filter!(m => m.getParameterTypeInfos == parameterTypeInfos) 65 | .takeOne 66 | .chain(null.only) 67 | .front; 68 | } 69 | 70 | final const(Method) getMethod(TList...)(string name) const 71 | { 72 | auto parameterTypeInfos = new TypeInfo[TList.length]; 73 | 74 | foreach(index, Type; TList) 75 | { 76 | parameterTypeInfos[index] = typeid(Type); 77 | } 78 | 79 | return this.getMethod(name, parameterTypeInfos); 80 | } 81 | 82 | const(Method)[] getMethods() const 83 | { 84 | const(Method)[] methods; 85 | 86 | // Flatten the overloads array. 87 | foreach(overloads; _methods.values) 88 | { 89 | methods ~= overloads; 90 | } 91 | 92 | return methods; 93 | } 94 | 95 | final string[] getMethodNames() const 96 | { 97 | return getMethods 98 | .map!"a.getName" 99 | .array; 100 | } 101 | 102 | const(Method)[] getMethods(string name) const 103 | { 104 | auto ptr = name in _methods; 105 | return ptr ? *ptr : [ ]; 106 | } 107 | 108 | abstract const(TypeInfo) getTypeInfo() const; 109 | 110 | @property 111 | bool isAggregate() const 112 | { 113 | return false; 114 | } 115 | 116 | @property 117 | bool isArray() const 118 | { 119 | return false; 120 | } 121 | 122 | @property 123 | bool isAssocArray() const 124 | { 125 | return false; 126 | } 127 | 128 | @property 129 | bool isBuiltIn() const 130 | { 131 | return false; 132 | } 133 | 134 | @property 135 | bool isClass() const 136 | { 137 | return false; 138 | } 139 | 140 | @property 141 | bool isInterface() const 142 | { 143 | return false; 144 | } 145 | 146 | @property 147 | bool isModule() const 148 | { 149 | return false; 150 | } 151 | 152 | @property 153 | bool isPointer() const 154 | { 155 | return false; 156 | } 157 | 158 | @property 159 | bool isPrimitive() const 160 | { 161 | return false; 162 | } 163 | 164 | @property 165 | bool isStaticArray() const 166 | { 167 | return false; 168 | } 169 | 170 | @property 171 | bool isString() const 172 | { 173 | return false; 174 | } 175 | 176 | @property 177 | bool isStruct() const 178 | { 179 | return false; 180 | } 181 | 182 | override string toString() const 183 | { 184 | return getFullName; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /source/witchcraft/unittests/accessible.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.unittests.accessible; 3 | 4 | version(unittest) 5 | { 6 | import witchcraft; 7 | 8 | private class Secret 9 | { 10 | bool b; 11 | 12 | this() 13 | { 14 | } 15 | 16 | this(bool b) 17 | { 18 | } 19 | 20 | int foo() 21 | { 22 | return 5; 23 | } 24 | } 25 | 26 | class Foo : Secret 27 | { 28 | int x; 29 | int y; 30 | 31 | private int z; 32 | 33 | this() 34 | { 35 | } 36 | 37 | private this(int x, int y) 38 | { 39 | } 40 | 41 | int bar() 42 | { 43 | return x + y + z; 44 | } 45 | } 46 | 47 | class Bar : Foo 48 | { 49 | mixin Witchcraft; 50 | 51 | string name; 52 | 53 | int foobar() 54 | { 55 | return foo * bar; 56 | } 57 | } 58 | } 59 | 60 | unittest 61 | { 62 | auto bClass = Bar.metaof; 63 | assert(bClass !is null); 64 | 65 | assert(bClass.getName == "Bar"); 66 | assert(bClass.isAccessible == true); 67 | assert(bClass.getField("name").isAccessible == true); 68 | 69 | version(aggressive) 70 | { 71 | assert(bClass.getSuperClass.getName == "Foo"); 72 | assert(bClass.getSuperClass.isAccessible == true); 73 | assert(bClass.getSuperClass.getField("x").isAccessible == true); 74 | assert(bClass.getSuperClass.getField("y").isAccessible == true); 75 | assert(bClass.getSuperClass.getField("z").isAccessible == false); 76 | 77 | assert(bClass.getSuperClass.getSuperClass.getName == "Secret"); 78 | assert(bClass.getSuperClass.getSuperClass.isAccessible == false); 79 | } 80 | else 81 | { 82 | assert(bClass.getSuperClass is null); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /source/witchcraft/unittests/base.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.unittests.base; 3 | 4 | import witchcraft; 5 | 6 | version(unittest) 7 | { 8 | import std.algorithm; 9 | import std.array; 10 | 11 | struct Entity 12 | { 13 | } 14 | 15 | struct Column 16 | { 17 | string name; 18 | } 19 | 20 | @Entity 21 | class User 22 | { 23 | mixin Witchcraft; 24 | 25 | @Column 26 | string username; 27 | 28 | @Column("pass_word") 29 | string password; 30 | 31 | string email; 32 | 33 | ulong createdAt; 34 | ulong updatedAt; 35 | 36 | this() 37 | { 38 | } 39 | 40 | this(string email) 41 | { 42 | this.email = email; 43 | } 44 | 45 | void updateEmail(string email) 46 | { 47 | this.email = email; 48 | updatedAt += 1; 49 | } 50 | } 51 | 52 | void testSuite(C)(C metaObject) 53 | { 54 | assert(metaObject.isAggregate == true); 55 | assert(metaObject.isClass == true); 56 | assert(metaObject.isStruct == false); 57 | 58 | /+ - Classes - +/ 59 | 60 | assert(metaObject.getName == "User"); 61 | assert(metaObject.getFullName == "witchcraft.unittests.base.User"); 62 | assert(metaObject.isAbstract == false); 63 | assert(metaObject.isFinal == false); 64 | 65 | /+ - Class Attributes - +/ 66 | 67 | assert(metaObject.getAttributes.empty == false); 68 | assert(metaObject.getAttributes!Entity.empty == false); 69 | assert(metaObject.getAttributes!Column.empty == true); 70 | 71 | assert(metaObject.getAttributes!Entity[0].isType == true); 72 | assert(metaObject.getAttributes!Entity[0].isExpression == false); 73 | 74 | /+ - Constructors - +/ 75 | 76 | assert(metaObject.getConstructors.length == 2); 77 | 78 | assert(metaObject.getConstructor !is null); 79 | assert(metaObject.getConstructor!string !is null); 80 | 81 | User user = metaObject.getConstructor!string.create!User("test@email.com"); 82 | 83 | assert(user !is null); 84 | assert(user.email == "test@email.com"); 85 | assert(user.updatedAt == 0); 86 | 87 | /+ - Fields - +/ 88 | 89 | assert(metaObject.getFieldNames.isPermutation([ 90 | "username", "password", "email", "createdAt", "updatedAt" 91 | ])); 92 | 93 | assert(metaObject.getField("username") !is null); 94 | auto username = metaObject.getField("username"); 95 | 96 | /+ - Field Attributes - +/ 97 | 98 | assert(username.getAttributes.empty == false); 99 | assert(username.getAttributes!Entity.empty == true); 100 | assert(username.getAttributes!Column.empty == false); 101 | 102 | assert(username.getAttributes!Column[0].isType == true); 103 | assert(username.getAttributes!Column[0].isExpression == false); 104 | 105 | assert(metaObject.getField("password") !is null); 106 | auto password = metaObject.getField("password"); 107 | 108 | /+ - Field Attributes (Expression) - +/ 109 | 110 | assert(password.getAttributes.empty == false); 111 | assert(password.getAttributes!Entity.empty == true); 112 | assert(password.getAttributes!Column.empty == false); 113 | 114 | assert(password.getAttributes!Column[0].isType == false); 115 | assert(password.getAttributes!Column[0].isExpression == true); 116 | 117 | assert(password.getAttributes!Column[0].get!Column.name == "pass_word"); 118 | 119 | /+ - Methods - +/ 120 | 121 | assert(metaObject.getLocalMethodNames.isPermutation([ "getMetaType", "updateEmail" ])); 122 | 123 | assert(metaObject.getMethods("updateEmail").empty == false); 124 | assert(metaObject.getMethod!(string)("updateEmail") !is null); 125 | auto updateEmail = metaObject.getMethod!(string)("updateEmail"); 126 | 127 | updateEmail.invoke(user, "user@email.com"); 128 | assert(user.email == "user@email.com"); 129 | assert(user.updatedAt == 1); 130 | } 131 | } 132 | 133 | unittest 134 | { 135 | assert(User.metaof !is null); 136 | assert(getMeta!User() !is null); 137 | auto metaObjectByProperty = User.metaof; 138 | auto metaObjectByFunction = getMeta!User(); 139 | 140 | // dirty hack to compare meta object... 141 | assert(metaObjectByProperty.getFullName() == metaObjectByFunction.getFullName()); 142 | 143 | // ...less dirty way to ensure the same behaviour 144 | testSuite(metaObjectByProperty); 145 | testSuite(metaObjectByFunction); 146 | } 147 | -------------------------------------------------------------------------------- /source/witchcraft/unittests/inheritance.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.unittests.inheritance; 3 | 4 | import witchcraft; 5 | 6 | version(unittest) 7 | { 8 | abstract class Entity 9 | { 10 | mixin Witchcraft; 11 | 12 | ulong id; 13 | } 14 | 15 | class User : Entity 16 | { 17 | mixin Witchcraft; 18 | 19 | string username; 20 | string password; 21 | } 22 | 23 | final class Admin : User 24 | { 25 | mixin Witchcraft; 26 | 27 | bool canBan; 28 | bool canEdit; 29 | bool canMute; 30 | } 31 | } 32 | 33 | unittest 34 | { 35 | import std.algorithm; 36 | 37 | Class eClass = Entity.metaof; 38 | Class uClass = User.metaof; 39 | Class aClass = Admin.metaof; 40 | 41 | assert(eClass !is null && uClass !is null && aClass !is null); 42 | 43 | assert(eClass.getName == "Entity"); 44 | assert(uClass.getName == "User"); 45 | assert(aClass.getName == "Admin"); 46 | 47 | assert(eClass.isAbstract == true); 48 | assert(uClass.isAbstract == false); 49 | assert(aClass.isAbstract == false); 50 | 51 | assert(eClass.isFinal == false); 52 | assert(uClass.isFinal == false); 53 | assert(aClass.isFinal == true); 54 | 55 | enum eFields = [ "id" ]; 56 | enum uFields = [ "username", "password" ]; 57 | enum aFields = [ "canBan", "canEdit", "canMute" ]; 58 | 59 | assert(eClass.getLocalFieldNames.isPermutation(eFields)); 60 | assert(uClass.getLocalFieldNames.isPermutation(uFields)); 61 | assert(aClass.getLocalFieldNames.isPermutation(aFields)); 62 | 63 | assert(eClass.getFieldNames.isPermutation(eFields)); 64 | assert(uClass.getFieldNames.isPermutation(eFields ~ uFields)); 65 | assert(aClass.getFieldNames.isPermutation(eFields ~ uFields ~ aFields)); 66 | } 67 | 68 | unittest 69 | { 70 | Entity u = new User; 71 | Entity a = new Admin; 72 | 73 | Class eClass = Entity.metaof; 74 | Class uClass = u.getMetaType; 75 | Class aClass = a.getMetaType; 76 | 77 | assert(eClass !is null && uClass !is null && aClass !is null); 78 | 79 | assert(eClass.getName == "Entity"); 80 | assert(uClass.getName == "User"); 81 | assert(aClass.getName == "Admin"); 82 | 83 | version(aggressive) 84 | { 85 | assert(eClass.getSuperClass !is null); 86 | assert(eClass.getSuperClass.getName == "Object"); 87 | } 88 | else 89 | { 90 | assert(eClass.getSuperClass is null); 91 | } 92 | 93 | assert(uClass.getSuperClass is eClass); 94 | assert(aClass.getSuperClass is uClass); 95 | 96 | assert(eClass.getField("id") !is null); 97 | assert(uClass.getField("id") !is null); 98 | assert(aClass.getField("id") !is null); 99 | 100 | assert(eClass.getField("username") is null); 101 | assert(uClass.getField("username") !is null); 102 | assert(aClass.getField("username") !is null); 103 | 104 | assert(eClass.getField("canEdit") is null); 105 | assert(uClass.getField("canEdit") is null); 106 | assert(aClass.getField("canEdit") !is null); 107 | } 108 | -------------------------------------------------------------------------------- /source/witchcraft/unittests/noMixin.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.unittests.noMixin; 3 | 4 | import witchcraft; 5 | 6 | version(unittest) 7 | { 8 | import std.algorithm; 9 | import std.array; 10 | 11 | struct Entity 12 | { 13 | } 14 | 15 | struct Column 16 | { 17 | string name; 18 | } 19 | 20 | @Entity 21 | class User 22 | { 23 | @Column 24 | string username; 25 | 26 | @Column("pass_word") 27 | string password; 28 | 29 | string email; 30 | 31 | ulong createdAt; 32 | ulong updatedAt; 33 | 34 | this() 35 | { 36 | } 37 | 38 | this(string email) 39 | { 40 | this.email = email; 41 | } 42 | 43 | void updateEmail(string email) 44 | { 45 | this.email = email; 46 | updatedAt += 1; 47 | } 48 | } 49 | 50 | void testSuite(C)(C metaObject) 51 | { 52 | assert(metaObject.isAggregate == true); 53 | assert(metaObject.isClass == true); 54 | assert(metaObject.isStruct == false); 55 | 56 | /+ - Classes - +/ 57 | 58 | assert(metaObject.getName == "User"); 59 | assert(metaObject.getFullName == "witchcraft.unittests.noMixin.User"); 60 | assert(metaObject.isAbstract == false); 61 | assert(metaObject.isFinal == false); 62 | 63 | /+ - Class Attributes - +/ 64 | 65 | assert(metaObject.getAttributes.empty == false); 66 | assert(metaObject.getAttributes!Entity.empty == false); 67 | assert(metaObject.getAttributes!Column.empty == true); 68 | 69 | assert(metaObject.getAttributes!Entity[0].isType == true); 70 | assert(metaObject.getAttributes!Entity[0].isExpression == false); 71 | 72 | /+ - Constructors - +/ 73 | 74 | assert(metaObject.getConstructors.length == 2); 75 | 76 | assert(metaObject.getConstructor !is null); 77 | assert(metaObject.getConstructor!string !is null); 78 | 79 | User user = metaObject.getConstructor!string.create!User("test@email.com"); 80 | 81 | assert(user !is null); 82 | assert(user.email == "test@email.com"); 83 | assert(user.updatedAt == 0); 84 | 85 | /+ - Fields - +/ 86 | 87 | assert(metaObject.getFieldNames.isPermutation([ 88 | "username", "password", "email", "createdAt", "updatedAt" 89 | ])); 90 | 91 | assert(metaObject.getField("username") !is null); 92 | auto username = metaObject.getField("username"); 93 | 94 | /+ - Field Attributes - +/ 95 | 96 | assert(username.getAttributes.empty == false); 97 | assert(username.getAttributes!Entity.empty == true); 98 | assert(username.getAttributes!Column.empty == false); 99 | 100 | assert(username.getAttributes!Column[0].isType == true); 101 | assert(username.getAttributes!Column[0].isExpression == false); 102 | 103 | assert(metaObject.getField("password") !is null); 104 | auto password = metaObject.getField("password"); 105 | 106 | /+ - Field Attributes (Expression) - +/ 107 | 108 | assert(password.getAttributes.empty == false); 109 | assert(password.getAttributes!Entity.empty == true); 110 | assert(password.getAttributes!Column.empty == false); 111 | 112 | assert(password.getAttributes!Column[0].isType == false); 113 | assert(password.getAttributes!Column[0].isExpression == true); 114 | 115 | assert(password.getAttributes!Column[0].get!Column.name == "pass_word"); 116 | 117 | /+ - Methods - +/ 118 | 119 | assert(metaObject.getLocalMethodNames == [ "updateEmail" ]); 120 | 121 | assert(metaObject.getMethods("updateEmail").empty == false); 122 | assert(metaObject.getMethod!(string)("updateEmail") !is null); 123 | auto updateEmail = metaObject.getMethod!(string)("updateEmail"); 124 | 125 | updateEmail.invoke(user, "user@email.com"); 126 | assert(user.email == "user@email.com"); 127 | assert(user.updatedAt == 1); 128 | } 129 | } 130 | 131 | unittest 132 | { 133 | auto metaObject = getMeta!User(); 134 | assert(metaObject !is null); 135 | 136 | testSuite(metaObject); 137 | } 138 | -------------------------------------------------------------------------------- /source/witchcraft/unittests/structs.d: -------------------------------------------------------------------------------- 1 | 2 | module witchcraft.unittests.structs; 3 | 4 | version(unittest) 5 | { 6 | import witchcraft; 7 | 8 | struct User 9 | { 10 | mixin Witchcraft; 11 | 12 | string username; 13 | string password; 14 | } 15 | 16 | void testSuite(C)(C metaObject) 17 | { 18 | assert(metaObject.isAggregate == true); 19 | assert(metaObject.isClass == false); 20 | assert(metaObject.isStruct == true); 21 | 22 | assert(metaObject.getName == "User"); 23 | assert(metaObject.getFullName == "witchcraft.unittests.structs.User"); 24 | } 25 | } 26 | 27 | unittest 28 | { 29 | auto metaObjectByProperty = User.metaof; 30 | auto metaObjectByFunction = getMeta!User(); 31 | 32 | assert(metaObjectByProperty !is null); 33 | assert(metaObjectByFunction !is null); 34 | 35 | testSuite(metaObjectByProperty); 36 | testSuite(metaObjectByFunction); 37 | } 38 | --------------------------------------------------------------------------------