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 std.c..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.isinf(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.isinf(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.isinf(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.isinf(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  strs[10] =
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.isinf(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               //std.c.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 .. std.c..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         if(vtype == V_NUMBER)
597         {
598             assert(2 <= radix && radix <= 36);
599             if(!isFinite(number))
600                 return toString();
601             return number >= 0.0 ? std.conv.to!(d_string)(cast(long)number, radix) : "-"~std.conv.to!(d_string)(cast(long)-number,radix);
602         }
603         else
604         {
605             return toString();
606         }
607     }
608 
609     d_string toSource()
610     {
611         switch(vtype)
612         {
613         case V_STRING:
614         { d_string s;
615 
616           s = "\"" ~ string ~ "\"";
617           return s; }
618         case V_OBJECT:
619         { Value* v;
620 
621           //writefln("Vobject.toSource()");
622           v = Get(TEXT_toSource);
623           if(!v)
624               v = &vundefined;
625           if(v.isPrimitive())
626               return v.toSource();
627           else          // it's an Object
628           {
629               void* a;
630               CallContext *cc;
631               Dobject o;
632               Value* ret;
633               Value val;
634 
635               o = v.object;
636               cc = Program.getProgram().callcontext;
637               ret = &val;
638               a = o.Call(cc, this.object, ret, null);
639               if(a)                             // if exception was thrown
640               {
641                   /*return a;*/
642                   writef("Vobject.toSource() failed with %x\n", a);
643               }
644               else if(ret.isPrimitive())
645                   return ret.toString();
646           }
647           return TEXT_undefined; }
648         default:
649             return toString();
650         }
651         assert(0);
652     }
653 
654     Dobject toObject()
655     {
656         switch(vtype)
657         {
658         case V_REF_ERROR:
659             throwRefError();
660             assert(0);
661         case V_UNDEFINED:
662             //RuntimeErrorx("cannot convert undefined to Object");
663             return null;
664         case V_NULL:
665             //RuntimeErrorx("cannot convert null to Object");
666             return null;
667         case V_BOOLEAN:
668             return new Dboolean(dbool);
669         case V_NUMBER:
670             return new Dnumber(number);
671         case V_STRING:
672             return new Dstring(string);
673         case V_OBJECT:
674             return object;
675         default:
676             assert(0);
677         }
678         assert(0);
679     }
680 
681     const bool opEquals(ref const (Value)v)
682     {
683         return(opCmp(v) == 0);
684     }
685 
686     /*********************************
687      * Use this instead of std.string.cmp() because
688      * we don't care about lexicographic ordering.
689      * This is faster.
690      */
691 
692     static int stringcmp(d_string s1, d_string s2)
693     {
694         int c = s1.length - s2.length;
695         if(c == 0)
696         {
697             if(s1.ptr == s2.ptr)
698                 return 0;
699             c = memcmp(s1.ptr, s2.ptr, s1.length);
700         }
701         return c;
702     }
703 
704     int opCmp(const (Value)v) const
705     {
706         switch(vtype)
707         {
708         case V_REF_ERROR:
709             throwRefError();
710             assert(0);
711         case V_UNDEFINED:
712             if(vtype == v.vtype)
713                 return 0;
714             break;
715         case V_NULL:
716             if(vtype == v.vtype)
717                 return 0;
718             break;
719         case V_BOOLEAN:
720             if(vtype == v.vtype)
721                 return v.dbool - dbool;
722             break;
723         case V_NUMBER:
724             if(v.vtype == V_NUMBER)
725             {
726                 if(number == v.number)
727                     return 0;
728                 if(isnan(number) && isnan(v.number))
729                     return 0;
730                 if(number > v.number)
731                     return 1;
732             }
733             else if(v.vtype == V_STRING)
734             {
735                 return stringcmp((cast(Value*)&this).toString(), v..string);    //TODO: remove this hack!
736             }
737             break;
738         case V_STRING:
739             if(v.vtype == V_STRING)
740             {
741                 //writefln("'%s'.compareTo('%s')", string, v.string);
742                 int len = string.length - v..string.length;
743                 if(len == 0)
744                 {
745                     if(string.ptr == v..string.ptr)
746                         return 0;
747                     len = memcmp(string.ptr, v..string.ptr, string.length);
748                 }
749                 return len;
750             }
751             else if(v.vtype == V_NUMBER)
752             {
753                 //writefln("'%s'.compareTo(%g)\n", string, v.number);
754                 return stringcmp(string, (cast(Value*)&v).toString());    //TODO: remove this hack!
755             }
756             break;
757         case V_OBJECT:
758             if(v.object == object)
759                 return 0;
760             break;
761         default:
762             assert(0);
763         }
764         return -1;
765     }
766 
767     void copyTo(Value* v)
768     {   // Copy everything, including vptr
769         copy(&this, v);
770     }
771 
772     d_string getType()
773     {
774         d_string s;
775 
776         switch(vtype)
777         {
778         case V_REF_ERROR:
779         case V_UNDEFINED:   s = TypeUndefined; break;
780         case V_NULL:        s = TypeNull;      break;
781         case V_BOOLEAN:     s = TypeBoolean;   break;
782         case V_NUMBER:      s = TypeNumber;    break;
783         case V_STRING:      s = TypeString;    break;
784         case V_OBJECT:      s = TypeObject;    break;
785         case V_ITER:        s = TypeIterator;  break;
786         default:
787             writefln("vtype = %d", vtype);
788             assert(0);
789         }
790         return s;
791     }
792 
793     d_string getTypeof()
794     {
795         d_string s;
796 
797         switch(vtype)
798         {
799         case V_REF_ERROR:
800         case V_UNDEFINED:   s = TEXT_undefined;     break;
801         case V_NULL:        s = TEXT_object;        break;
802         case V_BOOLEAN:     s = TEXT_boolean;       break;
803         case V_NUMBER:      s = TEXT_number;        break;
804         case V_STRING:      s = TEXT_string;        break;
805         case V_OBJECT:      s = object.getTypeof(); break;
806         default:
807             writefln("vtype = %d", vtype);
808             assert(0);
809         }
810         return s;
811     }
812 
813     int isUndefined()
814     {
815         return vtype == V_UNDEFINED;
816     }
817     int isNull()
818     {
819         return vtype == V_NULL;
820     }
821     int isBoolean()
822     {
823         return vtype == V_BOOLEAN;
824     }
825     int isNumber()
826     {
827         return vtype == V_NUMBER;
828     }
829     int isString()
830     {
831         return vtype == V_STRING;
832     }
833     int isObject()
834     {
835         return vtype == V_OBJECT;
836     }
837     int isIterator()
838     {
839         return vtype == V_ITER;
840     }
841 
842     int isUndefinedOrNull()
843     {
844         return vtype == V_UNDEFINED || vtype == V_NULL;
845     }
846     int isPrimitive()
847     {
848         return vtype != V_OBJECT;
849     }
850 
851     int isArrayIndex(out d_uint32 index)
852     {
853         switch(vtype)
854         {
855         case V_NUMBER:
856             index = toUint32();
857             return true;
858         case V_STRING:
859             return StringToIndex(string, index);
860         default:
861             index = 0;
862             return false;
863         }
864         assert(0);
865     }
866 
867     static uint calcHash(uint u)
868     {
869         return u ^ 0x55555555;
870     }
871 
872     static uint calcHash(double d)
873     {
874         return calcHash(cast(uint)d);
875     }
876 
877     static uint calcHash(d_string s)
878     {
879         uint hash;
880 
881         /* If it looks like an array index, hash it to the
882          * same value as if it was an array index.
883          * This means that "1234" hashes to the same value as 1234.
884          */
885         hash = 0;
886         foreach(tchar c; s)
887         {
888             switch(c)
889             {
890             case '0':       hash *= 10;             break;
891             case '1':       hash = hash * 10 + 1;   break;
892 
893             case '2':
894             case '3':
895             case '4':
896             case '5':
897             case '6':
898             case '7':
899             case '8':
900             case '9':
901                 hash = hash * 10 + (c - '0');
902                 break;
903 
904             default:
905             { uint len = s.length;
906               ubyte *str = cast(ubyte*)s.ptr;
907 
908               hash = 0;
909               while(1)
910               {
911                   switch(len)
912                   {
913                   case 0:
914                       break;
915 
916                   case 1:
917                       hash *= 9;
918                       hash += *cast(ubyte *)str;
919                       break;
920 
921                   case 2:
922                       hash *= 9;
923                       hash += *cast(ushort *)str;
924                       break;
925 
926                   case 3:
927                       hash *= 9;
928                       hash += (*cast(ushort *)str << 8) +
929                               (cast(ubyte *)str)[2];
930                       break;
931 
932                   default:
933                       hash *= 9;
934                       hash += *cast(uint *)str;
935                       str += 4;
936                       len -= 4;
937                       continue;
938                   }
939                   break;
940               }
941               break; }
942                 // return s.hash;
943             }
944         }
945         return calcHash(hash);
946     }
947 
948     uint toHash()
949     {
950         uint h;
951 
952         switch(vtype)
953         {
954         case V_REF_ERROR:
955             throwRefError();
956             assert(0);
957         case V_UNDEFINED:
958         case V_NULL:
959             h = 0;
960             break;
961         case V_BOOLEAN:
962             h = dbool ? 1 : 0;
963             break;
964         case V_NUMBER:
965             h = calcHash(number);
966             break;
967         case V_STRING:
968             // Since strings are immutable, if we've already
969             // computed the hash, use previous value
970             if(!hash)
971                 hash = calcHash(string);
972             h = hash;
973             break;
974         case V_OBJECT:
975             /* Uses the address of the object as the hash.
976              * Since the object never moves, it will work
977              * as its hash.
978              * BUG: shouldn't do this.
979              */
980             h = cast(uint)cast(void*)object;
981             break;
982         default:
983             assert(0);
984         }
985         //writefln("\tValue.toHash() = %x", h);
986         return h;
987     }
988 
989     Value* Put(d_string PropertyName, Value* value)
990     {
991         if(vtype == V_OBJECT)
992             return object.Put(PropertyName, value, 0);
993         else
994         {
995             ErrInfo errinfo;
996 
997             return Dobject.RuntimeError(&errinfo,
998                                         errmsgtbl[ERR_CANNOT_PUT_TO_PRIMITIVE],
999                                         PropertyName, value.toString(),
1000                                         getType());
1001         }
1002     }
1003 
1004     Value* Put(d_uint32 index, Value* vindex, Value* value)
1005     {
1006         if(vtype == V_OBJECT)
1007             return object.Put(index, vindex, value, 0);
1008         else
1009         {
1010             ErrInfo errinfo;
1011 
1012             return Dobject.RuntimeError(&errinfo,
1013                                         errmsgtbl[ERR_CANNOT_PUT_INDEX_TO_PRIMITIVE],
1014                                         index,
1015                                         value.toString(), getType());
1016         }
1017     }
1018 
1019     Value* Get(d_string PropertyName)
1020     {
1021         if(vtype == V_OBJECT)
1022             return object.Get(PropertyName);
1023         else
1024         {
1025             // Should we generate the error, or just return undefined?
1026             d_string msg;
1027 
1028             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1029                                     PropertyName, getType(), toString());
1030             throw new ScriptException(msg);
1031             //return &vundefined;
1032         }
1033     }
1034 
1035     Value* Get(d_uint32 index)
1036     {
1037         if(vtype == V_OBJECT)
1038             return object.Get(index);
1039         else
1040         {
1041             // Should we generate the error, or just return undefined?
1042             d_string msg;
1043 
1044             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_INDEX_FROM_PRIMITIVE],
1045                                     index, getType(), toString());
1046             throw new ScriptException(msg);
1047             //return &vundefined;
1048         }
1049     }
1050 
1051     Value* Get(Identifier *id)
1052     {
1053         if(vtype == V_OBJECT)
1054             return object.Get(id);
1055         else if(vtype == V_REF_ERROR){
1056             throwRefError();
1057             assert(0);
1058         }
1059         else
1060         {
1061             // Should we generate the error, or just return undefined?
1062             d_string msg;
1063 
1064             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1065                                     id.toString(), getType(), toString());
1066             throw new ScriptException(msg);
1067             //return &vundefined;
1068         }
1069     }
1070 /+
1071     Value* Get(d_string PropertyName, uint hash)
1072     {
1073         if (vtype == V_OBJECT)
1074             return object.Get(PropertyName, hash);
1075         else
1076         {
1077             // Should we generate the error, or just return undefined?
1078             tchar[] msg;
1079 
1080             msg = std.string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1081                 PropertyName, getType(), toString());
1082             throw new ScriptException(msg);
1083             //return &vundefined;
1084         }
1085     }
1086  +/
1087     void* Construct(CallContext *cc, Value *ret, Value[] arglist)
1088     {
1089         if(vtype == V_OBJECT)
1090             return object.Construct(cc, ret, arglist);
1091         else if(vtype == V_REF_ERROR){
1092             throwRefError();
1093             assert(0);
1094         }
1095         else
1096         {
1097             ErrInfo errinfo;
1098             ret.putVundefined();
1099             return Dobject.RuntimeError(&errinfo,
1100                                         errmsgtbl[ERR_PRIMITIVE_NO_CONSTRUCT], getType());
1101         }
1102     }
1103 
1104     void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
1105     {
1106         if(vtype == V_OBJECT)
1107         {
1108             void* a;
1109 
1110             a = object.Call(cc, othis, ret, arglist);
1111             //if (a) writef("Vobject.Call() returned %x\n", a);
1112             return a;
1113         }
1114         else if(vtype == V_REF_ERROR){
1115             throwRefError();
1116             assert(0);
1117         }
1118         else
1119         {
1120             ErrInfo errinfo;
1121             //PRINTF("Call method not implemented for primitive %p (%s)\n", this, d_string_ptr(toString()));
1122             ret.putVundefined();
1123             return Dobject.RuntimeError(&errinfo,
1124                                         errmsgtbl[ERR_PRIMITIVE_NO_CALL], getType());
1125         }
1126     }
1127 
1128     Value* putIterator(Value* v)
1129     {
1130         if(vtype == V_OBJECT)
1131             return object.putIterator(v);
1132         else
1133         {
1134             ErrInfo errinfo;
1135             v.putVundefined();
1136             return Dobject.RuntimeError(&errinfo,
1137                                         errmsgtbl[ERR_FOR_IN_MUST_BE_OBJECT]);
1138         }
1139     }
1140 
1141 
1142     void getErrInfo(ErrInfo *perrinfo, int linnum)
1143     {
1144         if(vtype == V_OBJECT)
1145             object.getErrInfo(perrinfo, linnum);
1146         else
1147         {
1148             ErrInfo errinfo;
1149 
1150             if(linnum && errinfo.linnum == 0)
1151                 errinfo.linnum = linnum;
1152             errinfo.message = "Unhandled exception: " ~ toString();
1153             if(perrinfo)
1154                 *perrinfo = errinfo;
1155         }
1156     }
1157 
1158     void dump()
1159     {
1160         uint *v = cast(uint *)&this;
1161 
1162         writef("v[%x] = %8x, %8x, %8x, %8x\n", cast(uint)v, v[0], v[1], v[2], v[3]);
1163     }
1164 }
1165 static if(size_t.sizeof == 4)
1166   static assert(Value.sizeof == 16);
1167 else
1168   static assert(Value.sizeof == 24); //fat string point 2*8 + type tag & hash
1169 
1170 Value vundefined = { V_UNDEFINED };
1171 Value vnull = { V_NULL };
1172 
1173 string TypeUndefined = "Undefined";
1174 string TypeNull = "Null";
1175 string TypeBoolean = "Boolean";
1176 string TypeNumber = "Number";
1177 string TypeString = "String";
1178 string TypeObject = "Object";
1179 
1180 string TypeIterator = "Iterator";
1181 
1182 
1183 Value* signalingUndefined(string id){
1184     Value* p;
1185     p = new Value;
1186     p.putSignalingUndefined(id);
1187     return p;
1188 }
1189 
1190 
1191 
1192