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