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.value;
19 
20 import undead.date;
21 import std.math;
22 import std..string;
23 import std.stdio;
24 import core.stdc..string;
25 
26 import dmdscript.script;
27 import dmdscript.dobject;
28 import dmdscript.iterator;
29 import dmdscript.identifier;
30 import dmdscript.errmsgs;
31 import dmdscript.text;
32 import dmdscript.program;
33 import dmdscript.dstring;
34 import dmdscript.dnumber;
35 import dmdscript.dboolean;
36 
37 // Porting issues:
38 // A lot of scaling is done on arrays of Value's. Therefore, adjusting
39 // it to come out to a size of 16 bytes makes the scaling an efficient
40 // operation. In fact, in some cases (opcodes.c) we prescale the addressing
41 // by 16 bytes at compile time instead of runtime.
42 // So, Value must be looked at in any port to verify that:
43 // 1) the size comes out as 16 bytes, padding as necessary
44 // 2) Value::copy() copies the used data bytes, NOT the padding.
45 //    It's faster to not copy the padding, and the
46 //    padding can contain garbage stack pointers which can
47 //    prevent memory from being garbage collected.
48 
49 version(DigitalMars)
50     version(D_InlineAsm)
51         version = UseAsm;
52 
53 enum
54 {
55     V_REF_ERROR = 0,//triggers ReferenceError expcetion when accessed
56     V_UNDEFINED = 1,
57     V_NULL      = 2,
58     V_BOOLEAN   = 3,
59     V_NUMBER    = 4,
60     V_STRING    = 5,
61     V_OBJECT    = 6,
62     V_ITER      = 7,
63 }
64 
65 struct Value
66 {
67     uint  hash;                 // cache 'hash' value
68     ubyte vtype = V_UNDEFINED;
69     union
70     {
71         d_boolean dbool;        // can be true or false
72         d_number  number;
73         d_string string;
74         Dobject   object;
75         d_int32   int32;
76         d_uint32  uint32;
77         d_uint16  uint16;
78 
79         Iterator* iter;         // V_ITER
80     }
81     void checkReference(){
82         if(vtype == V_REF_ERROR)
83             throwRefError();
84     }
85     void throwRefError() const{
86         throw new ErrorValue(Dobject.ReferenceError(errmsgtbl[ERR_UNDEFINED_VAR],string));
87     }
88     
89     void putSignalingUndefined(d_string id){
90         vtype = V_REF_ERROR;
91         string = id;
92     }
93     void putVundefined()
94     {
95         vtype = V_UNDEFINED;
96         hash = 0;
97         string = null;
98     }
99 
100     void putVnull()
101     {
102         vtype = V_NULL;
103     }
104 
105     void putVboolean(d_boolean b)
106     in
107     {
108         assert(b == 1 || b == 0);
109     }
110     body
111     { vtype = V_BOOLEAN;
112       dbool = b; }
113 
114     void putVnumber(d_number n)
115     {
116         vtype = V_NUMBER;
117         number = n;
118     }
119 
120     void putVtime(d_time n)
121     {
122         vtype = V_NUMBER;
123         number = (n == d_time_nan) ? d_number.nan : n;
124     }
125 
126     void putVstring(d_string s)
127     {
128         vtype = V_STRING;
129         hash = 0;
130         string = s;
131     }
132 
133     void putVstring(d_string s, uint hash)
134     {
135         vtype = V_STRING;
136         this.hash = hash;
137         this.string = s;
138     }
139 
140     void putVobject(Dobject o)
141     {
142         vtype = V_OBJECT;
143         object = o;
144     }
145 
146     void putViterator(Iterator* i)
147     {
148         vtype = V_ITER;
149         iter = i;
150     }
151 
152     invariant()
153     {
154 /+
155         switch (vtype)
156         {
157             case V_UNDEFINED:
158             case V_NULL:
159                 break;
160             case V_BOOLEAN:
161                 assert(dbool == 1 || dbool == 0);
162                 break;
163             case V_NUMBER:
164             case V_STRING:
165             case V_OBJECT:
166             case V_ITER:
167                 break;
168             case V_NONE:
169                 break;
170             default:
171                 writefln("vtype = %d", vtype);
172                 assert(0);
173                 break;
174         }
175  +/
176     }
177 
178     static void copy(Value* to, Value* from)
179     in { }
180     out { assert(memcmp(to, from, Value.sizeof) == 0); }
181     body
182  
183     {
184         /+version(all /*UseAsm*/)
185         {
186             asm
187             { naked;
188               push ESI;
189               mov ECX, [EAX];
190               mov ESI, 8[ESP];
191               mov     [ESI], ECX;
192               mov EDX, 4[EAX];
193               mov ECX, 8[EAX];
194               mov EAX, 12[EAX];
195               mov     4[ESI], EDX;
196               mov     8[ESI], ECX;
197               mov     12[ESI], EAX;
198               pop ESI;
199               ret     4; }
200         }
201         else+/
202         {
203             *to = *from;
204             //(cast(uint *)to)[0] = (cast(uint *)from)[0];
205             //(cast(uint *)to)[1] = (cast(uint *)from)[1];
206             //(cast(uint *)to)[2] = (cast(uint *)from)[2];
207             //(cast(uint *)to)[3] = (cast(uint *)from)[3];
208         }
209     }
210 
211     void* toPrimitive(Value* v, d_string PreferredType)
212     {
213         if(vtype == V_OBJECT)
214         {
215             /*	ECMA 9.1
216                 Return a default value for the Object.
217                 The default value of an object is retrieved by
218                 calling the internal [[DefaultValue]] method
219                 of the object, passing the optional hint
220                 PreferredType. The behavior of the [[DefaultValue]]
221                 method is defined by this specification for all
222                 native ECMAScript objects (see section 8.6.2.6).
223                 If the return value is of type Object or Reference,
224                 a runtime error is generated.
225              */
226             void* a;
227 
228             assert(object);
229             a = object.DefaultValue(v, PreferredType);
230             if(a)
231                 throw new ErrorValue(cast(Value*)a);
232             if(!v.isPrimitive())
233             {
234                 ErrInfo errinfo;
235 
236                 v.putVundefined();
237                 throw new ErrorValue(Dobject.RuntimeError(&errinfo, errmsgtbl[ERR_OBJECT_CANNOT_BE_PRIMITIVE]));
238             }
239         }
240         else
241         {
242             copy(v, &this);
243         }
244         return null;
245     }
246 
247 
248     d_boolean toBoolean()
249     {
250         switch(vtype)
251         {
252         case V_REF_ERROR:
253             throwRefError();
254             assert(0);
255         case V_UNDEFINED:
256         case V_NULL:
257             return false;
258         case V_BOOLEAN:
259             return dbool;
260         case V_NUMBER:
261             return !(number == 0.0 || isNaN(number));
262         case V_STRING:
263             return string.length ? true : false;
264         case V_OBJECT:
265             return true;
266         default:
267             assert(0);
268         }
269         assert(0);
270     }
271 
272 
273     d_number toNumber()
274     {
275         switch(vtype)
276         {
277         case V_REF_ERROR:
278             throwRefError();
279             assert(0);
280         case V_UNDEFINED:
281             return d_number.nan;
282         case V_NULL:
283             return 0;
284         case V_BOOLEAN:
285             return dbool ? 1 : 0;
286         case V_NUMBER:
287             return number;
288         case V_STRING:
289         {
290             d_number n;
291             size_t len;
292             size_t endidx;
293 
294             len = string.length;
295             n = StringNumericLiteral(string, endidx, 0);
296 
297             // Consume trailing whitespace
298             //writefln("n = %s, string = '%s', endidx = %s, length = %s", n, string, endidx, string.length);
299             foreach(dchar c; string[endidx .. $])
300             {
301                 if(!isStrWhiteSpaceChar(c))
302                 {
303                     n = d_number.nan;
304                     break;
305                 }
306             }
307 
308             return n;
309         }
310         case V_OBJECT:
311         { Value val;
312           Value* v;
313           void* a;
314 
315           //writefln("Vobject.toNumber()");
316           v = &val;
317           a = toPrimitive(v, TypeNumber);
318           /*if(a)//rerr
319                   return d_number.nan;*/
320           if(v.isPrimitive())
321               return v.toNumber();
322           else
323               return d_number.nan;
324         }
325         default:
326             assert(0);
327         }
328         assert(0);
329     }
330 
331 
332     d_time toDtime()
333     {
334         return cast(d_time)toNumber();
335     }
336 
337 
338     d_number toInteger()
339     {
340         switch(vtype)
341         {
342         case V_REF_ERROR:
343             throwRefError();
344             assert(0);
345         case V_UNDEFINED:
346             return d_number.nan;
347         case V_NULL:
348             return 0;
349         case V_BOOLEAN:
350             return dbool ? 1 : 0;
351 
352         default:
353         { d_number number;
354 
355           number = toNumber();
356           if(isNaN(number))
357               number = 0;
358           else if(number == 0 || std.math.isInfinity(number))
359           {
360           }
361           else if(number > 0)
362               number = std.math.floor(number);
363           else
364               number = -std.math.floor(-number);
365           return number; }
366         }
367         assert(0);
368     }
369 
370 
371     d_int32 toInt32()
372     {
373         switch(vtype)
374         {
375         case V_REF_ERROR:
376             throwRefError();
377             assert(0);
378         case V_UNDEFINED:
379         case V_NULL:
380             return 0;
381         case V_BOOLEAN:
382             return dbool ? 1 : 0;
383 
384         default:
385         { d_int32 int32;
386           d_number number;
387           long ll;
388 
389           number = toNumber();
390           if(isNaN(number))
391               int32 = 0;
392           else if(number == 0 || std.math.isInfinity(number))
393               int32 = 0;
394           else
395           {
396               if(number > 0)
397                   number = std.math.floor(number);
398               else
399                   number = -std.math.floor(-number);
400 
401               ll = cast(long)number;
402               int32 = cast(int)ll;
403           }
404           return int32; }
405         }
406         assert(0);
407     }
408 
409 
410     d_uint32 toUint32()
411     {
412         switch(vtype)
413         {
414         case V_REF_ERROR:
415             throwRefError();
416             assert(0);
417         case V_UNDEFINED:
418         case V_NULL:
419             return 0;
420         case V_BOOLEAN:
421             return dbool ? 1 : 0;
422 
423         default:
424         { d_uint32 uint32;
425           d_number number;
426           long ll;
427 
428           number = toNumber();
429           if(isNaN(number))
430               uint32 = 0;
431           else if(number == 0 || std.math.isInfinity(number))
432               uint32 = 0;
433           else
434           {
435               if(number > 0)
436                   number = std.math.floor(number);
437               else
438                   number = -std.math.floor(-number);
439 
440               ll = cast(long)number;
441               uint32 = cast(uint)ll;
442           }
443           return uint32; }
444         }
445         assert(0);
446     }
447 
448     d_uint16 toUint16()
449     {
450         switch(vtype)
451         {
452         case V_REF_ERROR:
453             throwRefError();
454             assert(0);
455         case V_UNDEFINED:
456         case V_NULL:
457             return 0;
458         case V_BOOLEAN:
459             return cast(d_uint16)(dbool ? 1 : 0);
460 
461         default:
462         { d_uint16 uint16;
463           d_number number;
464 
465           number = toNumber();
466           if(isNaN(number))
467               uint16 = 0;
468           else if(number == 0 || std.math.isInfinity(number))
469               uint16 = 0;
470           else
471           {
472               if(number > 0)
473                   number = std.math.floor(number);
474               else
475                   number = -std.math.floor(-number);
476 
477               uint16 = cast(ushort)number;
478           }
479           return uint16; }
480         }
481         assert(0);
482     }
483 
484     d_string toString()
485     {
486         switch(vtype)
487         {
488         case V_REF_ERROR:
489             throwRefError();
490             assert(0);
491         case V_UNDEFINED:
492             return TEXT_undefined;
493         case V_NULL:
494             return TEXT_null;
495         case V_BOOLEAN:
496             return dbool ? TEXT_true : TEXT_false;
497         case V_NUMBER:
498         { d_string str;
499           static enum d_string[10]  strs =
500           [   TEXT_0, TEXT_1, TEXT_2, TEXT_3, TEXT_4,
501                       TEXT_5, TEXT_6, TEXT_7, TEXT_8, TEXT_9 ];
502 
503           //writefln("Vnumber.tostr(%g)", number);
504           if(isNaN(number))
505               str = TEXT_NaN;
506           else if(number >= 0 && number <= 9 && number == cast(int)number)
507               str = strs[cast(int)number];
508           else if(std.math.isInfinity(number))
509           {
510               if(number < 0)
511                   str = TEXT_negInfinity;
512               else
513                   str = TEXT_Infinity;
514           }
515           else
516           {
517               tchar[100] buffer;                // should shrink this to max size,
518                                                 // but doesn't really matter
519               tchar* p;
520 
521               // ECMA 262 requires %.21g (21 digits) of precision. But, the
522               // C runtime library doesn't handle that. Until the C runtime
523               // library is upgraded to ANSI C 99 conformance, use
524               // 16 digits, which is all the GCC library will round correctly.
525 
526               std..string.sformat(buffer, "%.16g\0", number);
527               //core.stdc.stdio.sprintf(buffer.ptr, "%.16g", number);
528 
529               // Trim leading spaces
530               for(p = buffer.ptr; *p == ' '; p++)
531               {
532               }
533 
534 
535               {             // Trim any 0's following exponent 'e'
536                   tchar* q;
537                   tchar* t;
538 
539                   for(q = p; *q; q++)
540                   {
541                       if(*q == 'e')
542                       {
543                           q++;
544                           if(*q == '+' || *q == '-')
545                               q++;
546                           t = q;
547                           while(*q == '0')
548                               q++;
549                           if(t != q)
550                           {
551                               for(;; )
552                               {
553                                   *t = *q;
554                                   if(*t == 0)
555                                       break;
556                                   t++;
557                                   q++;
558                               }
559                           }
560                           break;
561                       }
562                   }
563               }
564               str = p[0 .. core.stdc..string.strlen(p)].idup;
565           }
566           //writefln("str = '%s'", str);
567           return str; }
568         case V_STRING:
569             return string;
570         case V_OBJECT:
571         { Value val;
572           Value* v = &val;
573           void* a;
574 
575           //writef("Vobject.toString()\n");
576           a = toPrimitive(v, TypeString);
577           //assert(!a);
578           if(v.isPrimitive())
579               return v.toString();
580           else
581               return v.toObject().classname;
582         }
583         default:
584             assert(0);
585         }
586         assert(0);
587     }
588 
589     d_string toLocaleString()
590     {
591         return toString();
592     }
593 
594     d_string toString(int radix)
595     {
596         import std.conv : to;
597 
598         if(vtype == V_NUMBER)
599         {
600             assert(2 <= radix && radix <= 36);
601             if(!isFinite(number))
602                 return toString();
603             return number >= 0.0 ? to!(d_string)(cast(long)number, radix) : "-"~to!(d_string)(cast(long)-number,radix);
604         }
605         else
606         {
607             return toString();
608         }
609     }
610 
611     d_string toSource()
612     {
613         switch(vtype)
614         {
615         case V_STRING:
616         { d_string s;
617 
618           s = "\"" ~ string ~ "\"";
619           return s; }
620         case V_OBJECT:
621         { Value* v;
622 
623           //writefln("Vobject.toSource()");
624           v = Get(TEXT_toSource);
625           if(!v)
626               v = &vundefined;
627           if(v.isPrimitive())
628               return v.toSource();
629           else          // it's an Object
630           {
631               void* a;
632               CallContext *cc;
633               Dobject o;
634               Value* ret;
635               Value val;
636 
637               o = v.object;
638               cc = Program.getProgram().callcontext;
639               ret = &val;
640               a = o.Call(cc, this.object, ret, null);
641               if(a)                             // if exception was thrown
642               {
643                   /*return a;*/
644                   writef("Vobject.toSource() failed with %x\n", a);
645               }
646               else if(ret.isPrimitive())
647                   return ret.toString();
648           }
649           return TEXT_undefined; }
650         default:
651             return toString();
652         }
653         assert(0);
654     }
655 
656     Dobject toObject()
657     {
658         switch(vtype)
659         {
660         case V_REF_ERROR:
661             throwRefError();
662             assert(0);
663         case V_UNDEFINED:
664             //RuntimeErrorx("cannot convert undefined to Object");
665             return null;
666         case V_NULL:
667             //RuntimeErrorx("cannot convert null to Object");
668             return null;
669         case V_BOOLEAN:
670             return new Dboolean(dbool);
671         case V_NUMBER:
672             return new Dnumber(number);
673         case V_STRING:
674             return new Dstring(string);
675         case V_OBJECT:
676             return object;
677         default:
678             assert(0);
679         }
680         assert(0);
681     }
682 
683     const bool opEquals(ref const (Value)v)
684     {
685         return(opCmp(v) == 0);
686     }
687 
688     /*********************************
689      * Use this instead of std.string.cmp() because
690      * we don't care about lexicographic ordering.
691      * This is faster.
692      */
693 
694     static int stringcmp(d_string s1, d_string s2)
695     {
696         sizediff_t c = s1.length - s2.length;
697         if(c == 0)
698         {
699             if(s1.ptr == s2.ptr)
700                 return 0;
701             c = memcmp(s1.ptr, s2.ptr, s1.length);
702         }
703         return cast(int)c;
704     }
705 
706     int opCmp(const (Value)v) const
707     {
708         switch(vtype)
709         {
710         case V_REF_ERROR:
711             throwRefError();
712             assert(0);
713         case V_UNDEFINED:
714             if(vtype == v.vtype)
715                 return 0;
716             break;
717         case V_NULL:
718             if(vtype == v.vtype)
719                 return 0;
720             break;
721         case V_BOOLEAN:
722             if(vtype == v.vtype)
723                 return v.dbool - dbool;
724             break;
725         case V_NUMBER:
726             if(v.vtype == V_NUMBER)
727             {
728                 if(number == v.number)
729                     return 0;
730                 if(isNaN(number) && isNaN(v.number))
731                     return 0;
732                 if(number > v.number)
733                     return 1;
734             }
735             else if(v.vtype == V_STRING)
736             {
737                 return stringcmp((cast(Value*)&this).toString(), v..string);    //TODO: remove this hack!
738             }
739             break;
740         case V_STRING:
741             if(v.vtype == V_STRING)
742             {
743                 //writefln("'%s'.compareTo('%s')", string, v.string);
744                 sizediff_t len = string.length - v..string.length;
745                 if(len == 0)
746                 {
747                     if(string.ptr == v..string.ptr)
748                         return 0;
749                     len = memcmp(string.ptr, v..string.ptr, string.length);
750                 }
751                 return cast(int)len;
752             }
753             else if(v.vtype == V_NUMBER)
754             {
755                 //writefln("'%s'.compareTo(%g)\n", string, v.number);
756                 return stringcmp(string, (cast(Value*)&v).toString());    //TODO: remove this hack!
757             }
758             break;
759         case V_OBJECT:
760             if(v.object == object)
761                 return 0;
762             break;
763         default:
764             assert(0);
765         }
766         return -1;
767     }
768 
769     void copyTo(Value* v)
770     {   // Copy everything, including vptr
771         copy(&this, v);
772     }
773 
774     d_string getType()
775     {
776         d_string s;
777 
778         switch(vtype)
779         {
780         case V_REF_ERROR:
781         case V_UNDEFINED:   s = TypeUndefined; break;
782         case V_NULL:        s = TypeNull;      break;
783         case V_BOOLEAN:     s = TypeBoolean;   break;
784         case V_NUMBER:      s = TypeNumber;    break;
785         case V_STRING:      s = TypeString;    break;
786         case V_OBJECT:      s = TypeObject;    break;
787         case V_ITER:        s = TypeIterator;  break;
788         default:
789             writefln("vtype = %d", vtype);
790             assert(0);
791         }
792         return s;
793     }
794 
795     d_string getTypeof()
796     {
797         d_string s;
798 
799         switch(vtype)
800         {
801         case V_REF_ERROR:
802         case V_UNDEFINED:   s = TEXT_undefined;     break;
803         case V_NULL:        s = TEXT_object;        break;
804         case V_BOOLEAN:     s = TEXT_boolean;       break;
805         case V_NUMBER:      s = TEXT_number;        break;
806         case V_STRING:      s = TEXT_string;        break;
807         case V_OBJECT:      s = object.getTypeof(); break;
808         default:
809             writefln("vtype = %d", vtype);
810             assert(0);
811         }
812         return s;
813     }
814 
815     int isUndefined()
816     {
817         return vtype == V_UNDEFINED;
818     }
819     int isNull()
820     {
821         return vtype == V_NULL;
822     }
823     int isBoolean()
824     {
825         return vtype == V_BOOLEAN;
826     }
827     int isNumber()
828     {
829         return vtype == V_NUMBER;
830     }
831     int isString()
832     {
833         return vtype == V_STRING;
834     }
835     int isObject()
836     {
837         return vtype == V_OBJECT;
838     }
839     int isIterator()
840     {
841         return vtype == V_ITER;
842     }
843 
844     int isUndefinedOrNull()
845     {
846         return vtype == V_UNDEFINED || vtype == V_NULL;
847     }
848     int isPrimitive()
849     {
850         return vtype != V_OBJECT;
851     }
852 
853     int isArrayIndex(out d_uint32 index)
854     {
855         switch(vtype)
856         {
857         case V_NUMBER:
858             index = toUint32();
859             return true;
860         case V_STRING:
861             return StringToIndex(string, index);
862         default:
863             index = 0;
864             return false;
865         }
866         assert(0);
867     }
868 
869     static uint calcHash(uint u)
870     {
871         return u ^ 0x55555555;
872     }
873 
874     static uint calcHash(double d)
875     {
876         return calcHash(cast(uint)d);
877     }
878 
879     static uint calcHash(d_string s)
880     {
881         uint hash;
882 
883         /* If it looks like an array index, hash it to the
884          * same value as if it was an array index.
885          * This means that "1234" hashes to the same value as 1234.
886          */
887         hash = 0;
888         foreach(tchar c; s)
889         {
890             switch(c)
891             {
892             case '0':       hash *= 10;             break;
893             case '1':       hash = hash * 10 + 1;   break;
894 
895             case '2':
896             case '3':
897             case '4':
898             case '5':
899             case '6':
900             case '7':
901             case '8':
902             case '9':
903                 hash = hash * 10 + (c - '0');
904                 break;
905 
906             default:
907             { size_t len = s.length;
908               ubyte *str = cast(ubyte*)s.ptr;
909 
910               hash = 0;
911               while(1)
912               {
913                   switch(len)
914                   {
915                   case 0:
916                       break;
917 
918                   case 1:
919                       hash *= 9;
920                       hash += *cast(ubyte *)str;
921                       break;
922 
923                   case 2:
924                       hash *= 9;
925                       hash += *cast(ushort *)str;
926                       break;
927 
928                   case 3:
929                       hash *= 9;
930                       hash += (*cast(ushort *)str << 8) +
931                               (cast(ubyte *)str)[2];
932                       break;
933 
934                   default:
935                       hash *= 9;
936                       hash += *cast(uint *)str;
937                       str += 4;
938                       len -= 4;
939                       continue;
940                   }
941                   break;
942               }
943               break; }
944                 // return s.hash;
945             }
946         }
947         return calcHash(hash);
948     }
949 
950     uint toHash()
951     {
952         uint h;
953 
954         switch(vtype)
955         {
956         case V_REF_ERROR:
957             throwRefError();
958             assert(0);
959         case V_UNDEFINED:
960         case V_NULL:
961             h = 0;
962             break;
963         case V_BOOLEAN:
964             h = dbool ? 1 : 0;
965             break;
966         case V_NUMBER:
967             h = calcHash(number);
968             break;
969         case V_STRING:
970             // Since strings are immutable, if we've already
971             // computed the hash, use previous value
972             if(!hash)
973                 hash = calcHash(string);
974             h = hash;
975             break;
976         case V_OBJECT:
977             /* Uses the address of the object as the hash.
978              * Since the object never moves, it will work
979              * as its hash.
980              * BUG: shouldn't do this.
981              */
982             h = cast(uint)cast(void*)object;
983             break;
984         default:
985             assert(0);
986         }
987         //writefln("\tValue.toHash() = %x", h);
988         return h;
989     }
990 
991     Value* Put(d_string PropertyName, Value* value)
992     {
993         if(vtype == V_OBJECT)
994             return object.Put(PropertyName, value, 0);
995         else
996         {
997             ErrInfo errinfo;
998 
999             return Dobject.RuntimeError(&errinfo,
1000                                         errmsgtbl[ERR_CANNOT_PUT_TO_PRIMITIVE],
1001                                         PropertyName, value.toString(),
1002                                         getType());
1003         }
1004     }
1005 
1006     Value* Put(d_uint32 index, Value* vindex, Value* value)
1007     {
1008         if(vtype == V_OBJECT)
1009             return object.Put(index, vindex, value, 0);
1010         else
1011         {
1012             ErrInfo errinfo;
1013 
1014             return Dobject.RuntimeError(&errinfo,
1015                                         errmsgtbl[ERR_CANNOT_PUT_INDEX_TO_PRIMITIVE],
1016                                         index,
1017                                         value.toString(), getType());
1018         }
1019     }
1020 
1021     Value* Get(d_string PropertyName)
1022     {
1023         if(vtype == V_OBJECT)
1024             return object.Get(PropertyName);
1025         else
1026         {
1027             // Should we generate the error, or just return undefined?
1028             d_string msg;
1029 
1030             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1031                                     PropertyName, getType(), toString());
1032             throw new ScriptException(msg);
1033             //return &vundefined;
1034         }
1035     }
1036 
1037     Value* Get(d_uint32 index)
1038     {
1039         if(vtype == V_OBJECT)
1040             return object.Get(index);
1041         else
1042         {
1043             // Should we generate the error, or just return undefined?
1044             d_string msg;
1045 
1046             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_INDEX_FROM_PRIMITIVE],
1047                                     index, getType(), toString());
1048             throw new ScriptException(msg);
1049             //return &vundefined;
1050         }
1051     }
1052 
1053     Value* Get(Identifier *id)
1054     {
1055         if(vtype == V_OBJECT)
1056             return object.Get(id);
1057         else if(vtype == V_REF_ERROR){
1058             throwRefError();
1059             assert(0);
1060         }
1061         else
1062         {
1063             // Should we generate the error, or just return undefined?
1064             d_string msg;
1065 
1066             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1067                                     id.toString(), getType(), toString());
1068             throw new ScriptException(msg);
1069             //return &vundefined;
1070         }
1071     }
1072 /+
1073     Value* Get(d_string PropertyName, uint hash)
1074     {
1075         if (vtype == V_OBJECT)
1076             return object.Get(PropertyName, hash);
1077         else
1078         {
1079             // Should we generate the error, or just return undefined?
1080             tchar[] msg;
1081 
1082             msg = std.string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1083                 PropertyName, getType(), toString());
1084             throw new ScriptException(msg);
1085             //return &vundefined;
1086         }
1087     }
1088  +/
1089     void* Construct(CallContext *cc, Value *ret, Value[] arglist)
1090     {
1091         if(vtype == V_OBJECT)
1092             return object.Construct(cc, ret, arglist);
1093         else if(vtype == V_REF_ERROR){
1094             throwRefError();
1095             assert(0);
1096         }
1097         else
1098         {
1099             ErrInfo errinfo;
1100             ret.putVundefined();
1101             return Dobject.RuntimeError(&errinfo,
1102                                         errmsgtbl[ERR_PRIMITIVE_NO_CONSTRUCT], getType());
1103         }
1104     }
1105 
1106     void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
1107     {
1108         if(vtype == V_OBJECT)
1109         {
1110             void* a;
1111 
1112             a = object.Call(cc, othis, ret, arglist);
1113             //if (a) writef("Vobject.Call() returned %x\n", a);
1114             return a;
1115         }
1116         else if(vtype == V_REF_ERROR){
1117             throwRefError();
1118             assert(0);
1119         }
1120         else
1121         {
1122             ErrInfo errinfo;
1123             //PRINTF("Call method not implemented for primitive %p (%s)\n", this, d_string_ptr(toString()));
1124             ret.putVundefined();
1125             return Dobject.RuntimeError(&errinfo,
1126                                         errmsgtbl[ERR_PRIMITIVE_NO_CALL], getType());
1127         }
1128     }
1129 
1130     Value* putIterator(Value* v)
1131     {
1132         if(vtype == V_OBJECT)
1133             return object.putIterator(v);
1134         else
1135         {
1136             ErrInfo errinfo;
1137             v.putVundefined();
1138             return Dobject.RuntimeError(&errinfo,
1139                                         errmsgtbl[ERR_FOR_IN_MUST_BE_OBJECT]);
1140         }
1141     }
1142 
1143 
1144     void getErrInfo(ErrInfo *perrinfo, int linnum)
1145     {
1146         if(vtype == V_OBJECT)
1147             object.getErrInfo(perrinfo, linnum);
1148         else
1149         {
1150             ErrInfo errinfo;
1151 
1152             if(linnum && errinfo.linnum == 0)
1153                 errinfo.linnum = linnum;
1154             errinfo.message = "Unhandled exception: " ~ toString();
1155             if(perrinfo)
1156                 *perrinfo = errinfo;
1157         }
1158     }
1159 
1160     void dump()
1161     {
1162         uint *v = cast(uint *)&this;
1163 
1164         writef("v[%x] = %8x, %8x, %8x, %8x\n", cast(uint)v, v[0], v[1], v[2], v[3]);
1165     }
1166 }
1167 static if(size_t.sizeof == 4)
1168   static assert(Value.sizeof == 16);
1169 else
1170   static assert(Value.sizeof == 24); //fat string point 2*8 + type tag & hash
1171 
1172 Value vundefined = { V_UNDEFINED };
1173 Value vnull = { V_NULL };
1174 
1175 immutable string TypeUndefined = "Undefined";
1176 immutable string TypeNull = "Null";
1177 immutable string TypeBoolean = "Boolean";
1178 immutable string TypeNumber = "Number";
1179 immutable string TypeString = "String";
1180 immutable string TypeObject = "Object";
1181 
1182 immutable string TypeIterator = "Iterator";
1183 
1184 
1185 Value* signalingUndefined(string id){
1186     Value* p;
1187     p = new Value;
1188     p.putSignalingUndefined(id);
1189     return p;
1190 }
1191 
1192 
1193 
1194