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