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(Value* vptr){
52         super("DMDScript exception");
53         value = *vptr;
54     }
55 }
56 //debug = LOG;
57 
58 /************************** Dobject_constructor *************************/
59 
60 class DobjectConstructor : Dfunction
61 {
62     this()
63     {
64         super(1, Dfunction_prototype);
65         if(Dobject_prototype)
66             Put(TEXT_prototype, 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(Dobject.getPrototype());
78         }
79         else
80         {
81             v = &arglist[0];
82             if(v.isPrimitive())
83             {
84                 if(v.isUndefinedOrNull())
85                 {
86                     o = new Dobject(Dobject.getPrototype());
87                 }
88                 else
89                     o = v.toObject();
90             }
91             else
92                 o = v.toObject();
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();
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();
200             buf ~= ':';
201             buf ~= p.value.toSource();
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();
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()
268     {
269         super(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(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;
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(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(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(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(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(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(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(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(Value* value)
415     {
416         // Not ECMA, Microsoft extension
417         //writef("Dobject.PutDefault(this = %p)\n", this);
418         ErrInfo errinfo;
419         return RuntimeError(&errinfo, ERR_NO_DEFAULT_PUT);
420     }
421 
422     Value* put_Value(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, 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(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                 CallContext *cc;
503 
504                 //writefln("\tfound default value");
505                 o = v.object;
506                 cc = Program.getProgram().callcontext;
507                 a = o.Call(cc, this, ret, null);
508                 if(a)                   // if exception was thrown
509                     return a;
510                 if(ret.isPrimitive())
511                     return null;
512             }
513             i ^= 1;
514         }
515         ErrInfo errinfo;
516         return Dobject.RuntimeError(&errinfo, "No [[DefaultValue]]");
517         //ErrInfo errinfo;
518         //return RuntimeError(&errinfo, DTEXT("no Default Value for object"));
519     }
520 
521     void *Construct(CallContext *cc, Value *ret, Value[] arglist)
522     {
523         ErrInfo errinfo;
524         return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_CONSTRUCT], classname);
525     }
526 
527     void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
528     {
529         ErrInfo errinfo;
530         return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_CALL], classname);
531     }
532 
533     void *HasInstance(Value* ret, Value* v)
534     {   // ECMA v3 8.6.2
535         ErrInfo errinfo;
536         return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_INSTANCE], classname);
537     }
538 
539     d_string getTypeof()
540     {   // ECMA 11.4.3
541         return TEXT_object;
542     }
543 
544 
545     int isClass(d_string classname) const
546     {
547         return this.classname == classname;
548     }
549 
550     int isDarray() const
551     {
552         return isClass(TEXT_Array);
553     }
554     int isDdate() const
555     {
556         return isClass(TEXT_Date);
557     }
558     int isDregexp() const
559     {
560         return isClass(TEXT_RegExp);
561     }
562 
563     int isDarguments() const
564     {
565         return false;
566     }
567     int isCatch() const
568     {
569         return false;
570     }
571     int isFinally() const
572     {
573         return false;
574     }
575 
576     void getErrInfo(ErrInfo *perrinfo, int linnum)
577     {
578         ErrInfo errinfo;
579         Value v;
580         v.putVobject(this);
581 
582         errinfo.message = v.toString();
583         if(perrinfo)
584             *perrinfo = errinfo;
585     }
586 
587     static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args)
588     {
589         return RuntimeError(perrinfo, errmsgtbl[msgnum], args);
590     }
591 
592     static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, string fmt, ARGS args)
593     {
594         import std.format : formattedWrite;
595 
596         Dobject o;
597 
598         //perrinfo.message = null;
599         char[] buffer = null;
600 
601         void putc(dchar c)
602         {
603             std.utf.encode(buffer, c);
604         }
605 
606         formattedWrite(&putc, fmt, args);
607         perrinfo.message = assumeUnique(buffer);
608         o = new typeerror.D0(perrinfo);
609         Value* v = new Value;
610         v.putVobject(o);
611         return v;
612     }
613     static Value* ReferenceError(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args)
614     {
615         return ReferenceError(perrinfo, errmsgtbl[msgnum], args);
616     }
617 
618     static Value* ReferenceError(ARGS...)(string fmt, ARGS args)
619     {
620         ErrInfo errinfo;
621         return ReferenceError(&errinfo, fmt, args);
622     }
623 
624     static Value* ReferenceError(ARGS...)(ErrInfo* perrinfo, string fmt, ARGS args)
625     {
626         import std.format : formattedWrite;
627 
628         Dobject o;
629         //perrinfo.message = null;
630         d_string buffer = null;
631 
632         void putc(dchar c)
633         {
634             dmdscript.utf.encode(buffer, c);
635         }
636 
637         formattedWrite(&putc, fmt, args);
638         perrinfo.message = buffer;
639 
640         o = new referenceerror.D0(perrinfo);
641         Value* v = new Value;
642         v.putVobject(o);
643 
644         return v;
645     }
646     static Value* RangeError(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args)
647     {
648         return RangeError(perrinfo, errmsgtbl[msgnum], args);
649     }
650 
651     static Value* RangeError(ARGS...)(ErrInfo *perrinfo, string fmt, ARGS args)
652     {
653         import std.format : formattedWrite;
654 
655         Dobject o;
656 
657         //perrinfo.message = null;
658         d_string buffer = null;
659 
660         void putc(dchar c)
661         {
662             dmdscript.utf.encode(buffer, c);
663         }
664 
665         formattedWrite(&putc, fmt, args);
666         perrinfo.message = buffer;
667 
668         o = new rangeerror.D0(perrinfo);
669         Value* v = new Value;
670         v.putVobject(o);
671         return v;
672     }
673 
674     Value* putIterator(Value* v)
675     {
676         Iterator* i = new Iterator;
677 
678         i.ctor(this);
679         v.putViterator(i);
680         return null;
681     }
682 
683     static Dfunction getConstructor()
684     {
685         return Dobject_constructor;
686     }
687 
688     static Dobject getPrototype()
689     {
690         return Dobject_prototype;
691     }
692 
693     static void initialize()
694     {
695         Dobject_prototype = new DobjectPrototype();
696         Dfunction.initialize();
697         Dobject_constructor = new DobjectConstructor();
698 
699         Dobject op = Dobject_prototype;
700         Dobject f = Dfunction_prototype;
701 
702         op.Put(TEXT_constructor, Dobject_constructor, DontEnum);
703 
704         static enum NativeFunctionData[] nfd =
705         [
706             { TEXT_toString, &Dobject_prototype_toString, 0 },
707             { TEXT_toLocaleString, &Dobject_prototype_toLocaleString, 0 },
708             { TEXT_toSource, &Dobject_prototype_toSource, 0 },
709             { TEXT_valueOf, &Dobject_prototype_valueOf, 0 },
710             { TEXT_hasOwnProperty, &Dobject_prototype_hasOwnProperty, 1 },
711             { TEXT_isPrototypeOf, &Dobject_prototype_isPrototypeOf, 0 },
712             { TEXT_propertyIsEnumerable, &Dobject_prototype_propertyIsEnumerable, 0 },
713         ];
714 
715         DnativeFunction.initialize(op, nfd, DontEnum);
716     }
717 }
718 
719 
720 /*********************************************
721  * Initialize the built-in's.
722  */
723 
724 void dobject_init()
725 {
726     //writef("dobject_init(tc = %x)\n", cast(uint)tc);
727     if(Dobject_prototype)
728         return;                 // already initialized for this thread
729 
730     version(none)
731     {
732         writef("sizeof(Dobject) = %d\n", sizeof(Dobject));
733         writef("sizeof(PropTable) = %d\n", sizeof(PropTable));
734         writef("offsetof(proptable) = %d\n", offsetof(Dobject, proptable));
735         writef("offsetof(internal_prototype) = %d\n", offsetof(Dobject, internal_prototype));
736         writef("offsetof(classname) = %d\n", offsetof(Dobject, classname));
737         writef("offsetof(value) = %d\n", offsetof(Dobject, value));
738     }
739 
740     Dobject.initialize();
741     Dboolean.initialize();
742     Dstring.initialize();
743     Dnumber.initialize();
744     Darray.initialize();
745     Dmath.initialize();
746     Ddate.initialize();
747     Dregexp.initialize();
748     Derror.initialize();
749 	
750 	// Call registered initializer for each object type
751     foreach(void function() fpinit; threadInitTable)
752         (*fpinit)();
753 	
754 }
755 /*Not used anyway
756 void dobject_term()
757 {
758     //writef("dobject_term(program = %x)\n", program);
759 
760     memset(&program, 0, ThreadContext.sizeof - Thread.sizeof);
761 }
762 */