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(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(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args) 588 { 589 return RuntimeError(perrinfo, errmsgtbl[msgnum], args); 590 } 591 592 static Value* RuntimeError(ARGS...)(ErrInfo *perrinfo, string fmt, ARGS args) 593 { 594 import std.format : formattedWrite; 595 596 Dobject o; 597 598 //perrinfo.message = null; 599 char[] buffer = null; 600 601 void putc(dchar c) 602 { 603 std.utf.encode(buffer, c); 604 } 605 606 formattedWrite(&putc, fmt, args); 607 perrinfo.message = assumeUnique(buffer); 608 o = new typeerror.D0(perrinfo); 609 Value* v = new Value; 610 v.putVobject(o); 611 return v; 612 } 613 static Value* ReferenceError(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args) 614 { 615 return ReferenceError(perrinfo, errmsgtbl[msgnum], args); 616 } 617 618 static Value* ReferenceError(ARGS...)(string fmt, ARGS args) 619 { 620 ErrInfo errinfo; 621 return ReferenceError(&errinfo, fmt, args); 622 } 623 624 static Value* ReferenceError(ARGS...)(ErrInfo* perrinfo, string fmt, ARGS args) 625 { 626 import std.format : formattedWrite; 627 628 Dobject o; 629 //perrinfo.message = null; 630 d_string buffer = null; 631 632 void putc(dchar c) 633 { 634 dmdscript.utf.encode(buffer, c); 635 } 636 637 formattedWrite(&putc, fmt, args); 638 perrinfo.message = buffer; 639 640 o = new referenceerror.D0(perrinfo); 641 Value* v = new Value; 642 v.putVobject(o); 643 644 return v; 645 } 646 static Value* RangeError(ARGS...)(ErrInfo *perrinfo, int msgnum, ARGS args) 647 { 648 return RangeError(perrinfo, errmsgtbl[msgnum], args); 649 } 650 651 static Value* RangeError(ARGS...)(ErrInfo *perrinfo, string fmt, ARGS args) 652 { 653 import std.format : formattedWrite; 654 655 Dobject o; 656 657 //perrinfo.message = null; 658 d_string buffer = null; 659 660 void putc(dchar c) 661 { 662 dmdscript.utf.encode(buffer, c); 663 } 664 665 formattedWrite(&putc, fmt, args); 666 perrinfo.message = buffer; 667 668 o = new rangeerror.D0(perrinfo); 669 Value* v = new Value; 670 v.putVobject(o); 671 return v; 672 } 673 674 Value* putIterator(Value* v) 675 { 676 Iterator* i = new Iterator; 677 678 i.ctor(this); 679 v.putViterator(i); 680 return null; 681 } 682 683 static Dfunction getConstructor() 684 { 685 return Dobject_constructor; 686 } 687 688 static Dobject getPrototype() 689 { 690 return Dobject_prototype; 691 } 692 693 static void initialize() 694 { 695 Dobject_prototype = new DobjectPrototype(); 696 Dfunction.initialize(); 697 Dobject_constructor = new DobjectConstructor(); 698 699 Dobject op = Dobject_prototype; 700 Dobject f = Dfunction_prototype; 701 702 op.Put(TEXT_constructor, Dobject_constructor, DontEnum); 703 704 static enum NativeFunctionData[] nfd = 705 [ 706 { TEXT_toString, &Dobject_prototype_toString, 0 }, 707 { TEXT_toLocaleString, &Dobject_prototype_toLocaleString, 0 }, 708 { TEXT_toSource, &Dobject_prototype_toSource, 0 }, 709 { TEXT_valueOf, &Dobject_prototype_valueOf, 0 }, 710 { TEXT_hasOwnProperty, &Dobject_prototype_hasOwnProperty, 1 }, 711 { TEXT_isPrototypeOf, &Dobject_prototype_isPrototypeOf, 0 }, 712 { TEXT_propertyIsEnumerable, &Dobject_prototype_propertyIsEnumerable, 0 }, 713 ]; 714 715 DnativeFunction.initialize(op, nfd, DontEnum); 716 } 717 } 718 719 720 /********************************************* 721 * Initialize the built-in's. 722 */ 723 724 void dobject_init() 725 { 726 //writef("dobject_init(tc = %x)\n", cast(uint)tc); 727 if(Dobject_prototype) 728 return; // already initialized for this thread 729 730 version(none) 731 { 732 writef("sizeof(Dobject) = %d\n", sizeof(Dobject)); 733 writef("sizeof(PropTable) = %d\n", sizeof(PropTable)); 734 writef("offsetof(proptable) = %d\n", offsetof(Dobject, proptable)); 735 writef("offsetof(internal_prototype) = %d\n", offsetof(Dobject, internal_prototype)); 736 writef("offsetof(classname) = %d\n", offsetof(Dobject, classname)); 737 writef("offsetof(value) = %d\n", offsetof(Dobject, value)); 738 } 739 740 Dobject.initialize(); 741 Dboolean.initialize(); 742 Dstring.initialize(); 743 Dnumber.initialize(); 744 Darray.initialize(); 745 Dmath.initialize(); 746 Ddate.initialize(); 747 Dregexp.initialize(); 748 Derror.initialize(); 749 750 // Call registered initializer for each object type 751 foreach(void function() fpinit; threadInitTable) 752 (*fpinit)(); 753 754 } 755 /*Not used anyway 756 void dobject_term() 757 { 758 //writef("dobject_term(program = %x)\n", program); 759 760 memset(&program, 0, ThreadContext.sizeof - Thread.sizeof); 761 } 762 */