root/trunk/minid/bind.d

Revision 430, 39.7 kB (checked in by JarrettBillingsley, 2 weeks ago)

New pretty docs.

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 License:
6 Copyright (c) 2008 Jarrett Billingsley
7
8 This software is provided 'as-is', without any express or implied warranty.
9 In no event will the authors be held liable for any damages arising from the
10 use of this software.
11
12 Permission is granted to anyone to use this software for any purpose,
13 including commercial applications, and to alter it and redistribute it freely,
14 subject to the following restrictions:
15
16     1. The origin of this software must not be misrepresented; you must not
17     claim that you wrote the original software. If you use this software in a
18     product, an acknowledgment in the product documentation would be
19     appreciated but is not required.
20
21     2. Altered source versions must be plainly marked as such, and must not
22     be misrepresented as being the original software.
23
24     3. This notice may not be removed or altered from any source distribution.
25 ******************************************************************************/
26
27 module minid.bind;
28
29 import tango.core.Traits;
30 import tango.core.Tuple;
31 import Utf = tango.text.convert.Utf;
32
33 import minid.ex;
34 import minid.interpreter;
35 import minid.types;
36 import minid.utils;
37
38 /**
39 Wraps a module.  This registers a custom module loader in the global modules.customLoaders
40 table of the given thread.  The members will not actually be wrapped until the module is imported
41 the first time.
42
43 Template Params:
44     name = The name of the module, in dotted form (like "foo.bar.baz").  This is the name that will
45         be used to import it.
46
47     Members = A variadic list of things to declare in this module.  These will be declared as module
48         globals, just as if you declared them globals in MiniD.  Supported member types include
49         WrapFunc, WrapNamespace, WrapValue, and WrapType.
50
51 Params:
52     t = This module's loader will be added into the global modules.customLoaders table accessible
53         from this thread.
54 */
55 public void WrapModule(char[] name, Members...)(MDThread* t)
56 {
57     pushGlobal(t, "modules");
58     field(t, -1, "customLoaders");
59
60     newFunction(t, function uword(MDThread* t, uword numParams)
61     {
62         commonNamespace!(name, true, Members)(t);
63         return 0;
64     }, name);
65
66     fielda(t, -2, name);
67     pop(t, 2);
68 }
69
70 /**
71 Wraps any number of values into the global namespace accessible from the given thread.  This is
72 the root global namespace, outside of any modules.  Works just like WrapModule otherwise.
73 Supported member types include WrapFunc, WrapNamespace, WrapValue, and WrapType.
74
75 The wrapped values are immediately loaded into the global namespace.
76 */
77 public void WrapGlobals(Members...)(MDThread* t)
78 {
79     commonNamespace!("", true, Members)(t);
80 }
81
82 private void commonNamespace(char[] name, bool isModule, Members...)(MDThread* t)
83 {
84     static if(!isModule)
85         newNamespace(t, name);
86
87     foreach(i, member; Members)
88     {
89         static if(is(typeof(member.isFunc)))
90             newFunction(t, &member.WrappedFunc, member.Name);
91         else static if(is(typeof(member.isNamespace)))
92             commonNamespace!(member.Name, false, member.Values)(t);
93         else static if(is(typeof(member.isValue)))
94             superPush(t, member.Value);
95         else static if(is(typeof(member.isType)))
96             member.init!(name)(t);
97         else static if(isModule)
98             static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped module '" ~ name ~ "'");
99         else
100             static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped namespace '" ~ name ~ "'");
101
102         static if(isModule)
103             newGlobal(t, member.Name);
104         else
105             fielda(t, -2, member.Name);
106     }
107 }
108
109 /**
110 Wraps a static function - that is, a function that doesn't have a 'this' parameter.  These four
111 template specializations allow you to fine-tune how the function is to be wrapped.
112
113 The first specialization takes just an alias to a function.  In this case, the first overload
114 of the function (if any) will be wrapped and the name of the function in MiniD will be the same
115 as in D.
116
117 The second specialization allows you to explicitly specify a function signature to choose, in the
118 case that the function you're wrapping is overloaded.  The signature should be a function type that
119 matches the signature of the overload you want to wrap.  In this case, though, the name in MiniD
120 will still be the name of the D function.
121
122 The third specialization allows you to rename the function without explicitly selecting an overload.
123
124 The fourth specialization allows you to both select an overload and give it the name that should
125 be used in MiniD.  This is the form you'll probably be using most often with overloaded D functions.
126
127 If you use one of the two forms where you explicitly specify the function signature, the resulting
128 wrapped function will only accept exactly as many parameters as are specified in the signature.
129 Otherwise, the wrapped function will be allowed to have optional parameters.
130 */
131 public struct WrapFunc(alias func)
132 {
133     const bool isFunc = true;
134     const char[] Name = NameOfFunc!(func);
135     mixin WrappedFunc!(func, Name, typeof(&func), false);
136 }
137
138 /// ditto
139 public struct WrapFunc(alias func, funcType)
140 {
141     const bool isFunc = true;
142     const char[] Name = NameOfFunc!(func);
143     mixin WrappedFunc!(func, Name, funcType, true);
144 }
145
146 /// ditto
147 public struct WrapFunc(alias func, char[] name)
148 {
149     const bool isFunc = true;
150     const char[] Name = name;
151     mixin WrappedFunc!(func, Name, typeof(&func), false);
152 }
153
154 /// ditto
155 public struct WrapFunc(alias func, char[] name, funcType)
156 {
157     const bool isFunc = true;
158     const char[] Name = name;
159     mixin WrappedFunc!(func, Name, funcType, true);
160 }
161
162 /**
163 Wraps a bunch of values into a namespace object.  This works virtually the same as WrapModule,
164 except that it's meant to be used as a member of something like WrapModule.  Legal member
165 types include WrapFunc, WrapValue, WrapNamespace, and WrapType.
166 */
167 public struct WrapNamespace(char[] name, members...)
168 {
169     const bool isNamespace = true;
170     const char[] Name = name;
171     alias members Values;
172 }
173
174 /**
175 Wraps a single value and gives it a name.  Despite the fact that the value parameter is
176 variadic, it is restricted to exactly one item.  It's variadic just so it can accept any
177 value type.
178 */
179 public struct WrapValue(char[] name, value...)
180 {
181     static assert(Value.length == 1 && isExpressionTuple!(Value), "WrapValue - must have exactly one expression");
182     const bool isValue = true;
183     const char[] Name = name;
184     alias value Value;
185 }
186
187 /**
188 Wraps a class or struct type.  This supports wrapping constructors (or static opCall for structs),
189 methods, properties (though they will be $(B functions) in MiniD), and arbitrary values.  That means
190 the valid member types are WrapCtors, WrapMethod, WrapProperty, and WrapValue.
191
192 Template Params:
193     Type = The class or struct type to be wrapped.
194     
195     name = The name that will be given to the type in MiniD.
196     
197     Members = The members of the type.
198 */
199 public struct WrapType(Type, char[] name = NameOfType!(Type), Members...)
200 {
201     // Why did I restrict this, again?
202 //  static assert(!is(Type == Object), "Wrapping Object is not allowed");
203
204     static assert(is(Type == class) || is(Type == struct), "Cannot wrap type " ~ Type.stringof);
205
206     const bool isType = true;
207     const char[] Name = name;
208     const char[] typeName = NameOfType!(Type);
209     alias GetCtors!(Members) Ctors;
210     static assert(Ctors.length <= 1, "Cannot have more than one WrapCtors instance in wrapped type parameters for type " ~ typeName);
211
212     static if(Ctors.length == 1)
213     {
214         // alias Ctors[0].Types blah; doesn't parse right
215         alias Ctors[0] DUMMY;
216         alias DUMMY.Types CleanCtors;
217     }
218     else
219         alias Tuple!() CleanCtors;
220
221     private static word init(char[] moduleName)(MDThread* t)
222     {
223         getWrappedClass(t, typeid(Type));
224
225         if(!isNull(t, -1))
226             throwException(t, "Native type " ~ typeName ~ " cannot be wrapped more than once");
227
228         pop(t);
229
230         static if(is(Type == class))
231         {
232             alias BaseTypeTupleOf!(Type)[0] BaseClass;
233
234             static if(!is(BaseClass == Object))
235                 auto base = getWrappedClass(t, typeid(BaseClass));
236             else
237                 auto base = pushNull(t);
238
239             newClass(t, base, name);
240         }
241         else
242             pushStructClass!(Type, moduleName, name)(t);
243
244         static if(moduleName.length == 0)
245             const TypeName = name;
246         else
247             const TypeName = moduleName ~ "." ~ name;
248
249         foreach(i, member; Members)
250         {
251             static if(is(typeof(member.isMethod)))
252             {
253                 auto f = &WrappedMethod!(member.Func, member.FuncType, Type, TypeName, member.explicitType);
254                 newFunction(t, f, name ~ "." ~ member.Name);
255                 fielda(t, -2, member.Name);
256             }
257             else static if(is(typeof(member.isProperty)))
258             {
259                 auto f = &WrappedProperty!(member.Func, member.FuncType, Type, TypeName);
260                 newFunction(t, f, name ~ "." ~ member.Name);
261                 fielda(t, -2, member.Name);
262             }
263             else static if(is(typeof(member.isCtors)))
264             {
265                 // ignore
266             }
267             else static if(is(typeof(member.isValue)))
268             {
269                 superPush(t, member.Value);
270                 fielda(t, -2, member.Name);
271             }
272             else
273                 static assert(false, "Invalid member type '" ~ member.stringof ~ "' in wrapped type '" ~ typeName ~ "'");
274         }
275
276         newFunction(t, &constructor!(TypeName), name ~ ".constructor");
277         fielda(t, -2, "constructor");
278        
279         newFunction(t, &allocator, name ~ ".allocator");
280         setAllocator(t, -2);
281
282         setWrappedClass(t, typeid(Type));
283         return stackSize(t) - 1;
284     }
285
286     private static uword allocator(MDThread* t, uword numParams)
287     {
288         newInstance(t, 0, 1);
289
290         dup(t);
291         pushNull(t);
292         rotateAll(t, 3);
293         methodCall(t, 2, "constructor", 0);
294         return 1;
295     }
296
297     static if(CleanCtors.length == 0)
298     {
299         static if(is(Type == class))
300             static assert(is(typeof(new Type())), "Cannot call default constructor for class " ~ typeName ~ "; please wrap a constructor explicitly");
301         else
302             static assert(is(typeof(Type())), "Cannot call default constructor for struct " ~ typeName ~ "; please wrap a constructor explicitly");
303
304         private static uword constructor(char[] FullName)(MDThread* t, uword numParams)
305         {
306             checkInstParam(t, 0, FullName);
307
308             static if(is(Type == class))
309             {
310                 auto obj = new Type();
311                 pushNativeObj(t, obj);
312                 setExtraVal(t, 0, 0);
313                 setWrappedInstance(t, obj, 0);
314             }
315             else
316             {
317                 pushNativeObj(t, new StructWrapper!(Type)(Type()));
318                 setExtraVal(t, 0, 0);
319             }
320
321             return 0;
322         }
323     }
324     else
325     {
326         private static uword constructor(char[] FullName)(MDThread* t, uword numParams)
327         {
328             checkInstParam(t, 0, FullName);
329
330             static if(is(Type == class))
331             {
332                 static if(is(typeof(new Type())))
333                     const minArgs = 0;
334                 else
335                     const minArgs = ParameterTupleOf!(CleanCtors[0]).length;
336             }
337             else
338             {
339                 static if(is(typeof(Type())))
340                     const minArgs = 0;
341                 else
342                     const minArgs = ParameterTupleOf!(CleanCtors[0]).length;
343             }
344
345             const maxArgs = ParameterTupleOf!(CleanCtors[$ - 1]).length;
346
347             if(numParams < minArgs)
348                 throwException(t, "At least " ~ minArgs.stringof ~ " parameter" ~ (minArgs == 1 ? "" : "s") ~ " expected, not {}", numParams);
349
350             if(numParams > maxArgs)
351                 numParams = maxArgs;
352
353             static if(minArgs == 0)
354             {
355                 if(numParams == 0)
356                 {
357                     static if(is(Type == class))
358                     {
359                         auto obj = new Type();
360                         pushNativeObj(t, obj);
361                         setExtraVal(t, 0, 0);
362                         setWrappedInstance(t, obj, 0);
363                     }
364                     else
365                     {
366                         pushNativeObj(t, new StructWrapper!(Type)(Type()));
367                         setExtraVal(t, 0, 0);
368                     }
369
370                     return 0;
371                 }
372             }
373
374             static if(is(Type == class))
375                 const Switch = CtorCases!(CleanCtors);
376             else
377                 const Switch = StructCtorCases!(CleanCtors);
378
379             mixin(Switch);
380
381             auto buf = StrBuffer(t);
382
383             buf.addChar('(');
384
385             if(numParams > 0)
386             {
387                 pushTypeString(t, 1);
388                 buf.addTop();
389
390                 for(uword i = 2; i <= numParams; i++)
391                 {
392                     buf.addString(", ");
393                     pushTypeString(t, i);
394                     buf.addTop();
395                 }
396             }
397
398             buf.addChar(')');
399             buf.finish();
400             throwException(t, "Parameter list {} passed to constructor does not match any wrapped constructors", getString(t, -1));
401         }
402     }
403 }
404
405 /**
406 D doesn't really provide any facilities for introspecting class constructors, so you'll have to specify
407 to the binding library the signatures of the constructors to expose.  You'll also have to do it for structs.
408 There can be at most one WrapCtors inside a WrapType, but since you specify as many constructors as you
409 want all at once, it doesn't matter.  The constructor signatures should be function types; the return type
410 is ignored, and only the parameter types are significant. 
411
412 Unlike wrapping other functions, a form of overloading is allowed for constructors.  That is, you can have
413 a constructor that takes (int) and another that takes (float), wrap them as two separate types, and they
414 will be correctly dispatched when the type is instantiated in MiniD.  This also means that the usual
415 implicit conversion from int to float that happens when calling other functions will not happen when calling
416 constructors.
417 */
418 public struct WrapCtors(T...)
419 {
420     static assert(T.length > 0, "WrapCtors must be instantiated with at least one type");
421     const bool isCtors = true;
422     alias Unique!(QSort!(SortByNumParams, T)) Types;
423 }
424
425 /**
426 Wraps a method of a class or struct type.  The argument to this template will look like "A.foo" for a given
427 type "A".  Other than the fact that it's a method (and therefore takes 'this'), this works pretty much
428 exactly the same as WrapFunction, including the differences between the multiple specializations.
429 */
430 public struct WrapMethod(alias func)
431 {
432     const bool isMethod = true;
433     const char[] Name = NameOfFunc!(func);
434     const bool explicitType = false;
435     alias func Func;
436     alias typeof(&func) FuncType;
437 }
438
439 /// ditto
440 public struct WrapMethod(alias func, char[] name)
441 {
442     const bool isMethod = true;
443     const char[] Name = name;
444     const bool explicitType = false;
445     alias func Func;
446     alias typeof(&func) FuncType;
447 }
448
449 /// ditto
450 public struct WrapMethod(alias func, funcType)
451 {
452     const bool isMethod = true;
453     const char[] Name = NameOfFunc!(func);
454     const bool explicitType = true;
455     alias func Func;
456     alias funcType FuncType;
457 }
458
459 /// ditto
460 public struct WrapMethod(alias func, char[] name, funcType)
461 {
462     const bool isMethod = true;
463     const char[] Name = name;
464     const bool explicitType = true;
465     alias func Func;
466     alias funcType FuncType;
467 }
468
469 /**
470 Wraps a D "property" as a MiniD $(B function).  Let me state that again: if you wrap a property named "x"
471 in D, you $(B will not) access it like "a.x" and "a.x = 5" in MiniD.  Instead it will work like "a.x()"
472 and "a.x(5)".  (this may change.) 
473
474 The D "property" must be one or two functions (either just a getter or a getter/setter pair).  The setter,
475 if any exists, must be able to take one parameter that is the same type as the getter's return type. 
476 The setter may optionally return a value.
477
478 It doesn't matter whether you pass an alias to the setter or the getter to this; the library will figure
479 out which one you gave and which one it needs.  So if you have a property "x" of a type "A", it'll just
480 be WrapProperty!(A.x).
481
482 Oh, and, since this is another variety of function wrapping, the parameters here all do the same thing
483 as for WrapFunction and WrapMethod.
484 */
485 public struct WrapProperty(alias func)
486 {
487     const bool isProperty = true;
488     const char[] Name = NameOfFunc!(func);
489     const bool explicitType = false;
490     alias func Func;
491     alias typeof(&func) FuncType;
492 }
493
494 /// ditto
495 public struct WrapProperty(alias func, char[] name)
496 {
497     const bool isProperty = true;
498     const char[] Name = name;
499     const bool explicitType = false;
500     alias func Func;
501     alias typeof(&func) FuncType;
502 }
503
504 /// ditto
505 public struct WrapProperty(alias func, funcType)
506 {
507     const bool isProperty = true;
508     const char[] Name = NameOfFunc!(func);
509     const bool explicitType = true;
510     alias func Func;
511     alias typeof(&func) FuncType;
512 }
513
514 /// ditto
515 public struct WrapProperty(alias func, char[] name, funcType)
516 {
517     const bool isProperty = true;
518     const char[] Name = name;
519     const bool explicitType = true;
520     alias func Func;
521     alias typeof(&func) FuncType;
522 }
523
524 /**
525 Given a TypeInfo instance of the desired class/struct type (that is, typeid(SomeType)), pushes
526 the corresponding wrapped MiniD class, or pushes null if the type has not been wrapped.
527
528 $(B You probably won't have to call this function under normal circumstances.)
529
530 Params:
531     ti = The runtime TypeInfo instance of the desired type.
532     
533 Returns:
534     The stack index of the newly-pushed value.
535 */
536 public word getWrappedClass(MDThread* t, TypeInfo ti)
537 {
538     pushWrappedClasses(t);
539     pushNativeObj(t, ti);
540
541     if(!opin(t, -1, -2))
542     {
543         pop(t, 2);
544         return pushNull(t);
545     }
546
547     idx(t, -2);
548     insertAndPop(t, -2);
549     return stackSize(t) - 1;
550 }
551
552 /**
553 Expects a class object on top of the stack, and sets it to be the MiniD class that corresponds
554 to the given runtime TypeInfo object.  The class object is not popped off the stack.
555
556 $(B You probably won't have to call this function under normal circumstances.)
557 */
558 public void setWrappedClass(MDThread* t, TypeInfo ti)
559 {
560     pushWrappedClasses(t);
561     pushNativeObj(t, ti);
562     dup(t, -3);
563     idxa(t, -3);
564     pop(t);
565 }
566
567 /**
568 Pushes the wrapped class table onto the stack.  If the wrapped class table for this VM
569 has not yet been created, creates it.
570
571 $(B You probably won't have to call this function under normal circumstances.)
572
573 Returns:
574     The stack index of the newly-pushed table.
575 */
576 public word pushWrappedClasses(MDThread* t)
577 {
578     auto reg = getRegistry(t);
579     pushString(t, "minid.bind.WrappedClasses");
580
581     if(!opin(t, -1, -2))
582     {
583         newTable(t);
584         swap(t);
585         dup(t, -2);
586         fielda(t, reg);
587     }
588     else
589         field(t, reg);
590
591     insertAndPop(t, -2);
592     return stackSize(t) - 1;
593 }
594
595 /**
596 For a given D object instance, sets the MiniD instance at the given stack index to be
597 its corresponding object. 
598
599 $(B You probably won't have to call this function under normal circumstances.)
600
601 Params:
602     o = The D object that should be linked to the given MiniD instance.
603     idx = The stack index of the MiniD instance that should be linked to the given D object.
604 */
605 public void setWrappedInstance(MDThread* t, Object o, word idx)
606 {
607     pushWrappedInstances(t);
608     pushNativeObj(t, o);
609     pushWeakRef(t, idx);
610     idxa(t, -3);
611     pop(t);
612 }
613
614 /**
615 Assuming a valid wrapped class is on the top of the stack, this function will take a D object
616 and push the corresponding MiniD instance.  If a MiniD instance has already been created for
617 this object, pushes that instance; otherwise, this will create an instance and link it to this
618 D object.
619
620 $(B You probably won't have to call this function under normal circumstances.)
621
622 Params:
623     o = The D object to convert to a MiniD instance.
624     
625 Returns:
626     The stack index of the newly-pushed instance.
627 */
628 public word getWrappedInstance(MDThread* t, Object o)
629 {
630     pushWrappedInstances(t);
631     pushNativeObj(t, o);
632     idx(t, -2);
633     deref(t, -1);
634
635     if(isNull(t, -1))
636     {
637         pop(t, 2);
638
639         newInstance(t, -3, 1);
640         pushNativeObj(t, o);
641         setExtraVal(t, -1, 0);
642
643         pushNativeObj(t, o);
644         pushWeakRef(t, -2);
645
646         idxa(t, -4);