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 }