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