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(CallContext* cc){
82         if(vtype == V_REF_ERROR)
83             throwRefError(cc);
84     }
85     void throwRefError(CallContext* cc) const{
86         throw new ErrorValue(cc, Dobject.ReferenceError(cc,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     do
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     do
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(CallContext* cc, 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(cc, v, PreferredType);
230             if(a)
231                 throw new ErrorValue(cc, cast(Value*)a);
232             if(!v.isPrimitive())
233             {
234                 ErrInfo errinfo;
235 
236                 v.putVundefined();
237                 throw new ErrorValue(cc, Dobject.RuntimeError(&errinfo, cc, 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(CallContext* cc)
249     {
250         switch(vtype)
251         {
252         case V_REF_ERROR:
253             throwRefError(cc);
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(CallContext* cc)
274     {
275         switch(vtype)
276         {
277         case V_REF_ERROR:
278             throwRefError(cc);
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(cc, v, TypeNumber);
318           /*if(a)//rerr
319                   return d_number.nan;*/
320           if(v.isPrimitive())
321               return v.toNumber(cc);
322           else
323               return d_number.nan;
324         }
325         default:
326             assert(0);
327         }
328         assert(0);
329     }
330 
331 
332     d_time toDtime(CallContext* cc)
333     {
334         return cast(d_time)toNumber(cc);
335     }
336 
337 
338     d_number toInteger(CallContext* cc)
339     {
340         switch(vtype)
341         {
342         case V_REF_ERROR:
343             throwRefError(cc);
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(cc);
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(CallContext* cc)
372     {
373         switch(vtype)
374         {
375         case V_REF_ERROR:
376             throwRefError(cc);
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(cc);
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(CallContext* cc)
411     {
412         switch(vtype)
413         {
414         case V_REF_ERROR:
415             throwRefError(cc);
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(cc);
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(CallContext* cc)
449     {
450         switch(vtype)
451         {
452         case V_REF_ERROR:
453             throwRefError(cc);
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(cc);
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(CallContext* cc)
485     {
486         switch(vtype)
487         {
488         case V_REF_ERROR:
489             throwRefError(cc);
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(cc, v, TypeString);
577           //assert(!a);
578           if(v.isPrimitive())
579               return v.toString(cc);
580           else
581               return v.toObject(cc).classname;
582         }
583         default:
584             assert(0);
585         }
586         assert(0);
587     }
588 
589     d_string toLocaleString(CallContext* cc)
590     {
591         return toString(cc);
592     }
593 
594     d_string toString(CallContext* cc, 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(cc);
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(cc);
608         }
609     }
610 
611     d_string toSource(CallContext* cc)
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(cc, TEXT_toSource);
625           if(!v)
626               v = &vundefined;
627           if(v.isPrimitive())
628               return v.toSource(cc);
629           else          // it's an Object
630           {
631               void* a;
632               Dobject o;
633               Value* ret;
634               Value val;
635 
636               o = v.object;
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(cc);
646           }
647           return TEXT_undefined; }
648         default:
649             return toString(cc);
650         }
651         assert(0);
652     }
653 
654     Dobject toObject(CallContext* cc)
655     {
656         switch(vtype)
657         {
658         case V_REF_ERROR:
659             throwRefError(cc);
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(cc, dbool);
669         case V_NUMBER:
670             return new Dnumber(cc, number);
671         case V_STRING:
672             return new Dstring(cc, string);
673         case V_OBJECT:
674             return object;
675         default:
676             assert(0);
677         }
678         assert(0);
679     }
680 
681     @disable bool opEquals(ref const(Value) v) const { assert(false); }
682 
683     const bool isEqual(CallContext* cc, ref const (Value)v)
684     {
685         return compare(cc, 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     @disable int opCmp(const(Value) v) const { assert(false); }
707 
708     int compare(CallContext* cc, const (Value)v) const
709     {
710         switch(vtype)
711         {
712         case V_REF_ERROR:
713             throwRefError(cc);
714             assert(0);
715         case V_UNDEFINED:
716             if(vtype == v.vtype)
717                 return 0;
718             break;
719         case V_NULL:
720             if(vtype == v.vtype)
721                 return 0;
722             break;
723         case V_BOOLEAN:
724             if(vtype == v.vtype)
725                 return v.dbool - dbool;
726             break;
727         case V_NUMBER:
728             if(v.vtype == V_NUMBER)
729             {
730                 if(number == v.number)
731                     return 0;
732                 if(isNaN(number) && isNaN(v.number))
733                     return 0;
734                 if(number > v.number)
735                     return 1;
736             }
737             else if(v.vtype == V_STRING)
738             {
739                 return stringcmp((cast(Value*)&this).toString(cc), v..string);    //TODO: remove this hack!
740             }
741             break;
742         case V_STRING:
743             if(v.vtype == V_STRING)
744             {
745                 //writefln("'%s'.compareTo('%s')", string, v.string);
746                 sizediff_t len = string.length - v..string.length;
747                 if(len == 0)
748                 {
749                     if(string.ptr == v..string.ptr)
750                         return 0;
751                     len = memcmp(string.ptr, v..string.ptr, string.length);
752                 }
753                 return cast(int)len;
754             }
755             else if(v.vtype == V_NUMBER)
756             {
757                 //writefln("'%s'.compareTo(%g)\n", string, v.number);
758                 return stringcmp(string, (cast(Value*)&v).toString(cc));    //TODO: remove this hack!
759             }
760             break;
761         case V_OBJECT:
762             if(v.object == object)
763                 return 0;
764             break;
765         default:
766             assert(0);
767         }
768         return -1;
769     }
770 
771     void copyTo(Value* v)
772     {   // Copy everything, including vptr
773         copy(&this, v);
774     }
775 
776     d_string getType()
777     {
778         d_string s;
779 
780         switch(vtype)
781         {
782         case V_REF_ERROR:
783         case V_UNDEFINED:   s = TypeUndefined; break;
784         case V_NULL:        s = TypeNull;      break;
785         case V_BOOLEAN:     s = TypeBoolean;   break;
786         case V_NUMBER:      s = TypeNumber;    break;
787         case V_STRING:      s = TypeString;    break;
788         case V_OBJECT:      s = TypeObject;    break;
789         case V_ITER:        s = TypeIterator;  break;
790         default:
791             writefln("vtype = %d", vtype);
792             assert(0);
793         }
794         return s;
795     }
796 
797     d_string getTypeof()
798     {
799         d_string s;
800 
801         switch(vtype)
802         {
803         case V_REF_ERROR:
804         case V_UNDEFINED:   s = TEXT_undefined;     break;
805         case V_NULL:        s = TEXT_object;        break;
806         case V_BOOLEAN:     s = TEXT_boolean;       break;
807         case V_NUMBER:      s = TEXT_number;        break;
808         case V_STRING:      s = TEXT_string;        break;
809         case V_OBJECT:      s = object.getTypeof(); break;
810         default:
811             writefln("vtype = %d", vtype);
812             assert(0);
813         }
814         return s;
815     }
816 
817     int isUndefined()
818     {
819         return vtype == V_UNDEFINED;
820     }
821     int isNull()
822     {
823         return vtype == V_NULL;
824     }
825     int isBoolean()
826     {
827         return vtype == V_BOOLEAN;
828     }
829     int isNumber()
830     {
831         return vtype == V_NUMBER;
832     }
833     int isString()
834     {
835         return vtype == V_STRING;
836     }
837     int isObject()
838     {
839         return vtype == V_OBJECT;
840     }
841     int isIterator()
842     {
843         return vtype == V_ITER;
844     }
845 
846     int isUndefinedOrNull()
847     {
848         return vtype == V_UNDEFINED || vtype == V_NULL;
849     }
850     int isPrimitive()
851     {
852         return vtype != V_OBJECT;
853     }
854 
855     int isArrayIndex(CallContext* cc, out d_uint32 index)
856     {
857         switch(vtype)
858         {
859         case V_NUMBER:
860             index = toUint32(cc);
861             return true;
862         case V_STRING:
863             return StringToIndex(string, index);
864         default:
865             index = 0;
866             return false;
867         }
868         assert(0);
869     }
870 
871     static uint calcHash(uint u)
872     {
873         return u ^ 0x55555555;
874     }
875 
876     static uint calcHash(double d)
877     {
878         return calcHash(cast(uint)d);
879     }
880 
881     static uint calcHash(d_string s)
882     {
883         uint hash;
884 
885         /* If it looks like an array index, hash it to the
886          * same value as if it was an array index.
887          * This means that "1234" hashes to the same value as 1234.
888          */
889         hash = 0;
890         foreach(tchar c; s)
891         {
892             switch(c)
893             {
894             case '0':       hash *= 10;             break;
895             case '1':       hash = hash * 10 + 1;   break;
896 
897             case '2':
898             case '3':
899             case '4':
900             case '5':
901             case '6':
902             case '7':
903             case '8':
904             case '9':
905                 hash = hash * 10 + (c - '0');
906                 break;
907 
908             default:
909             { size_t len = s.length;
910               ubyte *str = cast(ubyte*)s.ptr;
911 
912               hash = 0;
913               while(1)
914               {
915                   switch(len)
916                   {
917                   case 0:
918                       break;
919 
920                   case 1:
921                       hash *= 9;
922                       hash += *cast(ubyte *)str;
923                       break;
924 
925                   case 2:
926                       hash *= 9;
927                       hash += *cast(ushort *)str;
928                       break;
929 
930                   case 3:
931                       hash *= 9;
932                       hash += (*cast(ushort *)str << 8) +
933                               (cast(ubyte *)str)[2];
934                       break;
935 
936                   default:
937                       hash *= 9;
938                       hash += *cast(uint *)str;
939                       str += 4;
940                       len -= 4;
941                       continue;
942                   }
943                   break;
944               }
945               break; }
946                 // return s.hash;
947             }
948         }
949         return calcHash(hash);
950     }
951 
952     @disable uint toHash();
953 
954     uint hashString()
955     {
956         assert(vtype == V_STRING);
957 
958         // Since strings are immutable, if we've already
959         // computed the hash, use previous value
960         if(!hash)
961             hash = calcHash(string);
962         return hash;
963     }
964 
965     uint toHash(CallContext* cc)
966     {
967         uint h;
968 
969         switch(vtype)
970         {
971         case V_REF_ERROR:
972             throwRefError(cc);
973             assert(0);
974         case V_UNDEFINED:
975         case V_NULL:
976             h = 0;
977             break;
978         case V_BOOLEAN:
979             h = dbool ? 1 : 0;
980             break;
981         case V_NUMBER:
982             h = calcHash(number);
983             break;
984         case V_STRING:
985             h = hashString();
986             break;
987         case V_OBJECT:
988             /* Uses the address of the object as the hash.
989              * Since the object never moves, it will work
990              * as its hash.
991              * BUG: shouldn't do this.
992              */
993             h = cast(uint)cast(void*)object;
994             break;
995         default:
996             assert(0);
997         }
998         //writefln("\tValue.toHash() = %x", h);
999         return h;
1000     }
1001 
1002     Value* Put(CallContext* cc, d_string PropertyName, Value* value)
1003     {
1004         if(vtype == V_OBJECT)
1005             return object.Put(cc, PropertyName, value, 0);
1006         else
1007         {
1008             ErrInfo errinfo;
1009 
1010             return Dobject.RuntimeError(&errinfo,
1011                                         cc,
1012                                         errmsgtbl[ERR_CANNOT_PUT_TO_PRIMITIVE],
1013                                         PropertyName, value.toString(cc),
1014                                         getType());
1015         }
1016     }
1017 
1018     Value* Put(CallContext* cc, d_uint32 index, Value* vindex, Value* value)
1019     {
1020         if(vtype == V_OBJECT)
1021             return object.Put(cc, index, vindex, value, 0);
1022         else
1023         {
1024             ErrInfo errinfo;
1025 
1026             return Dobject.RuntimeError(&errinfo,
1027                                         cc,
1028                                         errmsgtbl[ERR_CANNOT_PUT_INDEX_TO_PRIMITIVE],
1029                                         index,
1030                                         value.toString(cc), getType());
1031         }
1032     }
1033 
1034     Value* Get(CallContext* cc, d_string PropertyName)
1035     {
1036         if(vtype == V_OBJECT)
1037             return object.Get(PropertyName);
1038         else
1039         {
1040             // Should we generate the error, or just return undefined?
1041             d_string msg;
1042 
1043             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1044                                     PropertyName, getType(), toString(cc));
1045             throw new ScriptException(msg);
1046             //return &vundefined;
1047         }
1048     }
1049 
1050     Value* Get(CallContext* cc,d_uint32 index)
1051     {
1052         if(vtype == V_OBJECT)
1053             return object.Get(index);
1054         else
1055         {
1056             // Should we generate the error, or just return undefined?
1057             d_string msg;
1058 
1059             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_INDEX_FROM_PRIMITIVE],
1060                                     index, getType(), toString(cc));
1061             throw new ScriptException(msg);
1062             //return &vundefined;
1063         }
1064     }
1065 
1066     Value* Get(CallContext* cc,Identifier *id)
1067     {
1068         if(vtype == V_OBJECT)
1069             return object.Get(id);
1070         else if(vtype == V_REF_ERROR){
1071             throwRefError(cc);
1072             assert(0);
1073         }
1074         else
1075         {
1076             // Should we generate the error, or just return undefined?
1077             d_string msg;
1078 
1079             msg = std..string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1080                                     id.toString(), getType(), toString(cc));
1081             throw new ScriptException(msg);
1082             //return &vundefined;
1083         }
1084     }
1085 /+
1086     Value* Get(d_string PropertyName, uint hash)
1087     {
1088         if (vtype == V_OBJECT)
1089             return object.Get(PropertyName, hash);
1090         else
1091         {
1092             // Should we generate the error, or just return undefined?
1093             tchar[] msg;
1094 
1095             msg = std.string.format(errmsgtbl[ERR_CANNOT_GET_FROM_PRIMITIVE],
1096                 PropertyName, getType(), toString());
1097             throw new ScriptException(msg);
1098             //return &vundefined;
1099         }
1100     }
1101  +/
1102     void* Construct(CallContext *cc, Value *ret, Value[] arglist)
1103     {
1104         if(vtype == V_OBJECT)
1105             return object.Construct(cc, ret, arglist);
1106         else if(vtype == V_REF_ERROR){
1107             throwRefError(cc);
1108             assert(0);
1109         }
1110         else
1111         {
1112             ErrInfo errinfo;
1113             ret.putVundefined();
1114             return Dobject.RuntimeError(&errinfo, cc,
1115                                         errmsgtbl[ERR_PRIMITIVE_NO_CONSTRUCT], getType());
1116         }
1117     }
1118 
1119     void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
1120     {
1121         if(vtype == V_OBJECT)
1122         {
1123             void* a;
1124 
1125             a = object.Call(cc, othis, ret, arglist);
1126             //if (a) writef("Vobject.Call() returned %x\n", a);
1127             return a;
1128         }
1129         else if(vtype == V_REF_ERROR){
1130             throwRefError(cc);
1131             assert(0);
1132         }
1133         else
1134         {
1135             ErrInfo errinfo;
1136             //PRINTF("Call method not implemented for primitive %p (%s)\n", this, d_string_ptr(toString()));
1137             ret.putVundefined();
1138             return Dobject.RuntimeError(&errinfo, cc,
1139                                         errmsgtbl[ERR_PRIMITIVE_NO_CALL], getType());
1140         }
1141     }
1142 
1143     Value* putIterator(CallContext* cc, Value* v)
1144     {
1145         if(vtype == V_OBJECT)
1146             return object.putIterator(cc, v);
1147         else
1148         {
1149             ErrInfo errinfo;
1150             v.putVundefined();
1151             return Dobject.RuntimeError(&errinfo, cc,
1152                                         errmsgtbl[ERR_FOR_IN_MUST_BE_OBJECT]);
1153         }
1154     }
1155 
1156 
1157     void getErrInfo(CallContext* cc, ErrInfo *perrinfo, int linnum)
1158     {
1159         if(vtype == V_OBJECT)
1160             object.getErrInfo(cc, perrinfo, linnum);
1161         else
1162         {
1163             ErrInfo errinfo;
1164 
1165             if(linnum && errinfo.linnum == 0)
1166                 errinfo.linnum = linnum;
1167             errinfo.message = "Unhandled exception: " ~ toString(cc);
1168             if(perrinfo)
1169                 *perrinfo = errinfo;
1170         }
1171     }
1172 
1173     void dump()
1174     {
1175         uint *v = cast(uint *)&this;
1176 
1177         writef("v[%x] = %8x, %8x, %8x, %8x\n", cast(uint)v, v[0], v[1], v[2], v[3]);
1178     }
1179 }
1180 static if(size_t.sizeof == 4)
1181   static assert(Value.sizeof == 16);
1182 else
1183   static assert(Value.sizeof == 24); //fat string point 2*8 + type tag & hash
1184 
1185 Value vundefined = { V_UNDEFINED };
1186 Value vnull = { V_NULL };
1187 
1188 immutable string TypeUndefined = "Undefined";
1189 immutable string TypeNull = "Null";
1190 immutable string TypeBoolean = "Boolean";
1191 immutable string TypeNumber = "Number";
1192 immutable string TypeString = "String";
1193 immutable string TypeObject = "Object";
1194 
1195 immutable string TypeIterator = "Iterator";
1196 
1197 
1198 Value* signalingUndefined(string id){
1199     Value* p;
1200     p = new Value;
1201     p.putSignalingUndefined(id);
1202     return p;
1203 }
1204 
1205 
1206 
1207