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