1 /* Digital Mars DMDScript source code.
2  * Copyright (c) 2000-2002 by Chromium Communications
3  * D version Copyright (c) 2004-2010 by Digital Mars
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  * written by Walter Bright
7  * http://www.digitalmars.com
8  *
9  * D2 port by Dmitry Olshansky 
10  *
11  * DMDScript is implemented in the D Programming Language,
12  * http://www.digitalmars.com/d/
13  *
14  * For a C++ implementation of DMDScript, including COM support, see
15  * http://www.digitalmars.com/dscript/cppscript.html
16  */
17 
18 
19 module dmdscript.dfunction;
20 
21 import std.string;
22 import core.stdc.stdlib;
23 
24 import dmdscript.script;
25 import dmdscript.dobject;
26 import dmdscript.value;
27 import dmdscript.protoerror;
28 import dmdscript.threadcontext;
29 import dmdscript.text;
30 import dmdscript.errmsgs;
31 import dmdscript.property;
32 import dmdscript.scopex;
33 import dmdscript.dnative;
34 import dmdscript.functiondefinition;
35 import dmdscript.parse;
36 import dmdscript.ddeclaredfunction;
37 
38 /* ===================== Dfunction_constructor ==================== */
39 
40 class DfunctionConstructor : Dfunction
41 {
42     this(CallContext* cc)
43     {
44         super(cc, 1, cc.tc.Dfunction_prototype);
45 
46         // Actually put in later by Dfunction::initialize()
47         //unsigned attributes = DontEnum | DontDelete | ReadOnly;
48         //Put(TEXT_prototype, Dfunction::getPrototype(), attributes);
49     }
50 
51     override void *Construct(CallContext *cc, Value *ret, Value[] arglist)
52     {
53         // ECMA 15.3.2.1
54         immutable(char)[] bdy;
55         immutable(char)[] P;
56         FunctionDefinition fd;
57         ErrInfo errinfo;
58 
59         //writef("Dfunction_constructor::Construct()\n");
60 
61         // Get parameter list (P) and body from arglist[]
62         if(arglist.length)
63         {
64             bdy = arglist[arglist.length - 1].toString(cc);
65             if(arglist.length >= 2)
66             {
67                 for(uint a = 0; a < arglist.length - 1; a++)
68                 {
69                     if(a)
70                         P ~= ',';
71                     P ~= arglist[a].toString(cc);
72                 }
73             }
74         }
75 
76         if(Parser.parseFunctionDefinition(fd, P, bdy, errinfo))
77             goto Lsyntaxerror;
78 
79         if(fd)
80         {
81             Scope sc;
82 
83             sc.ctor(fd);
84             fd.semantic(&sc);
85             errinfo = sc.errinfo;
86             if(errinfo.message)
87                 goto Lsyntaxerror;
88             fd.toIR(null);
89             Dfunction fobj = new DdeclaredFunction(cc, fd);
90             assert(cc.scoperoot <= cc.scopex.length);
91             fobj.scopex = cc.scopex[0..cc.scoperoot].dup;
92             ret.putVobject(fobj);
93         }
94         else
95             ret.putVundefined();
96 
97         return null;
98 
99         Lsyntaxerror:
100         Dobject o;
101 
102         ret.putVundefined();
103         o = new syntaxerror.D0(cc, &errinfo);
104         Value* v = new Value;
105         v.putVobject(o);
106         return v;
107     }
108 
109     override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
110     {
111         // ECMA 15.3.1
112         return Construct(cc, ret, arglist);
113     }
114 }
115 
116 
117 /* ===================== Dfunction_prototype_toString =============== */
118 
119 void* Dfunction_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
120 {
121     immutable(char)[] s;
122     Dfunction f;
123 
124     //writef("function.prototype.toString()\n");
125     // othis must be a Function
126     if(!othis.isClass(TEXT_Function))
127     {
128         ErrInfo errinfo;
129         ret.putVundefined();
130         return Dobject.RuntimeError(&errinfo, cc, ERR_TS_NOT_TRANSFERRABLE);
131     }
132     else
133     {
134         // Generate string that looks like a FunctionDeclaration
135         // FunctionDeclaration:
136         //	function Identifier (Identifier, ...) Block
137 
138         // If anonymous function, the name should be "anonymous"
139         // per ECMA 15.3.2.1.19
140 
141         f = cast(Dfunction)othis;
142         s = f.toString();
143         ret.putVstring(s);
144     }
145     return null;
146 }
147 
148 /* ===================== Dfunction_prototype_apply =============== */
149 
150 void* Dfunction_prototype_apply(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
151 {
152     // ECMA v3 15.3.4.3
153 
154     Value* thisArg;
155     Value* argArray;
156     Dobject o;
157     void* v;
158 
159     thisArg = &vundefined;
160     argArray = &vundefined;
161     switch(arglist.length)
162     {
163     case 0:
164         break;
165     default:
166         argArray = &arglist[1];
167         goto case;
168     case 1:
169         thisArg = &arglist[0];
170         break;
171     }
172 
173     if(thisArg.isUndefinedOrNull())
174         o = cc.global;
175     else
176         o = thisArg.toObject(cc);
177 
178     if(argArray.isUndefinedOrNull())
179     {
180         v = othis.Call(cc, o, ret, null);
181     }
182     else
183     {
184         if(argArray.isPrimitive())
185         {
186             Ltypeerror:
187             ret.putVundefined();
188             ErrInfo errinfo;
189             return Dobject.RuntimeError(&errinfo, cc, ERR_ARRAY_ARGS);
190         }
191         Dobject a;
192 
193         a = argArray.toObject(cc);
194 
195         // Must be array or arguments object
196         if(!a.isDarray() && !a.isDarguments())
197             goto Ltypeerror;
198 
199         uint len;
200         uint i;
201         Value[] alist;
202         Value* x;
203 
204         x = a.Get(TEXT_length);
205         len = x ? x.toUint32(cc) : 0;
206 
207         Value[] p1;
208         Value* v1;
209         if(len < 128)
210             v1 = cast(Value*)alloca(len * Value.sizeof);
211         if(v1)
212             alist = v1[0 .. len];
213         else
214         {
215             p1 = new Value[len];
216             alist = p1;
217         }
218 
219         for(i = 0; i < len; i++)
220         {
221             x = a.Get(i);
222             Value.copy(&alist[i], x);
223         }
224 
225         v = othis.Call(cc, o, ret, alist);
226 
227         destroy(p1);
228     }
229     return v;
230 }
231 
232 /* ===================== Dfunction_prototype_call =============== */
233 
234 void* Dfunction_prototype_call(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
235 {
236     // ECMA v3 15.3.4.4
237     Value* thisArg;
238     Dobject o;
239     void* v;
240 
241     if(arglist.length == 0)
242     {
243         o = cc.global;
244         v = othis.Call(cc, o, ret, arglist);
245     }
246     else
247     {
248         thisArg = &arglist[0];
249         if(thisArg.isUndefinedOrNull())
250             o = cc.global;
251         else
252             o = thisArg.toObject(cc);
253         v = othis.Call(cc, o, ret, arglist[1 .. $]);
254     }
255     return v;
256 }
257 
258 /* ===================== Dfunction_prototype ==================== */
259 
260 class DfunctionPrototype : Dfunction
261 {
262     this(CallContext* cc)
263     {
264         super(cc, 0, cc.tc.Dobject_prototype);
265 
266         uint attributes = DontEnum;
267 
268         classname = TEXT_Function;
269         name = "prototype";
270         Put(cc, TEXT_constructor, cc.tc.Dfunction_constructor, attributes);
271 
272         static enum NativeFunctionData[] nfd =
273         [
274             { TEXT_toString, &Dfunction_prototype_toString, 0 },
275             { TEXT_apply, &Dfunction_prototype_apply, 2 },
276             { TEXT_call, &Dfunction_prototype_call, 1 },
277         ];
278 
279         DnativeFunction.initialize(this, cc, nfd, attributes);
280     }
281 
282     override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
283     {
284         // ECMA v3 15.3.4
285         // Accept any arguments and return "undefined"
286         ret.putVundefined();
287         return null;
288     }
289 }
290 
291 
292 /* ===================== Dfunction ==================== */
293 
294 class Dfunction : Dobject
295 { const (char)[] name;
296   Dobject[] scopex;     // Function object's scope chain per 13.2 step 7
297 
298   this(CallContext* cc, d_uint32 length)
299   {
300       this(cc, length, Dfunction.getPrototype(cc));
301   }
302 
303   this(CallContext* cc, d_uint32 length, Dobject prototype)
304   {
305       super(cc, prototype);
306       classname = TEXT_Function;
307       name = TEXT_Function;
308       Put(cc, TEXT_length, length, DontDelete | DontEnum | ReadOnly);
309       Put(cc, TEXT_arity, length, DontDelete | DontEnum | ReadOnly);
310   }
311 
312   override immutable(char)[] getTypeof()
313   {     // ECMA 11.4.3
314       return TEXT_function;
315   }
316 
317   override string toString()
318   {
319       // Native overrides of this function replace Identifier with the actual name.
320       // Don't need to do parameter list, though.
321       immutable(char)[] s;
322 
323       s = std..string.format("function %s() { [native code] }", name);
324       return s;
325   }
326 
327   override void *HasInstance(CallContext* cc, Value* ret, Value* v)
328   {
329       // ECMA v3 15.3.5.3
330       Dobject V;
331       Value* w;
332       Dobject o;
333 
334       if(v.isPrimitive())
335           goto Lfalse;
336       V = v.toObject(cc);
337       w = Get(TEXT_prototype);
338       if(w.isPrimitive())
339       {
340           ErrInfo errinfo;
341           return RuntimeError(&errinfo, cc, errmsgtbl[ERR_MUST_BE_OBJECT], w.getType());
342       }
343       o = w.toObject(cc);
344       for(;; )
345       {
346           V = V.internal_prototype;
347           if(!V)
348               goto Lfalse;
349           if(o == V)
350               goto Ltrue;
351       }
352 
353       Ltrue:
354       ret.putVboolean(true);
355       return null;
356 
357       Lfalse:
358       ret.putVboolean(false);
359       return null;
360   }
361 
362   static Dfunction isFunction(Value* v, CallContext* cc)
363   {
364       Dfunction r;
365       Dobject o;
366 
367       r = null;
368       if(!v.isPrimitive())
369       {
370           o = v.toObject(cc);
371           if(o.isClass(TEXT_Function))
372               r = cast(Dfunction)o;
373       }
374       return r;
375   }
376 
377 
378   static Dfunction getConstructor(CallContext* cc)
379   {
380       return cc.tc.Dfunction_constructor;
381   }
382 
383   static Dobject getPrototype(CallContext* cc)
384   {
385       return cc.tc.Dfunction_prototype;
386   }
387 
388   static void initialize(CallContext* cc)
389   {
390       cc.tc.Dfunction_constructor = new DfunctionConstructor(cc);
391       cc.tc.Dfunction_prototype = new DfunctionPrototype(cc);
392 
393       cc.tc.Dfunction_constructor.Put(cc, TEXT_prototype, cc.tc.Dfunction_prototype, DontEnum | DontDelete | ReadOnly);
394 
395       cc.tc.Dfunction_constructor.internal_prototype = cc.tc.Dfunction_prototype;
396       cc.tc.Dfunction_constructor.proptable.previous = cc.tc.Dfunction_prototype.proptable;
397   }
398 }