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