1 
2 module witchcraft.mixins.methods;
3 
4 mixin template WitchcraftMethod(T)
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                 if(typeid(instance.type) != typeid(TypeInfo_Class)) {
147                     throw new Exception("The instance's type is not a class!");
148                 }
149 
150                 ClassInfo this_ = cast(ClassInfo) typeid(T);
151                 ClassInfo other = cast(ClassInfo) instance.type;
152 
153                 // Ensure both types exist and can be converted.
154                 if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_)))
155                 {
156                     assert(0, "The instance can't cast to `" ~ T.stringof ~ "`.");
157                 }
158                 // FIXME: Needing refactor or cleanup -@zhangxueping at 4/17/2019, 6:21:23 PM
159                 // The Variant's coerce can't handle a instance from a interface
160                 Unqual!T obj = instance.coerce!(Unqual!T);
161             }
162             else static if(is(T))
163             {
164                 Unqual!T obj = instance.get!(Unqual!T);
165             }
166             else
167             {
168                 alias obj = T;
169             }
170 
171             static if(!is(ReturnType!method == void))
172             {
173                 mixin("auto result = __traits(getOverloads, obj, name)[overload](" ~ invokeString ~ ");");
174 
175                 return Variant(result);
176             }
177             else
178             {
179                 mixin("__traits(getOverloads, obj, name)[overload](" ~ invokeString ~ ");");
180 
181                 return Variant(null);
182             }
183         }
184 
185         @property
186         final bool isAccessible() const
187         {
188             return true;
189         }
190 
191         @property
192         override bool isFinal() const
193         {
194             return __traits(isFinalFunction, method);
195         }
196 
197         @property
198         override bool isOverride() const
199         {
200             return __traits(isOverrideFunction, method);
201         }
202 
203         @property
204         override bool isStatic() const
205         {
206             return __traits(isStaticFunction, method);
207         }
208 
209         @property
210         bool isVarArgs() const
211         {
212             return variadicFunctionStyle!method != Variadic.no;
213         }
214     }
215 }