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