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 module dmdscript.dobject;
19 
20 import std.string;
21 import core.stdc.stdarg;
22 import core.stdc.string;
23 import std.exception;
24 
25 import dmdscript.script;
26 import dmdscript.value;
27 import dmdscript.dfunction;
28 import dmdscript.property;
29 import dmdscript.threadcontext;
30 import dmdscript.iterator;
31 import dmdscript.identifier;
32 import dmdscript.errmsgs;
33 import dmdscript.text;
34 import dmdscript.program;
35 
36 import dmdscript.dboolean;
37 import dmdscript.dstring;
38 import dmdscript.dnumber;
39 import dmdscript.darray;
40 import dmdscript.dmath;
41 import dmdscript.ddate;
42 import dmdscript.dregexp;
43 import dmdscript.derror;
44 import dmdscript.dnative;
45 
46 import dmdscript.protoerror;
47 import dmdscript.utf;
48 
49 class ErrorValue: Exception {
50     Value value;
51     this(CallContext* cc, Value* vptr){
52         super(vptr.toString(cc));
53         value = *vptr;
54     }
55 }
56 //debug = LOG;
57 
58 /************************** Dobject_constructor *************************/
59 
60 class DobjectConstructor : Dfunction
61 {
62     this(CallContext* cc)
63     {
64         super(cc, 1, cc.tc.Dfunction_prototype);
65         if(cc.tc.Dobject_prototype)
66             Put(cc, TEXT_prototype, cc.tc.Dobject_prototype, DontEnum | DontDelete | ReadOnly);
67     }
68 
69     override void *Construct(CallContext *cc, Value *ret, Value[] arglist)
70     {
71         Dobject o;
72         Value* v;
73 
74         // ECMA 15.2.2
75         if(arglist.length == 0)
76         {
77             o = new Dobject(cc, Dobject.getPrototype(cc));
78         }
79         else
80         {
81             v = &arglist[0];
82             if(v.isPrimitive())
83             {
84                 if(v.isUndefinedOrNull())
85                 {
86                     o = new Dobject(cc, Dobject.getPrototype(cc));
87                 }
88                 else
89                     o = v.toObject(cc);
90             }
91             else
92                 o = v.toObject(cc);
93         }
94         //printf("constructed object o=%p, v=%p,'%s'\n", o, v,v.getType());
95         ret.putVobject(o);
96         return null;
97     }
98 
99     override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
100     {
101         Dobject o;
102         void *result;
103 
104         // ECMA 15.2.1
105         if(arglist.length == 0)
106         {
107             result = Construct(cc, ret, arglist);
108         }
109         else
110         {
111             Value* v;
112 
113             v = &arglist[0];
114             if(v.isUndefinedOrNull())
115                 result = Construct(cc, ret, arglist);
116             else
117             {
118                 o = v.toObject(cc);
119                 ret.putVobject(o);
120                 result = null;
121             }
122         }
123         return result;
124     }
125 }
126 
127 
128 /* ===================== Dobject_prototype_toString ================ */
129 
130 void* Dobject_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
131 {
132     d_string s;
133     d_string string;
134 
135     //debug (LOG) writef("Dobject.prototype.toString(ret = %x)\n", ret);
136 
137     s = othis.classname;
138 /+
139     // Should we do [object] or [object Object]?
140     if (s == TEXT_Object)
141         string = TEXT_bobjectb;
142     else
143  +/
144     string = std..string.format("[object %s]", s);
145     ret.putVstring(string);
146     return null;
147 }
148 
149 /* ===================== Dobject_prototype_toLocaleString ================ */
150 
151 void* Dobject_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
152 {
153     // ECMA v3 15.2.4.3
154     //	"This function returns the result of calling toString()."
155 
156     Value* v;
157 
158     //writef("Dobject.prototype.toLocaleString(ret = %x)\n", ret);
159     v = othis.Get(TEXT_toString);
160     if(v && !v.isPrimitive())   // if it's an Object
161     {
162         void *a;
163         Dobject o;
164 
165         o = v.object;
166         a = o.Call(cc, othis, ret, arglist);
167         if(a)                   // if exception was thrown
168             return a;
169     }
170     return null;
171 }
172 
173 /* ===================== Dobject_prototype_valueOf ================ */
174 
175 void* Dobject_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
176 {
177     ret.putVobject(othis);
178     return null;
179 }
180 
181 /* ===================== Dobject_prototype_toSource ================ */
182 
183 void* Dobject_prototype_toSource(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
184 {
185     d_string buf;
186     int any;
187 
188     //writef("Dobject.prototype.toSource(this = %p, ret = %p)\n", this, ret);
189 
190     buf = "{";
191     any = 0;
192     foreach(Value key, Property p; *othis.proptable)
193     {
194         if(!(p.attributes & (DontEnum | Deleted)))
195         {
196             if(any)
197                 buf ~= ',';
198             any = 1;
199             buf ~= key.toString(cc);
200             buf ~= ':';
201             buf ~= p.value.toSource(cc);
202         }
203     }
204     buf ~= '}';
205     ret.putVstring(buf);
206     return null;
207 }
208 
209 /* ===================== Dobject_prototype_hasOwnProperty ================ */
210 
211 void* Dobject_prototype_hasOwnProperty(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
212 {
213     // ECMA v3 15.2.4.5
214     Value* v;
215 
216     v = arglist.length ? &arglist[0] : &vundefined;
217     ret.putVboolean(othis.proptable.hasownproperty(v, 0));
218     return null;
219 }
220 
221 /* ===================== Dobject_prototype_isPrototypeOf ================ */
222 
223 void* Dobject_prototype_isPrototypeOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
224 {
225     // ECMA v3 15.2.4.6
226     d_boolean result = false;
227     Value* v;
228     Dobject o;
229 
230     v = arglist.length ? &arglist[0] : &vundefined;
231     if(!v.isPrimitive())
232     {
233         o = v.toObject(cc);
234         for(;; )
235         {
236             o = o.internal_prototype;
237             if(!o)
238                 break;
239             if(o == othis)
240             {
241                 result = true;
242                 break;
243             }
244         }
245     }
246 
247     ret.putVboolean(result);
248     return null;
249 }
250 
251 /* ===================== Dobject_prototype_propertyIsEnumerable ================ */
252 
253 void* Dobject_prototype_propertyIsEnumerable(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
254 {
255     // ECMA v3 15.2.4.7
256     Value* v;
257 
258     v = arglist.length ? &arglist[0] : &vundefined;
259     ret.putVboolean(othis.proptable.hasownproperty(v, 1));
260     return null;
261 }
262 
263 /* ===================== Dobject_prototype ========================= */
264 
265 class DobjectPrototype : Dobject
266 {
267     this(CallContext* cc)
268     {
269         super(cc, null);
270     }
271 }
272 
273 
274 /* ====================== Dobject ======================= */
275 
276 class Dobject
277 {
278     PropTable* proptable;
279     Dobject internal_prototype;
280     string classname;
281     Value value;
282 
283     enum uint DOBJECT_SIGNATURE = 0xAA31EE31;
284     uint signature;
285 
286     invariant()
287     {
288         assert(signature == DOBJECT_SIGNATURE);
289     }
290 
291     this(CallContext* cc, Dobject prototype)
292     {
293         //writef("new Dobject = %x, prototype = %x, line = %d, file = '%s'\n", this, prototype, GC.line, ascii2unicode(GC.file));
294         //writef("Dobject(prototype = %p)\n", prototype);
295         proptable = new PropTable(cc);
296         internal_prototype = prototype;
297         if(prototype)
298             proptable.previous = prototype.proptable;
299         classname = TEXT_Object;
300         value.putVobject(this);
301 
302         signature = DOBJECT_SIGNATURE;
303     }
304 
305     Dobject Prototype()
306     {
307         return internal_prototype;
308     }
309 
310     Value* Get(d_string PropertyName)
311     {
312         return Get(PropertyName, Value.calcHash(PropertyName));
313     }
314 
315     Value* Get(Identifier* id)
316     {
317         Value* v;
318 
319         //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
320         //writef("\tinternal_prototype = %p\n", this.internal_prototype);
321         //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
322         v = proptable.get(&id.value, id.value.hash);
323         //if (v) writef("found it %p\n", v.object);
324         return v;
325     }
326 
327     Value* Get(d_string PropertyName, uint hash)
328     {
329         Value* v;
330 
331         //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash);
332         //writef("\tinternal_prototype = %p\n", this.internal_prototype);
333         //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype());
334         v = proptable.get(PropertyName, hash);
335         //if (v) writef("found it %p\n", v.object);
336         return v;
337     }
338 
339     Value* Get(d_uint32 index)
340     {
341         Value* v;
342 
343         v = proptable.get(index);
344         //    if (!v)
345         //	v = &vundefined;
346         return v;
347     }
348 
349     Value* Get(d_uint32 index, Value* vindex)
350     {
351         return proptable.get(vindex, Value.calcHash(index));
352     }
353 
354     Value* Put(CallContext* cc, d_string PropertyName, Value* value, uint attributes)
355     {
356         // ECMA 8.6.2.2
357         //writef("Dobject.Put(this = %p)\n", this);
358         proptable.put(PropertyName, value, attributes);
359         return null;
360     }
361 
362     Value* Put(CallContext* cc, Identifier* key, Value* value, uint attributes)
363     {
364         // ECMA 8.6.2.2
365         //writef("Dobject.Put(this = %p)\n", this);
366         proptable.put(&key.value, key.value.hash, value, attributes);
367         return null;
368     }
369 
370     Value* Put(CallContext* cc, d_string PropertyName, Dobject o, uint attributes)
371     {
372         // ECMA 8.6.2.2
373         Value v;
374         v.putVobject(o);
375 
376         proptable.put(PropertyName, &v, attributes);
377         return null;
378     }
379 
380     Value* Put(CallContext* cc, d_string PropertyName, d_number n, uint attributes)
381     {
382         // ECMA 8.6.2.2
383         Value v;
384         v.putVnumber(n);
385 
386         proptable.put(PropertyName, &v, attributes);
387         return null;
388     }
389 
390     Value* Put(CallContext* cc, d_string PropertyName, d_string s, uint attributes)
391     {
392         // ECMA 8.6.2.2
393         Value v;
394         v.putVstring(s);
395 
396         proptable.put(PropertyName, &v, attributes);
397         return null;
398     }
399 
400     Value* Put(CallContext* cc, d_uint32 index, Value* vindex, Value* value, uint attributes)
401     {
402         // ECMA 8.6.2.2
403         proptable.put(vindex, Value.calcHash(index), value, attributes);
404         return null;
405     }
406 
407     Value* Put(CallContext* cc, d_uint32 index, Value* value, uint attributes)
408     {
409         // ECMA 8.6.2.2
410         proptable.put(index, value, attributes);
411         return null;
412     }
413 
414     Value* PutDefault(CallContext* cc, Value* value)
415     {
416         // Not ECMA, Microsoft extension
417         //writef("Dobject.PutDefault(this = %p)\n", this);
418         ErrInfo errinfo;
419         return RuntimeError(&errinfo, cc, ERR_NO_DEFAULT_PUT);
420     }
421 
422     Value* put_Value(CallContext* cc, Value* ret, Value[] arglist)
423     {
424         // Not ECMA, Microsoft extension
425         //writef("Dobject.put_Value(this = %p)\n", this);
426         ErrInfo errinfo;
427         return RuntimeError(&errinfo, cc, ERR_FUNCTION_NOT_LVALUE);
428     }
429 
430     int CanPut(d_string PropertyName)
431     {
432         // ECMA 8.6.2.3
433         return proptable.canput(PropertyName);
434     }
435 
436     int HasProperty(d_string PropertyName)
437     {
438         // ECMA 8.6.2.4
439         return proptable.hasproperty(PropertyName);
440     }
441 
442     /***********************************
443      * Return:
444      *	TRUE	not found or successful delete
445      *	FALSE	property is marked with DontDelete attribute
446      */
447 
448     int Delete(d_string PropertyName)
449     {
450         // ECMA 8.6.2.5
451         //writef("Dobject.Delete('%ls')\n", d_string_ptr(PropertyName));
452         return proptable.del(PropertyName);
453     }
454 
455     int Delete(d_uint32 index)
456     {
457         // ECMA 8.6.2.5
458         return proptable.del(index);
459     }
460 
461     int implementsDelete()
462     {
463         // ECMA 8.6.2 says every object implements [[Delete]],
464         // but ECMA 11.4.1 says that some objects may not.
465         // Assume the former is correct.
466         return true;
467     }
468 
469     void *DefaultValue(CallContext* cc, Value* ret, d_string Hint)
470     {
471         Dobject o;
472         Value* v;
473         static enum d_string[2] table = [ TEXT_toString, TEXT_valueOf ];
474         int i = 0;                      // initializer necessary for /W4
475 
476         // ECMA 8.6.2.6
477         //writef("Dobject.DefaultValue(ret = %x, Hint = '%s')\n", cast(uint)ret, Hint);
478 
479         if(Hint == TypeString ||
480            (Hint == null && this.isDdate()))
481         {
482             i = 0;
483         }
484         else if(Hint == TypeNumber ||
485                 Hint == null)
486         {
487             i = 1;
488         }
489         else
490             assert(0);
491 
492         for(int j = 0; j < 2; j++)
493         {
494             d_string htab = table[i];
495 
496             //writefln("\ti = %d, htab = '%s'", i, htab);
497             v = Get(htab, Value.calcHash(htab));
498             //writefln("\tv = %x", cast(uint)v);
499             if(v && !v.isPrimitive())   // if it's an Object
500             {
501                 void *a;
502 
503                 //writefln("\tfound default value");
504                 o = v.object;
505                 a = o.Call(cc, this, ret, null);
506                 if(a)                   // if exception was thrown
507                     return a;
508                 if(ret.isPrimitive())
509                     return null;
510             }
511             i ^= 1;
512         }
513         ErrInfo errinfo;
514         return Dobject.RuntimeError(&errinfo, cc, "No [[DefaultValue]]");
515         //ErrInfo errinfo;
516         //return RuntimeError(&errinfo, DTEXT("no Default Value for object"));
517     }
518 
519     void *Construct(CallContext *cc, Value *ret, Value[] arglist)
520     {
521         ErrInfo errinfo;
522         return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_CONSTRUCT], classname);
523     }
524 
525     void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
526     {
527         ErrInfo errinfo;
528         return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_CALL], classname);
529     }
530 
531     void *HasInstance(CallContext* cc, Value* ret, Value* v)
532     {   // ECMA v3 8.6.2
533         ErrInfo errinfo;
534         return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_INSTANCE], classname);
535     }
536 
537     d_string getTypeof()
538     {   // ECMA 11.4.3
539         return TEXT_object;
540     }
541 
542 
543     int isClass(d_string classname) const
544     {
545         return this.classname == classname;
546     }
547 
548     int isDarray() const
549     {
550         return isClass(TEXT_Array);
551     }
552     int isDdate() const
553     {
554         return isClass(TEXT_Date);
555     }
556     int isDregexp() const
557     {
558         return isClass(TEXT_RegExp);
559     }
560 
561     int isDarguments() const
562     {
563         return false;
564     }
565     int isCatch() const
566     {
567         return false;
568     }
569     int isFinally() const
570     {
571         return false;
572     }
573 
574     void getErrInfo(CallContext* cc, ErrInfo *perrinfo, int linnum)
575     {
576         ErrInfo errinfo;
577         Value v;
578         v.putVobject(this);
579 
580         errinfo.message = v.toString(cc);
581         if(perrinfo)
582             *perrinfo = errinfo;
583     }
584 
585     static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args)
586     {
587         return RuntimeError(perrinfo, cc, errmsgtbl[msgnum], args);
588     }
589 
590     static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, string fmt, ARGS args)
591     {
592         import std.format : formattedWrite;
593 
594         Dobject o;
595 
596         //perrinfo.message = null;
597         char[] buffer = null;
598 
599         void putc(dchar c)
600         {
601             std.utf.encode(buffer, c);
602         }
603 
604         formattedWrite(&putc, fmt, args);
605         perrinfo.message = assumeUnique(buffer);
606         o = new typeerror.D0(cc, perrinfo);
607         Value* v = new Value;
608         v.putVobject(o);
609         return v;
610     }
611     static Value* ReferenceError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args)
612     {
613         return ReferenceError(perrinfo, cc, errmsgtbl[msgnum], args);
614     }
615 
616     static Value* ReferenceError(ARGS...)(CallContext* cc, string fmt, ARGS args)
617     {
618         ErrInfo errinfo;
619         return ReferenceError(&errinfo, cc, fmt, args);
620     }
621 
622     static Value* ReferenceError(ARGS...)(ErrInfo* perrinfo, CallContext* cc, string fmt, ARGS args)
623     {
624         import std.format : formattedWrite;
625 
626         Dobject o;
627         //perrinfo.message = null;
628         d_string buffer = null;
629 
630         void putc(dchar c)
631         {
632             dmdscript.utf.encode(buffer, c);
633         }
634 
635         formattedWrite(&putc, fmt, args);
636         perrinfo.message = buffer;
637 
638         o = new referenceerror.D0(cc, perrinfo);
639         Value* v = new Value;
640         v.putVobject(o);
641 
642         return v;
643     }
644     static Value* RangeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args)
645     {
646         return RangeError(perrinfo, cc, errmsgtbl[msgnum], args);
647     }
648 
649     static Value* RangeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, string fmt, ARGS args)
650     {
651         import std.format : formattedWrite;
652 
653         Dobject o;
654 
655         //perrinfo.message = null;
656         d_string buffer = null;
657 
658         void putc(dchar c)
659         {
660             dmdscript.utf.encode(buffer, c);
661         }
662 
663         formattedWrite(&putc, fmt, args);
664         perrinfo.message = buffer;
665 
666         o = new rangeerror.D0(cc, perrinfo);
667         Value* v = new Value;
668         v.putVobject(o);
669         return v;
670     }
671 
672     Value* putIterator(CallContext* cc, Value* v)
673     {
674         Iterator* i = new Iterator;
675 
676         i.ctor(cc, this);
677         v.putViterator(i);
678         return null;
679     }
680 
681     static Dfunction getConstructor(CallContext* cc)
682     {
683         return cc.tc.Dobject_constructor;
684     }
685 
686     static Dobject getPrototype(CallContext* cc)
687     {
688         return cc.tc.Dobject_prototype;
689     }
690 
691     static void initialize(CallContext* cc)
692     {
693         cc.tc.Dobject_prototype = new DobjectPrototype(cc);
694         Dfunction.initialize(cc);
695         cc.tc.Dobject_constructor = new DobjectConstructor(cc);
696 
697         Dobject op = cc.tc.Dobject_prototype;
698         Dobject f = cc.tc.Dfunction_prototype;
699 
700         op.Put(cc, TEXT_constructor, cc.tc.Dobject_constructor, DontEnum);
701 
702         static enum NativeFunctionData[] nfd =
703         [
704             { TEXT_toString, &Dobject_prototype_toString, 0 },
705             { TEXT_toLocaleString, &Dobject_prototype_toLocaleString, 0 },
706             { TEXT_toSource, &Dobject_prototype_toSource, 0 },
707             { TEXT_valueOf, &Dobject_prototype_valueOf, 0 },
708             { TEXT_hasOwnProperty, &Dobject_prototype_hasOwnProperty, 1 },
709             { TEXT_isPrototypeOf, &Dobject_prototype_isPrototypeOf, 0 },
710             { TEXT_propertyIsEnumerable, &Dobject_prototype_propertyIsEnumerable, 0 },
711         ];
712 
713         DnativeFunction.initialize(op, cc, nfd, DontEnum);
714     }
715 }
716 
717 
718 /*********************************************
719  * Initialize the built-in's.
720  */
721 
722 void dobject_init(CallContext* cc)
723 {
724     //writef("dobject_init(tc = %x)\n", cast(uint)tc);
725     if(cc.tc.Dobject_prototype)
726         return;                 // already initialized for this thread
727 
728     version(none)
729     {
730         writef("sizeof(Dobject) = %d\n", sizeof(Dobject));
731         writef("sizeof(PropTable) = %d\n", sizeof(PropTable));
732         writef("offsetof(proptable) = %d\n", offsetof(Dobject, proptable));
733         writef("offsetof(internal_prototype) = %d\n", offsetof(Dobject, internal_prototype));
734         writef("offsetof(classname) = %d\n", offsetof(Dobject, classname));
735         writef("offsetof(value) = %d\n", offsetof(Dobject, value));
736     }
737 
738     Dobject.initialize(cc);
739     Dboolean.initialize(cc);
740     Dstring.initialize(cc);
741     Dnumber.initialize(cc);
742     Darray.initialize(cc);
743     Dmath.initialize(cc);
744     Ddate.initialize(cc);
745     Dregexp.initialize(cc);
746     Derror.initialize(cc);
747 	
748 	// Call registered initializer for each object type
749     foreach(fpinit; cc.tc.threadInitTable)
750         fpinit(cc);
751 }
752 /*Not used anyway
753 void dobject_term()
754 {
755     //writef("dobject_term(program = %x)\n", program);
756 
757     memset(&program, 0, ThreadContext.sizeof - Thread.sizeof);
758 }
759 */