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 core.stdc.stdarg; 22 import core.stdc.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(CallContext* cc, Value* vptr){ 52 super(vptr.toString(cc)); 53 value = *vptr; 54 } 55 } 56 //debug = LOG; 57 58 /************************** Dobject_constructor *************************/ 59 60 class DobjectConstructor : Dfunction 61 { 62 this(CallContext* cc) 63 { 64 super(cc, 1, cc.tc.Dfunction_prototype); 65 if(cc.tc.Dobject_prototype) 66 Put(cc, TEXT_prototype, cc.tc.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(cc, Dobject.getPrototype(cc)); 78 } 79 else 80 { 81 v = &arglist[0]; 82 if(v.isPrimitive()) 83 { 84 if(v.isUndefinedOrNull()) 85 { 86 o = new Dobject(cc, Dobject.getPrototype(cc)); 87 } 88 else 89 o = v.toObject(cc); 90 } 91 else 92 o = v.toObject(cc); 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(cc); 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(cc); 200 buf ~= ':'; 201 buf ~= p.value.toSource(cc); 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(cc); 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(CallContext* cc) 268 { 269 super(cc, 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(CallContext* cc, 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(cc); 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, 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(CallContext* cc, Value* value) 415 { 416 // Not ECMA, Microsoft extension 417 //writef("Dobject.PutDefault(this = %p)\n", this); 418 ErrInfo errinfo; 419 return RuntimeError(&errinfo, cc, ERR_NO_DEFAULT_PUT); 420 } 421 422 Value* put_Value(CallContext* cc, 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, cc, 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(CallContext* cc, 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 503 //writefln("\tfound default value"); 504 o = v.object; 505 a = o.Call(cc, this, ret, null); 506 if(a) // if exception was thrown 507 return a; 508 if(ret.isPrimitive()) 509 return null; 510 } 511 i ^= 1; 512 } 513 ErrInfo errinfo; 514 return Dobject.RuntimeError(&errinfo, cc, "No [[DefaultValue]]"); 515 //ErrInfo errinfo; 516 //return RuntimeError(&errinfo, DTEXT("no Default Value for object")); 517 } 518 519 void *Construct(CallContext *cc, Value *ret, Value[] arglist) 520 { 521 ErrInfo errinfo; 522 return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_CONSTRUCT], classname); 523 } 524 525 void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist) 526 { 527 ErrInfo errinfo; 528 return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_CALL], classname); 529 } 530 531 void *HasInstance(CallContext* cc, Value* ret, Value* v) 532 { // ECMA v3 8.6.2 533 ErrInfo errinfo; 534 return RuntimeError(&errinfo, cc, errmsgtbl[ERR_S_NO_INSTANCE], classname); 535 } 536 537 d_string getTypeof() 538 { // ECMA 11.4.3 539 return TEXT_object; 540 } 541 542 543 int isClass(d_string classname) const 544 { 545 return this.classname == classname; 546 } 547 548 int isDarray() const 549 { 550 return isClass(TEXT_Array); 551 } 552 int isDdate() const 553 { 554 return isClass(TEXT_Date); 555 } 556 int isDregexp() const 557 { 558 return isClass(TEXT_RegExp); 559 } 560 561 int isDarguments() const 562 { 563 return false; 564 } 565 int isCatch() const 566 { 567 return false; 568 } 569 int isFinally() const 570 { 571 return false; 572 } 573 574 void getErrInfo(CallContext* cc, ErrInfo *perrinfo, int linnum) 575 { 576 ErrInfo errinfo; 577 Value v; 578 v.putVobject(this); 579 580 errinfo.message = v.toString(cc); 581 if(perrinfo) 582 *perrinfo = errinfo; 583 } 584 585 static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args) 586 { 587 return RuntimeError(perrinfo, cc, errmsgtbl[msgnum], args); 588 } 589 590 static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, string fmt, ARGS args) 591 { 592 import std.format : formattedWrite; 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 formattedWrite(&putc, fmt, args); 605 perrinfo.message = assumeUnique(buffer); 606 o = new typeerror.D0(cc, perrinfo); 607 Value* v = new Value; 608 v.putVobject(o); 609 return v; 610 } 611 static Value* ReferenceError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args) 612 { 613 return ReferenceError(perrinfo, cc, errmsgtbl[msgnum], args); 614 } 615 616 static Value* ReferenceError(ARGS...)(CallContext* cc, string fmt, ARGS args) 617 { 618 ErrInfo errinfo; 619 return ReferenceError(&errinfo, cc, fmt, args); 620 } 621 622 static Value* ReferenceError(ARGS...)(ErrInfo* perrinfo, CallContext* cc, string fmt, ARGS args) 623 { 624 import std.format : formattedWrite; 625 626 Dobject o; 627 //perrinfo.message = null; 628 d_string buffer = null; 629 630 void putc(dchar c) 631 { 632 dmdscript.utf.encode(buffer, c); 633 } 634 635 formattedWrite(&putc, fmt, args); 636 perrinfo.message = buffer; 637 638 o = new referenceerror.D0(cc, perrinfo); 639 Value* v = new Value; 640 v.putVobject(o); 641 642 return v; 643 } 644 static Value* RangeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, int msgnum, ARGS args) 645 { 646 return RangeError(perrinfo, cc, errmsgtbl[msgnum], args); 647 } 648 649 static Value* RangeError(ARGS...)(ErrInfo *perrinfo, CallContext* cc, string fmt, ARGS args) 650 { 651 import std.format : formattedWrite; 652 653 Dobject o; 654 655 //perrinfo.message = null; 656 d_string buffer = null; 657 658 void putc(dchar c) 659 { 660 dmdscript.utf.encode(buffer, c); 661 } 662 663 formattedWrite(&putc, fmt, args); 664 perrinfo.message = buffer; 665 666 o = new rangeerror.D0(cc, perrinfo); 667 Value* v = new Value; 668 v.putVobject(o); 669 return v; 670 } 671 672 Value* putIterator(CallContext* cc, Value* v) 673 { 674 Iterator* i = new Iterator; 675 676 i.ctor(cc, this); 677 v.putViterator(i); 678 return null; 679 } 680 681 static Dfunction getConstructor(CallContext* cc) 682 { 683 return cc.tc.Dobject_constructor; 684 } 685 686 static Dobject getPrototype(CallContext* cc) 687 { 688 return cc.tc.Dobject_prototype; 689 } 690 691 static void initialize(CallContext* cc) 692 { 693 cc.tc.Dobject_prototype = new DobjectPrototype(cc); 694 Dfunction.initialize(cc); 695 cc.tc.Dobject_constructor = new DobjectConstructor(cc); 696 697 Dobject op = cc.tc.Dobject_prototype; 698 Dobject f = cc.tc.Dfunction_prototype; 699 700 op.Put(cc, TEXT_constructor, cc.tc.Dobject_constructor, DontEnum); 701 702 static enum NativeFunctionData[] nfd = 703 [ 704 { TEXT_toString, &Dobject_prototype_toString, 0 }, 705 { TEXT_toLocaleString, &Dobject_prototype_toLocaleString, 0 }, 706 { TEXT_toSource, &Dobject_prototype_toSource, 0 }, 707 { TEXT_valueOf, &Dobject_prototype_valueOf, 0 }, 708 { TEXT_hasOwnProperty, &Dobject_prototype_hasOwnProperty, 1 }, 709 { TEXT_isPrototypeOf, &Dobject_prototype_isPrototypeOf, 0 }, 710 { TEXT_propertyIsEnumerable, &Dobject_prototype_propertyIsEnumerable, 0 }, 711 ]; 712 713 DnativeFunction.initialize(op, cc, nfd, DontEnum); 714 } 715 } 716 717 718 /********************************************* 719 * Initialize the built-in's. 720 */ 721 722 void dobject_init(CallContext* cc) 723 { 724 //writef("dobject_init(tc = %x)\n", cast(uint)tc); 725 if(cc.tc.Dobject_prototype) 726 return; // already initialized for this thread 727 728 version(none) 729 { 730 writef("sizeof(Dobject) = %d\n", sizeof(Dobject)); 731 writef("sizeof(PropTable) = %d\n", sizeof(PropTable)); 732 writef("offsetof(proptable) = %d\n", offsetof(Dobject, proptable)); 733 writef("offsetof(internal_prototype) = %d\n", offsetof(Dobject, internal_prototype)); 734 writef("offsetof(classname) = %d\n", offsetof(Dobject, classname)); 735 writef("offsetof(value) = %d\n", offsetof(Dobject, value)); 736 } 737 738 Dobject.initialize(cc); 739 Dboolean.initialize(cc); 740 Dstring.initialize(cc); 741 Dnumber.initialize(cc); 742 Darray.initialize(cc); 743 Dmath.initialize(cc); 744 Ddate.initialize(cc); 745 Dregexp.initialize(cc); 746 Derror.initialize(cc); 747 748 // Call registered initializer for each object type 749 foreach(fpinit; cc.tc.threadInitTable) 750 fpinit(cc); 751 } 752 /*Not used anyway 753 void dobject_term() 754 { 755 //writef("dobject_term(program = %x)\n", program); 756 757 memset(&program, 0, ThreadContext.sizeof - Thread.sizeof); 758 } 759 */