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.dobject; 19 20 import std..string; 21 import std.c.stdarg; 22 import std.c..string; 23 import std.exception; 24 25 import dmdscript.script; 26 import dmdscript.value; 27 import dmdscript.dfunction; 28 import dmdscript.property; 29 import dmdscript.threadcontext; 30 import dmdscript.iterator; 31 import dmdscript.identifier; 32 import dmdscript.errmsgs; 33 import dmdscript.text; 34 import dmdscript.program; 35 36 import dmdscript.dboolean; 37 import dmdscript.dstring; 38 import dmdscript.dnumber; 39 import dmdscript.darray; 40 import dmdscript.dmath; 41 import dmdscript.ddate; 42 import dmdscript.dregexp; 43 import dmdscript.derror; 44 import dmdscript.dnative; 45 46 import dmdscript.protoerror; 47 import dmdscript.utf; 48 49 class ErrorValue: Exception { 50 Value value; 51 this(Value* vptr){ 52 super("DMDScript exception"); 53 value = *vptr; 54 } 55 } 56 //debug = LOG; 57 58 /************************** Dobject_constructor *************************/ 59 60 class DobjectConstructor : Dfunction 61 { 62 this() 63 { 64 super(1, Dfunction_prototype); 65 if(Dobject_prototype) 66 Put(TEXT_prototype, Dobject_prototype, DontEnum | DontDelete | ReadOnly); 67 } 68 69 override void *Construct(CallContext *cc, Value *ret, Value[] arglist) 70 { 71 Dobject o; 72 Value* v; 73 74 // ECMA 15.2.2 75 if(arglist.length == 0) 76 { 77 o = new Dobject(Dobject.getPrototype()); 78 } 79 else 80 { 81 v = &arglist[0]; 82 if(v.isPrimitive()) 83 { 84 if(v.isUndefinedOrNull()) 85 { 86 o = new Dobject(Dobject.getPrototype()); 87 } 88 else 89 o = v.toObject(); 90 } 91 else 92 o = v.toObject(); 93 } 94 //printf("constructed object o=%p, v=%p,'%s'\n", o, v,v.getType()); 95 ret.putVobject(o); 96 return null; 97 } 98 99 override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 100 { 101 Dobject o; 102 void *result; 103 104 // ECMA 15.2.1 105 if(arglist.length == 0) 106 { 107 result = Construct(cc, ret, arglist); 108 } 109 else 110 { 111 Value* v; 112 113 v = &arglist[0]; 114 if(v.isUndefinedOrNull()) 115 result = Construct(cc, ret, arglist); 116 else 117 { 118 o = v.toObject(); 119 ret.putVobject(o); 120 result = null; 121 } 122 } 123 return result; 124 } 125 } 126 127 128 /* ===================== Dobject_prototype_toString ================ */ 129 130 void* Dobject_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 131 { 132 d_string s; 133 d_string string; 134 135 //debug (LOG) writef("Dobject.prototype.toString(ret = %x)\n", ret); 136 137 s = othis.classname; 138 /+ 139 // Should we do [object] or [object Object]? 140 if (s == TEXT_Object) 141 string = TEXT_bobjectb; 142 else 143 +/ 144 string = std..string.format("[object %s]", s); 145 ret.putVstring(string); 146 return null; 147 } 148 149 /* ===================== Dobject_prototype_toLocaleString ================ */ 150 151 void* Dobject_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 152 { 153 // ECMA v3 15.2.4.3 154 // "This function returns the result of calling toString()." 155 156 Value* v; 157 158 //writef("Dobject.prototype.toLocaleString(ret = %x)\n", ret); 159 v = othis.Get(TEXT_toString); 160 if(v && !v.isPrimitive()) // if it's an Object 161 { 162 void *a; 163 Dobject o; 164 165 o = v.object; 166 a = o.Call(cc, othis, ret, arglist); 167 if(a) // if exception was thrown 168 return a; 169 } 170 return null; 171 } 172 173 /* ===================== Dobject_prototype_valueOf ================ */ 174 175 void* Dobject_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 176 { 177 ret.putVobject(othis); 178 return null; 179 } 180 181 /* ===================== Dobject_prototype_toSource ================ */ 182 183 void* Dobject_prototype_toSource(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 184 { 185 d_string buf; 186 int any; 187 188 //writef("Dobject.prototype.toSource(this = %p, ret = %p)\n", this, ret); 189 190 buf = "{"; 191 any = 0; 192 foreach(Value key, Property p; *othis.proptable) 193 { 194 if(!(p.attributes & (DontEnum | Deleted))) 195 { 196 if(any) 197 buf ~= ','; 198 any = 1; 199 buf ~= key.toString(); 200 buf ~= ':'; 201 buf ~= p.value.toSource(); 202 } 203 } 204 buf ~= '}'; 205 ret.putVstring(buf); 206 return null; 207 } 208 209 /* ===================== Dobject_prototype_hasOwnProperty ================ */ 210 211 void* Dobject_prototype_hasOwnProperty(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 212 { 213 // ECMA v3 15.2.4.5 214 Value* v; 215 216 v = arglist.length ? &arglist[0] : &vundefined; 217 ret.putVboolean(othis.proptable.hasownproperty(v, 0)); 218 return null; 219 } 220 221 /* ===================== Dobject_prototype_isPrototypeOf ================ */ 222 223 void* Dobject_prototype_isPrototypeOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 224 { 225 // ECMA v3 15.2.4.6 226 d_boolean result = false; 227 Value* v; 228 Dobject o; 229 230 v = arglist.length ? &arglist[0] : &vundefined; 231 if(!v.isPrimitive()) 232 { 233 o = v.toObject(); 234 for(;; ) 235 { 236 o = o.internal_prototype; 237 if(!o) 238 break; 239 if(o == othis) 240 { 241 result = true; 242 break; 243 } 244 } 245 } 246 247 ret.putVboolean(result); 248 return null; 249 } 250 251 /* ===================== Dobject_prototype_propertyIsEnumerable ================ */ 252 253 void* Dobject_prototype_propertyIsEnumerable(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist) 254 { 255 // ECMA v3 15.2.4.7 256 Value* v; 257 258 v = arglist.length ? &arglist[0] : &vundefined; 259 ret.putVboolean(othis.proptable.hasownproperty(v, 1)); 260 return null; 261 } 262 263 /* ===================== Dobject_prototype ========================= */ 264 265 class DobjectPrototype : Dobject 266 { 267 this() 268 { 269 super(null); 270 } 271 } 272 273 274 /* ====================== Dobject ======================= */ 275 276 class Dobject 277 { 278 PropTable* proptable; 279 Dobject internal_prototype; 280 string classname; 281 Value value; 282 283 enum uint DOBJECT_SIGNATURE = 0xAA31EE31; 284 uint signature; 285 286 invariant() 287 { 288 assert(signature == DOBJECT_SIGNATURE); 289 } 290 291 this(Dobject prototype) 292 { 293 //writef("new Dobject = %x, prototype = %x, line = %d, file = '%s'\n", this, prototype, GC.line, ascii2unicode(GC.file)); 294 //writef("Dobject(prototype = %p)\n", prototype); 295 proptable = new PropTable; 296 internal_prototype = prototype; 297 if(prototype) 298 proptable.previous = prototype.proptable; 299 classname = TEXT_Object; 300 value.putVobject(this); 301 302 signature = DOBJECT_SIGNATURE; 303 } 304 305 Dobject Prototype() 306 { 307 return internal_prototype; 308 } 309 310 Value* Get(d_string PropertyName) 311 { 312 return Get(PropertyName, Value.calcHash(PropertyName)); 313 } 314 315 Value* Get(Identifier* id) 316 { 317 Value* v; 318 319 //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash); 320 //writef("\tinternal_prototype = %p\n", this.internal_prototype); 321 //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype()); 322 v = proptable.get(&id.value, id.value.hash); 323 //if (v) writef("found it %p\n", v.object); 324 return v; 325 } 326 327 Value* Get(d_string PropertyName, uint hash) 328 { 329 Value* v; 330 331 //writefln("Dobject.Get(this = %x, '%s', hash = %x)", cast(uint)cast(void*)this, PropertyName, hash); 332 //writef("\tinternal_prototype = %p\n", this.internal_prototype); 333 //writef("\tDfunction.getPrototype() = %p\n", Dfunction.getPrototype()); 334 v = proptable.get(PropertyName, hash); 335 //if (v) writef("found it %p\n", v.object); 336 return v; 337 } 338 339 Value* Get(d_uint32 index) 340 { 341 Value* v; 342 343 v = proptable.get(index); 344 // if (!v) 345 // v = &vundefined; 346 return v; 347 } 348 349 Value* Get(d_uint32 index, Value* vindex) 350 { 351 return proptable.get(vindex, Value.calcHash(index)); 352 } 353 354 Value* Put(d_string PropertyName, Value* value, uint attributes) 355 { 356 // ECMA 8.6.2.2 357 //writef("Dobject.Put(this = %p)\n", this); 358 proptable.put(PropertyName, value, attributes); 359 return null; 360 } 361 362 Value* Put(Identifier* key, Value* value, uint attributes) 363 { 364 // ECMA 8.6.2.2 365 //writef("Dobject.Put(this = %p)\n", this); 366 proptable.put(&key.value, key.value.hash, value, attributes); 367 return null; 368 } 369 370 Value* Put(d_string PropertyName, Dobject o, uint attributes) 371 { 372 // ECMA 8.6.2.2 373 Value v; 374 v.putVobject(o); 375 376 proptable.put(PropertyName, &v, attributes); 377 return null; 378 } 379 380 Value* Put(d_string PropertyName, d_number n, uint attributes) 381 { 382 // ECMA 8.6.2.2 383 Value v; 384 v.putVnumber(n); 385 386 proptable.put(PropertyName, &v, attributes); 387 return null; 388 } 389 390 Value* Put(d_string PropertyName, d_string s, uint attributes) 391 { 392 // ECMA 8.6.2.2 393 Value v; 394 v.putVstring(s); 395 396 proptable.put(PropertyName, &v, attributes); 397 return null; 398 } 399 400 Value* Put(d_uint32 index, Value* vindex, Value* value, uint attributes) 401 { 402 // ECMA 8.6.2.2 403 proptable.put(vindex, Value.calcHash(index), value, attributes); 404 return null; 405 } 406 407 Value* Put(d_uint32 index, Value* value, uint attributes) 408 { 409 // ECMA 8.6.2.2 410 proptable.put(index, value, attributes); 411 return null; 412 } 413 414 Value* PutDefault(Value* value) 415 { 416 // Not ECMA, Microsoft extension 417 //writef("Dobject.PutDefault(this = %p)\n", this); 418 ErrInfo errinfo; 419 return RuntimeError(&errinfo, ERR_NO_DEFAULT_PUT); 420 } 421 422 Value* put_Value(Value* ret, Value[] arglist) 423 { 424 // Not ECMA, Microsoft extension 425 //writef("Dobject.put_Value(this = %p)\n", this); 426 ErrInfo errinfo; 427 return RuntimeError(&errinfo, ERR_FUNCTION_NOT_LVALUE); 428 } 429 430 int CanPut(d_string PropertyName) 431 { 432 // ECMA 8.6.2.3 433 return proptable.canput(PropertyName); 434 } 435 436 int HasProperty(d_string PropertyName) 437 { 438 // ECMA 8.6.2.4 439 return proptable.hasproperty(PropertyName); 440 } 441 442 /*********************************** 443 * Return: 444 * TRUE not found or successful delete 445 * FALSE property is marked with DontDelete attribute 446 */ 447 448 int Delete(d_string PropertyName) 449 { 450 // ECMA 8.6.2.5 451 //writef("Dobject.Delete('%ls')\n", d_string_ptr(PropertyName)); 452 return proptable.del(PropertyName); 453 } 454 455 int Delete(d_uint32 index) 456 { 457 // ECMA 8.6.2.5 458 return proptable.del(index); 459 } 460 461 int implementsDelete() 462 { 463 // ECMA 8.6.2 says every object implements [[Delete]], 464 // but ECMA 11.4.1 says that some objects may not. 465 // Assume the former is correct. 466 return true; 467 } 468 469 void *DefaultValue(Value* ret, d_string Hint) 470 { 471 Dobject o; 472 Value* v; 473 static enum d_string[2] table = [ TEXT_toString, TEXT_valueOf ]; 474 int i = 0; // initializer necessary for /W4 475 476 // ECMA 8.6.2.6 477 //writef("Dobject.DefaultValue(ret = %x, Hint = '%s')\n", cast(uint)ret, Hint); 478 479 if(Hint == TypeString || 480 (Hint == null && this.isDdate())) 481 { 482 i = 0; 483 } 484 else if(Hint == TypeNumber || 485 Hint == null) 486 { 487 i = 1; 488 } 489 else 490 assert(0); 491 492 for(int j = 0; j < 2; j++) 493 { 494 d_string htab = table[i]; 495 496 //writefln("\ti = %d, htab = '%s'", i, htab); 497 v = Get(htab, Value.calcHash(htab)); 498 //writefln("\tv = %x", cast(uint)v); 499 if(v && !v.isPrimitive()) // if it's an Object 500 { 501 void *a; 502 CallContext *cc; 503 504 //writefln("\tfound default value"); 505 o = v.object; 506 cc = Program.getProgram().callcontext; 507 a = o.Call(cc, this, ret, null); 508 if(a) // if exception was thrown 509 return a; 510 if(ret.isPrimitive()) 511 return null; 512 } 513 i ^= 1; 514 } 515 ErrInfo errinfo; 516 return Dobject.RuntimeError(&errinfo, "No [[DefaultValue]]"); 517 //ErrInfo errinfo; 518 //return RuntimeError(&errinfo, DTEXT("no Default Value for object")); 519 } 520 521 void *Construct(CallContext *cc, Value *ret, Value[] arglist) 522 { 523 ErrInfo errinfo; 524 return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_CONSTRUCT], classname); 525 } 526 527 void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 528 { 529 ErrInfo errinfo; 530 return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_CALL], classname); 531 } 532 533 void *HasInstance(Value* ret, Value* v) 534 { // ECMA v3 8.6.2 535 ErrInfo errinfo; 536 return RuntimeError(&errinfo, errmsgtbl[ERR_S_NO_INSTANCE], classname); 537 } 538 539 d_string getTypeof() 540 { // ECMA 11.4.3 541 return TEXT_object; 542 } 543 544 545 int isClass(d_string classname) const 546 { 547 return this.classname == classname; 548 } 549 550 int isDarray() const 551 { 552 return isClass(TEXT_Array); 553 } 554 int isDdate() const 555 { 556 return isClass(TEXT_Date); 557 } 558 int isDregexp() const 559 { 560 return isClass(TEXT_RegExp); 561 } 562 563 int isDarguments() const 564 { 565 return false; 566 } 567 int isCatch() const 568 { 569 return false; 570 } 571 int isFinally() const 572 { 573 return false; 574 } 575 576 void getErrInfo(ErrInfo *perrinfo, int linnum) 577 { 578 ErrInfo errinfo; 579 Value v; 580 v.putVobject(this); 581 582 errinfo.message = v.toString(); 583 if(perrinfo) 584 *perrinfo = errinfo; 585 } 586 587 static Value* RuntimeError(ErrInfo *perrinfo, int msgnum) 588 { 589 return RuntimeError(perrinfo, errmsgtbl[msgnum]); 590 } 591 592 static Value* RuntimeError(ErrInfo *perrinfo, ...) 593 { 594 Dobject o; 595 596 //perrinfo.message = null; 597 char[] buffer = null; 598 599 void putc(dchar c) 600 { 601 std.utf.encode(buffer, c); 602 } 603 604 std.format.doFormat(&putc, _arguments, _argptr); 605 perrinfo.message = assumeUnique(buffer); 606 o = new typeerror.D0(perrinfo); 607 Value* v = new Value; 608 v.putVobject(o); 609 return v; 610 } 611 static Value* ReferenceError(ErrInfo *perrinfo, int msgnum) 612 { 613 return ReferenceError(perrinfo, errmsgtbl[msgnum]); 614 } 615 616 static Value* ReferenceError(...) 617 { 618 Dobject o; 619 ErrInfo errinfo; 620 //perrinfo.message = null; 621 d_string buffer = null; 622 623 void putc(dchar c) 624 { 625 dmdscript.utf.encode(buffer, c); 626 } 627 628 std.format.doFormat(&putc, _arguments, _argptr); 629 errinfo.message = buffer; 630 631 o = new referenceerror.D0(&errinfo); 632 Value* v = new Value; 633 v.putVobject(o); 634 return v; 635 } 636 static Value* RangeError(ErrInfo *perrinfo, int msgnum) 637 { 638 return RangeError(perrinfo, errmsgtbl[msgnum]); 639 } 640 641 static Value* RangeError(ErrInfo *perrinfo, ...) 642 { 643 Dobject o; 644 645 //perrinfo.message = null; 646 d_string buffer = null; 647 648 void putc(dchar c) 649 { 650 dmdscript.utf.encode(buffer, c); 651 } 652 653 std.format.doFormat(&putc, _arguments, _argptr); 654 perrinfo.message = buffer; 655 656 o = new rangeerror.D0(perrinfo); 657 Value* v = new Value; 658 v.putVobject(o); 659 return v; 660 } 661 662 Value* putIterator(Value* v) 663 { 664 Iterator* i = new Iterator; 665 666 i.ctor(this); 667 v.putViterator(i); 668 return null; 669 } 670 671 static Dfunction getConstructor() 672 { 673 return Dobject_constructor; 674 } 675 676 static Dobject getPrototype() 677 { 678 return Dobject_prototype; 679 } 680 681 static void initialize() 682 { 683 Dobject_prototype = new DobjectPrototype(); 684 Dfunction.initialize(); 685 Dobject_constructor = new DobjectConstructor(); 686 687 Dobject op = Dobject_prototype; 688 Dobject f = Dfunction_prototype; 689 690 op.Put(TEXT_constructor, Dobject_constructor, DontEnum); 691 692 static enum NativeFunctionData nfd[] = 693 [ 694 { TEXT_toString, &Dobject_prototype_toString, 0 }, 695 { TEXT_toLocaleString, &Dobject_prototype_toLocaleString, 0 }, 696 { TEXT_toSource, &Dobject_prototype_toSource, 0 }, 697 { TEXT_valueOf, &Dobject_prototype_valueOf, 0 }, 698 { TEXT_hasOwnProperty, &Dobject_prototype_hasOwnProperty, 1 }, 699 { TEXT_isPrototypeOf, &Dobject_prototype_isPrototypeOf, 0 }, 700 { TEXT_propertyIsEnumerable, &Dobject_prototype_propertyIsEnumerable, 0 }, 701 ]; 702 703 DnativeFunction.initialize(op, nfd, DontEnum); 704 } 705 } 706 707 708 /********************************************* 709 * Initialize the built-in's. 710 */ 711 712 void dobject_init() 713 { 714 //writef("dobject_init(tc = %x)\n", cast(uint)tc); 715 if(Dobject_prototype) 716 return; // already initialized for this thread 717 718 version(none) 719 { 720 writef("sizeof(Dobject) = %d\n", sizeof(Dobject)); 721 writef("sizeof(PropTable) = %d\n", sizeof(PropTable)); 722 writef("offsetof(proptable) = %d\n", offsetof(Dobject, proptable)); 723 writef("offsetof(internal_prototype) = %d\n", offsetof(Dobject, internal_prototype)); 724 writef("offsetof(classname) = %d\n", offsetof(Dobject, classname)); 725 writef("offsetof(value) = %d\n", offsetof(Dobject, value)); 726 } 727 728 Dobject.initialize(); 729 Dboolean.initialize(); 730 Dstring.initialize(); 731 Dnumber.initialize(); 732 Darray.initialize(); 733 Dmath.initialize(); 734 Ddate.initialize(); 735 Dregexp.initialize(); 736 Derror.initialize(); 737 738 // Call registered initializer for each object type 739 foreach(void function() fpinit; threadInitTable) 740 (*fpinit)(); 741 742 } 743 /*Not used anyway 744 void dobject_term() 745 { 746 //writef("dobject_term(program = %x)\n", program); 747 748 memset(&program, 0, ThreadContext.sizeof - Thread.sizeof); 749 } 750 */