1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 30 import static jdk.nashorn.internal.lookup.Lookup.MH; 31 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 33 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 34 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 35 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET; 36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET; 37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 39 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 40 41 import java.lang.invoke.MethodHandle; 42 import java.lang.invoke.MethodHandles; 43 import java.lang.invoke.MethodType; 44 import java.util.AbstractMap; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collection; 48 import java.util.Collections; 49 import java.util.HashSet; 50 import java.util.Iterator; 51 import java.util.LinkedHashSet; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.Set; 55 import jdk.internal.dynalink.CallSiteDescriptor; 56 import jdk.internal.dynalink.linker.GuardedInvocation; 57 import jdk.internal.dynalink.linker.LinkRequest; 58 import jdk.internal.dynalink.support.CallSiteDescriptorFactory; 59 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 60 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 61 import jdk.nashorn.internal.lookup.Lookup; 62 import jdk.nashorn.internal.lookup.MethodHandleFactory; 63 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor; 64 import jdk.nashorn.internal.objects.DataPropertyDescriptor; 65 import jdk.nashorn.internal.runtime.arrays.ArrayData; 66 import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 67 import jdk.nashorn.internal.runtime.linker.Bootstrap; 68 import jdk.nashorn.internal.runtime.linker.LinkerCallSite; 69 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 70 import jdk.nashorn.internal.runtime.linker.NashornGuards; 71 72 /** 73 * Base class for generic JavaScript objects. 74 * <p> 75 * Notes: 76 * <ul> 77 * <li>The map is used to identify properties in the object.</li> 78 * <li>If the map is modified then it must be cloned and replaced. This notifies 79 * any code that made assumptions about the object that things have changed. 80 * Ex. CallSites that have been validated must check to see if the map has 81 * changed (or a map from a different object type) and hence relink the method 82 * to call.</li> 83 * <li>Modifications of the map include adding/deleting attributes or changing a 84 * function field value.</li> 85 * </ul> 86 */ 87 88 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess { 89 90 /** Search fall back routine name for "no such method" */ 91 static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 92 93 /** Search fall back routine name for "no such property" */ 94 static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 95 96 /** Per ScriptObject flag - is this a scope object? */ 97 public static final int IS_SCOPE = 0b0000_0001; 98 99 /** Per ScriptObject flag - is this an array object? */ 100 public static final int IS_ARRAY = 0b0000_0010; 101 102 /** Per ScriptObject flag - is this an arguments object? */ 103 public static final int IS_ARGUMENTS = 0b0000_0100; 104 105 /** Is this a prototype PropertyMap? */ 106 public static final int IS_PROTOTYPE = 0b0000_1000; 107 108 /** Is length property not-writable? */ 109 public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000; 110 111 /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */ 112 public static final int SPILL_RATE = 8; 113 114 /** Map to property information and accessor functions. Ordered by insertion. */ 115 private PropertyMap map; 116 117 /** objects proto. */ 118 private ScriptObject proto; 119 120 /** Context of the object, lazily cached. */ 121 private Context context; 122 123 /** Object flags. */ 124 private int flags; 125 126 /** Area for properties added to object after instantiation, see {@link AccessorProperty} */ 127 public Object[] spill; 128 129 /** Indexed array data. */ 130 private ArrayData arrayData; 131 132 static final MethodHandle SETFIELD = findOwnMH("setField", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class); 133 static final MethodHandle SETSPILL = findOwnMH("setSpill", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class); 134 static final MethodHandle SETSPILLWITHNEW = findOwnMH("setSpillWithNew", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class); 135 static final MethodHandle SETSPILLWITHGROW = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class); 136 137 private static final MethodHandle TRUNCATINGFILTER = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class); 138 private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class); 139 140 /** Method handle for getting a function argument at a given index. Used from MapCreator */ 141 public static final Call GET_ARGUMENT = virtualCall(ScriptObject.class, "getArgument", Object.class, int.class); 142 143 /** Method handle for setting a function argument at a given index. Used from MapCreator */ 144 public static final Call SET_ARGUMENT = virtualCall(ScriptObject.class, "setArgument", void.class, int.class, Object.class); 145 146 /** Method handle for getting the proto of a ScriptObject */ 147 public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class); 148 149 /** Method handle for setting the proto of a ScriptObject */ 150 public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class); 151 152 /** Method handle for setting the user accessors of a ScriptObject */ 153 public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class); 154 155 /** 156 * Constructor 157 */ 158 public ScriptObject() { 159 this(null); 160 } 161 162 /** 163 * Constructor 164 * 165 * @param map {@link PropertyMap} used to create the initial object 166 */ 167 public ScriptObject(final PropertyMap map) { 168 if (Context.DEBUG) { 169 ScriptObject.count++; 170 } 171 172 this.arrayData = ArrayData.EMPTY_ARRAY; 173 174 if (map == null) { 175 this.setMap(PropertyMap.newMap(getClass())); 176 return; 177 } 178 179 this.setMap(map); 180 } 181 182 /** 183 * Copy all properties from the source object with their receiver bound to the source. 184 * This function was known as mergeMap 185 * 186 * @param source The source object to copy from. 187 */ 188 public void addBoundProperties(final ScriptObject source) { 189 PropertyMap newMap = this.getMap(); 190 191 for (final Property property : source.getMap().getProperties()) { 192 final String key = property.getKey(); 193 194 if (newMap.findProperty(key) == null) { 195 if (property instanceof UserAccessorProperty) { 196 final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); 197 newMap = newMap.addProperty(prop); 198 } else { 199 newMap = newMap.newPropertyBind((AccessorProperty)property, source); 200 } 201 } 202 } 203 204 this.setMap(newMap); 205 } 206 207 /** 208 * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the 209 * first argument in lieu of the bound argument). 210 * @param methodHandle Method handle to bind to. 211 * @param receiver Object to bind. 212 * @return Bound method handle. 213 */ 214 static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) { 215 return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0)); 216 } 217 218 /** 219 * Return a property iterator. 220 * @return Property iterator. 221 */ 222 public Iterator<String> propertyIterator() { 223 return new KeyIterator(this); 224 } 225 226 /** 227 * Return a property value iterator. 228 * @return Property value iterator. 229 */ 230 public Iterator<Object> valueIterator() { 231 return new ValueIterator(this); 232 } 233 234 /** 235 * ECMA 8.10.1 IsAccessorDescriptor ( Desc ) 236 * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter 237 */ 238 public final boolean isAccessorDescriptor() { 239 return has(GET) || has(SET); 240 } 241 242 /** 243 * ECMA 8.10.2 IsDataDescriptor ( Desc ) 244 * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable 245 */ 246 public final boolean isDataDescriptor() { 247 return has(VALUE) || has(WRITABLE); 248 } 249 250 /** 251 * ECMA 8.10.3 IsGenericDescriptor ( Desc ) 252 * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor} 253 */ 254 public final boolean isGenericDescriptor() { 255 return isAccessorDescriptor() || isDataDescriptor(); 256 } 257 258 /** 259 * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 260 * 261 * @return property descriptor 262 */ 263 public final PropertyDescriptor toPropertyDescriptor() { 264 final GlobalObject global = (GlobalObject) Context.getGlobalTrusted(); 265 266 final PropertyDescriptor desc; 267 if (isDataDescriptor()) { 268 if (has(SET) || has(GET)) { 269 throw typeError((ScriptObject)global, "inconsistent.property.descriptor"); 270 } 271 272 desc = global.newDataDescriptor(UNDEFINED, false, false, false); 273 } else if (isAccessorDescriptor()) { 274 if (has(VALUE) || has(WRITABLE)) { 275 throw typeError((ScriptObject)global, "inconsistent.property.descriptor"); 276 } 277 278 desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false); 279 } else { 280 desc = global.newGenericDescriptor(false, false); 281 } 282 283 return desc.fillFrom(this); 284 } 285 286 /** 287 * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 288 * 289 * @param global global scope object 290 * @param obj object to create property descriptor from 291 * 292 * @return property descriptor 293 */ 294 public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) { 295 if (obj instanceof ScriptObject) { 296 return ((ScriptObject)obj).toPropertyDescriptor(); 297 } 298 299 throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); 300 } 301 302 /** 303 * ECMA 8.12.1 [[GetOwnProperty]] (P) 304 * 305 * @param key property key 306 * 307 * @return Returns the Property Descriptor of the named own property of this 308 * object, or undefined if absent. 309 */ 310 public Object getOwnPropertyDescriptor(final String key) { 311 final Property property = getMap().findProperty(key); 312 313 final GlobalObject global = (GlobalObject)Context.getGlobalTrusted(); 314 315 if (property != null) { 316 final ScriptFunction get = property.getGetterFunction(this); 317 final ScriptFunction set = property.getSetterFunction(this); 318 319 final boolean configurable = property.isConfigurable(); 320 final boolean enumerable = property.isEnumerable(); 321 final boolean writable = property.isWritable(); 322 323 if (property instanceof UserAccessorProperty) { 324 return global.newAccessorDescriptor( 325 (get != null) ? 326 get : 327 UNDEFINED, 328 (set != null) ? 329 set : 330 UNDEFINED, 331 configurable, 332 enumerable); 333 } 334 335 return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable); 336 } 337 338 final int index = ArrayIndex.getArrayIndex(key); 339 final ArrayData array = getArray(); 340 341 if (array.has(index)) { 342 return array.getDescriptor(global, index); 343 } 344 345 return UNDEFINED; 346 } 347 348 /** 349 * ECMA 8.12.2 [[GetProperty]] (P) 350 * 351 * @param key property key 352 * 353 * @return Returns the fully populated Property Descriptor of the named property 354 * of this object, or undefined if absent. 355 */ 356 public Object getPropertyDescriptor(final String key) { 357 final Object res = getOwnPropertyDescriptor(key); 358 359 if (res != UNDEFINED) { 360 return res; 361 } else if (getProto() != null) { 362 return getProto().getOwnPropertyDescriptor(key); 363 } else { 364 return UNDEFINED; 365 } 366 } 367 368 /** 369 * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) 370 * 371 * @param key the property key 372 * @param propertyDesc the property descriptor 373 * @param reject is the property extensible - true means new definitions are rejected 374 * 375 * @return true if property was successfully defined 376 */ 377 public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) { 378 final ScriptObject global = Context.getGlobalTrusted(); 379 final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc); 380 final Object current = getOwnPropertyDescriptor(key); 381 final String name = JSType.toString(key); 382 383 if (current == UNDEFINED) { 384 if (isExtensible()) { 385 // add a new own property 386 addOwnProperty(key, desc); 387 return true; 388 } 389 // new property added to non-extensible object 390 if (reject) { 391 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this)); 392 } 393 return false; 394 } 395 // modifying an existing property 396 final PropertyDescriptor currentDesc = (PropertyDescriptor) current; 397 final PropertyDescriptor newDesc = desc; 398 399 if (newDesc.type() == PropertyDescriptor.GENERIC && 400 ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) { 401 // every descriptor field is absent 402 return true; 403 } 404 405 if (currentDesc.equals(newDesc)) { 406 // every descriptor field of the new is same as the current 407 return true; 408 } 409 410 if (! currentDesc.isConfigurable()) { 411 if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) { 412 // not configurable can not be made configurable 413 if (reject) { 414 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 415 } 416 return false; 417 } 418 419 if (newDesc.has(ENUMERABLE) && 420 currentDesc.isEnumerable() != newDesc.isEnumerable()) { 421 // cannot make non-enumerable as enumerable or vice-versa 422 if (reject) { 423 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 424 } 425 return false; 426 } 427 } 428 429 int propFlags = Property.mergeFlags(currentDesc, newDesc); 430 Property property = getMap().findProperty(key); 431 432 if (currentDesc.type() == PropertyDescriptor.DATA && 433 (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) { 434 if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) { 435 if (newDesc.has(WRITABLE) && newDesc.isWritable() || 436 newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) { 437 if (reject) { 438 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 439 } 440 return false; 441 } 442 } 443 444 final boolean newValue = newDesc.has(VALUE); 445 final Object value = newValue? newDesc.getValue() : currentDesc.getValue(); 446 if (newValue && property != null) { 447 // Temporarily clear flags. 448 property = modifyOwnProperty(property, 0); 449 set(key, value, false); 450 } 451 452 if (property == null) { 453 // promoting an arrayData value to actual property 454 addOwnProperty(key, propFlags, value); 455 removeArraySlot(key); 456 } else { 457 // Now set the new flags 458 modifyOwnProperty(property, propFlags); 459 } 460 } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR && 461 (newDesc.type() == PropertyDescriptor.ACCESSOR || 462 newDesc.type() == PropertyDescriptor.GENERIC)) { 463 if (! currentDesc.isConfigurable()) { 464 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) || 465 newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) { 466 if (reject) { 467 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 468 } 469 return false; 470 } 471 } 472 473 // New set the new features. 474 modifyOwnProperty(property, propFlags, 475 newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(), 476 newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter()); 477 } else { 478 // changing descriptor type 479 if (! currentDesc.isConfigurable()) { 480 // not configurable can not be made configurable 481 if (reject) { 482 throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 483 } 484 return false; 485 } 486 487 propFlags = 0; 488 489 // Preserve only configurable and enumerable from current desc 490 // if those are not overridden in the new property descriptor. 491 boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable(); 492 if (!value) { 493 propFlags |= Property.NOT_CONFIGURABLE; 494 } 495 value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable(); 496 if (!value) { 497 propFlags |= Property.NOT_ENUMERABLE; 498 } 499 500 final int type = newDesc.type(); 501 if (type == PropertyDescriptor.DATA) { 502 // get writable from the new descriptor 503 value = newDesc.has(WRITABLE) && newDesc.isWritable(); 504 if (! value) { 505 propFlags |= Property.NOT_WRITABLE; 506 } 507 508 // delete the old property 509 deleteOwnProperty(property); 510 // add new data property 511 addOwnProperty(key, propFlags, newDesc.getValue()); 512 } else if (type == PropertyDescriptor.ACCESSOR) { 513 if (property == null) { 514 addOwnProperty(key, propFlags, 515 newDesc.has(GET) ? newDesc.getGetter() : null, 516 newDesc.has(SET) ? newDesc.getSetter() : null); 517 } else { 518 // Modify old property with the new features. 519 modifyOwnProperty(property, propFlags, 520 newDesc.has(GET) ? newDesc.getGetter() : null, 521 newDesc.has(SET) ? newDesc.getSetter() : null); 522 } 523 } 524 } 525 526 checkIntegerKey(key); 527 528 return true; 529 } 530 531 /** 532 * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in 533 * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set 534 * method in such cases. This is because set method uses inherited setters (if any) 535 * from any object in proto chain such as Array.prototype, Object.prototype. 536 * This method directly sets a particular element value in the current object. 537 * 538 * @param index key for property 539 * @param value value to define 540 */ 541 protected final void defineOwnProperty(final int index, final Object value) { 542 assert ArrayIndex.isValidArrayIndex(index) : "invalid array index"; 543 final long longIndex = ArrayIndex.toLongIndex(index); 544 if (longIndex >= getArray().length()) { 545 // make array big enough to hold.. 546 setArray(getArray().ensure(longIndex)); 547 } 548 setArray(getArray().set(index, value, false)); 549 } 550 551 private void checkIntegerKey(final String key) { 552 final int index = ArrayIndex.getArrayIndex(key); 553 554 if (ArrayIndex.isValidArrayIndex(index)) { 555 final ArrayData data = getArray(); 556 557 if (data.has(index)) { 558 setArray(data.delete(index)); 559 } 560 } 561 } 562 563 private void removeArraySlot(final String key) { 564 final int index = ArrayIndex.getArrayIndex(key); 565 final ArrayData array = getArray(); 566 567 if (array.has(index)) { 568 setArray(array.delete(index)); 569 } 570 } 571 572 /** 573 * Add a new property to the object. 574 * 575 * @param key property key 576 * @param propertyDesc property descriptor for property 577 */ 578 public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) { 579 // Already checked that there is no own property with that key. 580 PropertyDescriptor pdesc = propertyDesc; 581 582 final int propFlags = Property.toFlags(pdesc); 583 584 if (pdesc.type() == PropertyDescriptor.GENERIC) { 585 final GlobalObject global = (GlobalObject) Context.getGlobalTrusted(); 586 final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false); 587 588 dDesc.fillFrom((ScriptObject)pdesc); 589 pdesc = dDesc; 590 } 591 592 final int type = pdesc.type(); 593 if (type == PropertyDescriptor.DATA) { 594 addOwnProperty(key, propFlags, pdesc.getValue()); 595 } else if (type == PropertyDescriptor.ACCESSOR) { 596 addOwnProperty(key, propFlags, 597 pdesc.has(GET) ? pdesc.getGetter() : null, 598 pdesc.has(SET) ? pdesc.getSetter() : null); 599 } 600 601 checkIntegerKey(key); 602 } 603 604 /** 605 * Low level property API (not using property descriptors) 606 * <p> 607 * Find a property in the prototype hierarchy. Note: this is final and not 608 * a good idea to override. If you have to, use 609 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 610 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 611 * overriding way to find array properties 612 * 613 * @see jdk.nashorn.internal.objects.NativeArray 614 * 615 * @param key Property key. 616 * @param deep Whether the search should look up proto chain. 617 * 618 * @return FindPropertyData or null if not found. 619 */ 620 public final FindProperty findProperty(final String key, final boolean deep) { 621 return findProperty(key, deep, false, this); 622 } 623 624 /** 625 * Low level property API (not using property descriptors) 626 * <p> 627 * Find a property in the prototype hierarchy. Note: this is not a good idea 628 * to override except as it was done in {@link WithObject}. 629 * If you have to, use 630 * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 631 * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 632 * overriding way to find array properties 633 * 634 * @see jdk.nashorn.internal.objects.NativeArray 635 * 636 * @param key Property key. 637 * @param deep Whether the search should look up proto chain. 638 * @param stopOnNonScope should a deep search stop on the first non-scope object? 639 * @param start the object on which the lookup was originally initiated 640 * 641 * @return FindPropertyData or null if not found. 642 */ 643 FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) { 644 // if doing deep search, stop search on the first non-scope object if asked to do so 645 if (stopOnNonScope && start != this && !isScope()) { 646 return null; 647 } 648 649 final PropertyMap selfMap = getMap(); 650 final Property property = selfMap.findProperty(key); 651 652 if (property != null) { 653 return new FindProperty(start, this, property); 654 } 655 656 if (deep) { 657 final ScriptObject myProto = getProto(); 658 if (myProto != null) { 659 return myProto.findProperty(key, deep, stopOnNonScope, start); 660 } 661 } 662 663 return null; 664 } 665 666 /** 667 * Add a new property to the object. 668 * <p> 669 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 670 * 671 * @param key Property key. 672 * @param propertyFlags Property flags. 673 * @param getter Property getter, or null if not defined 674 * @param setter Property setter, or null if not defined 675 * 676 * @return New property. 677 */ 678 public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 679 return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter)); 680 } 681 682 /** 683 * Add a new property to the object. 684 * <p> 685 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 686 * 687 * @param key Property key. 688 * @param propertyFlags Property flags. 689 * @param value Value of property 690 * 691 * @return New property. 692 */ 693 public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) { 694 final Property property = addSpillProperty(key, propertyFlags); 695 property.setObjectValue(this, this, value, false); 696 return property; 697 } 698 699 /** 700 * Add a new property to the object. 701 * <p> 702 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 703 * 704 * @param newProperty property to add 705 * 706 * @return New property. 707 */ 708 public final Property addOwnProperty(final Property newProperty) { 709 PropertyMap oldMap = getMap(); 710 711 while (true) { 712 final PropertyMap newMap = oldMap.addProperty(newProperty); 713 714 if (!compareAndSetMap(oldMap, newMap)) { 715 oldMap = getMap(); 716 final Property oldProperty = oldMap.findProperty(newProperty.getKey()); 717 718 if (oldProperty != null) { 719 return oldProperty; 720 } 721 } else { 722 return newProperty; 723 } 724 } 725 } 726 727 private void erasePropertyValue(final Property property) { 728 // Erase the property field value with undefined. If the property is defined 729 // by user-defined accessors, we don't want to call the setter!! 730 if (!(property instanceof UserAccessorProperty)) { 731 property.setObjectValue(this, this, UNDEFINED, false); 732 } 733 } 734 735 /** 736 * Delete a property from the object. 737 * 738 * @param property Property to delete. 739 * 740 * @return true if deleted. 741 */ 742 public final boolean deleteOwnProperty(final Property property) { 743 erasePropertyValue(property); 744 PropertyMap oldMap = getMap(); 745 746 while (true) { 747 final PropertyMap newMap = oldMap.deleteProperty(property); 748 749 if (newMap == null) { 750 return false; 751 } 752 753 if (!compareAndSetMap(oldMap, newMap)) { 754 oldMap = getMap(); 755 } else { 756 // delete getter and setter function references so that we don't leak 757 if (property instanceof UserAccessorProperty) { 758 final UserAccessorProperty uc = (UserAccessorProperty) property; 759 setSpill(uc.getGetterSlot(), null); 760 setSpill(uc.getSetterSlot(), null); 761 } 762 return true; 763 } 764 } 765 } 766 767 /** 768 * Modify a property in the object 769 * 770 * @param oldProperty property to modify 771 * @param propertyFlags new property flags 772 * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 773 * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 774 * 775 * @return new property 776 */ 777 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 778 Property newProperty; 779 if (oldProperty instanceof UserAccessorProperty) { 780 // re-use the slots of the old user accessor property. 781 final UserAccessorProperty uc = (UserAccessorProperty) oldProperty; 782 783 int getterSlot = uc.getGetterSlot(); 784 // clear the old getter and set the new getter 785 setSpill(getterSlot, getter); 786 // if getter function is null, flag the slot to be negative (less by 1) 787 if (getter == null) { 788 getterSlot = -getterSlot - 1; 789 } 790 791 int setterSlot = uc.getSetterSlot(); 792 // clear the old setter and set the new setter 793 setSpill(setterSlot, setter); 794 // if setter function is null, flag the slot to be negative (less by 1) 795 if (setter == null) { 796 setterSlot = -setterSlot - 1; 797 } 798 799 newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot); 800 // if just flipping getter and setter with new functions, no need to change property or map 801 if (oldProperty.equals(newProperty)) { 802 return oldProperty; 803 } 804 } else { 805 // erase old property value and create new user accessor property 806 erasePropertyValue(oldProperty); 807 newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter); 808 } 809 810 notifyPropertyModified(this, oldProperty, newProperty); 811 812 return modifyOwnProperty(oldProperty, newProperty); 813 } 814 815 /** 816 * Modify a property in the object 817 * 818 * @param oldProperty property to modify 819 * @param propertyFlags new property flags 820 * 821 * @return new property 822 */ 823 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) { 824 return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags)); 825 } 826 827 /** 828 * Modify a property in the object, replacing a property with a new one 829 * 830 * @param oldProperty property to replace 831 * @param newProperty property to replace it with 832 * 833 * @return new property 834 */ 835 private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) { 836 assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key"; 837 838 PropertyMap oldMap = getMap(); 839 840 while (true) { 841 final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty); 842 843 if (!compareAndSetMap(oldMap, newMap)) { 844 oldMap = getMap(); 845 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey()); 846 847 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) { 848 return oldPropertyLookup; 849 } 850 } else { 851 return newProperty; 852 } 853 } 854 } 855 856 /** 857 * Update getter and setter in an object literal. 858 * 859 * @param key Property key. 860 * @param getter {@link UserAccessorProperty} defined getter, or null if none 861 * @param setter {@link UserAccessorProperty} defined setter, or null if none 862 */ 863 public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) { 864 final Property oldProperty = getMap().findProperty(key); 865 if (oldProperty != null) { 866 final UserAccessorProperty newProperty = newUserAccessors(oldProperty.getKey(), oldProperty.getFlags(), getter, setter); 867 modifyOwnProperty(oldProperty, newProperty); 868 } else { 869 final UserAccessorProperty newProperty = newUserAccessors(key, 0, getter, setter); 870 addOwnProperty(newProperty); 871 } 872 } 873 874 private static int getIntValue(final FindProperty find) { 875 final MethodHandle getter = find.getGetter(int.class); 876 if (getter != null) { 877 try { 878 return (int)getter.invokeExact((Object)find.getGetterReceiver()); 879 } catch (final Error|RuntimeException e) { 880 throw e; 881 } catch (final Throwable e) { 882 throw new RuntimeException(e); 883 } 884 } 885 886 return ObjectClassGenerator.UNDEFINED_INT; 887 } 888 889 private static long getLongValue(final FindProperty find) { 890 final MethodHandle getter = find.getGetter(long.class); 891 if (getter != null) { 892 try { 893 return (long)getter.invokeExact((Object)find.getGetterReceiver()); 894 } catch (final Error|RuntimeException e) { 895 throw e; 896 } catch (final Throwable e) { 897 throw new RuntimeException(e); 898 } 899 } 900 901 return ObjectClassGenerator.UNDEFINED_LONG; 902 } 903 904 private static double getDoubleValue(final FindProperty find) { 905 final MethodHandle getter = find.getGetter(double.class); 906 if (getter != null) { 907 try { 908 return (double)getter.invokeExact((Object)find.getGetterReceiver()); 909 } catch (final Error|RuntimeException e) { 910 throw e; 911 } catch (final Throwable e) { 912 throw new RuntimeException(e); 913 } 914 } 915 916 return ObjectClassGenerator.UNDEFINED_DOUBLE; 917 } 918 919 /** 920 * Get the object value of a property 921 * 922 * @param find {@link FindProperty} lookup result 923 * 924 * @return the value of the property 925 */ 926 protected static Object getObjectValue(final FindProperty find) { 927 return find.getObjectValue(); 928 } 929 930 /** 931 * Return methodHandle of value function for call. 932 * 933 * @param find data from find property. 934 * @param type method type of function. 935 * @param bindName null or name to bind to second argument (property not found method.) 936 * 937 * @return value of property as a MethodHandle or null. 938 */ 939 protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) { 940 return getCallMethodHandle(getObjectValue(find), type, bindName); 941 } 942 943 /** 944 * Return methodHandle of value function for call. 945 * 946 * @param value value of receiver, it not a {@link ScriptFunction} this will return null. 947 * @param type method type of function. 948 * @param bindName null or name to bind to second argument (property not found method.) 949 * 950 * @return value of property as a MethodHandle or null. 951 */ 952 protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) { 953 return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null; 954 } 955 956 /** 957 * Get value using found property. 958 * 959 * @param property Found property. 960 * 961 * @return Value of property. 962 */ 963 public final Object getWithProperty(final Property property) { 964 return getObjectValue(new FindProperty(this, this, property)); 965 } 966 967 /** 968 * Get a property given a key 969 * 970 * @param key property key 971 * 972 * @return property for key 973 */ 974 public final Property getProperty(final String key) { 975 return getMap().findProperty(key); 976 } 977 978 static String convertKey(final Object key) { 979 return (key instanceof String) ? (String)key : JSType.toString(key); 980 } 981 982 /** 983 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 984 * Used for argument access in a vararg function using parameter name. 985 * Returns the argument at a given key (index) 986 * 987 * @param key argument index 988 * 989 * @return the argument at the given position, or undefined if not present 990 */ 991 public Object getArgument(final int key) { 992 return get(key); 993 } 994 995 /** 996 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 997 * Used for argument access in a vararg function using parameter name. 998 * Returns the argument at a given key (index) 999 * 1000 * @param key argument index 1001 * @param value the value to write at the given index 1002 */ 1003 public void setArgument(final int key, final Object value) { 1004 set(key, value, false); 1005 } 1006 1007 /** 1008 * Return true if the script object context is strict 1009 * @return true if strict context 1010 */ 1011 public final boolean isStrictContext() { 1012 return getContext()._strict; 1013 } 1014 1015 /** 1016 * Return the current context from the object's map. 1017 * @return Current context. 1018 */ 1019 protected final Context getContext() { 1020 if (context == null) { 1021 context = Context.fromClass(getClass()); 1022 } 1023 return context; 1024 } 1025 1026 /** 1027 * Return the map of an object. 1028 * @return PropertyMap object. 1029 */ 1030 public final PropertyMap getMap() { 1031 return map; 1032 } 1033 1034 /** 1035 * Set the initial map. 1036 * @param map Initial map. 1037 */ 1038 public final void setMap(final PropertyMap map) { 1039 this.map = map; 1040 } 1041 1042 /** 1043 * Conditionally set the new map if the old map is the same. 1044 * @param oldMap Map prior to manipulation. 1045 * @param newMap Replacement map. 1046 * @return true if the operation succeeded. 1047 */ 1048 protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) { 1049 final boolean update = oldMap == this.map; 1050 1051 if (update) { 1052 this.map = newMap; 1053 } 1054 1055 return update; 1056 } 1057 1058 /** 1059 * Return the __proto__ of an object. 1060 * @return __proto__ object. 1061 */ 1062 public final ScriptObject getProto() { 1063 return proto; 1064 } 1065 1066 /** 1067 * Set the __proto__ of an object. 1068 * @param newProto new __proto__ to set. 1069 */ 1070 public synchronized final void setProto(final ScriptObject newProto) { 1071 final ScriptObject oldProto = proto; 1072 map = map.changeProto(oldProto, newProto); 1073 1074 if (newProto != null) { 1075 newProto.setIsPrototype(); 1076 } 1077 1078 proto = newProto; 1079 1080 if (isPrototype()) { 1081 if (oldProto != null) { 1082 oldProto.removePropertyListener(this); 1083 } 1084 1085 if (newProto != null) { 1086 newProto.addPropertyListener(this); 1087 } 1088 } 1089 } 1090 1091 /** 1092 * Set the __proto__ of an object with checks. 1093 * @param newProto Prototype to set. 1094 */ 1095 public final void setProtoCheck(final Object newProto) { 1096 if (newProto == null || newProto instanceof ScriptObject) { 1097 setProto((ScriptObject)newProto); 1098 } else { 1099 final ScriptObject global = Context.getGlobalTrusted(); 1100 final Object newProtoObject = JSType.toScriptObject(global, newProto); 1101 1102 if (newProtoObject instanceof ScriptObject) { 1103 setProto((ScriptObject)newProtoObject); 1104 } else { 1105 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); 1106 } 1107 } 1108 } 1109 1110 /** 1111 * return an array of own property keys associated with the object. 1112 * 1113 * @param all True if to include non-enumerable keys. 1114 * @return Array of keys. 1115 */ 1116 public String[] getOwnKeys(final boolean all) { 1117 final List<Object> keys = new ArrayList<>(); 1118 final PropertyMap selfMap = this.getMap(); 1119 1120 final ArrayData array = getArray(); 1121 final long length = array.length(); 1122 1123 for (long i = 0; i < length; i = array.nextIndex(i)) { 1124 if (array.has((int)i)) { 1125 keys.add(JSType.toString(i)); 1126 } 1127 } 1128 1129 for (final Property property : selfMap.getProperties()) { 1130 if (all || property.isEnumerable()) { 1131 keys.add(property.getKey()); 1132 } 1133 } 1134 1135 return keys.toArray(new String[keys.size()]); 1136 } 1137 1138 /** 1139 * Check if this ScriptObject has array entries. This means that someone has 1140 * set values with numeric keys in the object. 1141 * 1142 * Note: this can be O(n) up to the array length 1143 * 1144 * @return true if array entries exists. 1145 */ 1146 public boolean hasArrayEntries() { 1147 final ArrayData array = getArray(); 1148 final long length = array.length(); 1149 1150 for (long i = 0; i < length; i++) { 1151 if (array.has((int)i)) { 1152 return true; 1153 } 1154 } 1155 1156 return false; 1157 } 1158 1159 /** 1160 * Return the valid JavaScript type name descriptor 1161 * 1162 * @return "Object" 1163 */ 1164 public String getClassName() { 1165 return "Object"; 1166 } 1167 1168 /** 1169 * {@code length} is a well known property. This is its getter. 1170 * Note that this *may* be optimized by other classes 1171 * 1172 * @return length property value for this ScriptObject 1173 */ 1174 public Object getLength() { 1175 return get("length"); 1176 } 1177 1178 /** 1179 * Stateless toString for ScriptObjects. 1180 * 1181 * @return string description of this object, e.g. {@code [object Object]} 1182 */ 1183 public String safeToString() { 1184 return "[object " + getClassName() + "]"; 1185 } 1186 1187 /** 1188 * Return the default value of the object with a given preferred type hint. 1189 * The preferred type hints are String.class for type String, Number.class 1190 * for type Number. <p> 1191 * 1192 * A <code>hint</code> of null means "no hint". 1193 * 1194 * ECMA 8.12.8 [[DefaultValue]](hint) 1195 * 1196 * @param typeHint the preferred type hint 1197 * @return the default value 1198 */ 1199 public Object getDefaultValue(final Class<?> typeHint) { 1200 // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and 1201 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts 1202 // are being executed in a long-running program, we move the code and their associated dynamic call sites 1203 // (Global.TO_STRING and Global.VALUE_OF) into per-context code. 1204 return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint); 1205 } 1206 1207 /** 1208 * Checking whether a script object is an instance of another. Used 1209 * in {@link ScriptFunction} for hasInstance implementation, walks 1210 * the proto chain 1211 * 1212 * @param instance instace to check 1213 * @return true if 'instance' is an instance of this object 1214 */ 1215 public boolean isInstance(final ScriptObject instance) { 1216 return false; 1217 } 1218 1219 /** 1220 * Flag this ScriptObject as non extensible 1221 * 1222 * @return the object after being made non extensible 1223 */ 1224 public ScriptObject preventExtensions() { 1225 PropertyMap oldMap = getMap(); 1226 1227 while (true) { 1228 final PropertyMap newMap = getMap().preventExtensions(); 1229 1230 if (!compareAndSetMap(oldMap, newMap)) { 1231 oldMap = getMap(); 1232 } else { 1233 return this; 1234 } 1235 } 1236 } 1237 1238 /** 1239 * Check whether if an Object (not just a ScriptObject) represents JavaScript array 1240 * 1241 * @param obj object to check 1242 * 1243 * @return true if array 1244 */ 1245 public static boolean isArray(final Object obj) { 1246 return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray(); 1247 } 1248 1249 /** 1250 * Check if this ScriptObject is an array 1251 * @return true if array 1252 */ 1253 public final boolean isArray() { 1254 return (flags & IS_ARRAY) != 0; 1255 } 1256 1257 /** 1258 * Flag this ScriptObject as being an array 1259 */ 1260 public final void setIsArray() { 1261 flags |= IS_ARRAY; 1262 } 1263 1264 /** 1265 * Check if this ScriptObject is an {@code arguments} vector 1266 * @return true if arguments vector 1267 */ 1268 public final boolean isArguments() { 1269 return (flags & IS_ARGUMENTS) != 0; 1270 } 1271 1272 /** 1273 * Flag this ScriptObject as being an {@code arguments} vector 1274 */ 1275 public final void setIsArguments() { 1276 flags |= IS_ARGUMENTS; 1277 } 1278 1279 /** 1280 * Check if this object is a prototype 1281 * 1282 * @return {@code true} if is prototype 1283 */ 1284 public final boolean isPrototype() { 1285 return (flags & IS_PROTOTYPE) != 0; 1286 } 1287 1288 /** 1289 * Flag this object as having a prototype. 1290 */ 1291 public final void setIsPrototype() { 1292 if (proto != null && !isPrototype()) { 1293 proto.addPropertyListener(this); 1294 } 1295 flags |= IS_PROTOTYPE; 1296 } 1297 1298 /** 1299 * Check if this object has non-writable length property 1300 * 1301 * @return {@code true} if 'length' property is non-writable 1302 */ 1303 public final boolean isLengthNotWritable() { 1304 return (flags & IS_LENGTH_NOT_WRITABLE) != 0; 1305 } 1306 1307 /** 1308 * Flag this object as having non-writable length property 1309 */ 1310 public void setIsLengthNotWritable() { 1311 flags |= IS_LENGTH_NOT_WRITABLE; 1312 } 1313 1314 /** 1315 * Get the {@link ArrayData} for this ScriptObject if it is an array 1316 * @return array data 1317 */ 1318 public final ArrayData getArray() { 1319 return arrayData; 1320 } 1321 1322 /** 1323 * Set the {@link ArrayData} for this ScriptObject if it is to be an array 1324 * @param arrayData the array data 1325 */ 1326 public final void setArray(final ArrayData arrayData) { 1327 this.arrayData = arrayData; 1328 } 1329 1330 /** 1331 * Check if this ScriptObject is extensible 1332 * @return true if extensible 1333 */ 1334 public boolean isExtensible() { 1335 return getMap().isExtensible(); 1336 } 1337 1338 /** 1339 * ECMAScript 15.2.3.8 - seal implementation 1340 * @return the sealed ScriptObject 1341 */ 1342 public ScriptObject seal() { 1343 PropertyMap oldMap = getMap(); 1344 1345 while (true) { 1346 final PropertyMap newMap = getMap().seal(); 1347 1348 if (!compareAndSetMap(oldMap, newMap)) { 1349 oldMap = getMap(); 1350 } else { 1351 setArray(ArrayData.seal(getArray())); 1352 return this; 1353 } 1354 } 1355 } 1356 1357 /** 1358 * Check whether this ScriptObject is sealed 1359 * @return true if sealed 1360 */ 1361 public boolean isSealed() { 1362 return getMap().isSealed(); 1363 } 1364 1365 /** 1366 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject 1367 * @return the frozen ScriptObject 1368 */ 1369 public ScriptObject freeze() { 1370 PropertyMap oldMap = getMap(); 1371 1372 while (true) { 1373 final PropertyMap newMap = getMap().freeze(); 1374 1375 if (!compareAndSetMap(oldMap, newMap)) { 1376 oldMap = getMap(); 1377 } else { 1378 setArray(ArrayData.freeze(getArray())); 1379 return this; 1380 } 1381 } 1382 } 1383 1384 /** 1385 * Check whether this ScriptObject is frozen 1386 * @return true if frozen 1387 */ 1388 public boolean isFrozen() { 1389 return getMap().isFrozen(); 1390 } 1391 1392 1393 /** 1394 * Flag this ScriptObject as scope 1395 */ 1396 public final void setIsScope() { 1397 if (Context.DEBUG) { 1398 scopeCount++; 1399 } 1400 flags |= IS_SCOPE; 1401 } 1402 1403 /** 1404 * Check whether this ScriptObject is scope 1405 * @return true if scope 1406 */ 1407 public final boolean isScope() { 1408 return (flags & IS_SCOPE) != 0; 1409 } 1410 1411 /** 1412 * Clears the properties from a ScriptObject 1413 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1414 */ 1415 public void clear() { 1416 final boolean strict = isStrictContext(); 1417 final Iterator<String> iter = propertyIterator(); 1418 while (iter.hasNext()) { 1419 delete(iter.next(), strict); 1420 } 1421 } 1422 1423 /** 1424 * Checks if a property with a given key is present in a ScriptObject 1425 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1426 * 1427 * @param key the key to check for 1428 * @return true if a property with the given key exists, false otherwise 1429 */ 1430 public boolean containsKey(final Object key) { 1431 return has(key); 1432 } 1433 1434 /** 1435 * Checks if a property with a given value is present in a ScriptObject 1436 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1437 * 1438 * @param value value to check for 1439 * @return true if a property with the given value exists, false otherwise 1440 */ 1441 public boolean containsValue(final Object value) { 1442 final Iterator<Object> iter = valueIterator(); 1443 while (iter.hasNext()) { 1444 if (iter.next().equals(value)) { 1445 return true; 1446 } 1447 } 1448 return false; 1449 } 1450 1451 /** 1452 * Returns the set of {@literal <property, value>} entries that make up this 1453 * ScriptObject's properties 1454 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1455 * 1456 * @return an entry set of all the properties in this object 1457 */ 1458 public Set<Map.Entry<Object, Object>> entrySet() { 1459 final Iterator<String> iter = propertyIterator(); 1460 final Set<Map.Entry<Object, Object>> entries = new HashSet<>(); 1461 while (iter.hasNext()) { 1462 final Object key = iter.next(); 1463 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key))); 1464 } 1465 return Collections.unmodifiableSet(entries); 1466 } 1467 1468 /** 1469 * Check whether a ScriptObject contains no properties 1470 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1471 * 1472 * @return true if object has no properties 1473 */ 1474 public boolean isEmpty() { 1475 return !propertyIterator().hasNext(); 1476 } 1477 1478 /** 1479 * Return the set of keys (property names) for all properties 1480 * in this ScriptObject 1481 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1482 * 1483 * @return keySet of this ScriptObject 1484 */ 1485 public Set<Object> keySet() { 1486 final Iterator<String> iter = propertyIterator(); 1487 final Set<Object> keySet = new HashSet<>(); 1488 while (iter.hasNext()) { 1489 keySet.add(iter.next()); 1490 } 1491 return Collections.unmodifiableSet(keySet); 1492 } 1493 1494 /** 1495 * Put a property in the ScriptObject 1496 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1497 * 1498 * @param key property key 1499 * @param value property value 1500 * @return oldValue if property with same key existed already 1501 */ 1502 public Object put(final Object key, final Object value) { 1503 final Object oldValue = get(key); 1504 set(key, value, isStrictContext()); 1505 return oldValue; 1506 } 1507 1508 /** 1509 * Put several properties in the ScriptObject given a mapping 1510 * of their keys to their values 1511 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1512 * 1513 * @param otherMap a {@literal <key,value>} map of properties to add 1514 */ 1515 public void putAll(final Map<?, ?> otherMap) { 1516 final boolean strict = isStrictContext(); 1517 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) { 1518 set(entry.getKey(), entry.getValue(), strict); 1519 } 1520 } 1521 1522 /** 1523 * Remove a property from the ScriptObject. 1524 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1525 * 1526 * @param key the key of the property 1527 * @return the oldValue of the removed property 1528 */ 1529 public Object remove(final Object key) { 1530 final Object oldValue = get(key); 1531 delete(key, isStrictContext()); 1532 return oldValue; 1533 } 1534 1535 /** 1536 * Delete a property from the ScriptObject. 1537 * (to help ScriptObjectMirror implementation) 1538 * 1539 * @param key the key of the property 1540 * @return if the delete was successful or not 1541 */ 1542 public boolean delete(final Object key) { 1543 return delete(key, isStrictContext()); 1544 } 1545 1546 /** 1547 * Return the size of the ScriptObject - i.e. the number of properties 1548 * it contains 1549 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1550 * 1551 * @return number of properties in ScriptObject 1552 */ 1553 public int size() { 1554 int n = 0; 1555 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) { 1556 n++; 1557 } 1558 return n; 1559 } 1560 1561 /** 1562 * Return the values of the properties in the ScriptObject 1563 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1564 * 1565 * @return collection of values for the properties in this ScriptObject 1566 */ 1567 public Collection<Object> values() { 1568 final List<Object> values = new ArrayList<>(size()); 1569 final Iterator<Object> iter = valueIterator(); 1570 while (iter.hasNext()) { 1571 values.add(iter.next()); 1572 } 1573 return Collections.unmodifiableList(values); 1574 } 1575 1576 /** 1577 * Lookup method that, given a CallSiteDescriptor, looks up the target 1578 * MethodHandle and creates a GuardedInvocation 1579 * with the appropriate guard(s). 1580 * 1581 * @param desc call site descriptor 1582 * @param request the link request 1583 * 1584 * @return GuardedInvocation for the callsite 1585 */ 1586 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) { 1587 final int c = desc.getNameTokenCount(); 1588 // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem 1589 // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't 1590 // care about them, and just link to whatever is the first operation. 1591 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); 1592 // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself 1593 // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are 1594 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the 1595 // operation has an associated name or not. 1596 switch (operator) { 1597 case "getProp": 1598 case "getElem": 1599 case "getMethod": 1600 return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request); 1601 case "setProp": 1602 case "setElem": 1603 return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc); 1604 case "call": 1605 return findCallMethod(desc, request); 1606 case "new": 1607 return findNewMethod(desc); 1608 case "callMethod": 1609 return findCallMethodMethod(desc, request); 1610 default: 1611 return null; 1612 } 1613 } 1614 1615 /** 1616 * Find the appropriate New method for an invoke dynamic call. 1617 * 1618 * @param desc The invoke dynamic call site descriptor. 1619 * 1620 * @return GuardedInvocation to be invoked at call site. 1621 */ 1622 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { 1623 return notAFunction(); 1624 } 1625 1626 /** 1627 * Find the appropriate CALL method for an invoke dynamic call. 1628 * This generates "not a function" always 1629 * 1630 * @param desc the call site descriptor. 1631 * @param request the link request 1632 * 1633 * @return GuardedInvocation to be invoed at call site. 1634 */ 1635 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1636 return notAFunction(); 1637 } 1638 1639 private GuardedInvocation notAFunction() { 1640 throw typeError("not.a.function", ScriptRuntime.safeToString(this)); 1641 } 1642 1643 /** 1644 * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses 1645 * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another 1646 * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external 1647 * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle. 1648 * 1649 * @param desc the call site descriptor. 1650 * @param request the link request 1651 * 1652 * @return GuardedInvocation to be invoked at call site. 1653 */ 1654 protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1655 // R(P0, P1, ...) 1656 final MethodType callType = desc.getMethodType(); 1657 // use type Object(P0) for the getter 1658 final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0))); 1659 final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod"); 1660 1661 // Object(P0) => Object(P0, P1, ...) 1662 final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount())); 1663 // R(Object, P0, P1, ...) 1664 final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType())); 1665 // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...) 1666 return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard()); 1667 } 1668 1669 /** 1670 * Find the appropriate GET method for an invoke dynamic call. 1671 * 1672 * @param desc the call site descriptor 1673 * @param request the link request 1674 * @param operator operator for get: getProp, getMethod, getElem etc 1675 * 1676 * @return GuardedInvocation to be invoked at call site. 1677 */ 1678 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { 1679 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 1680 final FindProperty find = findProperty(name, true); 1681 1682 MethodHandle methodHandle; 1683 1684 if (find == null) { 1685 if ("getProp".equals(operator)) { 1686 return noSuchProperty(desc, request); 1687 } else if ("getMethod".equals(operator)) { 1688 return noSuchMethod(desc, request); 1689 } else if ("getElem".equals(operator)) { 1690 return createEmptyGetter(desc, name); 1691 } 1692 throw new AssertionError(); // never invoked with any other operation 1693 } 1694 1695 if (request.isCallSiteUnstable()) { 1696 return findMegaMorphicGetMethod(desc, name); 1697 } 1698 1699 final Class<?> returnType = desc.getMethodType().returnType(); 1700 final Property property = find.getProperty(); 1701 methodHandle = find.getGetter(returnType); 1702 1703 // getMap() is fine as we have the prototype switchpoint depending on where the property was found 1704 final MethodHandle guard = NashornGuards.getMapGuard(getMap()); 1705 1706 if (methodHandle != null) { 1707 assert methodHandle.type().returnType().equals(returnType); 1708 if (find.isSelf()) { 1709 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY && 1710 NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard); 1711 } 1712 1713 final ScriptObject prototype = find.getOwner(); 1714 1715 if (!property.hasGetterFunction()) { 1716 methodHandle = bindTo(methodHandle, prototype); 1717 } 1718 return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard); 1719 } 1720 1721 assert !NashornCallSiteDescriptor.isFastScope(desc); 1722 return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard); 1723 } 1724 1725 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) { 1726 final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class); 1727 final GuardedInvocation inv = findGetIndexMethod(mhType); 1728 1729 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 1730 } 1731 1732 /** 1733 * Find the appropriate GETINDEX method for an invoke dynamic call. 1734 * 1735 * @param desc the call site descriptor 1736 * @param request the link request 1737 * 1738 * @return GuardedInvocation to be invoked at call site. 1739 */ 1740 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1741 return findGetIndexMethod(desc.getMethodType()); 1742 } 1743 1744 /** 1745 * Find the appropriate GETINDEX method for an invoke dynamic call. 1746 * 1747 * @param callType the call site method type 1748 * @return GuardedInvocation to be invoked at call site. 1749 */ 1750 private static GuardedInvocation findGetIndexMethod(final MethodType callType) { 1751 final Class<?> returnClass = callType.returnType(); 1752 final Class<?> keyClass = callType.parameterType(1); 1753 1754 String name = "get"; 1755 if (returnClass.isPrimitive()) { 1756 //turn e.g. get with a double into getDouble 1757 final String returnTypeName = returnClass.getName(); 1758 name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length()); 1759 } 1760 1761 return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType)); 1762 } 1763 1764 private static MethodHandle getScriptObjectGuard(final MethodType type) { 1765 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(); 1766 } 1767 1768 /** 1769 * Find the appropriate SET method for an invoke dynamic call. 1770 * 1771 * @param desc the call site descriptor 1772 * @param request the link request 1773 * 1774 * @return GuardedInvocation to be invoked at call site. 1775 */ 1776 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1777 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 1778 if(request.isCallSiteUnstable()) { 1779 return findMegaMorphicSetMethod(desc, name); 1780 } 1781 1782 final boolean scope = isScope(); 1783 /* 1784 * If doing property set on a scope object, we should stop proto search on the first 1785 * non-scope object. Without this, for example, when assigning "toString" on global scope, 1786 * we'll end up assigning it on it's proto - which is Object.prototype.toString !! 1787 * 1788 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString 1789 */ 1790 FindProperty find = findProperty(name, true, scope, this); 1791 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors. 1792 if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { 1793 // We should still check if inherited data property is not writable 1794 if (isExtensible() && !find.getProperty().isWritable()) { 1795 return createEmptySetMethod(desc, "property.not.writable", false); 1796 } 1797 // Otherwise, forget the found property 1798 find = null; 1799 } 1800 1801 if (find != null) { 1802 if(!find.getProperty().isWritable()) { 1803 // Existing, non-writable property 1804 return createEmptySetMethod(desc, "property.not.writable", true); 1805 } 1806 } else if (!isExtensible()) { 1807 // Non-existing property on a non-extensible object 1808 return createEmptySetMethod(desc, "object.non.extensible", false); 1809 } 1810 1811 return new SetMethodCreator(this, find, desc).createGuardedInvocation(); 1812 } 1813 1814 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) { 1815 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 1816 if (NashornCallSiteDescriptor.isStrict(desc)) { 1817 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this))); 1818 } 1819 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc); 1820 final PropertyMap myMap = getMap(); 1821 return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap)); 1822 } 1823 1824 @SuppressWarnings("unused") 1825 private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable { 1826 final ScriptObject obj = (ScriptObject)self; 1827 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); 1828 if (!obj.isExtensible()) { 1829 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj)); 1830 } else if (obj.compareAndSetMap(oldMap, newMap)) { 1831 setter.invokeExact(self, value); 1832 } else { 1833 obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict); 1834 } 1835 } 1836 1837 @SuppressWarnings("unused") 1838 private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) { 1839 final ScriptObject obj = (ScriptObject)self; 1840 if (obj.trySetSpill(desc, oldMap, newMap, value)) { 1841 obj.spill[index] = value; 1842 } 1843 } 1844 1845 private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) { 1846 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); 1847 if (!isExtensible() && isStrict) { 1848 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this)); 1849 } else if (compareAndSetMap(oldMap, newMap)) { 1850 return true; 1851 } else { 1852 set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict); 1853 return false; 1854 } 1855 } 1856 1857 @SuppressWarnings("unused") 1858 private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) { 1859 final ScriptObject obj = (ScriptObject)self; 1860 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); 1861 1862 if (!obj.isExtensible()) { 1863 if (isStrict) { 1864 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj)); 1865 } 1866 } else if (obj.compareAndSetMap(oldMap, newMap)) { 1867 obj.spill = new Object[SPILL_RATE]; 1868 obj.spill[index] = value; 1869 } else { 1870 obj.set(desc.getNameToken(2), value, isStrict); 1871 } 1872 } 1873 1874 @SuppressWarnings("unused") 1875 private static void setSpillWithGrow(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final int newLength, final Object self, final Object value) { 1876 final ScriptObject obj = (ScriptObject)self; 1877 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); 1878 1879 if (!obj.isExtensible()) { 1880 if (isStrict) { 1881 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj)); 1882 } 1883 } else if (obj.compareAndSetMap(oldMap, newMap)) { 1884 final int oldLength = obj.spill.length; 1885 final Object[] newSpill = new Object[newLength]; 1886 System.arraycopy(obj.spill, 0, newSpill, 0, oldLength); 1887 obj.spill = newSpill; 1888 obj.spill[index] = value; 1889 } else { 1890 obj.set(desc.getNameToken(2), value, isStrict); 1891 } 1892 } 1893 1894 private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { 1895 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class); 1896 final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc)); 1897 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 1898 } 1899 1900 private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value 1901 return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc)); 1902 } 1903 1904 /** 1905 * Find the appropriate SETINDEX method for an invoke dynamic call. 1906 * 1907 * @param callType the method type at the call site 1908 * @param isStrict are we in strict mode? 1909 * 1910 * @return GuardedInvocation to be invoked at call site. 1911 */ 1912 private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) { 1913 assert callType.parameterCount() == 3; 1914 1915 final Class<?> keyClass = callType.parameterType(1); 1916 final Class<?> valueClass = callType.parameterType(2); 1917 1918 MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class); 1919 methodHandle = MH.insertArguments(methodHandle, 3, isStrict); 1920 1921 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType)); 1922 } 1923 1924 /** 1925 * Fall back if a function property is not found. 1926 * @param desc The call site descriptor 1927 * @param request the link request 1928 * @return GuardedInvocation to be invoked at call site. 1929 */ 1930 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1931 final String name = desc.getNameToken(2); 1932 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 1933 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc); 1934 1935 if (find == null) { 1936 return noSuchProperty(desc, request); 1937 } 1938 1939 final ScriptFunction func = (ScriptFunction)getObjectValue(find); 1940 final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this; 1941 // TODO: It'd be awesome if we could bind "name" without binding "this". 1942 return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class, 1943 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class), 1944 null, NashornGuards.getMapGuard(getMap())); 1945 } 1946 1947 /** 1948 * Fall back if a property is not found. 1949 * @param desc the call site descriptor. 1950 * @param request the link request 1951 * @return GuardedInvocation to be invoked at call site. 1952 */ 1953 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 1954 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 1955 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 1956 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); 1957 1958 if (find != null) { 1959 final ScriptFunction func = (ScriptFunction)getObjectValue(find); 1960 MethodHandle methodHandle = getCallMethodHandle(func, desc.getMethodType(), name); 1961 1962 if (methodHandle != null) { 1963 if (scopeAccess && func.isStrict()) { 1964 methodHandle = bindTo(methodHandle, UNDEFINED); 1965 } 1966 return new GuardedInvocation(methodHandle, 1967 find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null, 1968 getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func)); 1969 } 1970 } 1971 1972 if (scopeAccess) { 1973 throw referenceError("not.defined", name); 1974 } 1975 1976 return createEmptyGetter(desc, name); 1977 } 1978 /** 1979 * Invoke fall back if a property is not found. 1980 * @param name Name of property. 1981 * @return Result from call. 1982 */ 1983 private Object invokeNoSuchProperty(final String name) { 1984 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 1985 1986 if (find != null) { 1987 final Object func = getObjectValue(find); 1988 1989 if (func instanceof ScriptFunction) { 1990 return ScriptRuntime.apply((ScriptFunction)func, this, name); 1991 } 1992 } 1993 1994 return UNDEFINED; 1995 } 1996 1997 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) { 1998 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap())); 1999 } 2000 2001 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> { 2002 protected T[] values; 2003 protected final ScriptObject object; 2004 private int index; 2005 2006 ScriptObjectIterator(final ScriptObject object) { 2007 this.object = object; 2008 } 2009 2010 protected abstract void init(); 2011 2012 @Override 2013 public boolean hasNext() { 2014 if (values == null) { 2015 init(); 2016 } 2017 return index < values.length; 2018 } 2019 2020 @Override 2021 public T next() { 2022 if (values == null) { 2023 init(); 2024 } 2025 return values[index++]; 2026 } 2027 2028 @Override 2029 public void remove() { 2030 throw new UnsupportedOperationException(); 2031 } 2032 } 2033 2034 private static class KeyIterator extends ScriptObjectIterator<String> { 2035 KeyIterator(final ScriptObject object) { 2036 super(object); 2037 } 2038 2039 @Override 2040 protected void init() { 2041 final Set<String> keys = new LinkedHashSet<>(); 2042 for (ScriptObject self = object; self != null; self = self.getProto()) { 2043 keys.addAll(Arrays.asList(self.getOwnKeys(false))); 2044 } 2045 this.values = keys.toArray(new String[keys.size()]); 2046 } 2047 } 2048 2049 private static class ValueIterator extends ScriptObjectIterator<Object> { 2050 ValueIterator(final ScriptObject object) { 2051 super(object); 2052 } 2053 2054 @Override 2055 protected void init() { 2056 final ArrayList<Object> valueList = new ArrayList<>(); 2057 for (ScriptObject self = object; self != null; self = self.getProto()) { 2058 for (final String key : self.getOwnKeys(false)) { 2059 valueList.add(self.get(key)); 2060 } 2061 } 2062 this.values = valueList.toArray(new Object[valueList.size()]); 2063 } 2064 } 2065 2066 /** 2067 * Add a spill property for the given key. 2068 * @param key Property key. 2069 * @param propertyFlags Property flags. 2070 * @return Added property. 2071 */ 2072 private Property addSpillProperty(final String key, final int propertyFlags) { 2073 int fieldCount = getMap().getFieldCount(); 2074 int fieldMaximum = getMap().getFieldMaximum(); 2075 Property property; 2076 2077 if (fieldCount < fieldMaximum) { 2078 property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount); 2079 notifyPropertyAdded(this, property); 2080 property = addOwnProperty(property); 2081 } else { 2082 int i = getMap().getSpillLength(); 2083 property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i); 2084 notifyPropertyAdded(this, property); 2085 property = addOwnProperty(property); 2086 i = property.getSlot(); 2087 2088 final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE; 2089 2090 if (spill == null || newLength > spill.length) { 2091 final Object[] newSpill = new Object[newLength]; 2092 2093 if (spill != null) { 2094 System.arraycopy(spill, 0, newSpill, 0, spill.length); 2095 } 2096 2097 spill = newSpill; 2098 } 2099 } 2100 2101 return property; 2102 } 2103 2104 2105 /** 2106 * Add a spill entry for the given key. 2107 * @param key Property key. 2108 * @return Setter method handle. 2109 */ 2110 MethodHandle addSpill(final String key) { 2111 final Property spillProperty = addSpillProperty(key, 0); 2112 final Class<?> type = Object.class; 2113 return spillProperty.getSetter(type, getMap()); //TODO specfields 2114 } 2115 2116 /** 2117 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2118 * fewer parameters than declared and other things that JavaScript allows. This might involve 2119 * creating collectors. 2120 * 2121 * @param methodHandle method handle for invoke 2122 * @param callType type of the call 2123 * 2124 * @return method handle with adjusted arguments 2125 */ 2126 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) { 2127 return pairArguments(methodHandle, callType, null); 2128 } 2129 2130 /** 2131 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2132 * fewer parameters than declared and other things that JavaScript allows. This might involve 2133 * creating collectors. 2134 * 2135 * Make sure arguments are paired correctly. 2136 * @param methodHandle MethodHandle to adjust. 2137 * @param callType MethodType of the call site. 2138 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the 2139 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a 2140 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites 2141 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters. 2142 * 2143 * @return method handle with adjusted arguments 2144 */ 2145 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) { 2146 2147 final MethodType methodType = methodHandle.type(); 2148 if (methodType.equals(callType)) { 2149 return methodHandle; 2150 } 2151 2152 final int parameterCount = methodType.parameterCount(); 2153 final int callCount = callType.parameterCount(); 2154 2155 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray(); 2156 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 && 2157 callType.parameterType(callCount - 1).isArray()); 2158 2159 if (callCount < parameterCount) { 2160 final int missingArgs = parameterCount - callCount; 2161 final Object[] fillers = new Object[missingArgs]; 2162 2163 Arrays.fill(fillers, UNDEFINED); 2164 2165 if (isCalleeVarArg) { 2166 fillers[missingArgs - 1] = new Object[0]; 2167 } 2168 2169 return MH.insertArguments( 2170 methodHandle, 2171 parameterCount - missingArgs, 2172 fillers); 2173 } 2174 2175 if (isCalleeVarArg) { 2176 return isCallerVarArg ? 2177 methodHandle : 2178 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1); 2179 } 2180 2181 if (isCallerVarArg) { 2182 final int spreadArgs = parameterCount - callCount + 1; 2183 return MH.filterArguments( 2184 MH.asSpreader( 2185 methodHandle, 2186 Object[].class, 2187 spreadArgs), 2188 callCount - 1, 2189 MH.insertArguments( 2190 TRUNCATINGFILTER, 2191 0, 2192 spreadArgs) 2193 ); 2194 } 2195 2196 if (callCount > parameterCount) { 2197 final int discardedArgs = callCount - parameterCount; 2198 2199 final Class<?>[] discards = new Class<?>[discardedArgs]; 2200 Arrays.fill(discards, Object.class); 2201 2202 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards); 2203 } 2204 2205 return methodHandle; 2206 } 2207 2208 @SuppressWarnings("unused") 2209 private static Object[] truncatingFilter(final int n, final Object[] array) { 2210 final int length = array == null ? 0 : array.length; 2211 if (n == length) { 2212 return array == null ? new Object[0] : array; 2213 } 2214 2215 final Object[] newArray = new Object[n]; 2216 2217 if (array != null) { 2218 for (int i = 0; i < n && i < length; i++) { 2219 newArray[i] = array[i]; 2220 } 2221 } 2222 2223 if (length < n) { 2224 final Object fill = UNDEFINED; 2225 2226 for (int i = length; i < n; i++) { 2227 newArray[i] = fill; 2228 } 2229 } 2230 2231 return newArray; 2232 } 2233 2234 /** 2235 * Numeric length setter for length property 2236 * 2237 * @param newLength new length to set 2238 */ 2239 public final void setLength(final long newLength) { 2240 final long arrayLength = getArray().length(); 2241 if (newLength == arrayLength) { 2242 return; 2243 } 2244 2245 final boolean isStrict = isStrictContext(); 2246 2247 if (newLength > arrayLength) { 2248 setArray(getArray().ensure(newLength - 1)); 2249 if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) { 2250 setArray(getArray().delete(arrayLength, (newLength - 1))); 2251 } 2252 return; 2253 } 2254 2255 if (newLength < arrayLength) { 2256 setArray(getArray().shrink(newLength)); 2257 getArray().setLength(newLength); 2258 } 2259 } 2260 2261 private int getInt(final int index, final String key) { 2262 if (ArrayIndex.isValidArrayIndex(index)) { 2263 for (ScriptObject object = this; ; ) { 2264 final FindProperty find = object.findProperty(key, false, false, this); 2265 2266 if (find != null) { 2267 return getIntValue(find); 2268 } 2269 2270 if ((object = object.getProto()) == null) { 2271 break; 2272 } 2273 2274 final ArrayData array = object.getArray(); 2275 2276 if (array.has(index)) { 2277 return array.getInt(index); 2278 } 2279 } 2280 } else { 2281 final FindProperty find = findProperty(key, true); 2282 2283 if (find != null) { 2284 return getIntValue(find); 2285 } 2286 } 2287 2288 return JSType.toInt32(invokeNoSuchProperty(key)); 2289 } 2290 2291 @Override 2292 public int getInt(final Object key) { 2293 final int index = ArrayIndex.getArrayIndex(key); 2294 final ArrayData array = getArray(); 2295 2296 if (array.has(index)) { 2297 return array.getInt(index); 2298 } 2299 2300 return getInt(index, convertKey(key)); 2301 } 2302 2303 @Override 2304 public int getInt(final double key) { 2305 final int index = ArrayIndex.getArrayIndex(key); 2306 final ArrayData array = getArray(); 2307 2308 if (array.has(index)) { 2309 return array.getInt(index); 2310 } 2311 2312 return getInt(index, convertKey(key)); 2313 } 2314 2315 @Override 2316 public int getInt(final long key) { 2317 final int index = ArrayIndex.getArrayIndex(key); 2318 final ArrayData array = getArray(); 2319 2320 if (array.has(index)) { 2321 return array.getInt(index); 2322 } 2323 2324 return getInt(index, convertKey(key)); 2325 } 2326 2327 @Override 2328 public int getInt(final int key) { 2329 final ArrayData array = getArray(); 2330 2331 if (array.has(key)) { 2332 return array.getInt(key); 2333 } 2334 2335 return getInt(key, convertKey(key)); 2336 } 2337 2338 private long getLong(final int index, final String key) { 2339 if (ArrayIndex.isValidArrayIndex(index)) { 2340 for (ScriptObject object = this; ; ) { 2341 final FindProperty find = object.findProperty(key, false, false, this); 2342 2343 if (find != null) { 2344 return getLongValue(find); 2345 } 2346 2347 if ((object = object.getProto()) == null) { 2348 break; 2349 } 2350 2351 final ArrayData array = object.getArray(); 2352 2353 if (array.has(index)) { 2354 return array.getLong(index); 2355 } 2356 } 2357 } else { 2358 final FindProperty find = findProperty(key, true); 2359 2360 if (find != null) { 2361 return getLongValue(find); 2362 } 2363 } 2364 2365 return JSType.toLong(invokeNoSuchProperty(key)); 2366 } 2367 2368 @Override 2369 public long getLong(final Object key) { 2370 final int index = ArrayIndex.getArrayIndex(key); 2371 final ArrayData array = getArray(); 2372 2373 if (array.has(index)) { 2374 return array.getLong(index); 2375 } 2376 2377 return getLong(index, convertKey(key)); 2378 } 2379 2380 @Override 2381 public long getLong(final double key) { 2382 final int index = ArrayIndex.getArrayIndex(key); 2383 final ArrayData array = getArray(); 2384 2385 if (array.has(index)) { 2386 return array.getLong(index); 2387 } 2388 2389 return getLong(index, convertKey(key)); 2390 } 2391 2392 @Override 2393 public long getLong(final long key) { 2394 final int index = ArrayIndex.getArrayIndex(key); 2395 final ArrayData array = getArray(); 2396 2397 if (array.has(index)) { 2398 return array.getLong(index); 2399 } 2400 2401 return getLong(index, convertKey(key)); 2402 } 2403 2404 @Override 2405 public long getLong(final int key) { 2406 final ArrayData array = getArray(); 2407 2408 if (array.has(key)) { 2409 return array.getLong(key); 2410 } 2411 2412 return getLong(key, convertKey(key)); 2413 } 2414 2415 private double getDouble(final int index, final String key) { 2416 if (ArrayIndex.isValidArrayIndex(index)) { 2417 for (ScriptObject object = this; ; ) { 2418 final FindProperty find = object.findProperty(key, false, false, this); 2419 2420 if (find != null) { 2421 return getDoubleValue(find); 2422 } 2423 2424 if ((object = object.getProto()) == null) { 2425 break; 2426 } 2427 2428 final ArrayData array = object.getArray(); 2429 2430 if (array.has(index)) { 2431 return array.getDouble(index); 2432 } 2433 } 2434 } else { 2435 final FindProperty find = findProperty(key, true); 2436 2437 if (find != null) { 2438 return getDoubleValue(find); 2439 } 2440 } 2441 2442 return JSType.toNumber(invokeNoSuchProperty(key)); 2443 } 2444 2445 @Override 2446 public double getDouble(final Object key) { 2447 final int index = ArrayIndex.getArrayIndex(key); 2448 final ArrayData array = getArray(); 2449 2450 if (array.has(index)) { 2451 return array.getDouble(index); 2452 } 2453 2454 return getDouble(index, convertKey(key)); 2455 } 2456 2457 @Override 2458 public double getDouble(final double key) { 2459 final int index = ArrayIndex.getArrayIndex(key); 2460 final ArrayData array = getArray(); 2461 2462 if (array.has(index)) { 2463 return array.getDouble(index); 2464 } 2465 2466 return getDouble(index, convertKey(key)); 2467 } 2468 2469 @Override 2470 public double getDouble(final long key) { 2471 final int index = ArrayIndex.getArrayIndex(key); 2472 final ArrayData array = getArray(); 2473 2474 if (array.has(index)) { 2475 return array.getDouble(index); 2476 } 2477 2478 return getDouble(index, convertKey(key)); 2479 } 2480 2481 @Override 2482 public double getDouble(final int key) { 2483 final ArrayData array = getArray(); 2484 2485 if (array.has(key)) { 2486 return array.getDouble(key); 2487 } 2488 2489 return getDouble(key, convertKey(key)); 2490 } 2491 2492 private Object get(final int index, final String key) { 2493 if (ArrayIndex.isValidArrayIndex(index)) { 2494 for (ScriptObject object = this; ; ) { 2495 final FindProperty find = object.findProperty(key, false, false, this); 2496 2497 if (find != null) { 2498 return getObjectValue(find); 2499 } 2500 2501 if ((object = object.getProto()) == null) { 2502 break; 2503 } 2504 2505 final ArrayData array = object.getArray(); 2506 2507 if (array.has(index)) { 2508 return array.getObject(index); 2509 } 2510 } 2511 } else { 2512 final FindProperty find = findProperty(key, true); 2513 2514 if (find != null) { 2515 return getObjectValue(find); 2516 } 2517 } 2518 2519 return invokeNoSuchProperty(key); 2520 } 2521 2522 @Override 2523 public Object get(final Object key) { 2524 final int index = ArrayIndex.getArrayIndex(key); 2525 final ArrayData array = getArray(); 2526 2527 if (array.has(index)) { 2528 return array.getObject(index); 2529 } 2530 2531 return get(index, convertKey(key)); 2532 } 2533 2534 @Override 2535 public Object get(final double key) { 2536 final int index = ArrayIndex.getArrayIndex(key); 2537 final ArrayData array = getArray(); 2538 2539 if (array.has(index)) { 2540 return array.getObject(index); 2541 } 2542 2543 return get(index, convertKey(key)); 2544 } 2545 2546 @Override 2547 public Object get(final long key) { 2548 final int index = ArrayIndex.getArrayIndex(key); 2549 final ArrayData array = getArray(); 2550 2551 if (array.has(index)) { 2552 return array.getObject(index); 2553 } 2554 2555 return get(index, convertKey(key)); 2556 } 2557 2558 @Override 2559 public Object get(final int key) { 2560 final ArrayData array = getArray(); 2561 2562 if (array.has(key)) { 2563 return array.getObject(key); 2564 } 2565 2566 return get(key, convertKey(key)); 2567 } 2568 2569 /** 2570 * Handle when an array doesn't have a slot - possibly grow and/or convert array. 2571 * 2572 * @param index key as index 2573 * @param value element value 2574 * @param strict are we in strict mode 2575 */ 2576 private void doesNotHave(final int index, final Object value, final boolean strict) { 2577 final long oldLength = getArray().length(); 2578 final long longIndex = index & JSType.MAX_UINT; 2579 2580 if (!getArray().has(index)) { 2581 final String key = convertKey(longIndex); 2582 final FindProperty find = findProperty(key, true); 2583 2584 if (find != null) { 2585 setObject(find, strict, key, value); 2586 return; 2587 } 2588 } 2589 2590 if (longIndex >= oldLength) { 2591 if (!isExtensible()) { 2592 if (strict) { 2593 throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this)); 2594 } 2595 return; 2596 } 2597 setArray(getArray().ensure(longIndex)); 2598 } 2599 2600 if (value instanceof Integer) { 2601 setArray(getArray().set(index, (int)value, strict)); 2602 } else if (value instanceof Long) { 2603 setArray(getArray().set(index, (long)value, strict)); 2604 } else if (value instanceof Double) { 2605 setArray(getArray().set(index, (double)value, strict)); 2606 } else { 2607 setArray(getArray().set(index, value, strict)); 2608 } 2609 2610 if (longIndex > oldLength) { 2611 ArrayData array = getArray(); 2612 2613 if (array.canDelete(oldLength, (longIndex - 1), strict)) { 2614 array = array.delete(oldLength, (longIndex - 1)); 2615 } 2616 2617 setArray(array); 2618 } 2619 } 2620 2621 /** 2622 * This is the most generic of all Object setters. Most of the others use this in some form. 2623 * TODO: should be further specialized 2624 * 2625 * @param find found property 2626 * @param strict are we in strict mode 2627 * @param key property key 2628 * @param value property value 2629 */ 2630 public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) { 2631 FindProperty f = find; 2632 2633 if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { 2634 f = null; 2635 } 2636 2637 if (f != null) { 2638 if (!f.getProperty().isWritable()) { 2639 if (strict) { 2640 throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this)); 2641 } 2642 2643 return; 2644 } 2645 2646 f.setObjectValue(value, strict); 2647 2648 } else if (!isExtensible()) { 2649 if (strict) { 2650 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); 2651 } 2652 } else { 2653 spill(key, value); 2654 } 2655 } 2656 2657 private void spill(final String key, final Object value) { 2658 addSpillProperty(key, 0).setObjectValue(this, this, value, false); 2659 } 2660 2661 2662 @Override 2663 public void set(final Object key, final int value, final boolean strict) { 2664 final int index = ArrayIndex.getArrayIndex(key); 2665 2666 if (ArrayIndex.isValidArrayIndex(index)) { 2667 if (getArray().has(index)) { 2668 setArray(getArray().set(index, value, strict)); 2669 } else { 2670 doesNotHave(index, value, strict); 2671 } 2672 2673 return; 2674 } 2675 2676 set(key, JSType.toObject(value), strict); 2677 } 2678 2679 @Override 2680 public void set(final Object key, final long value, final boolean strict) { 2681 final int index = ArrayIndex.getArrayIndex(key); 2682 2683 if (ArrayIndex.isValidArrayIndex(index)) { 2684 if (getArray().has(index)) { 2685 setArray(getArray().set(index, value, strict)); 2686 } else { 2687 doesNotHave(index, value, strict); 2688 } 2689 2690 return; 2691 } 2692 2693 set(key, JSType.toObject(value), strict); 2694 } 2695 2696 @Override 2697 public void set(final Object key, final double value, final boolean strict) { 2698 final int index = ArrayIndex.getArrayIndex(key); 2699 2700 if (ArrayIndex.isValidArrayIndex(index)) { 2701 if (getArray().has(index)) { 2702 setArray(getArray().set(index, value, strict)); 2703 } else { 2704 doesNotHave(index, value, strict); 2705 } 2706 2707 return; 2708 } 2709 2710 set(key, JSType.toObject(value), strict); 2711 } 2712 2713 @Override 2714 public void set(final Object key, final Object value, final boolean strict) { 2715 final int index = ArrayIndex.getArrayIndex(key); 2716 2717 if (ArrayIndex.isValidArrayIndex(index)) { 2718 if (getArray().has(index)) { 2719 setArray(getArray().set(index, value, strict)); 2720 } else { 2721 doesNotHave(index, value, strict); 2722 } 2723 2724 return; 2725 } 2726 2727 final String propName = convertKey(key); 2728 final FindProperty find = findProperty(propName, true); 2729 2730 setObject(find, strict, propName, value); 2731 } 2732 2733 @Override 2734 public void set(final double key, final int value, final boolean strict) { 2735 final int index = ArrayIndex.getArrayIndex(key); 2736 2737 if (ArrayIndex.isValidArrayIndex(index)) { 2738 if (getArray().has(index)) { 2739 setArray(getArray().set(index, value, strict)); 2740 } else { 2741 doesNotHave(index, value, strict); 2742 } 2743 2744 return; 2745 } 2746 2747 set(JSType.toObject(key), JSType.toObject(value), strict); 2748 } 2749 2750 @Override 2751 public void set(final double key, final long value, final boolean strict) { 2752 final int index = ArrayIndex.getArrayIndex(key); 2753 2754 if (ArrayIndex.isValidArrayIndex(index)) { 2755 if (getArray().has(index)) { 2756 setArray(getArray().set(index, value, strict)); 2757 } else { 2758 doesNotHave(index, value, strict); 2759 } 2760 2761 return; 2762 } 2763 2764 set(JSType.toObject(key), JSType.toObject(value), strict); 2765 } 2766 2767 @Override 2768 public void set(final double key, final double value, final boolean strict) { 2769 final int index = ArrayIndex.getArrayIndex(key); 2770 2771 if (ArrayIndex.isValidArrayIndex(index)) { 2772 if (getArray().has(index)) { 2773 setArray(getArray().set(index, value, strict)); 2774 } else { 2775 doesNotHave(index, value, strict); 2776 } 2777 2778 return; 2779 } 2780 2781 set(JSType.toObject(key), JSType.toObject(value), strict); 2782 } 2783 2784 @Override 2785 public void set(final double key, final Object value, final boolean strict) { 2786 final int index = ArrayIndex.getArrayIndex(key); 2787 2788 if (ArrayIndex.isValidArrayIndex(index)) { 2789 if (getArray().has(index)) { 2790 setArray(getArray().set(index, value, strict)); 2791 } else { 2792 doesNotHave(index, value, strict); 2793 } 2794 2795 return; 2796 } 2797 2798 set(JSType.toObject(key), value, strict); 2799 } 2800 2801 @Override 2802 public void set(final long key, final int value, final boolean strict) { 2803 final int index = ArrayIndex.getArrayIndex(key); 2804 2805 if (ArrayIndex.isValidArrayIndex(index)) { 2806 if (getArray().has(index)) { 2807 setArray(getArray().set(index, value, strict)); 2808 } else { 2809 doesNotHave(index, value, strict); 2810 } 2811 2812 return; 2813 } 2814 2815 set(JSType.toObject(key), JSType.toObject(value), strict); 2816 } 2817 2818 @Override 2819 public void set(final long key, final long value, final boolean strict) { 2820 final int index = ArrayIndex.getArrayIndex(key); 2821 2822 if (ArrayIndex.isValidArrayIndex(index)) { 2823 if (getArray().has(index)) { 2824 setArray(getArray().set(index, value, strict)); 2825 } else { 2826 doesNotHave(index, value, strict); 2827 } 2828 2829 return; 2830 } 2831 2832 set(JSType.toObject(key), JSType.toObject(value), strict); 2833 } 2834 2835 @Override 2836 public void set(final long key, final double value, final boolean strict) { 2837 final int index = ArrayIndex.getArrayIndex(key); 2838 2839 if (ArrayIndex.isValidArrayIndex(index)) { 2840 if (getArray().has(index)) { 2841 setArray(getArray().set(index, value, strict)); 2842 } else { 2843 doesNotHave(index, value, strict); 2844 } 2845 2846 return; 2847 } 2848 2849 set(JSType.toObject(key), JSType.toObject(value), strict); 2850 } 2851 2852 @Override 2853 public void set(final long key, final Object value, final boolean strict) { 2854 final int index = ArrayIndex.getArrayIndex(key); 2855 2856 if (ArrayIndex.isValidArrayIndex(index)) { 2857 if (getArray().has(index)) { 2858 setArray(getArray().set(index, value, strict)); 2859 } else { 2860 doesNotHave(index, value, strict); 2861 } 2862 2863 return; 2864 } 2865 2866 set(JSType.toObject(key), value, strict); 2867 } 2868 2869 @Override 2870 public void set(final int key, final int value, final boolean strict) { 2871 final int index = ArrayIndex.getArrayIndex(key); 2872 2873 if (ArrayIndex.isValidArrayIndex(index)) { 2874 if (getArray().has(index)) { 2875 setArray(getArray().set(index, value, strict)); 2876 } else { 2877 doesNotHave(index, value, strict); 2878 } 2879 2880 return; 2881 } 2882 2883 set(JSType.toObject(key), JSType.toObject(value), strict); 2884 } 2885 2886 @Override 2887 public void set(final int key, final long value, final boolean strict) { 2888 final int index = ArrayIndex.getArrayIndex(key); 2889 2890 if (ArrayIndex.isValidArrayIndex(index)) { 2891 if (getArray().has(index)) { 2892 setArray(getArray().set(index, value, strict)); 2893 } else { 2894 doesNotHave(index, value, strict); 2895 } 2896 2897 return; 2898 } 2899 2900 set(JSType.toObject(key), JSType.toObject(value), strict); 2901 } 2902 2903 @Override 2904 public void set(final int key, final double value, final boolean strict) { 2905 final int index = ArrayIndex.getArrayIndex(key); 2906 2907 if (ArrayIndex.isValidArrayIndex(index)) { 2908 if (getArray().has(index)) { 2909 setArray(getArray().set(index, value, strict)); 2910 } else { 2911 doesNotHave(index, value, strict); 2912 } 2913 2914 return; 2915 } 2916 2917 set(JSType.toObject(key), JSType.toObject(value), strict); 2918 } 2919 2920 @Override 2921 public void set(final int key, final Object value, final boolean strict) { 2922 final int index = ArrayIndex.getArrayIndex(key); 2923 2924 if (ArrayIndex.isValidArrayIndex(index)) { 2925 if (getArray().has(index)) { 2926 setArray(getArray().set(index, value, strict)); 2927 } else { 2928 doesNotHave(index, value, strict); 2929 } 2930 2931 return; 2932 } 2933 2934 set(JSType.toObject(key), value, strict); 2935 } 2936 2937 @Override 2938 public boolean has(final Object key) { 2939 final int index = ArrayIndex.getArrayIndex(key); 2940 2941 if (ArrayIndex.isValidArrayIndex(index)) { 2942 for (ScriptObject self = this; self != null; self = self.getProto()) { 2943 if (self.getArray().has(index)) { 2944 return true; 2945 } 2946 } 2947 } 2948 2949 final FindProperty find = findProperty(convertKey(key), true); 2950 2951 return find != null; 2952 } 2953 2954 @Override 2955 public boolean has(final double key) { 2956 final int index = ArrayIndex.getArrayIndex(key); 2957 2958 if (ArrayIndex.isValidArrayIndex(index)) { 2959 for (ScriptObject self = this; self != null; self = self.getProto()) { 2960 if (self.getArray().has(index)) { 2961 return true; 2962 } 2963 } 2964 } 2965 2966 final FindProperty find = findProperty(convertKey(key), true); 2967 2968 return find != null; 2969 } 2970 2971 @Override 2972 public boolean has(final long key) { 2973 final int index = ArrayIndex.getArrayIndex(key); 2974 2975 if (ArrayIndex.isValidArrayIndex(index)) { 2976 for (ScriptObject self = this; self != null; self = self.getProto()) { 2977 if (self.getArray().has(index)) { 2978 return true; 2979 } 2980 } 2981 } 2982 2983 final FindProperty find = findProperty(convertKey(key), true); 2984 2985 return find != null; 2986 } 2987 2988 @Override 2989 public boolean has(final int key) { 2990 final int index = ArrayIndex.getArrayIndex(key); 2991 2992 if (ArrayIndex.isValidArrayIndex(index)) { 2993 for (ScriptObject self = this; self != null; self = self.getProto()) { 2994 if (self.getArray().has(index)) { 2995 return true; 2996 } 2997 } 2998 } 2999 3000 final FindProperty find = findProperty(convertKey(key), true); 3001 3002 return find != null; 3003 } 3004 3005 @Override 3006 public boolean hasOwnProperty(final Object key) { 3007 final int index = ArrayIndex.getArrayIndex(key); 3008 3009 if (getArray().has(index)) { 3010 return true; 3011 } 3012 3013 final FindProperty find = findProperty(convertKey(key), false); 3014 3015 return find != null; 3016 } 3017 3018 @Override 3019 public boolean hasOwnProperty(final int key) { 3020 final int index = ArrayIndex.getArrayIndex(key); 3021 3022 if (getArray().has(index)) { 3023 return true; 3024 } 3025 3026 final FindProperty find = findProperty(convertKey(key), false); 3027 3028 return find != null; 3029 } 3030 3031 @Override 3032 public boolean hasOwnProperty(final long key) { 3033 final int index = ArrayIndex.getArrayIndex(key); 3034 3035 if (getArray().has(index)) { 3036 return true; 3037 } 3038 3039 final FindProperty find = findProperty(convertKey(key), false); 3040 3041 return find != null; 3042 } 3043 3044 @Override 3045 public boolean hasOwnProperty(final double key) { 3046 final int index = ArrayIndex.getArrayIndex(key); 3047 3048 if (getArray().has(index)) { 3049 return true; 3050 } 3051 3052 final FindProperty find = findProperty(convertKey(key), false); 3053 3054 return find != null; 3055 } 3056 3057 @Override 3058 public boolean delete(final int key, final boolean strict) { 3059 final int index = ArrayIndex.getArrayIndex(key); 3060 final ArrayData array = getArray(); 3061 3062 if (array.has(index)) { 3063 if (array.canDelete(index, strict)) { 3064 setArray(array.delete(index)); 3065 return true; 3066 } 3067 return false; 3068 } 3069 3070 return deleteObject(JSType.toObject(key), strict); 3071 } 3072 3073 @Override 3074 public boolean delete(final long key, final boolean strict) { 3075 final int index = ArrayIndex.getArrayIndex(key); 3076 final ArrayData array = getArray(); 3077 3078 if (array.has(index)) { 3079 if (array.canDelete(index, strict)) { 3080 setArray(array.delete(index)); 3081 return true; 3082 } 3083 return false; 3084 } 3085 3086 return deleteObject(JSType.toObject(key), strict); 3087 } 3088 3089 @Override 3090 public boolean delete(final double key, final boolean strict) { 3091 final int index = ArrayIndex.getArrayIndex(key); 3092 final ArrayData array = getArray(); 3093 3094 if (array.has(index)) { 3095 if (array.canDelete(index, strict)) { 3096 setArray(array.delete(index)); 3097 return true; 3098 } 3099 return false; 3100 } 3101 3102 return deleteObject(JSType.toObject(key), strict); 3103 } 3104 3105 @Override 3106 public boolean delete(final Object key, final boolean strict) { 3107 final int index = ArrayIndex.getArrayIndex(key); 3108 final ArrayData array = getArray(); 3109 3110 if (array.has(index)) { 3111 if (array.canDelete(index, strict)) { 3112 setArray(array.delete(index)); 3113 return true; 3114 } 3115 return false; 3116 } 3117 3118 return deleteObject(key, strict); 3119 } 3120 3121 private boolean deleteObject(final Object key, final boolean strict) { 3122 final String propName = convertKey(key); 3123 final FindProperty find = findProperty(propName, false); 3124 3125 if (find == null) { 3126 return true; 3127 } 3128 3129 if (!find.getProperty().isConfigurable()) { 3130 if (strict) { 3131 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this)); 3132 } 3133 return false; 3134 } 3135 3136 final Property prop = find.getProperty(); 3137 notifyPropertyDeleted(this, prop); 3138 deleteOwnProperty(prop); 3139 3140 return true; 3141 } 3142 3143 /* 3144 * Make a new UserAccessorProperty property. getter and setter functions are stored in 3145 * this ScriptObject and slot values are used in property object. 3146 */ 3147 private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 3148 int oldSpillLength = getMap().getSpillLength(); 3149 3150 int getterSlot = oldSpillLength++; 3151 setSpill(getterSlot, getter); 3152 // if getter function is null, flag the slot to be negative (less by 1) 3153 if (getter == null) { 3154 getterSlot = -getterSlot - 1; 3155 } 3156 3157 int setterSlot = oldSpillLength++; 3158 3159 setSpill(setterSlot, setter); 3160 // if setter function is null, flag the slot to be negative (less by 1) 3161 if (setter == null) { 3162 setterSlot = -setterSlot - 1; 3163 } 3164 3165 return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot); 3166 } 3167 3168 private void setSpill(final int slot, final Object value) { 3169 if (slot >= 0) { 3170 final int index = slot; 3171 if (spill == null) { 3172 // create new spill. 3173 spill = new Object[Math.max(index + 1, SPILL_RATE)]; 3174 } else if (index >= spill.length) { 3175 // grow spill as needed 3176 final Object[] newSpill = new Object[index + 1]; 3177 System.arraycopy(spill, 0, newSpill, 0, spill.length); 3178 spill = newSpill; 3179 } 3180 3181 spill[index] = value; 3182 } 3183 } 3184 3185 // user accessors are either stored in spill array slots 3186 // get the accessor value using slot number. Note that slot is spill array index. 3187 Object getSpill(final int slot) { 3188 final int index = slot; 3189 return (index < 0 || (index >= spill.length)) ? null : spill[index]; 3190 } 3191 3192 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 3193 final Class<?> own = ScriptObject.class; 3194 final MethodType mt = MH.type(rtype, types); 3195 try { 3196 return MH.findStatic(MethodHandles.lookup(), own, name, mt); 3197 } catch (final MethodHandleFactory.LookupException e) { 3198 return MH.findVirtual(MethodHandles.lookup(), own, name, mt); 3199 } 3200 } 3201 3202 private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) { 3203 return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func); 3204 } 3205 3206 @SuppressWarnings("unused") 3207 private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) { 3208 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3209 try { 3210 return getter.invokeExact(where) == func; 3211 } catch (final RuntimeException | Error e) { 3212 throw e; 3213 } catch (final Throwable t) { 3214 throw new RuntimeException(t); 3215 } 3216 } 3217 3218 return false; 3219 } 3220 3221 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ 3222 private static int count; 3223 3224 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */ 3225 private static int scopeCount; 3226 3227 /** 3228 * Get number of {@code ScriptObject} instances created. If not running in debug 3229 * mode this is always 0 3230 * 3231 * @return number of ScriptObjects created 3232 */ 3233 public static int getCount() { 3234 return count; 3235 } 3236 3237 /** 3238 * Get number of scope {@code ScriptObject} instances created. If not running in debug 3239 * mode this is always 0 3240 * 3241 * @return number of scope ScriptObjects created 3242 */ 3243 public static int getScopeCount() { 3244 return scopeCount; 3245 } 3246 3247 }