1 28 29 package com.caucho.es; 30 31 import com.caucho.util.CharBuffer; 32 import com.caucho.util.IntMap; 33 34 import java.util.HashMap ; 35 import java.util.Iterator ; 36 37 40 public class ESObject extends ESBase { 41 static ESId TO_STRING = ESId.intern("toString"); 42 static ESId VALUE_OF = ESId.intern("valueOf"); 43 static ESId CALL = ESId.intern("call"); 44 static ESId CONSTRUCT = ESId.intern("construct"); 45 static int DIRTY = 0; 46 static int CLEAN = DIRTY + 1; 47 static int COW = CLEAN + 1; 48 49 int copyState = DIRTY; 50 51 ESString []propNames; 52 ESBase []propValues; 53 ESBase []propWatch; 54 int []propFlags; 55 int size; 56 int fill; 57 int mask; 58 59 int mark; protected boolean snapPrototype; 61 62 protected ESObject() 63 { 64 } 65 66 69 public ESObject(String className, ESBase proto) 70 { 71 init(className, proto, 16); 72 } 73 74 protected ESObject(String className, ESBase proto, int hashSize) 75 { 76 init(className, proto, hashSize < 16 ? 16 : hashSize); 77 } 78 79 private void init(String className, ESBase proto, int hashSize) 80 { 81 if (proto == null) { 82 Global resin = Global.getGlobalProto(); 83 proto = resin == null ? null : resin.objProto; 84 } 85 if (className == null && proto != null) 86 className = proto.className; 87 prototype = proto; 88 89 propNames = new ESString[hashSize]; 90 propValues = new ESBase[hashSize]; 91 propFlags = new int[hashSize]; 92 mask = propNames.length - 1; 93 size = 0; 94 fill = 0; 95 copyState = DIRTY; 96 97 this.className = className == null ? "Object" : className; 98 } 99 100 void init(String className, ESBase proto) 101 { 102 init(className, proto, 16); 103 } 104 105 void setClean() 106 { 107 copyState = CLEAN; 108 } 109 110 113 private void resize(int newSize) 114 { 115 ESString []newNames = new ESString[newSize]; 116 ESBase []newValues = new ESBase[newSize]; 117 int []newFlags = new int[newSize]; 118 ESBase []newWatch = null; 119 if (propWatch != null) 120 newWatch = new ESBase[newSize]; 121 122 mask = newNames.length - 1; 123 124 for (int i = 0; i < propNames.length; i++) { 125 if (propValues[i] == null && (propFlags[i] & WATCH) == 0) 126 continue; 127 128 int hash = propNames[i].hashCode() & mask; 129 130 while (true) { 131 if (newNames[hash] == null) { 132 newNames[hash] = propNames[i]; 133 newValues[hash] = propValues[i]; 134 newFlags[hash] = propFlags[i]; 135 if (newWatch != null) 136 newWatch[hash] = propWatch[i]; 137 break; 138 } 139 hash = (hash + 1) & mask; 140 } 141 } 142 143 propNames = newNames; 144 propValues = newValues; 145 propFlags = newFlags; 146 propWatch = newWatch; 147 fill = size; 148 } 149 150 private void refill() 151 { 152 for (int i = 0; i < propNames.length; i++) { 153 if (propValues[i] == null && (propFlags[i] & WATCH) == 0) { 154 propNames[i] = null; 155 continue; 156 } 157 158 int hash = propNames[i].hashCode() & mask; 159 160 while (true) { 161 if (propValues[hash] == null && (propFlags[hash] & WATCH) == 0) { 162 propNames[hash] = propNames[i]; 163 propValues[hash] = propValues[i]; 164 propFlags[hash] = propFlags[i]; 165 propNames[i] = null; 166 propValues[i] = null; 167 propFlags[i] = 0; 168 break; 169 } 170 hash = (hash + 1) & mask; 171 } 172 } 173 174 fill = size; 175 } 176 177 180 public ESBase getProperty(ESString name) throws Throwable 181 { 182 int hash = name.hashCode() & mask; 183 184 while (true) { 185 ESString propName = propNames[hash]; 186 187 if (propName == name || name.equals(propName)) { 188 ESBase value = propValues[hash]; 189 return value == null ? prototype.getProperty(name) : value; 190 } 191 else if (propName == null) { 192 ESBase value = prototype.getProperty(name); 193 if (snapPrototype) 194 setProperty(name, value); 195 return value; 196 } 197 198 hash = (hash + 1) & mask; 199 } 200 } 201 202 protected boolean canPut(ESString name) 203 { 204 int hash = name.hashCode() & mask; 205 206 while (true) { 207 ESString propName = propNames[hash]; 208 209 if (name.equals(propName) && propValues[hash] != null) 210 return (propFlags[hash] & READ_ONLY) == 0; 211 else if (propName == null) { 212 if (prototype instanceof ESObject) 213 return ((ESObject) prototype).canPut(name); 214 else 215 return true; 216 } 217 218 hash = (hash + 1) & mask; 219 } 220 } 221 222 243 244 247 public void setProperty(ESString name, ESBase value) throws Throwable 248 { 249 if (copyState != DIRTY) { 250 if (copyState == COW) 251 copyAll(); 252 copyState = DIRTY; 253 } 254 255 if (value == esEmpty) 256 value = esUndefined; 257 258 int hash = name.hashCode() & mask; 259 260 while (true) { 261 ESString propName = propNames[hash]; 262 263 if (propValues[hash] == null) { 264 if (! prototype.canPut(name)) 265 return; 266 267 if (propName == null) 268 fill++; 269 270 propNames[hash] = name; 271 275 propValues[hash] = value; 276 propFlags[hash] = 0; 277 278 size++; 279 280 if (propNames.length <= 4 * size) { 281 resize(4 * propNames.length); 282 } 283 else if (propNames.length <= 2 * fill) 284 refill(); 285 286 return; 287 } 288 else if (propName != name && ! propName.equals(name)) { 289 hash = (hash + 1) & mask; 290 continue; 291 } 292 else if ((propFlags[hash] & READ_ONLY) != 0) 293 return; 294 else { 295 299 propValues[hash] = value; 300 return; 301 } 302 } 303 } 304 305 public void put(ESString name, ESBase value, int flags) 306 { 307 int hash = name.hashCode() & mask; 308 309 while (true) { 310 ESString propName = propNames[hash]; 311 312 if (propName == null || 313 propValues[hash] == null || propName.equals(name)) { 314 if (propName == null) 315 fill++; 316 if (propValues[hash] == null) 317 size++; 318 319 propNames[hash] = name; 320 propValues[hash] = value; 321 propFlags[hash] = flags; 322 323 if (propNames.length <= 4 * size) { 324 resize(4 * propNames.length); 325 } 326 else if (propNames.length <= 2 * fill) 327 refill(); 328 329 return; 330 } 331 332 hash = (hash + 1) & mask; 333 } 334 } 335 336 public void put(String name, ESBase value, int flags) 337 { 338 ESId id = ESId.intern(name); 339 340 put(id, value, flags); 341 } 342 343 346 public ESBase delete(ESString name) throws Throwable 347 { 348 if (copyState != DIRTY) { 349 if (copyState == COW) 350 copyAll(); 351 copyState = DIRTY; 352 } 353 354 int hash = name.hashCode() & mask; 355 356 while (true) { 357 ESString hashName = propNames[hash]; 358 359 if (hashName == null) 360 return ESBoolean.FALSE; 361 else if (propValues[hash] != null && hashName.equals(name)) { 362 if ((propFlags[hash] & DONT_DELETE) != 0) 363 return ESBoolean.FALSE; 364 else { 365 propValues[hash] = null; 366 size--; 367 return ESBoolean.TRUE; 368 } 369 } 370 371 hash = (hash + 1) & mask; 372 } 373 } 374 375 public void watch(ESString name, ESBase fun) 376 { 377 if (copyState != DIRTY) { 378 if (copyState == COW) 379 copyAll(); 380 copyState = DIRTY; 381 } 382 383 int hash = name.hashCode() & mask; 384 385 while (true) { 386 ESString propName = propNames[hash]; 387 388 if (propValues[hash] == null) { 389 if (! prototype.canPut(name)) 390 return; 391 392 if (propName == null) 393 fill++; 394 395 propNames[hash] = name; 396 propValues[hash] = esEmpty; 397 propFlags[hash] = WATCH; 398 if (propWatch == null) 399 propWatch = new ESBase[propFlags.length]; 400 propWatch[hash] = fun; 401 402 size++; 403 404 if (propNames.length <= 4 * size) 405 resize(4 * propNames.length); 406 else if (propNames.length <= 2 * fill) 407 refill(); 408 409 return; 410 } 411 else if (propName != name && ! propName.equals(name)) { 412 hash = (hash + 1) & mask; 413 continue; 414 } 415 else if ((propFlags[hash] & READ_ONLY) != 0) 416 return; 417 else { 418 propFlags[hash] |= WATCH; 419 if (propWatch == null) 420 propWatch = new ESBase[propFlags.length]; 421 422 propWatch[hash] = fun; 423 return; 424 } 425 } 426 } 427 428 public void unwatch(ESString name) 429 { 430 if (copyState != DIRTY) { 431 if (copyState == COW) 432 copyAll(); 433 copyState = DIRTY; 434 } 435 436 int hash = name.hashCode() & mask; 437 438 while (true) { 439 ESString propName = propNames[hash]; 440 441 if (propName == null) 442 return; 443 else if (propName.equals(name)) { 444 propFlags[hash] &= ~WATCH; 445 446 return; 447 } 448 } 449 } 450 451 454 public void put(int i, ESBase value, int flags) 455 { 456 put(ESString.create(i), value, flags); 457 } 458 459 public Iterator keys() throws ESException 460 { 461 return new PropertyEnumeration(this); 462 } 463 464 public ESBase typeof() throws ESException 465 { 466 return ESString.create("object"); 467 } 468 469 472 public ESBase toPrimitive(int hint) throws Throwable 473 { 474 Global resin = Global.getGlobalProto(); 475 Call eval = resin.getCall(); 476 eval.global = resin.getGlobal(); 477 478 try { 479 ESBase fun = hasProperty(hint == STRING ? TO_STRING : VALUE_OF); 480 481 if (fun instanceof ESClosure || fun instanceof Native) { 482 eval.stack[0] = this; 483 eval.top = 1; 484 ESBase value = fun.call(eval, 0); 485 486 if (value instanceof ESBase && ! (value instanceof ESObject)) 487 return value; 488 } 489 490 fun = hasProperty(hint == STRING ? VALUE_OF : TO_STRING); 491 492 if (fun instanceof ESClosure || fun instanceof Native) { 493 eval.stack[0] = this; 494 eval.top = 1; 495 ESBase value = fun.call(eval, 0); 496 497 if (value instanceof ESBase && ! (value instanceof ESObject)) 498 return value; 499 } 500 501 throw new ESException("cannot convert object to primitive type"); 502 } finally { 503 resin.freeCall(eval); 504 } 505 } 506 507 public ESObject toObject() { return this; } 508 509 public Object toJavaObject() throws ESException 510 { 511 return this; 512 } 513 514 517 public double toNum() throws Throwable 518 { 519 ESBase value = toPrimitive(NUMBER); 520 521 if (value instanceof ESObject) 522 throw new ESException("toPrimitive must return primitive"); 523 524 return value.toNum(); 525 } 526 527 530 public ESString toStr() throws Throwable 531 { 532 ESBase prim = toPrimitive(STRING); 533 534 if (prim instanceof ESObject) 535 throw new ESException("toPrimitive must return primitive"); 536 537 return prim.toStr(); 538 } 539 540 public ESString toSource(IntMap map, boolean isLoopPass) throws Throwable 541 { 542 CharBuffer cb = new CharBuffer(); 543 Global resin = Global.getGlobalProto(); 544 545 int mark = map.get(this); 546 547 if (mark > 0 && isLoopPass) 548 return null; 549 else if (mark > 0) { 550 cb.append("#" + mark + "="); 551 map.put(this, -mark); 552 } else if (mark == 0 && isLoopPass) { 553 map.put(this, resin.addMark()); 554 return null; 555 } else if (mark < 0 && ! isLoopPass) { 556 return ESString.create("#" + -mark + "#"); 557 } 558 559 cb.append("{"); 560 561 if (isLoopPass) 562 map.put(this, 0); 563 564 Iterator e = keys(); 565 566 boolean isFirst = true; 567 while (e.hasNext()) { 568 if (! isFirst) 569 cb.append(", "); 570 isFirst = false; 571 572 ESString key = (ESString) e.next(); 573 574 cb.append(key); 575 cb.append(":"); 576 ESBase value = getProperty(key); 577 if (isLoopPass) 578 value.toSource(map, isLoopPass); 579 else 580 cb.append(value.toSource(map, isLoopPass)); 581 } 582 583 cb.append("}"); 584 585 return new ESString(cb.toString()); 586 } 587 588 public boolean toBoolean() { return true; } 589 590 ESObject dup() { return new ESObject(); } 591 592 public Object copy(HashMap refs) 593 { 594 Object ref = refs.get(this); 595 if (ref != null) 596 return ref; 597 598 ESObject copy = dup(); 599 refs.put(this, copy); 600 601 copy(refs, copy); 602 603 return copy; 604 } 605 606 private void copyAll() 607 { 608 copyState = DIRTY; 609 610 int len = propValues.length; 611 612 ESString []newPropNames = new ESString[len]; 613 int []newPropFlags = new int[len]; 614 ESBase []newPropValues = new ESBase[len]; 615 616 System.arraycopy(propNames, 0, newPropNames, 0, len); 617 System.arraycopy(propFlags, 0, newPropFlags, 0, len); 618 System.arraycopy(propValues, 0, newPropValues, 0, len); 619 620 propNames = newPropNames; 621 propFlags = newPropFlags; 622 propValues = newPropValues; 623 624 if (propWatch != null) { 625 ESBase []newPropWatch = new ESBase[len]; 626 System.arraycopy(propWatch, 0, newPropWatch, 0, len); 627 propWatch = newPropWatch; 628 } 629 } 630 631 ESObject resinCopy() 632 { 633 ESObject obj = dup(); 634 635 copy(obj); 636 637 return obj; 638 } 639 640 protected void copy(Object newObj) 641 { 642 ESObject obj = (ESObject) newObj; 643 644 obj.prototype = prototype; 645 obj.className = className; 646 647 obj.propNames = propNames; 648 obj.propValues = propValues; 649 obj.propFlags = propFlags; 650 obj.propWatch = propWatch; 651 obj.size = size; 652 obj.fill = fill; 653 obj.mask = mask; 654 obj.copyState = copyState; 655 656 if (obj.copyState == DIRTY) { 657 throw new RuntimeException (); 658 } 659 else if (copyState == CLEAN) { 660 copyState = COW; 661 obj.copyState = COW; 662 } 663 } 664 665 protected void copy(HashMap refs, Object newObj) 666 { 667 ESObject obj = (ESObject) newObj; 668 669 obj.prototype = (ESBase) prototype.copy(refs); 670 obj.className = className; 671 672 obj.propNames = propNames; 673 obj.propValues = propValues; 674 obj.propFlags = propFlags; 675 obj.propWatch = propWatch; 676 obj.size = size; 677 obj.fill = fill; 678 obj.mask = mask; 679 obj.copyState = copyState; 680 681 if (obj.copyState == DIRTY) { 682 obj.copyAll(); 683 } 684 else if (copyState == CLEAN) { 685 copyState = COW; 686 obj.copyState = COW; 687 } 688 } 689 690 ESObject shallowCopy() 691 { 692 ESObject obj = dup(); 693 694 shallowCopy(obj); 695 696 return obj; 697 } 698 699 protected void shallowCopy(Object newObj) 700 { 701 ESObject obj = (ESObject) newObj; 702 703 obj.prototype = prototype; 704 obj.className = className; 705 706 int len = propValues.length; 707 708 if (propWatch != null) { 709 obj.propWatch = new ESBase[len]; 710 System.arraycopy(propWatch, 0, obj.propWatch, 0, len); 711 } 712 713 obj.propNames = new ESString[len]; 714 obj.propFlags = new int[len]; 715 obj.propValues = new ESBase[len]; 716 717 ESString []newNames = obj.propNames; 718 ESString []oldNames = propNames; 719 ESBase []newValues = obj.propValues; 720 ESBase []oldValues = propValues; 721 int []newFlags = obj.propFlags; 722 int []oldFlags = propFlags; 723 for (int i = 0; i < len; i++) { 724 newNames[i] = oldNames[i]; 725 newValues[i] = oldValues[i]; 726 newFlags[i] = oldFlags[i]; 727 } 728 729 obj.size = size; 730 obj.mask = mask; 731 obj.fill = fill; 732 obj.copyState = DIRTY; 733 } 734 735 public boolean ecmaEquals(ESBase b) throws Throwable 736 { 737 if (b instanceof ESObject || b instanceof ESThunk) 738 return this == b; 739 else 740 return toPrimitive(NONE).ecmaEquals(b); 741 } 742 743 public ESBase call(Call call, int length) throws Throwable 744 { 745 ESBase callFun = hasProperty(CALL); 746 747 if (callFun != null) { 748 call.setThis(this); 749 return callFun.call(call, length); 750 } 751 752 throw new ESNullException(toStr() + " is not a function"); 753 } 754 755 public ESBase construct(Call call, int length) throws Throwable 756 { 757 ESBase callFun = hasProperty(CONSTRUCT); 758 759 if (callFun != null) { 760 call.setThis(this); 761 return callFun.construct(call, length); 762 } 763 764 throw new ESNullException(toStr() + " is not a constructor"); 765 } 766 } 767 | Popular Tags |