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