1 /* Distributed under the Boost Software License, Version 1.0.
2  * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
3  *
4  * extending.d - experimental facility for ease of extending DMDScript 
5  *
6  * written by Dmitry Olshansky 2010
7  * 
8  * DMDScript is implemented in the D Programming Language,
9  * http://www.digitalmars.com/d/
10  *
11  * For a C++ implementation of DMDScript, including COM support, see
12  * http://www.digitalmars.com/dscript/cppscript.html
13  */
14 module dmdscript.extending;
15 
16 import dmdscript.script;
17 import dmdscript.value;
18 import dmdscript.dobject;
19 import dmdscript.dfunction;
20 import dmdscript.dnative;
21 import dmdscript.program;
22 import dmdscript.property;
23 import dmdscript.threadcontext;
24 
25 import std.typecons;
26 import std.traits;
27 import std.typetuple;
28 import std.file;
29 
30 
31 T convert(T)(Value* v, CallContext* cc){
32 	static if(is(T == int)){
33 		return v.toInt32(cc);
34     }else static if(isSomeString!T ){
35         return v.toString(cc);   
36 	}else{
37 		assert(0);	
38 	}
39 }
40 
41 void convertPut(T)(ref T what,Value* v){
42     static if(isIntegral!T || isFloatingPoint!T){
43         v.putVnumber(what);
44     }
45 }
46 
47 //experimental stuff, eventually will be moved to the main library
48 void extendGlobal(alias fn)(Program pg, string name)
49 if(isCallable!fn) {
50     alias ParameterTypeTuple!fn Args;
51     alias ReturnType!fn R;
52     alias staticMap!(Unqual,Args) Uargs;
53     static void* embedded(Dobject pthis, CallContext* cc,
54                           Dobject othis, Value* ret, Value[] arglist){
55        
56         Tuple!(Uargs) tup = convertAll!(Uargs)(arglist, cc);
57         if(arglist.length < tup.length){
58             auto len = arglist.length;
59             arglist.length = tup.length;
60             arglist[len .. $] = vundefined;
61         }
62         arglist = arglist[0..tup.length];
63         
64         static if(is(R == void)){
65             fn(tup.expand);
66         }else{
67             R r = fn(tup.expand);
68             convertPut(r,ret);
69         }
70         return null;
71     }
72 
73     NativeFunctionData[] nfd = [
74         {
75             name,
76             &embedded,
77             Args.length
78         }
79     ];
80 
81     DnativeFunction.initialize(pg.callcontext.global,pg.callcontext,nfd,DontEnum);
82 }
83                                     
84 void fitArray(T...)(ref Value[] arglist){
85     enum staticLen = T.length;
86     if(arglist.length < staticLen){
87         auto len = arglist.length;
88         arglist.length = staticLen;
89         arglist[len .. $] = vundefined;
90     }
91     arglist = arglist[0..staticLen];
92 }
93                  
94 void extendMethod(T,alias fn)(Dobject obj, CallContext* cc, string name)
95 if(is(T == class) && isCallable!fn){
96     alias ParameterTypeTuple!fn Args;
97     alias ReturnType!fn R;
98     alias staticMap!(Unqual,Args) Uargs;
99 	static void* embedded(Dobject pthis, CallContext* cc,
100                           Dobject othis, Value* ret,	Value[] arglist){
101 
102         static if(Uargs.length){
103             Tuple!(Uargs) tup = convertAll!(Uargs)(arglist, cc);
104         
105             fitArray(arglist);
106         }
107         assert(cast(T)othis,"Wrong this pointer in external func ");
108         static if(Uargs.length){
109              auto dg = (){ mixin("(cast(T)othis).wrapped."~(&fn).stringof[2..$]~"(tup.expand);"); };
110         } else{
111              auto dg = (){ mixin("(cast(T)othis).wrapped."~(&fn).stringof[2..$]~"();"); };   
112         }
113         
114         static if(is(R == void)){
115             dg();
116         }else{
117             R r = dg();
118             convertPut(r,ret);
119         }
120         return null;
121     }
122     NativeFunctionData[] nfd = [
123         {
124             name,
125             &embedded,
126             Args.length
127         }
128     ];
129     DnativeFunction.initialize(obj,cc,nfd,DontEnum);
130 }                          
131 class Wrap(Which,string ClassName,Base=Dobject): Base{
132     Which wrapped;
133     static Wrap _prototype;
134     static Constructor _constructor;
135     static class Constructor: Dfunction{
136         this(CallContext* cc){
137             super(cc, ConstructorArgs.length, cc.tc.Dfunction_prototype);
138             name = ClassName;
139         }
140 
141         override void *Construct(CallContext *cc, Value *ret, Value[] arglist){
142             fitArray!(ConstructorArgs)(arglist);
143             Dobject o = new Wrap(cc, convertAll!(UConstructorArgs)(arglist, cc).expand);
144             ret.putVobject(o);
145             return null;
146         }
147 
148         override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist){
149             return Construct(cc,ret,arglist);
150         }
151     
152     }
153     static void initialize(CallContext* cc){
154          _prototype = new Wrap(cc, Base.getPrototype(cc));
155         _constructor = new Constructor(cc);
156         _prototype.Put(cc, "constructor", _constructor, DontEnum);
157         _constructor.Put(cc, "prototype", _prototype, DontEnum | DontDelete | ReadOnly);
158 
159         // we need to directly store the constructor, because this will be
160         // called after the global object has already been created
161         //cc.tc.ctorTable[ClassName] = _constructor;
162         cc.global.Put(cc, ClassName, _constructor, DontEnum);
163     }
164     static this(){
165         threadInitTable ~= cc => initialize(cc);
166     }
167     private this(CallContext* cc, Dobject prototype){ 
168         super(cc, prototype); 
169         classname = ClassName;
170         //Put(TEXT_constructor,
171     }
172     alias ParameterTypeTuple!(Which.__ctor) ConstructorArgs;
173     alias staticMap!(Unqual,ConstructorArgs) UConstructorArgs;
174     this(CallContext* cc, ConstructorArgs args){
175         super(cc, _prototype);
176         static if (is(Which == struct)){
177             wrapped = Which(args);
178         } 
179     }
180     static void methods(Methods...)(){
181         static if(Methods.length >= 1){
182             threadInitTable ~= (cc) {
183                 extendMethod!(Wrap,Methods[0])(_prototype,cc,(&Methods[0]).stringof[2..$]);
184             };
185         
186             methods!(Methods[1..$])();
187         }
188     }
189 }       
190                         
191 auto convertAll(Args...)(Value[] dest, CallContext* cc){
192     static if(Args.length > 1){
193         return tuple(convert!(Args[0])(&dest[0], cc),convertAll!(Args[1..$])(dest[1..$], cc).expand);  
194     }else 
195         return tuple(convert!(Args[0])(&dest[0], cc));
196 }
197