Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Ticket #129: bind.d

File bind.d, 59.5 kB (added by Ligustah, 8 years ago)

Changed bind.d, works in my tests, probably needs some more work though.

Line 
1 /******************************************************************************
2 This module contains scary template stuff to make it possible to wrap D functions,
3 classes, and structs and expose them as functions and types in MiniD.
4
5 This binding library is not supposed to be the most flexible or capable.  For
6 example, its class wrapping is meant to be usable with classes to whose source
7 code you don't necessarily have access to, or whose code you can't change (like
8 in third-party libraries).  This library has to sacrifice some efficiency and
9 capabilities to be able to do this. 
10
11 However, if you're really only concerned with integrating your $(I own) code with
12 MiniD, something like $(LINK2 xpose http://team0xf.com:8080/xf/file/37d8e57b1c4d/xpose/)
13 might be more appropriate.  Xpose allows you to add reflection info to your types
14 which you can then use to make bindings to MiniD.
15
16 License:
17 Copyright (c) 2008 Jarrett Billingsley
18
19 This software is provided 'as-is', without any express or implied warranty.
20 In no event will the authors be held liable for any damages arising from the
21 use of this software.
22
23 Permission is granted to anyone to use this software for any purpose,
24 including commercial applications, and to alter it and redistribute it freely,
25 subject to the following restrictions:
26
27     1. The origin of this software must not be misrepresented; you must not
28     claim that you wrote the original software. If you use this software in a
29     product, an acknowledgment in the product documentation would be
30     appreciated but is not required.
31
32     2. Altered source versions must be plainly marked as such, and must not
33     be misrepresented as being the original software.
34
35     3. This notice may not be removed or altered from any source distribution.
36 ******************************************************************************/
37
38 module minid.bind;
39
40 import tango.core.Traits;
41 import tango.core.Tuple;
42 import Utf = tango.text.convert.Utf;
43
44 import minid.ex;
45 import minid.interpreter;
46 import minid.types;
47 import minid.utils;
48 import minid.vm;
49
50 alias minid.utils.isArrayType isArrayType;
51
52 // ================================================================================================================================================
53 // Public
54 // ================================================================================================================================================
55
56 /**
57 Wraps a module.  This registers a custom module loader in the global modules.customLoaders
58 table of the given thread.  The members will not actually be wrapped until the module is imported
59 the first time.
60
61 Template Params:
62     name = The name of the module, in dotted form (like "foo.bar.baz").  This is the name that will
63         be used to import it.
64
65     Members = A variadic list of things to declare in this module.  These will be declared as module
66         globals, just as if you declared them globals in MiniD.  Supported member types include
67         WrapFunc, WrapNamespace, WrapValue, and WrapType.
68
69 Params:
70     t = This module's loader will be added into the global modules.customLoaders table accessible
71         from this thread.
72 */
73 public void WrapModule(char[] name, Members...)(MDThread* t)
74 {
75     makeModule(t, name, function uword(MDThread* t)
76     {
77         commonNamespace!(name, true, Members)(t);
78         return 0;
79     });
80 }
81
82 /**
83 Wraps any number of values into the global namespace accessible from the given thread.  This is
84 the root global namespace, outside of any modules.  Works just like WrapModule otherwise.
85 Supported member types include WrapFunc, WrapNamespace, WrapValue, and WrapType.
86
87 The wrapped values are immediately loaded into the global namespace.
88 */
89 public void WrapGlobals(Members...)(MDThread* t)
90 {
91     commonNamespace!("", true, Members)(t);
92 }
93
94 /**
95 Wraps a static function - that is, a function that doesn't have a 'this' parameter.  These four
96 template specializations allow you to fine-tune how the function is to be wrapped.
97
98 The first specialization takes just an alias to a function.  In this case, the first overload
99 of the function (if any) will be wrapped and the name of the function in MiniD will be the same
100 as in D.
101
102 The second specialization allows you to explicitly specify a function signature to choose, in the
103 case that the function you're wrapping is overloaded.  The signature should be a function type that
104 matches the signature of the overload you want to wrap.  In this case, though, the name in MiniD
105 will still be the name of the D function.
106
107 The third specialization allows you to rename the function without explicitly selecting an overload.
108
109 The fourth specialization allows you to both select an overload and give it the name that should
110 be used in MiniD.  This is the form you'll probably be using most often with overloaded D functions.
111
112 If you use one of the two forms where you explicitly specify the function signature, the resulting
113 wrapped function will only accept exactly as many parameters as are specified in the signature.
114 Otherwise, the wrapped function will be allowed to have optional parameters.
115 */
116 public struct WrapFunc(alias func)
117 {
118     const bool isFunc = true;
119     const char[] Name = NameOfFunc!(func);
120     mixin WrappedFunc!(func, Name, typeof(&func), false);
121 }
122
123 /// ditto
124 public struct WrapFunc(alias func, funcType)
125 {
126     const bool isFunc = true;
127     const char[] Name = NameOfFunc!(func);
128     mixin WrappedFunc!(func, Name, funcType, true);
129 }
130
131 /// ditto
132 public struct WrapFunc(alias func, char[] name)
133 {
134     const bool isFunc = true;
135     const char[] Name = name;
136     mixin WrappedFunc!(func, Name, typeof(&func), false);
137 }
138
139 /// ditto
140 public struct WrapFunc(alias func, char[] name, funcType)
141 {
142     const bool isFunc = true;
143     const char[] Name = name;
144     mixin WrappedFunc!(func, Name, funcType, true);
145 }
146
147 /**
148 Wraps a bunch of values into a namespace object.  This works virtually the same as WrapModule,
149 except that it's meant to be used as a member of something like WrapModule.  Legal member
150 types include WrapFunc, WrapValue, WrapNamespace, and WrapType.
151 */
152 public struct WrapNamespace(char[] name, members...)
153 {
154     const bool isNamespace = true;
155     const char[] Name = name;
156     alias members Values;
157 }
158
159 /**
160 Wraps a single value and gives it a name.  Despite the fact that the value parameter is
161 variadic, it is restricted to exactly one item.  It's variadic just so it can accept any
162 value type.
163 */
164 public struct WrapValue(char[] name, value...)
165 {
166     static assert(Value.length == 1 && isExpressionTuple!(Value), "WrapValue - must have exactly one expression");
167     const bool isValue = true;
168     const char[] Name = name;
169     alias value Value;
170 }
171
172 /**
173 Wraps a class or struct type.  This supports wrapping constructors (or static opCall for structs),
174 methods, properties (though they will be $(B functions) in MiniD), and arbitrary values.  That means
175 the valid member types are WrapCtors, WrapMethod, WrapProperty, and WrapValue.
176
177 Template Params:
178     Type = The class or struct type to be wrapped.
179
180     name = The name that will be given to the type in MiniD.
181
182     Members = The members of the type.
183     
184 Bugs:
185     Abstract classes cannot be wrapped.  D1 does not provide enough reflective information to do so reliably.
186 */
187 public struct WrapType(Type, char[] name = NameOfType!(Type), Members...)
188 {
189     // Because it's pointless (and MiniD has its own Object).
190     static assert(!is(Type == Object), "Wrapping Object is not allowed");
191     static assert(is(Type == class) || is(Type == struct), "Cannot wrap type " ~ Type.stringof);
192
193     const bool isType = true;
194     const char[] Name = name;
195
196     private static word init(char[] moduleName)(MDThread* t)
197     {
198         checkInitialized(t);
199
200         // Check if this type has already been wrapped
201         getWrappedClass(t, typeid(Type));
202
203         if(!isNull(t, -1))
204             throwException(t, "Native type " ~ NameOfType!(Type) ~ " cannot be wrapped more than once");
205
206         pop(t);
207        
208         // Wrap it
209         static if(is(Type == class))
210             WrappedClass!(Type, name, moduleName, Members).init(t);
211         else
212             WrappedStruct!(Type, name, moduleName, Members).init(t);
213
214         // Set the allocator
215         newFunction(t, &classAllocator, name ~ ".allocator");
216         setAllocator(t, -2);
217
218         // Set the class
219         setWrappedClass(t, typeid(Type));
220         return stackSize(t) - 1;
221     }
222
223     private static uword classAllocator(MDThread* t)
224     {
225         newInstance(t, 0, 1);
226
227         dup(t);
228         pushNull(t);
229         rotateAll(t, 3);
230         methodCall(t, 2, "constructor", 0);
231         return 1;
232     }
233 }
234
235 /**
236 D doesn't really provide any facilities for introspecting class constructors, so you'll have to specify
237 to the binding library the signatures of the constructors to expose.  You'll also have to do it for structs.
238 There can be at most one WrapCtors inside a WrapType, but since you specify as many constructors as you
239 want all at once, it doesn't matter.  The constructor signatures should be function types; the return type
240 is ignored, and only the parameter types are significant.
241
242 Unlike wrapping other functions, a form of overloading is allowed for constructors.  That is, you can have
243 a constructor that takes (int) and another that takes (float), wrap them as two separate types, and they
244 will be correctly dispatched when the type is instantiated in MiniD.  This also means that the usual
245 implicit conversion from int to float that happens when calling other functions will not happen when calling
246 constructors.
247 */
248 public struct WrapCtors(T...)
249 {
250     static assert(T.length > 0, "WrapCtors must be instantiated with at least one type");
251     const bool isCtors = true;
252     alias Unique!(QSort!(SortByNumParams, T)) Types;
253 }
254
255 /**
256 Wraps a method of a class or struct type.  The argument to this template will look like "A.foo" for a given
257 type "A".  Other than the fact that it's a method (and therefore takes 'this'), this works pretty much
258 exactly the same as WrapFunction, including the differences between the multiple specializations.
259 */
260 public struct WrapMethod(alias func)
261 {
262     const bool isMethod = true;
263     const char[] Name = NameOfFunc!(func);
264     const bool explicitType = false;
265     alias func Func;
266     alias typeof(&func) FuncType;
267 }
268
269 /// ditto
270 public struct WrapMethod(alias func, char[] name)
271 {
272     const bool isMethod = true;
273     const char[] Name = name;
274     const bool explicitType = false;
275     alias func Func;
276     alias typeof(&func) FuncType;
277 }
278
279 /// ditto
280 public struct WrapMethod(alias func, funcType)
281 {
282     const bool isMethod = true;
283     const char[] Name = NameOfFunc!(func);
284     const bool explicitType = true;
285     alias func Func;
286     alias funcType FuncType;
287 }
288
289 /// ditto
290 public struct WrapMethod(alias func, char[] name, funcType)
291 {
292     const bool isMethod = true;
293     const char[] Name = name;
294     const bool explicitType = true;
295     alias func Func;
296     alias funcType FuncType;
297 }
298
299 /**
300 Wraps a D "property."  D of course does not have real properties but only syntactic sugar for function
301 calls.  These wrap a pair of functions (or just one function, if the property is read-only) that denote
302 a property.  In MiniD, each property has a method named "_prop_name" which does the actual setting and
303 getting, and the wrapped type is given opField and opFieldAssign metamethods which dispatch field access
304 to the appropriate property accessors.  If you want to override the behavior of setting/getting a property,
305 you can do so by overriding the "_prop_name" method.
306
307 The D "property" must be one or two functions (either just a getter or a getter/setter pair).  The setter,
308 if any exists, must be able to take one parameter that is the same type as the getter's return type.
309 The setter may optionally return a value.
310
311 It doesn't matter whether you pass an alias to the setter or the getter to this; the library will figure
312 out which one you gave and which one it needs.  So if you have a property "x" of a type "A", it'll just
313 be WrapProperty!(A.x).
314
315 Since this is another variety of function wrapping, the parameters here all do the same thing as for
316 WrapFunction and WrapMethod.
317
318 Bugs:
319     Currently overridden setters/getters are not called polymorphically and therefore will not be called
320     by D code accessing the properties.
321 */
322 public struct WrapProperty(alias func)
323 {
324     const bool isProperty = true;
325     const char[] Name = NameOfFunc!(func);
326     const char[] DName = NameOfFunc!(func);
327     const bool readOnly = ReadOnly!(func, typeof(&func));
328     alias PropType!(func, typeof(&func)) propType;
329 }
330
331 /// ditto
332 public struct WrapProperty(alias func, char[] name)
333 {
334     const bool isProperty = true;
335     const char[] Name = name;
336     const char[] DName = NameOfFunc!(func);
337     const bool readOnly = ReadOnly!(func, typeof(&func));
338     alias PropType!(func, typeof(&func)) propType;
339 }
340
341 /// ditto
342 public struct WrapProperty(alias func, funcType)
343 {
344     const bool isProperty = true;
345     const char[] Name = NameOfFunc!(func);
346     const char[] DName = NameOfFunc!(func);
347     const bool readOnly = ReadOnly!(func, funcType);
348     alias PropType!(func, funcType) propType;
349 }
350
351 /// ditto
352 public struct WrapProperty(alias func, char[] name, funcType)
353 {
354     const bool isProperty = true;
355     const char[] Name = name;
356     const char[] DName = NameOfFunc!(func);
357     const bool readOnly = ReadOnly!(func, funcType);
358     alias PropType!(func, funcType) propType;
359 }
360
361 /**
362 Given a TypeInfo instance of the desired class/struct type (that is, typeid(SomeType)), pushes
363 the corresponding wrapped MiniD class, or pushes null if the type has not been wrapped.
364
365 $(B You probably won't have to call this function under normal circumstances.)
366
367 Params:
368     ti = The runtime TypeInfo instance of the desired type.
369
370 Returns:
371     The stack index of the newly-pushed value.
372 */
373 public word getWrappedClass(MDThread* t, TypeInfo ti)
374 {
375     if(auto tic = cast(TypeInfo_Class)ti)
376         return getWrappedClass(t, tic.info);
377     else
378     {
379         getRegistryVar(t, "minid.bind.WrappedClasses");
380         pushNativeObj(t, ti);
381
382         if(!opin(t, -1, -2))
383         {
384             pop(t, 2);
385             return pushNull(t);
386         }
387
388         idx(t, -2);
389         insertAndPop(t, -2);
390         return stackSize(t) - 1;
391     }
392 }
393
394 /**
395 Given a ClassInfo instance of the desired class type, pushes
396 the corresponding wrapped MiniD class, or pushes null if the type has not been wrapped.
397
398 $(B You probably won't have to call this function under normal circumstances.)
399
400 Params:
401     ci = The runtime ClassInfo instance of the desired class.
402
403 Returns:
404     The stack index of the newly-pushed value.
405 */
406 public word getWrappedClass(MDThread* t, ClassInfo ci)
407 {
408     getRegistryVar(t, "minid.bind.WrappedClasses");
409     pushNativeObj(t, ci);
410
411     if(!opin(t, -1, -2))
412     {
413         pop(t, 2);
414         return pushNull(t);
415     }
416
417     idx(t, -2);
418     insertAndPop(t, -2);
419     return stackSize(t) - 1;
420 }
421
422 /**
423 Given a ClassInfo instance of the desired class type, pushes
424 the corresponding wrapped MiniD class, or pushes null if the type has not been wrapped.
425 This version looks for a super class if a direct match cannot be found.
426
427 $(B You probably won't have to call this function under normal circumstances.)
428
429 Params:
430     ci = The runtime ClassInfo instance of the desired class.
431
432 Returns:
433     The stack index of the newly-pushed value.
434 */
435 public word getWrappedClassOrSuper(MDThread* t, ClassInfo ci)
436 {
437     getRegistryVar(t, "minid.bind.WrappedClasses");
438
439     while(ci !is Object.classinfo && ci !is null)
440     {
441         pushNativeObj(t, ci);
442
443         if(opin(t, -1, -2))
444         {
445             idx(t, -2);
446             insertAndPop(t, -2);
447             return stackSize(t) - 1;
448         }
449         else
450         {
451             pop(t);
452             ci = ci.base;
453         }
454     }
455
456     pop(t);
457     return pushNull(t);
458 }
459
460 /**
461 Expects a class object on top of the stack, and sets it to be the MiniD class that corresponds
462 to the given runtime TypeInfo object.  The class object is $(B not) popped off the stack.
463
464 $(B You probably won't have to call this function under normal circumstances.)
465 */
466 public void setWrappedClass(MDThread* t, TypeInfo ti)
467 {
468     if(auto tic = cast(TypeInfo_Class)ti)
469         return setWrappedClass(t, tic.info);
470     else
471     {
472         getRegistryVar(t, "minid.bind.WrappedClasses");
473         pushNativeObj(t, ti);
474         dup(t, -3);
475         idxa(t, -3);
476         pop(t);
477     }
478 }
479
480 /**
481 Expects a class object on top of the stack, and sets it to be the MiniD class that corresponds
482 to the given runtime ClassInfo object.  The class object is $(B not) popped off the stack.
483
484 $(B You probably won't have to call this function under normal circumstances.)
485 */
486 public void setWrappedClass(MDThread* t, ClassInfo ci)
487 {
488     getRegistryVar(t, "minid.bind.WrappedClasses");
489     pushNativeObj(t, ci);
490     dup(t, -3);
491     idxa(t, -3);
492     pop(t);
493 }
494
495 /**
496 Assuming a valid wrapped class is on the top of the stack, this function will take a D object
497 and push the corresponding MiniD instance.  If a MiniD instance has already been created for
498 this object, pushes that instance; otherwise, this will create an instance and link it to this
499 D object.  The class is popped off, meaning the wrapped instance takes its place.
500
501 $(B You probably won't have to call this function under normal circumstances.)
502
503 Params:
504     o = The D object to convert to a MiniD instance.
505
506 Returns:
507     The stack index of the newly-pushed instance.
508 */
509 public word getWrappedInstance(MDThread* t, Object o)
510 {
511     getRegistryVar(t, "minid.bind.WrappedInstances");
512     pushNativeObj(t, o);
513     idx(t, -2);
514     deref(t, -1);
515
516     if(isNull(t, -1))
517     {
518         pop(t, 2);
519
520         newInstance(t, -2, 1);
521         pushNativeObj(t, o);
522         setExtraVal(t, -2, 0);
523
524         pushNativeObj(t, o);
525         pushWeakRef(t, -2);
526         idxa(t, -4);
527
528         insertAndPop(t, -3);
529     }
530     else
531         insertAndPop(t, -4);
532
533     return stackSize(t) - 1;
534 }
535
536 /**
537 For a given D object instance, sets the MiniD instance at the given stack index to be
538 its corresponding object. 
539
540 $(B You probably won't have to call this function under normal circumstances.)
541
542 Params:
543     o = The D object that should be linked to the given MiniD instance.
544     idx = The stack index of the MiniD instance that should be linked to the given D object.
545 */
546 public void setWrappedInstance(MDThread* t, Object o, word idx)
547 {
548     getRegistryVar(t, "minid.bind.WrappedInstances");
549     pushNativeObj(t, o);
550     pushWeakRef(t, idx);
551     idxa(t, -3);
552     pop(t);
553 }
554
555 /**
556 Checks that the 'this' parameter passed to a native function is an instance of the given struct
557 type, and returns a pointer to the struct object that is referenced by 'this'.
558
559 Template Params:
560     Type = The D struct type that corresponds to 'this'.
561
562     FullName = The name of the type in MiniD, in dotted form.
563
564 Returns:
565     A pointer to the struct object referenced by 'this'.
566 */
567 public Type* checkStructSelf(Type, char[] FullName)(MDThread* t)
568 {
569     static assert(is(Type == struct), "checkStructSelf must be instantiated with a struct type, not with '" ~ Type.stringof ~ "'");
570     checkInstParam(t, 0, FullName);
571     getExtraVal(t, 0, 0);
572     auto ret = &(cast(StructWrapper!(Type))cast(void*)getNativeObj(t, -1)).inst;
573     pop(t);
574     return ret;
575 }
576
577 /**
578 Checks that the 'this' parameter passed to a native function is an instance of the given class
579 type, and returns the reference to the D object instance that is referenced by 'this'.
580
581 Template Params:
582     Type = The D class type that corresponds to 'this'.
583
584     FullName = The name of the type in MiniD, in dotted form.
585
586 Returns:
587     A reference to the D object instance referenced by 'this'.
588 */
589 static Type checkClassSelf(Type, char[] FullName)(MDThread* t)
590 {
591     static if(is(Type == interface))
592     {
593         checkInstParam(t, 0, FullName);
594         getExtraVal(t, 0, 0);
595         auto ret = cast(Type)cast(Object)cast(void*)getNativeObj(t, -1);
596         pop(t);
597         return ret;
598     }
599     else
600     {
601         static assert(is(Type == class), "checkClassSelf must be instantiated with a class type, not with '" ~ Type.stringof ~ "'");
602         checkInstParam(t, 0, FullName);
603         getExtraVal(t, 0, 0);
604         auto ret = cast(Type)cast(void*)getNativeObj(t, -1);
605         pop(t);
606         return ret;
607     }
608 }
609
610 /**
611 It's superPush!  It's better than your average push.
612
613 This is a templated push function that will take any D type that is convertible to a MiniD type
614 and push its MiniD conversion onto the stack.  This includes not only simple value types, but also
615 arrays, associative arrays, classes, and structs.  Classes and structs are convertible as long as they
616 have been wrapped.  Arrays are convertible as long as their element type is convertible.  AAs are
617 convertible as long as their key and value types are convertible.  Arrays will become MiniD arrays,
618 and AAs will become MiniD tables.  Classes and structs will become MiniD instances of the wrapped
619 MiniD class type.
620
621 Returns:
622     The stack index of the newly-pushed value.
623 */
624 public word superPush(Type)(MDThread* t, Type val)
625 {
626     alias realType!(Type) T;
627
628     static if(is(T == bool))
629         return pushBool(t, cast(T)val);
630     else static if(isIntegerType!(T))
631         return pushInt(t, cast(T)val);
632     else static if(isRealType!(T))
633         return pushFloat(t, cast(T)val);
634     else static if(isCharType!(T))
635         return pushChar(t, cast(T)val);
636     else static if(isStringType!(T))
637     {
638         static if(is(T : char[]))
639             return pushString(t, cast(T)val);
640         else
641             return pushString(t, Utf.toString(cast(T)val));
642     }
643     else static if(isAAType!(T))
644     {
645         auto ret = newTable(t, val.length);
646
647         foreach(k, v; val)
648         {
649             superPush(t, k);
650             superPush(t, v);
651             idxa(t, ret);
652         }
653
654         return ret;
655     }
656     else static if(isArrayType!(T))
657     {
658         auto ret = newArray(t, val.length);
659
660         foreach(i, v; val)
661         {
662             superPush(t, v);
663             idxai(t, ret, i);
664         }
665
666         return ret;
667     }
668     else static if(is(T == interface))
669     {       
670         auto obj = cast(Object)val;
671
672         if(obj is null)
673             return pushNull(t);
674         else
675         {
676             getWrappedClassOrSuper(t, obj.classinfo);
677            
678             if(isNull(t, -1))
679                 throwException(t, "Cannot convert class {} to a MiniD value; class type has not been wrapped", typeid(T));
680             else
681                 return getWrappedInstance(t, obj);
682         }
683        
684         assert(false);
685     }
686     else static if(is(T : Object))
687     {
688         if(val is null)
689             return pushNull(t);
690         else
691         {
692             getWrappedClassOrSuper(t, val.classinfo);
693            
694             if(isNull(t, -1))
695                 throwException(t, "Cannot convert class {} to a MiniD value; class type has not been wrapped", typeid(T));
696             else
697                 return getWrappedInstance(t, val);
698         }
699        
700         assert(false);
701     }
702     else static if(is(T == struct))
703     {
704         getWrappedClass(t, typeid(T));
705
706         if(isNull(t, -1))
707             throwException(t, "Cannot convert struct {} to a MiniD value; struct type has not been wrapped", typeid(T));
708
709         newInstance(t, -1, 1);
710         insertAndPop(t, -2);
711         pushNativeObj(t, new StructWrapper!(Type)(val));
712         setExtraVal(t, -2, 0);
713         return stackSize(t) - 1;
714     }
715     else static if(is(T == MDThread*))
716         return pushThread(t, cast(T)val);
717     else
718         static assert(false, "superPush - Invalid argument type '" ~ T.stringof ~ "'");
719 }
720
721 /**
722 Like superPush, but pushes multiple values onto the stack in one function call.  Calls superPush
723 internally, so any types that are legal to pass to superPush are legal to pass to this.
724
725 Params:
726     arg1 = The first value to push.  This is separated to force you to push at least one value.
727     args = Any additional values to push.
728
729 Returns:
730     The stack index of the first value that was pushed.
731 */
732 public word multiPush(T, U...)(MDThread* t, T arg1, U args)
733 {
734     auto ret = superPush(t, arg1);
735
736     foreach(i, arg; args)
737         superPush(t, args[i]);
738
739     return ret;
740 }
741
742 /**
743 The inverse of superPush, this function allows you to get any type of value from the MiniD stack
744 and convert it into a D type.  The rules in this direction are pretty much the same as in the other:
745 a MiniD array can only be converted into a D array as long as its elements can be converted to the
746 D array's element type, and similarly for MiniD tables.
747
748 Strings will also be converted to the correct Unicode encoding.  Keep in mind, however, that this
749 function will duplicate the string data onto the D heap, unlike the raw API getString function.
750 This is because handing off pointers to internal MiniD memory to arbitrary D libraries is probably
751 not a good idea.
752 */
753 public Type superGet(Type)(MDThread* t, word idx)
754 {
755     alias realType!(Type) T;
756
757     static if(!isStringType!(T) && isArrayType!(T))
758     {
759         alias typeof(T[0]) ElemType;
760
761         if(!isArray(t, idx))
762         {
763             pushTypeString(t, idx);
764             throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "'", getString(t, -1));
765         }
766
767         auto data = getArray(t, idx).slice;
768         auto ret = new T(data.length);
769
770         foreach(i, ref elem; data)
771         {
772             auto elemIdx = push(t, elem);
773
774             if(!canCastTo!(ElemType)(t, elemIdx))
775             {
776                 pushTypeString(t, idx);
777                 pushTypeString(t, elemIdx);
778                 throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "': element {} should be '" ~
779                     ElemType.stringof ~ "', not '{}'", getString(t, -2), i, getString(t, -1));
780             }
781
782             ret[i] = superGet!(ElemType)(t, elemIdx);
783             pop(t);
784         }
785
786         return cast(Type)ret;
787     }
788     else static if(isAAType!(T))
789     {
790         alias typeof(T.init.keys[0]) KeyType;
791         alias typeof(T.init.values[0]) ValueType;
792
793         if(!isTable(t, idx))
794         {
795             pushTypeString(t, idx);
796             throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "'", getString(t, -1));
797         }
798
799         T ret;
800
801         foreach(ref key, ref val; getTable(t, idx).data)
802         {
803             auto keyIdx = push(t, key);
804
805             if(!canCastTo!(KeyType)(t, keyIdx))
806             {
807                 pushTypeString(t, idx);
808                 pushTypeString(t, keyIdx);
809                 throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "': key should be '" ~
810                     KeyType.stringof ~ "', not '{}'", getString(t, -2), getString(t, -1));
811             }
812
813             auto valIdx = push(t, val);
814
815             if(!canCastTo!(ValueType)(t, valIdx))
816             {
817                 pushTypeString(t, idx);
818                 pushTypeString(t, valIdx);
819                 throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "': value should be '" ~
820                     ValueType.stringof ~ "', not '{}'", getString(t, -2), getString(t, -1));
821             }
822
823             ret[superGet!(KeyType)(t, keyIdx)] = superGet!(ValueType)(t, valIdx);
824             pop(t, 2);
825         }
826
827         return cast(Type)ret;
828     }
829     else static if(is(T == interface))
830     {
831         idx = absIndex(t, idx);
832
833         if(isNull(t, idx))
834             return null;
835
836         getWrappedClass(t, typeid(T));
837
838         if(!as(t, idx, -1))
839             paramTypeError(t, idx, "instance of " ~ Type.stringof);
840
841         pop(t);
842
843         getExtraVal(t, idx, 0);
844         auto ret = cast(Type)cast(Object)cast(void*)getNativeObj(t, -1);
845         pop(t);
846
847         return ret;
848     }
849     else static if(is(T : Object))
850     {
851         idx = absIndex(t, idx);
852
853         if(isNull(t, idx))
854             return null;
855
856         getWrappedClass(t, T.classinfo);
857
858         if(!as(t, idx, -1))
859             paramTypeError(t, idx, "instance of " ~ Type.stringof);
860
861         pop(t);
862
863         getExtraVal(t, idx, 0);
864         auto ret = cast(Type)cast(void*)getNativeObj(t, -1);
865         pop(t);
866
867         return ret;
868     }
869     else static if(is(T == struct))
870     {
871         idx = absIndex(t, idx);
872
873         getWrappedClass(t, typeid(T));
874         // the wrapped class will always be non-null, since the struct got on the stack in the first place..
875
876         if(!as(t, idx, -1))
877             paramTypeError(t, idx, "instance of " ~ Type.stringof);
878
879         pop(t);
880
881         getExtraVal(t, idx, 0);
882
883         static if(!is(Type == T))
884             auto ret = cast(Type)(cast(StructWrapper!(T))getNativeObj(t, -1)).inst;
885         else
886             auto ret = (cast(StructWrapper!(T))getNativeObj(t, -1)).inst;
887
888         pop(t);
889
890         return ret;
891     }
892     else
893     {
894         if(!canCastTo!(T)(t, idx))
895         {
896             pushTypeString(t, idx);
897             throwException(t, "superGet - Cannot convert MiniD type '{}' to D type '" ~ Type.stringof ~ "'", getString(t, -1));
898         }
899
900         static if(is(T == bool))
901         {
902             return cast(Type)getBool(t, idx);
903         }
904         else static if(isIntegerType!(T))
905         {
906             return cast(Type)getInt(t, idx);
907         }
908         else static if(isRealType!(T))
909         {
910             if(isInt(t, idx))
911                 return cast(Type)getInt(t, idx);
912             else if(isFloat(t, idx))
913                 return cast(Type)getFloat(t, idx);
914             else
915                 assert(false, "superGet!(" ~ T.stringof ~ ")");
916         }
917         else static if(isCharType!(T))
918         {
919             return cast(Type)getChar(t, idx);
920         }
921         else static if(isStringType!(T))
922         {
923             static if(is(T == char[]))
924                 return cast(Type)getString(t, idx).dup;
925             else static if(is(T == wchar[]))
926                 return cast(Type)Utf.toString16(getString(t, idx));
927             else
928                 return cast(Type)Utf.toString32(getString(t, idx));
929         }
930         else
931             static assert(false, "superGet - Invalid argument type '" ~ Type.stringof ~ "'");
932     }
933 }
934
935 /**
936 Like superGet, but gets multiple consecutive values off the stack.  There must be at least
937 as many values after the start index as you have values to get.  This calls superGet internally,
938 so any types that are legal to get with superGet are legal here too.
939
940 Params:
941     start = The stack index of the first value to retrieve.
942     arg1 = The first value to get.  This is separate to force you to get at least one value.
943     args = Any additional values to get.
944 */
945 public void multiGet(T, U...)(MDThread* t, word start, ref T arg1, ref U args)
946 {
947     if(stackSize(t) - start < (U.length + 1))
948         throwException(t, "multiGet - Attempting to get more values ({}) than there are after the given index ({} values)", U.length + 1, stackSize(t) - start);
949
950     arg1 = superGet!(T)(t, start);
951
952     foreach(i, arg; args)
953         args[i] = superGet!(U[i])(t, start + i + 1);
954 }
955
956 /**
957 Returns true if the value at the given stack index can be converted to the given D type,
958 or false otherwise.  That's all.
959 */
960 public bool canCastTo(Type)(MDThread* t, word idx)
961 {
962     alias realType!(Type) T;
963
964     static if(is(T == bool))
965     {
966         return isBool(t, idx);
967     }
968     else static if(isIntegerType!(T))
969     {
970         return isInt(t, idx);
971     }
972     else static if(isRealType!(T))
973     {
974         return isNum(t, idx);
975     }
976     else static if(isCharType!(T))
977     {
978         return isChar(t, idx);
979     }
980     else static if(isStringType!(T) || is(T : MDString))
981     {
982         return isString(t, idx);
983     }
984     else static if(isAAType!(T))
985     {
986         if(!isTable(t, idx))
987             return false;
988
989         alias typeof(T.init.keys[0]) KeyType;
990         alias typeof(T.init.values[0]) ValueType;
991
992         foreach(ref k, ref v; mTable)
993         {
994             auto keyIdx = push(t, k);
995
996             if(!canCastTo!(KeyType)(t, keyIdx))
997             {
998                 pop(t);
999                 return false;
1000             }
1001
1002             auto valIdx = push(t, v);
1003
1004             if(!canCastTo!(ValueType)(t, valIdx))
1005             {
1006                 pop(t, 2);
1007                 return false;
1008             }
1009
1010             pop(t, 2);
1011         }
1012
1013         return true;
1014     }
1015     else static if(isArrayType!(T))
1016     {
1017         if(!isArray(t, idx))
1018             return false;
1019
1020         alias typeof(T[0]) ElemType;
1021
1022         foreach(ref v; mArray)
1023         {
1024             auto valIdx = push(t, v);
1025
1026             if(!canCastTo!(ElemType)(t, valIdx))
1027             {
1028                 pop(t);
1029                 return false;
1030             }
1031
1032             pop(t);
1033         }
1034
1035         return true;
1036     }
1037     else static if(is(T : Object))
1038     {
1039         if(isNull(t, idx))
1040             return true;
1041
1042         getWrappedClass(t, T.classinfo);
1043         auto ret = as(t, idx, -1);
1044         pop(t);
1045         return ret;
1046     }
1047     else static if(is(T == struct))
1048     {
1049         getWrappedClass(t, typeid(T));
1050         auto ret = as(t, idx, -1);
1051         pop(t);
1052         return ret;
1053     }
1054     else
1055         return false;
1056 }
1057
1058 // ================================================================================================================================================
1059 // Private
1060 // ================================================================================================================================================
1061
1062 private template PropAnalysis(alias func, funcType)
1063 {
1064     alias ParameterTupleOf!(funcType) Args;
1065
1066     static if(Args.length == 0)
1067     {
1068         alias realType!(ReturnTypeOf!(funcType)) propType;
1069
1070         static assert(!is(propType == void), "property getter '" ~ NameOfFunc!(func) ~ "' may not return void");
1071
1072         static if(is(typeof(func(InitOf!(propType))) T))
1073             const bool readOnly = false;
1074         else
1075             const bool readOnly = true;
1076     }
1077     else
1078     {
1079         const bool readOnly = false;
1080         alias Args[0] propType;
1081     }
1082 }
1083
1084 private template ReadOnly(alias func, funcType)
1085 {
1086     const bool ReadOnly = PropAnalysis!(func, funcType).readOnly;
1087 }
1088
1089 private template PropType(alias func, funcType)
1090 {
1091     alias PropAnalysis!(func, funcType).propType PropType;
1092 }
1093
1094 private void commonNamespace(char[] name, bool isModule, Members...)(MDThread* t)
1095 {
1096     static if(!isModule)
1097         newNamespace(t, name);
1098
1099     foreach(i, member; Members)
1100     {
1101         static if(is(typeof(member.isFunc)))
1102             newFunction(t, &member.WrappedFunc, member.Name);
1103         else static if(is(typeof(member.isNamespace)))
1104             commonNamespace!(member.Name, false, member.Values)(t);
1105         else static if(is(typeof(member.isValue)))
1106             superPush(t, member.Value);
1107         else static if(is(typeof(member.isType)))
1108             member.init!(name)(t);
1109         else static if(isModule)
1110             static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped module '" ~ name ~ "'");
1111         else
1112             static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped namespace '" ~ name ~ "'");
1113
1114         static if(isModule)
1115             newGlobal(t, member.Name);
1116         else
1117             fielda(t, -2, member.Name);
1118     }
1119 }
1120
1121 private void checkInitialized(MDThread* t)
1122 {
1123     getRegistry(t);
1124     pushString(t, "minid.bind.initialized");
1125
1126     if(!opin(t, -1, -2))
1127     {
1128         newTable(t);       fielda(t, -3, "minid.bind.WrappedClasses");
1129         newTable(t);       fielda(t, -3, "minid.bind.WrappedInstances");
1130         pushBool(t, true); fielda(t, -3);
1131         pop(t);
1132     }
1133     else
1134         pop(t, 2);
1135 }
1136
1137 private word pushStructClass(Type, char[] ModName, char[] StructName)(MDThread* t)
1138 {
1139     return newClass(t, StructName);
1140 }
1141
1142 private class WrappedClass(Type, char[] _classname_, char[] moduleName, Members...) : Type
1143 {
1144     protected MDVM* _vm_;
1145
1146     const char[] typeName = NameOfType!(Type);
1147     alias GetCtors!(Members) Ctors;
1148     static assert(Ctors.length <= 1, "Cannot have more than one WrapCtors for type " ~ typeName);
1149
1150     static if(moduleName.length == 0)
1151         const TypeName = _classname_;
1152     else
1153         const TypeName = moduleName ~ "." ~ _classname_;
1154        
1155     MDThread* _haveMDOverload_(char[] methodName)
1156     {
1157         auto t = currentThread(_vm_);
1158
1159         getRegistryVar(t, "minid.bind.WrappedInstances");
1160         pushNativeObj(t, this);
1161         idx(t, -2);
1162         deref(t, -1);
1163
1164         if(isNull(t, -1))
1165         {
1166             pop(t, 3);
1167             return null;
1168         }
1169         else
1170         {
1171             superOf(t, -1);
1172             field(t, -1, methodName);
1173
1174             if(funcIsNative(t, -1))
1175             {
1176                 pop(t, 5);
1177                 return null;
1178             }
1179             else
1180             {
1181                 pop(t, 2);
1182                 insertAndPop(t, -3);
1183                 return t;
1184             }
1185         }
1186     }
1187
1188     static if(Ctors.length == 1)
1189     {
1190         // alias Ctors[0].Types blah; doesn't parse right
1191         alias Ctors[0] DUMMY;
1192        
1193         static if(is(typeof(new Type())))
1194             alias Unique!(Tuple!(void function(), DUMMY.Types)) CleanCtors;
1195         else
1196             alias DUMMY.Types CleanCtors;
1197
1198         mixin(ClassCtorShims!(CleanCtors));
1199
1200         private static uword constructor(MDThread* t)
1201         {
1202             auto numParams = stackSize(t) - 1;
1203             checkInstParam(t, 0, TypeName);
1204
1205             static if(is(typeof(new Type())))
1206                 const minArgs = 0;
1207             else
1208                 const minArgs = ParameterTupleOf!(CleanCtors[0]).length;
1209
1210             const maxArgs = ParameterTupleOf!(CleanCtors[$ - 1]).length;
1211
1212             if(numParams < minArgs)
1213                 throwException(t, "At least " ~ minArgs.stringof ~ " parameter" ~ (minArgs == 1 ? "" : "s") ~ " expected, not {}", numParams);
1214
1215             if(numParams > maxArgs)
1216                 numParams = maxArgs;
1217
1218             static if(minArgs == 0)
1219             {
1220                 if(numParams == 0)
1221                 {
1222                     auto obj = new typeof(this)(getVM(t));
1223                     pushNativeObj(t, obj);
1224                     setExtraVal(t, 0, 0);
1225                     setWrappedInstance(t, obj, 0);
1226                     return 0;
1227                 }
1228             }
1229
1230             const Switch = ClassCtorCases!(CleanCtors);
1231             mixin(Switch);
1232
1233             auto buf = StrBuffer(t);
1234
1235             buf.addChar('(');
1236
1237             if(numParams > 0)
1238             {
1239                 pushTypeString(t, 1);
1240                 buf.addTop();
1241
1242                 for(uword i = 2; i <= numParams; i++)
1243                 {
1244                     buf.addString(", ");
1245                     pushTypeString(t, i);
1246                     buf.addTop();
1247                 }
1248             }
1249
1250             buf.addChar(')');
1251             buf.finish();
1252             throwException(t, "Parameter list {} passed to constructor does not match any wrapped constructors", getString(t, -1));
1253         }
1254     }
1255     else
1256     {
1257         static assert(is(typeof(new Type())), "Cannot call default constructor for class " ~ typeName ~ "; please wrap a constructor explicitly");
1258
1259         private this(MDVM* vm)
1260         {
1261             _vm_ = vm;
1262
1263             // BUG: is, uh, _ctor supposed to be usable?
1264             static if(is(typeof(&Type._ctor)))
1265                 super();
1266         }
1267
1268         private static uword constructor(MDThread* t)
1269         {
1270             checkInstParam(t, 0, TypeName);
1271             auto obj = new typeof(this)(getVM(t));
1272             pushNativeObj(t, obj);
1273             setExtraVal(t, 0, 0);
1274             setWrappedInstance(t, obj, 0);
1275             return 0;
1276         }
1277     }
1278
1279     mixin ClassOverrideMethods!(Type, TypeName, GetMethods!(Members));
1280     mixin ClassMiniDMethods!(Type, TypeName, GetMethods!(Members));
1281     mixin ClassProperties!(Type, GetProperties!(Members));
1282
1283     private static word init(MDThread* t)
1284     {
1285         alias BaseTypeTupleOf!(Type)[0] BaseClass;
1286
1287         static if(!is(BaseClass == Object))
1288             auto base = getWrappedClass(t, BaseClass.classinfo);
1289         else
1290             auto base = pushNull(t);
1291
1292         newClass(t, base, _classname_);
1293
1294         foreach(i, member; Members)
1295         {
1296             static if(is(typeof(member.isMethod)))
1297             {
1298                 auto f = mixin("&md_" ~ member.Name);
1299                 newFunction(t, f, _classname_ ~ "." ~ member.Name);
1300                 fielda(t, -2, member.Name);
1301             }
1302             else static if(is(typeof(member.isProperty)))
1303             {
1304                 auto f = mixin("&_prop_" ~ member.Name);
1305                 newFunction(t, f, _classname_ ~ "._prop_" ~ member.Name);
1306                 fielda(t, -2, "_prop_" ~ member.Name);
1307             }
1308             else static if(is(typeof(member.isCtors)))
1309             {
1310                 // ignore
1311             }
1312             else static if(is(typeof(member.isValue)))
1313             {
1314                 superPush(t, member.Value);
1315                 fielda(t, -2, member.Name);
1316             }
1317             else
1318                 static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped type '" ~ typeName ~ "'");
1319         }
1320
1321         static if(haveProperties)
1322         {
1323             newFunction(t, &opField, _classname_ ~ ".opField");             fielda(t, -2, "opField");
1324             newFunction(t, &opFieldAssign, _classname_ ~ ".opFieldAssign"); fielda(t, -2, "opFieldAssign");
1325         }
1326
1327         newFunction(t, &constructor, _classname_ ~ ".constructor");
1328         fielda(t, -2, "constructor");
1329
1330         insertAndPop(t, -2);
1331         return stackSize(t) - 1;
1332     }
1333 }
1334
1335 private class StructWrapper(Type)
1336 {
1337     Type inst;
1338    
1339     this(Type t)
1340     {
1341         inst = t;
1342     }
1343 }
1344
1345 private struct WrappedStruct(Type, char[] name, char[] moduleName, Members...)
1346 {
1347 static:
1348     const char[] typeName = NameOfType!(Type);
1349     alias GetCtors!(Members) Ctors;
1350     static assert(Ctors.length <= 1, "Cannot have more than one WrapCtors for type " ~ typeName);
1351
1352     static if(moduleName.length == 0)
1353         const TypeName = name;
1354     else
1355         const TypeName = moduleName ~ "." ~ name;
1356
1357     static if(Ctors.length == 1)
1358     {
1359         // alias Ctors[0].Types blah; doesn't parse right
1360         alias Ctors[0] DUMMY;
1361         alias DUMMY.Types CleanCtors;
1362
1363         private static uword constructor(MDThread* t)
1364         {
1365             auto numParams = stackSize(t) - 1;
1366             checkInstParam(t, 0, TypeName);
1367
1368             static if(is(typeof(Type())))
1369                 const minArgs = 0;
1370             else
1371                 const minArgs = ParameterTupleOf!(CleanCtors[0]).length;
1372
1373             const maxArgs = ParameterTupleOf!(CleanCtors[$ - 1]).length;
1374
1375             if(numParams < minArgs)
1376                 throwException(t, "At least " ~ minArgs.stringof ~ " parameter" ~ (minArgs == 1 ? "" : "s") ~ " expected, not {}", numParams);
1377
1378             if(numParams > maxArgs)
1379                 numParams = maxArgs;
1380
1381             static if(minArgs == 0)
1382             {
1383                 if(numParams == 0)
1384                 {
1385                     pushNativeObj(t, new StructWrapper!(Type)(Type()));
1386                     setExtraVal(t, 0, 0);
1387                     return 0;
1388                 }
1389             }
1390
1391             const Switch = StructCtorCases!(CleanCtors);
1392             mixin(Switch);
1393
1394             auto buf = StrBuffer(t);
1395
1396             buf.addChar('(');
1397
1398             if(numParams > 0)
1399             {
1400                 pushTypeString(t, 1);
1401                 buf.addTop();
1402
1403                 for(uword i = 2; i <= numParams; i++)
1404                 {
1405                     buf.addString(", ");
1406                     pushTypeString(t, i);
1407                     buf.addTop();
1408                 }
1409             }
1410
1411             buf.addChar(')');
1412             buf.finish();
1413             throwException(t, "Parameter list {} passed to constructor does not match any wrapped constructors", getString(t, -1));
1414         }
1415     }
1416     else
1417     {
1418         static assert(is(typeof(Type())), "Cannot call default constructor for struct " ~ typeName ~ "; please wrap a constructor explicitly");
1419
1420         private static uword constructor(MDThread* t)
1421         {
1422             checkInstParam(t, 0, TypeName);
1423             pushNativeObj(t, new StructWrapper!(Type)(Type()));
1424             setExtraVal(t, 0, 0);
1425             return 0;
1426         }
1427     }
1428
1429     mixin StructProperties!(Type, StructFieldsToProps!(Type), GetProperties!(Members));
1430
1431     private static word init(MDThread* t)
1432     {
1433         pushStructClass!(Type, moduleName, name)(t);
1434
1435         foreach(i, member; Tuple!(StructFieldsToProps!(Type), Members))
1436         {
1437             static if(is(typeof(member.isMethod)))
1438             {
1439                 auto f = &WrappedStructMethod!(member.Func, member.FuncType, Type, TypeName, member.explicitType);
1440                 newFunction(t, f, name ~ "." ~ member.Name);
1441                 fielda(t, -2, member.Name);
1442             }
1443             else static if(is(typeof(member.isProperty)))
1444             {
1445                 auto f = mixin("&_prop_" ~ member.Name);
1446                 newFunction(t, f, name ~ "._prop_" ~ member.Name);
1447                 fielda(t, -2, "_prop_" ~ member.Name);
1448             }
1449             else static if(is(typeof(member.isCtors)))
1450             {
1451                 // ignore
1452             }
1453             else static if(is(typeof(member.isValue)))
1454             {
1455                 superPush(t, member.Value);
1456                 fielda(t, -2, member.Name);
1457             }
1458             else
1459                 static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped type '" ~ typeName ~ "'");
1460         }
1461
1462         static if(haveProperties)
1463         {
1464             newFunction(t, &opField, name ~ ".opField");             fielda(t, -2, "opField");
1465             newFunction(t, &opFieldAssign, name ~ ".opFieldAssign"); fielda(t, -2, "opFieldAssign");
1466         }
1467
1468         newFunction(t, &constructor, name ~ ".constructor");
1469         fielda(t, -2, "constructor");
1470
1471         return stackSize(t) - 1;
1472     }
1473 }
1474
1475 template ClassCtorShims(Ctors...)
1476 {
1477     const ClassCtorShims = ClassCtorShimsImpl!(0, Ctors);
1478 }
1479
1480 template ClassCtorShimsImpl(uint idx, Ctors...)
1481 {
1482     static if(idx >= Ctors.length)
1483         const ClassCtorShimsImpl = "";
1484     else
1485     {
1486         static if(ParameterTupleOf!(Ctors[idx]).length == 0)
1487         {
1488             const ClassCtorShimsImpl =
1489             "this(MDVM* vm)\n"
1490             "{\n"
1491             "   _vm_ = vm;\n"
1492             "   super();\n"
1493             "}\n" ~ ClassCtorShimsImpl!(idx + 1, Ctors);
1494         }
1495         else
1496         {
1497             const ClassCtorShimsImpl =
1498             "this(MDVM* vm, ParameterTupleOf!(CleanCtors[" ~ idx.stringof ~ "]) args)\n"
1499             "{\n"
1500             "   _vm_ = vm;\n"
1501             "   super(args);\n"
1502             "}\n" ~ ClassCtorShimsImpl!(idx + 1, Ctors);
1503         }
1504     }
1505 }
1506
1507 // When you wrap a method, three things happen.  The first is that an overriding D method is created
1508 // in the shim class which detects whether or not a MiniD overload exists, and dispatches appropriately.
1509 // Continued below..
1510 private template ClassOverrideMethods(Type, char[] TypeName) {}
1511 private template ClassOverrideMethods(Type, char[] TypeName, alias X, T...)
1512 {
1513     mixin("ReturnTypeOf!(X.FuncType) " ~ X.Name ~ "(ParameterTupleOf!(X.Func) args)\n"
1514     "{\n" ~
1515     "   if(auto t = _haveMDOverload_(`" ~ X.Name ~ "`))\n"
1516     "   {\n"
1517     "       // instance is on top\n"
1518     "       auto reg = stackSize(t) - 1;\n"
1519     "       pushNull(t);\n"
1520     "       foreach(arg; args) superPush(t, arg);\n" ~
1521     (is(ReturnTypeOf!(X.FuncType) == void)
1522     ?
1523     "       methodCall(t, reg, `" ~ X.Name ~ "`, 0); return;\n"
1524     :
1525     "       methodCall(t, reg, `" ~ X.Name ~ "`, 1);\n"
1526     "       auto ret = superGet!(ReturnTypeOf!(X.FuncType))(t, -1);\n"
1527     "       pop(t); return ret;\n") ~
1528     "   }\n"
1529     "   else\n"
1530     "       return super." ~ NameOfFunc!(X.Func) ~ "(args);\n"
1531     "}\n");
1532
1533      mixin ClassOverrideMethods!(Type, TypeName, T);
1534 }
1535
1536 // ..the other two things that happen is that two methods - one static and one dynamic - are created.
1537 // The static one is the one that is actually exposed to MiniD and all it does is check that the 'this'
1538 // parameter is correct and calls the dynamic one.  The dynamic one gets the params off the stack and
1539 // calls the real D method.
1540 private template ClassMiniDMethods(Type, char[] TypeName) {}
1541 private template ClassMiniDMethods(Type, char[] TypeName, alias X, T...)
1542 {
1543     mixin("mixin .WrappedMethod!(X.Func, X.FuncType, Type, TypeName, X.explicitType) wrapped_" ~ X.Name ~ ";");
1544
1545     mixin(
1546     "static uword md_" ~ X.Name ~ "(MDThread* t)\n"
1547     "{\nauto numParams = stackSize(t) - 1;\n" ~
1548     (X.explicitType
1549     ? " const minArgs = NumParams!(X.FuncType);\n"
1550     : " const minArgs = MinArgs!(X.Func);\n") ~
1551     "   const maxArgs = NumParams!(X.FuncType);\n"
1552
1553     "   if(numParams < minArgs)\n"
1554     "       throwException(t, `At least ` ~ minArgs.stringof ~ ` parameter` ~ (minArgs == 1 ? `` : `s`) ~ ` expected, not {}`, numParams);\n"
1555
1556     "   if(numParams > maxArgs)\n"
1557     "       numParams = maxArgs;\n"
1558
1559     "   auto self = checkClassSelf!(Type, TypeName)(t);\n"
1560    
1561     "   assert(self !is null, `Invalid 'this' parameter passed to method ` ~ Type.stringof ~ `.` ~ X.Name);\n"
1562
1563     "   if(auto wrappedSelf = cast(typeof(this))self)\n"
1564     "       return wrappedSelf.wrapped_" ~ X.Name ~ ".WrappedMethod(t);\n"
1565     "   else\n"
1566     "       return WrappedNativeMethod!(X.Func, X.FuncType, X.explicitType)(t, self);\n"
1567     "}\n");
1568
1569     mixin ClassMiniDMethods!(Type, TypeName, T);
1570 }
1571
1572 // For each property that the class defines, two things happen - one, a method
1573 // called _prop_name is created that does the actual getting and setting.  Two,
1574 // an entry is created in the opField and opFieldAssign methods that will call
1575 // that method when the given field is accessed.
1576 // If the class defines no properties, no opField[Assign] methods are generated.
1577
1578 private template ClassProperties(Type)
1579 {
1580     const haveProperties = false;
1581 }
1582
1583 private template ClassProperties(Type, X, T...)
1584 {
1585     const haveProperties = true;
1586     mixin ClassPropertiesImpl!(Type, X, T);
1587     mixin PropertiesImpl!(Type, X, T);
1588 }
1589
1590 // generates opField and opFieldAssign methods.
1591 private template ClassPropertiesImpl(Type, T...)
1592 {
1593     mixin(
1594     "static uword opField(MDThread* t)\n"
1595     "{\n"
1596     "   auto self = checkClassSelf!(Type, TypeName)(t);\n"
1597     "   auto fieldName = checkStringParam(t, 1);\n" ~
1598         GetField!(Type, T) ~
1599     "   return 1;\n"
1600     "}\n"
1601
1602     "static uword opFieldAssign(MDThread* t)\n"
1603     "{\n"
1604     "   auto self = checkClassSelf!(Type, TypeName)(t);\n"
1605     "   auto fieldName = checkStringParam(t, 1);\n" ~
1606         SetField!(Type, T) ~
1607     "   return 0;\n"
1608     "}\n");
1609 }
1610
1611 private template StructProperties(Type)
1612 {
1613     const haveProperties = false;
1614 }
1615
1616 private template StructProperties(Type, X, T...)
1617 {
1618     const haveProperties = true;
1619     mixin StructPropertiesImpl!(Type, X, T);
1620     mixin PropertiesImpl!(Type, X, T);
1621 }
1622
1623 private template StructPropertiesImpl(Type, T...)
1624 {
1625     mixin(
1626     "static uword opField(MDThread* t)\n"
1627     "{\n"
1628     "   auto self = checkStructSelf!(Type, TypeName)(t);\n"
1629     "   auto fieldName = checkStringParam(t, 1);\n" ~
1630         GetField!(Type, T) ~
1631     "   return 1;\n"
1632     "}\n"
1633
1634     "static uword opFieldAssign(MDThread* t)\n"
1635     "{\n"
1636     "   auto self = checkStructSelf!(Type, TypeName)(t);\n"
1637     "   auto fieldName = checkStringParam(t, 1);\n" ~
1638         SetField!(Type, T) ~
1639     "   return 0;\n"
1640     "}\n");
1641 }
1642
1643 // Common to both classes and structs, generates the _prop_name methods.
1644 private template PropertiesImpl(Type) {}
1645 private template PropertiesImpl(Type, X, T...)
1646 {
1647     mixin(
1648     "static uword _prop_" ~ X.Name ~ "(MDThread* t)\n"
1649     "{\n"
1650     "   auto self = check" ~ (is(Type == class) ? "Class" : "Struct") ~ "Self!(Type, TypeName)(t);\n"
1651     "   return PropImpl!(`" ~ X.DName ~ "`, X.readOnly, X.propType, TypeName)(t, self);\n"
1652     "}\n");
1653
1654     mixin PropertiesImpl!(Type, T);
1655 }
1656
1657 private template GetField(Type, T...)
1658 {
1659     const GetField =
1660     "switch(fieldName)\n"
1661     "{\n"
1662     "   default:\n"
1663     "       throwException(t, `Attempting to access nonexistent field '{}' from type " ~ Type.stringof ~ "`, fieldName);\n" ~
1664         GetFieldImpl!(T) ~
1665     "}\n";
1666 }
1667
1668 private template GetFieldImpl(Fields...)
1669 {
1670     static if(Fields.length == 0)
1671         const GetFieldImpl = "";
1672     else
1673     {
1674         const GetFieldImpl =
1675         "case `" ~ Fields[0].Name ~ "`:\n"
1676         "   static if(!is(typeof(self." ~ Fields[0].DName ~ ")))\n"
1677         "       goto default;\n"
1678         "   else\n"
1679         "   {\n"
1680         "       dup(t, 0);\n"
1681         "       pushNull(t);\n"
1682         "       methodCall(t, -2, `_prop_" ~ Fields[0].Name ~ "`, 1);\n"
1683         "       break;\n"
1684         "   }\n" ~
1685         GetFieldImpl!(Fields[1 .. $]);
1686     }
1687 }
1688
1689 private template SetField(Type, T...)
1690 {
1691     const SetField =
1692     "switch(fieldName)\n"
1693     "{\n"
1694     "   default:\n"
1695     "       throwException(t, `Attempting to access nonexistent field '{}' from type " ~ Type.stringof ~ "`, fieldName);\n" ~
1696         SetFieldImpl!(T) ~
1697     "}\n";
1698 }
1699
1700 private template SetFieldImpl(Fields...)
1701 {
1702     static if(Fields.length == 0)
1703         const SetFieldImpl = "";
1704     else
1705     {
1706         const SetFieldImpl =
1707         "case `" ~ Fields[0].Name ~ "`:\n"
1708         "   static if(!is(typeof(self." ~ Fields[0].DName ~ ")))\n"
1709         "       goto default;\n"
1710         "   else\n"
1711         "   {\n"
1712         "       dup(t, 0);\n"
1713         "       pushNull(t);\n"
1714         "       dup(t, 2);\n"
1715         "       methodCall(t, -3, `_prop_" ~ Fields[0].Name ~ "`, 0);\n"
1716         "       break;\n"
1717         "   }\n" ~
1718         SetFieldImpl!(Fields[1 .. $]);
1719     }
1720 }
1721
1722 private template WrappedFunc(alias func, char[] name, funcType, bool explicitType)
1723 {
1724     static uword WrappedFunc(MDThread* t)
1725     {
1726         auto numParams = stackSize(t) - 1;
1727         static if(explicitType)
1728             const minArgs = NumParams!(funcType);
1729         else
1730             const minArgs = MinArgs!(func);
1731
1732         const maxArgs = NumParams!(funcType);
1733
1734         if(numParams < minArgs)
1735             throwException(t, "At least" ~ minArgs.stringof ~ " parameter" ~ (minArgs == 1 ? "" : "s") ~ " expected, not {}", numParams);
1736
1737         if(numParams > maxArgs)
1738             numParams = maxArgs;
1739
1740         static if(NumParams!(funcType) == 0)
1741         {
1742             static if(is(ReturnTypeOf!(funcType) == void))
1743             {
1744                 safeCode(t, func());
1745                 return 0;
1746             }
1747             else
1748             {
1749                 superPush(t, safeCode(t, func()));
1750                 return 1;
1751             }
1752         }
1753         else
1754         {
1755             ParameterTupleOf!(funcType) args;
1756
1757             static if(minArgs == 0)
1758             {
1759                 if(numParams == 0)
1760                 {
1761                     static if(is(ReturnTypeOf!(funcType) == void))
1762                     {
1763                         safeCode(t, func());
1764                         return 0;
1765                     }
1766                     else
1767                     {
1768                         superPush(t, safeCode(t, func()));
1769                         return 1;
1770                     }
1771                 }
1772             }
1773
1774             foreach(i, arg; args)
1775             {
1776                 const argNum = i + 1;
1777
1778                 if(i < numParams)
1779                     args[i] = superGet!(typeof(args[i]))(t, argNum);
1780
1781                 static if(argNum >= minArgs && argNum <= maxArgs)
1782                 {
1783                     if(argNum == numParams)
1784                     {
1785                         static if(is(ReturnTypeOf!(funcType) == void))
1786                         {
1787                             safeCode(t, func(args[0 .. argNum]));
1788                             return 0;
1789                         }
1790                         else
1791                         {
1792                             superPush(t, safeCode(t, func(args[0 .. argNum])));
1793                             return 1;
1794                         }
1795                     }
1796                 }
1797             }
1798
1799             assert(false, "WrappedFunc (" ~ name ~ ") should never ever get here.");
1800         }
1801     }
1802 }
1803
1804 private template WrappedNativeMethod(alias func, funcType, bool explicitType)
1805 {
1806     private uword WrappedNativeMethod(Type)(MDThread* t, Type self)
1807     {
1808         auto numParams = stackSize(t) - 1;
1809         static if(explicitType)
1810             const minArgs = NumParams!(funcType);
1811         else
1812             const minArgs = MinArgs!(func);
1813
1814         const maxArgs = NumParams!(funcType);
1815         const name = NameOfFunc!(func);
1816
1817         static if(NumParams!(funcType) == 0)
1818         {
1819             static if(is(ReturnTypeOf!(funcType) == void))
1820             {
1821                 safeCode(t, mixin("self." ~ name ~ "()"));
1822                 return 0;
1823             }
1824             else
1825             {
1826                 superPush(t, safeCode(t, mixin("self." ~ name ~ "()")));
1827                 return 1;
1828             }
1829         }
1830         else
1831         {
1832             ParameterTupleOf!(funcType) args;
1833
1834             static if(minArgs == 0)
1835             {
1836                 if(numParams == 0)
1837                 {
1838                     static if(is(ReturnTypeOf!(funcType) == void))
1839                     {
1840                         safeCode(t, mixin("self." ~  name ~ "()"));
1841                         return 0;
1842                     }
1843                     else
1844                     {
1845                         superPush(t, safeCode(t, mixin("self." ~ name ~ "()")));
1846                         return 1;
1847                     }
1848                 }
1849             }
1850    
1851             foreach(i, arg; args)
1852             {
1853                 const argNum = i + 1;
1854    
1855                 if(i < numParams)
1856                     args[i] = superGet!(typeof(args[i]))(t, argNum);
1857    
1858                 static if(argNum >= minArgs && argNum <= maxArgs)
1859                 {
1860                     if(argNum == numParams)
1861                     {
1862                         static if(is(ReturnTypeOf!(funcType) == void))
1863                         {
1864                             safeCode(t, mixin("self." ~ name ~ "(args[0 .. argNum])"));
1865                             return 0;
1866                         }
1867                         else
1868                         {
1869                             superPush(t, safeCode(t, mixin("self." ~ name ~ "(args[0 .. argNum])")));
1870                             return 1;
1871                         }
1872                     }
1873                 }
1874             }
1875         }
1876    
1877         assert(false, "WrappedNativeMethod (" ~ name ~ ") should never ever get here.");
1878     }
1879 }
1880
1881 private template WrappedMethod(alias func, funcType, Type, char[] FullName, bool explicitType)
1882 {
1883     private uword WrappedMethod(MDThread* t)
1884     {
1885         auto numParams = stackSize(t) - 1;
1886         static if(explicitType)
1887             const minArgs = NumParams!(funcType);
1888         else
1889             const minArgs = MinArgs!(func);
1890
1891         const maxArgs = NumParams!(funcType);
1892         const name = NameOfFunc!(func);
1893
1894         static if(NumParams!(funcType) == 0)
1895         {
1896             static if(is(ReturnTypeOf!(funcType) == void))
1897             {
1898                 safeCode(t, mixin("super." ~ name ~ "()"));
1899                 return 0;
1900             }
1901             else
1902             {
1903                 superPush(t, safeCode(t, mixin("super." ~ name ~ "()")));
1904                 return 1;
1905             }
1906         }
1907         else
1908         {
1909             ParameterTupleOf!(funcType) args;
1910
1911             static if(minArgs == 0)
1912             {
1913                 if(numParams == 0)
1914                 {
1915                     static if(is(ReturnTypeOf!(funcType) == void))
1916                     {
1917                         safeCode(t, mixin("super." ~  name ~ "()"));
1918                         return 0;
1919                     }
1920                     else
1921                     {
1922                         superPush(t, safeCode(t, mixin("super." ~ name ~ "()")));
1923                         return 1;
1924                     }
1925                 }
1926             }
1927            
1928             foreach(i, arg; args)
1929             {
1930                 const argNum = i + 1;
1931
1932                 if(i < numParams)
1933                     args[i] = superGet!(typeof(args[i]))(t, argNum);
1934
1935                 static if(argNum >= minArgs && argNum <= maxArgs)
1936                 {
1937                     if(argNum == numParams)
1938                     {
1939                         static if(is(ReturnTypeOf!(funcType) == void))
1940                         {
1941                             safeCode(t, mixin("super." ~ name ~ "(args[0 .. argNum])"));
1942                             return 0;
1943                         }
1944                         else
1945                         {
1946                             superPush(t, safeCode(t, mixin("super." ~ name ~ "(args[0 .. argNum])")));
1947                             return 1;
1948                         }
1949                     }
1950                 }
1951             }
1952         }
1953    
1954         assert(false, "WrappedMethod (" ~ name ~ ") should never ever get here.");
1955     }
1956 }
1957
1958 private uword WrappedStructMethod(alias func, funcType, Type, char[] FullName, bool explicitType)(MDThread* t)
1959 {
1960     auto numParams = stackSize(t) - 1;
1961     static if(explicitType)
1962         const minArgs = NumParams!(funcType);
1963     else
1964         const minArgs = MinArgs!(func);
1965
1966     const maxArgs = NumParams!(funcType);
1967     const name = NameOfFunc!(func);
1968
1969     auto self = checkStructSelf!(Type, FullName)(t);
1970     assert(self !is null, "Invalid 'this' parameter passed to method " ~ Type.stringof ~ "." ~ name);
1971
1972     static if(NumParams!(funcType) == 0)
1973     {
1974         static if(is(ReturnTypeOf!(funcType) == void))
1975         {
1976             safeCode(t, mixin("self." ~ name ~ "()"));
1977             return 0;
1978         }
1979         else
1980         {
1981             superPush(t, safeCode(t, mixin("self." ~ name ~ "()")));
1982             return 1;
1983         }
1984     }
1985     else
1986     {
1987         ParameterTupleOf!(funcType) args;
1988
1989         static if(minArgs == 0)
1990         {
1991             if(numParams == 0)
1992             {
1993                 static if(is(ReturnTypeOf!(funcType) == void))
1994                 {
1995                     safeCode(t, mixin("self." ~  name ~ "()"));
1996                     return 0;
1997                 }
1998                 else
1999                 {
2000                     superPush(t, safeCode(t, mixin("self." ~ name ~ "()")));
2001                     return 1;
2002                 }
2003             }
2004         }
2005
2006         foreach(i, arg; args)
2007         {
2008             const argNum = i + 1;
2009
2010             if(i < numParams)
2011                 args[i] = superGet!(typeof(args[i]))(t, argNum);
2012
2013             static if(argNum >= minArgs && argNum <= maxArgs)
2014             {
2015                 if(argNum == numParams)
2016                 {
2017                     static if(is(ReturnTypeOf!(funcType) == void))
2018                     {
2019                         safeCode(t, mixin("self." ~ name ~ "(args[0 .. argNum])"));
2020                         return 0;
2021                     }
2022                     else
2023                     {
2024                         superPush(t, safeCode(t, mixin("self." ~ name ~ "(args[0 .. argNum])")));
2025                         return 1;
2026                     }
2027                 }
2028             }
2029         }
2030     }
2031
2032     assert(false, "WrappedStructMethod (" ~ name ~ ") should never ever get here.");
2033 }
2034
2035 private template PropImpl(char[] name, bool readOnly, propType, char[] FullName)
2036 {
2037     uword PropImpl(Type)(MDThread* t, Type self)
2038     {
2039         auto numParams = stackSize(t) - 1;
2040         static if(readOnly)
2041         {
2042             if(numParams == 0)
2043             {
2044                 superPush(t, safeCode(t, mixin("self." ~ name)));
2045                 return 1;
2046             }
2047             else
2048             {
2049                 throwException(t, "Attempting to set read-only property '" ~ name ~ "' of type '" ~ FullName ~ "'");
2050                 assert(false, "PropImpl should never ever get here.");
2051             }
2052         }
2053         else
2054         {
2055             // GODDOMMOT FRONK
2056             // I can't check anything in here because FUCKING DMDFE will EAT THE GODDAMNED ERROR
2057             // and cause RANDOM ERRORS ON VALID CODE in WrappedClass.  WONDERFUL.
2058 //          static assert(is(typeof({mixin("self." ~ name ~ " = self." ~ name ~ ";");})),
2059 //              "property '" ~ NameOfFunc!(func) ~ "' has no setter");
2060
2061             if(numParams == 0)
2062             {
2063                 superPush(t, safeCode(t, mixin("self." ~ name)));
2064                 return 1;
2065             }
2066             else
2067             {
2068                 safeCode(t, mixin("self." ~ name ~ " = superGet!(propType)(t, 1)"));
2069                 return 0;
2070             }
2071         }
2072     }
2073 }
2074
2075 private template ClassCtorCases(Ctors...)
2076 {
2077     const ClassCtorCases = "switch(numParams) { default: throwException(t, \"Invalid number of parameters ({})\", numParams);\n"
2078         ~ ClassCtorCasesImpl!(-1, 0, Ctors) ~ "\nbreak; }";
2079 }
2080
2081 private template ClassCtorCasesImpl(int num, int idx, Ctors...)
2082 {
2083     static if(Ctors.length == 0)
2084         const ClassCtorCasesImpl = "";
2085     else static if(NumParams!(Ctors[0]) != num)
2086         const ClassCtorCasesImpl = "break;\ncase " ~ NumParams!(Ctors[0]).stringof ~ ":\n" ~ ClassCtorCasesImpl!(NumParams!(Ctors[0]), idx, Ctors);
2087     else
2088     {
2089         const ClassCtorCasesImpl = "if(TypesMatch!(ParameterTupleOf!(CleanCtors[" ~ idx.stringof ~ "]))(t))
2090 {
2091     ParameterTupleOf!(CleanCtors[" ~ idx.stringof ~ "]) params;
2092
2093     foreach(i, arg; params)
2094         params[i] = superGet!(typeof(arg))(t, i + 1);
2095
2096     auto obj = new typeof(this)(getVM(t), params);
2097     pushNativeObj(t, obj);
2098     setExtraVal(t, 0, 0);
2099     setWrappedInstance(t, obj, 0);
2100     return 0;
2101 }\n\n" ~ ClassCtorCasesImpl!(num, idx + 1, Ctors[1 .. $]);
2102     }
2103 }
2104
2105 private template StructCtorCases(Ctors...)
2106 {
2107     const StructCtorCases = "switch(numParams) { default: throwException(t, \"Invalid number of parameters ({})\", numParams);\n"
2108         ~ StructCtorCasesImpl!(-1, 0, Ctors) ~ "\nbreak; }";
2109 }
2110
2111 private template StructCtorCasesImpl(int num, int idx, Ctors...)
2112 {
2113     static if(Ctors.length == 0)
2114         const StructCtorCasesImpl = "";
2115     else static if(NumParams!(Ctors[0]) != num)
2116         const StructCtorCasesImpl = "break;\ncase " ~ NumParams!(Ctors[0]).stringof ~ ":\n" ~ StructCtorCasesImpl!(NumParams!(Ctors[0]), idx, Ctors);
2117     else
2118     {
2119         const StructCtorCasesImpl = "if(TypesMatch!(ParameterTupleOf!(CleanCtors[" ~ idx.stringof ~ "]))(t))
2120 {
2121     ParameterTupleOf!(CleanCtors[" ~ idx.stringof ~ "]) params;
2122
2123     foreach(i, arg; params)
2124         params[i] = superGet!(typeof(arg))(t, i + 1);
2125
2126     pushNativeObj(t, new StructWrapper!(Type)(Type(params)));
2127     setExtraVal(t, 0, 0);
2128     //self.updateFields();
2129     return 0;
2130 }\n\n" ~ StructCtorCasesImpl!(num, idx + 1, Ctors[1 .. $]);
2131     }
2132 }
2133
2134 private template NumParams(T)
2135 {
2136     const NumParams = ParameterTupleOf!(T).length;
2137 }
2138
2139 private template SortByNumParams(T1, T2)
2140 {
2141     const SortByNumParams = cast(int)NumParams!(T1) - cast(int)NumParams!(T2);
2142 }
2143
2144 private template GetCtors(T...)
2145 {
2146     static if(T.length == 0)
2147         alias Tuple!() GetCtors;
2148     else static if(is(typeof(T[0].isCtors)))
2149         alias Tuple!(T[0], GetCtors!(T[1 .. $])) GetCtors;
2150     else
2151         alias GetCtors!(T[1 .. $]) GetCtors;
2152 }
2153
2154 private template GetMethods(T...)
2155 {
2156     static if(T.length == 0)
2157         alias Tuple!() GetMethods;
2158     else static if(is(typeof(T[0].isMethod)))
2159         alias Tuple!(T[0], GetMethods!(T[1 .. $])) GetMethods;
2160     else
2161         alias GetMethods!(T[1 .. $]) GetMethods;
2162 }
2163
2164 private template GetProperties(T...)
2165 {
2166     static if(T.length == 0)
2167         alias Tuple!() GetProperties;
2168     else static if(is(typeof(T[0].isProperty)))
2169         alias Tuple!(T[0], GetProperties!(T[1 .. $])) GetProperties;
2170     else
2171         alias GetProperties!(T[1 .. $]) GetProperties;
2172 }
2173
2174 private struct StructFieldProp(char[] name, type, size_t idx)
2175 {
2176     const bool isProperty = true;
2177     const char[] Name = name;
2178     const char[] DName = "tupleof[" ~ idx.stringof ~ "]";
2179     const bool readOnly = false;
2180     alias type propType;
2181 }
2182
2183 template IsAccessibleProp(T, uint idx)
2184 {
2185     const IsAccessibleProp = is(typeof({ MDThread* t; typeof(T.tupleof[idx]) x; superPush(t, x); }));
2186 }
2187
2188 template StructFieldsToProps(T, uint idx = 0)
2189 {
2190     static if(idx >= T.tupleof.length)
2191         alias Tuple!() StructFieldsToProps;
2192     else static if(IsAccessibleProp!(T, idx))
2193         alias Tuple!(StructFieldProp!(GetLastName!(T.tupleof[idx].stringof), typeof(T.tupleof[idx]), idx), StructFieldsToProps!(T, idx + 1)) StructFieldsToProps;
2194     else
2195         alias StructFieldsToProps!(T, idx + 1) StructFieldsToProps;
2196 }
2197
2198 private bool TypesMatch(T...)(MDThread* t)
2199 {
2200     foreach(i, type; T)
2201     {
2202         if(!canCastTo!(type)(t, i + 1))
2203             return false;
2204         else
2205         {
2206             static if(isRealType!(type))
2207             {
2208                 if(isInt(t, i + 1))
2209                     return false;
2210             }
2211         }
2212     }
2213
2214     return true;
2215 }