1 /*
   2  * Copyright (c) 2010, 2016, 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.lookup.Lookup.MH;
  32 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
  33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  34 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
  35 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
  36 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
  37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
  38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
  39 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
  40 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
  41 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
  42 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  43 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  44 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
  45 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
  46 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
  47 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag;
  48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag;
  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.lang.reflect.Array;
  56 import java.util.AbstractMap;
  57 import java.util.ArrayList;
  58 import java.util.Arrays;
  59 import java.util.Collection;
  60 import java.util.Collections;
  61 import java.util.HashSet;
  62 import java.util.Iterator;
  63 import java.util.LinkedHashSet;
  64 import java.util.List;
  65 import java.util.Map;
  66 import java.util.Set;
  67 import java.util.concurrent.atomic.LongAdder;
  68 import jdk.dynalink.CallSiteDescriptor;
  69 import jdk.dynalink.NamedOperation;
  70 import jdk.dynalink.linker.GuardedInvocation;
  71 import jdk.dynalink.linker.LinkRequest;
  72 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  73 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  74 import jdk.nashorn.internal.codegen.types.Type;
  75 import jdk.nashorn.internal.lookup.Lookup;
  76 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
  77 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
  78 import jdk.nashorn.internal.objects.Global;
  79 import jdk.nashorn.internal.objects.NativeArray;
  80 import jdk.nashorn.internal.runtime.arrays.ArrayData;
  81 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
  82 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
  83 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  84 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  85 
  86 /**
  87  * Base class for generic JavaScript objects.
  88  * <p>
  89  * Notes:
  90  * <ul>
  91  * <li>The map is used to identify properties in the object.</li>
  92  * <li>If the map is modified then it must be cloned and replaced.  This notifies
  93  *     any code that made assumptions about the object that things have changed.
  94  *     Ex. CallSites that have been validated must check to see if the map has
  95  *     changed (or a map from a different object type) and hence relink the method
  96  *     to call.</li>
  97  * <li>Modifications of the map include adding/deleting attributes or changing a
  98  *     function field value.</li>
  99  * </ul>
 100  */
 101 
 102 public abstract class ScriptObject implements PropertyAccess, Cloneable {
 103     /** __proto__ special property name inside object literals. ES6 draft. */
 104     public static final String PROTO_PROPERTY_NAME   = "__proto__";
 105 
 106     /** Search fall back routine name for "no such method" */
 107     public static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
 108 
 109     /** Search fall back routine name for "no such property" */
 110     public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
 111 
 112     /** Per ScriptObject flag - is this an array object? */
 113     public static final int IS_ARRAY               = 1 << 0;
 114 
 115     /** Per ScriptObject flag - is this an arguments object? */
 116     public static final int IS_ARGUMENTS           = 1 << 1;
 117 
 118     /** Is length property not-writable? */
 119     public static final int IS_LENGTH_NOT_WRITABLE = 1 << 2;
 120 
 121     /** Is this a builtin object? */
 122     public static final int IS_BUILTIN             = 1 << 3;
 123 
 124     /** Is this an internal object that should not be visible to scripts? */
 125     public static final int IS_INTERNAL            = 1 << 4;
 126 
 127     /**
 128      * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
 129      * {@link ScriptObject#objectSpill} when full
 130      */
 131     public static final int SPILL_RATE = 8;
 132 
 133     /** Map to property information and accessor functions. Ordered by insertion. */
 134     private PropertyMap map;
 135 
 136     /** objects proto. */
 137     private ScriptObject proto;
 138 
 139     /** Object flags. */
 140     private int flags;
 141 
 142     /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */
 143     protected long[]   primitiveSpill;
 144 
 145     /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
 146     protected Object[] objectSpill;
 147 
 148     /** Indexed array data. */
 149     private ArrayData arrayData;
 150 
 151     /** Method handle to retrieve prototype of this object */
 152     public static final MethodHandle GETPROTO      = findOwnMH_V("getProto", ScriptObject.class);
 153 
 154     static final MethodHandle MEGAMORPHIC_GET    = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class);
 155     static final MethodHandle GLOBALFILTER       = findOwnMH_S("globalFilter", Object.class, Object.class);
 156     static final MethodHandle DECLARE_AND_SET    = findOwnMH_V("declareAndSet", void.class, String.class, Object.class);
 157 
 158     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class);
 159     private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class);
 160     private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class);
 161 
 162     private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>();
 163 
 164     /** Method handle for getting the array data */
 165     public static final Call GET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
 166 
 167     /** Method handle for getting a function argument at a given index. Used from MapCreator */
 168     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
 169 
 170     /** Method handle for setting a function argument at a given index. Used from MapCreator */
 171     public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
 172 
 173     /** Method handle for getting the proto of a ScriptObject */
 174     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 175 
 176     /** Method handle for getting the proto of a ScriptObject */
 177     public static final Call GET_PROTO_DEPTH    = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class);
 178 
 179     /** Method handle for setting the proto of a ScriptObject */
 180     public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class);
 181 
 182     /** Method handle for setting the proto of a ScriptObject after checking argument */
 183     public static final Call SET_PROTO_FROM_LITERAL    = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class);
 184 
 185     /** Method handle for setting the user accessors of a ScriptObject */
 186     //TODO fastpath this
 187     public static final Call SET_USER_ACCESSORS = virtualCallNoLookup(ScriptObject.class, "setUserAccessors", void.class, Object.class, ScriptFunction.class, ScriptFunction.class);
 188 
 189     /** Method handle for generic property setter */
 190     public static final Call GENERIC_SET = virtualCallNoLookup(ScriptObject.class, "set", void.class, Object.class, Object.class, int.class);
 191 
 192     static final MethodHandle[] SET_SLOW = new MethodHandle[] {
 193         findOwnMH_V("set", void.class, Object.class, int.class, int.class),
 194         findOwnMH_V("set", void.class, Object.class, double.class, int.class),
 195         findOwnMH_V("set", void.class, Object.class, Object.class, int.class)
 196     };
 197 
 198     /** Method handle to reset the map of this ScriptObject */
 199     public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class);
 200 
 201     static final MethodHandle CAS_MAP           = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class);
 202     static final MethodHandle EXTENSION_CHECK   = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class);
 203     static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class);
 204 
 205     /**
 206      * Constructor
 207      */
 208     public ScriptObject() {
 209         this(null);
 210     }
 211 
 212     /**
 213     * Constructor
 214     *
 215     * @param map {@link PropertyMap} used to create the initial object
 216     */
 217     public ScriptObject(final PropertyMap map) {
 218         if (Context.DEBUG) {
 219             ScriptObject.count.increment();
 220         }
 221         this.arrayData = ArrayData.EMPTY_ARRAY;
 222         this.setMap(map == null ? PropertyMap.newMap() : map);
 223     }
 224 
 225     /**
 226      * Constructor that directly sets the prototype to {@code proto} and property map to
 227      * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)}
 228      * would do. This should only be used for objects that are always constructed with the
 229      * same combination of prototype and property map.
 230      *
 231      * @param proto the prototype object
 232      * @param map initial {@link PropertyMap}
 233      */
 234     protected ScriptObject(final ScriptObject proto, final PropertyMap map) {
 235         this(map);
 236         this.proto = proto;
 237     }
 238 
 239     /**
 240      * Constructor used to instantiate spill properties directly. Used from
 241      * SpillObjectCreator.
 242      *
 243      * @param map            property maps
 244      * @param primitiveSpill primitive spills
 245      * @param objectSpill    reference spills
 246      */
 247     public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
 248         this(map);
 249         this.primitiveSpill = primitiveSpill;
 250         this.objectSpill    = objectSpill;
 251         assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
 252     }
 253 
 254     /**
 255      * Check whether this is a global object
 256      * @return true if global
 257      */
 258     protected boolean isGlobal() {
 259         return false;
 260     }
 261 
 262     private static int alignUp(final int size, final int alignment) {
 263         return size + alignment - 1 & ~(alignment - 1);
 264     }
 265 
 266     /**
 267      * Given a number of properties, return the aligned to SPILL_RATE
 268      * buffer size required for the smallest spill pool needed to
 269      * house them
 270      * @param nProperties number of properties
 271      * @return property buffer length, a multiple of SPILL_RATE
 272      */
 273     public static int spillAllocationLength(final int nProperties) {
 274         return alignUp(nProperties, SPILL_RATE);
 275     }
 276 
 277     /**
 278      * Copy all properties from the source object with their receiver bound to the source.
 279      * This function was known as mergeMap
 280      *
 281      * @param source The source object to copy from.
 282      */
 283     public void addBoundProperties(final ScriptObject source) {
 284         addBoundProperties(source, source.getMap().getProperties());
 285     }
 286 
 287     /**
 288      * Copy all properties from the array with their receiver bound to the source.
 289      *
 290      * @param source The source object to copy from.
 291      * @param properties The array of properties to copy.
 292      */
 293     public void addBoundProperties(final ScriptObject source, final Property[] properties) {
 294         PropertyMap newMap = this.getMap();
 295         final boolean extensible = newMap.isExtensible();
 296 
 297         for (final Property property : properties) {
 298             newMap = addBoundProperty(newMap, source, property, extensible);
 299         }
 300 
 301         this.setMap(newMap);
 302     }
 303 
 304     /**
 305      * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the
 306      * new interim property map.
 307      *
 308      * @param propMap the property map
 309      * @param source the source object
 310      * @param property the property to be added
 311      * @param extensible whether the current object is extensible or not
 312      * @return the new property map
 313      */
 314     protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) {
 315         PropertyMap newMap = propMap;
 316         final Object key = property.getKey();
 317         final Property oldProp = newMap.findProperty(key);
 318         if (oldProp == null) {
 319             if (! extensible) {
 320                 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
 321             }
 322 
 323             if (property instanceof UserAccessorProperty) {
 324                 // Note: we copy accessor functions to this object which is semantically different from binding.
 325                 final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
 326                 newMap = newMap.addPropertyNoHistory(prop);
 327             } else {
 328                 newMap = newMap.addPropertyBind((AccessorProperty)property, source);
 329             }
 330         } else {
 331             // See ECMA section 10.5 Declaration Binding Instantiation
 332             // step 5 processing each function declaration.
 333             if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
 334                 if (oldProp instanceof UserAccessorProperty ||
 335                         !(oldProp.isWritable() && oldProp.isEnumerable())) {
 336                     throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 337                 }
 338             }
 339         }
 340         return newMap;
 341     }
 342 
 343     /**
 344      * Copy all properties from the array with their receiver bound to the source.
 345      *
 346      * @param source The source object to copy from.
 347      * @param properties The collection of accessor properties to copy.
 348      */
 349     public void addBoundProperties(final Object source, final AccessorProperty[] properties) {
 350         PropertyMap newMap = this.getMap();
 351         final boolean extensible = newMap.isExtensible();
 352 
 353         for (final AccessorProperty property : properties) {
 354             final Object key = property.getKey();
 355 
 356             if (newMap.findProperty(key) == null) {
 357                 if (! extensible) {
 358                     throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
 359                 }
 360                 newMap = newMap.addPropertyBind(property, source);
 361             }
 362         }
 363 
 364         this.setMap(newMap);
 365     }
 366 
 367     /**
 368      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
 369      * first argument in lieu of the bound argument).
 370      * @param methodHandle Method handle to bind to.
 371      * @param receiver     Object to bind.
 372      * @return Bound method handle.
 373      */
 374     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
 375         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
 376     }
 377 
 378     /**
 379      * Return a property iterator.
 380      * @return Property iterator.
 381      */
 382     public Iterator<String> propertyIterator() {
 383         return new KeyIterator(this);
 384     }
 385 
 386     /**
 387      * Return a property value iterator.
 388      * @return Property value iterator.
 389      */
 390     public Iterator<Object> valueIterator() {
 391         return new ValueIterator(this);
 392     }
 393 
 394     /**
 395      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
 396      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
 397      */
 398     public final boolean isAccessorDescriptor() {
 399         return has(GET) || has(SET);
 400     }
 401 
 402     /**
 403      * ECMA 8.10.2 IsDataDescriptor ( Desc )
 404      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
 405      */
 406     public final boolean isDataDescriptor() {
 407         return has(VALUE) || has(WRITABLE);
 408     }
 409 
 410     /**
 411       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 412       *
 413       * @return property descriptor
 414       */
 415     public final PropertyDescriptor toPropertyDescriptor() {
 416         final Global global = Context.getGlobal();
 417 
 418         final PropertyDescriptor desc;
 419         if (isDataDescriptor()) {
 420             if (has(SET) || has(GET)) {
 421                 throw typeError(global, "inconsistent.property.descriptor");
 422             }
 423 
 424             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
 425         } else if (isAccessorDescriptor()) {
 426             if (has(VALUE) || has(WRITABLE)) {
 427                 throw typeError(global, "inconsistent.property.descriptor");
 428             }
 429 
 430             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
 431         } else {
 432             desc = global.newGenericDescriptor(false, false);
 433         }
 434 
 435         return desc.fillFrom(this);
 436     }
 437 
 438     /**
 439      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 440      *
 441      * @param global  global scope object
 442      * @param obj object to create property descriptor from
 443      *
 444      * @return property descriptor
 445      */
 446     public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) {
 447         if (obj instanceof ScriptObject) {
 448             return ((ScriptObject)obj).toPropertyDescriptor();
 449         }
 450 
 451         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
 452     }
 453 
 454     /**
 455      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 456      *
 457      * @param key property key
 458      *
 459      * @return Returns the Property Descriptor of the named own property of this
 460      * object, or undefined if absent.
 461      */
 462     public Object getOwnPropertyDescriptor(final Object key) {
 463         final Property property = getMap().findProperty(key);
 464 
 465         final Global global = Context.getGlobal();
 466 
 467         if (property != null) {
 468             final ScriptFunction get   = property.getGetterFunction(this);
 469             final ScriptFunction set   = property.getSetterFunction(this);
 470 
 471             final boolean configurable = property.isConfigurable();
 472             final boolean enumerable   = property.isEnumerable();
 473             final boolean writable     = property.isWritable();
 474 
 475             if (property.isAccessorProperty()) {
 476                 return global.newAccessorDescriptor(
 477                     get != null ?
 478                         get :
 479                         UNDEFINED,
 480                     set != null ?
 481                         set :
 482                         UNDEFINED,
 483                     configurable,
 484                     enumerable);
 485             }
 486 
 487             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
 488         }
 489 
 490         final int index = getArrayIndex(key);
 491         final ArrayData array = getArray();
 492 
 493         if (array.has(index)) {
 494             return array.getDescriptor(global, index);
 495         }
 496 
 497         return UNDEFINED;
 498     }
 499 
 500     /**
 501      * ECMA 8.12.2 [[GetProperty]] (P)
 502      *
 503      * @param key property key
 504      *
 505      * @return Returns the fully populated Property Descriptor of the named property
 506      * of this object, or undefined if absent.
 507      */
 508     public Object getPropertyDescriptor(final String key) {
 509         final Object res = getOwnPropertyDescriptor(key);
 510 
 511         if (res != UNDEFINED) {
 512             return res;
 513         } else if (getProto() != null) {
 514             return getProto().getOwnPropertyDescriptor(key);
 515         } else {
 516             return UNDEFINED;
 517         }
 518     }
 519 
 520     /**
 521      * Invalidate any existing global constant method handles that may exist for {@code key}.
 522      * @param key the property name
 523      */
 524     protected void invalidateGlobalConstant(final Object key) {
 525         final GlobalConstants globalConstants = getGlobalConstants();
 526         if (globalConstants != null) {
 527             globalConstants.delete(key);
 528         }
 529     }
 530 
 531     /**
 532      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
 533      *
 534      * @param key the property key
 535      * @param propertyDesc the property descriptor
 536      * @param reject is the property extensible - true means new definitions are rejected
 537      *
 538      * @return true if property was successfully defined
 539      */
 540     public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) {
 541         final Global             global  = Context.getGlobal();
 542         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
 543         final Object             current = getOwnPropertyDescriptor(key);
 544 
 545         invalidateGlobalConstant(key);
 546 
 547         if (current == UNDEFINED) {
 548             if (isExtensible()) {
 549                 // add a new own property
 550                 addOwnProperty(key, desc);
 551                 return true;
 552             }
 553             // new property added to non-extensible object
 554             if (reject) {
 555                 throw typeError(global, "object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
 556             }
 557             return false;
 558         }
 559 
 560         // modifying an existing property
 561         final PropertyDescriptor currentDesc = (PropertyDescriptor)current;
 562         final PropertyDescriptor newDesc     = desc;
 563 
 564         if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) {
 565             // every descriptor field is absent
 566             return true;
 567         }
 568 
 569         if (newDesc.hasAndEquals(currentDesc)) {
 570             // every descriptor field of the new is same as the current
 571             return true;
 572         }
 573 
 574         if (!currentDesc.isConfigurable()) {
 575             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
 576                 // not configurable can not be made configurable
 577                 if (reject) {
 578                     throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 579                 }
 580                 return false;
 581             }
 582 
 583             if (newDesc.has(ENUMERABLE) &&
 584                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
 585                 // cannot make non-enumerable as enumerable or vice-versa
 586                 if (reject) {
 587                     throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 588                 }
 589                 return false;
 590             }
 591         }
 592 
 593         int propFlags = Property.mergeFlags(currentDesc, newDesc);
 594         Property property = getMap().findProperty(key);
 595 
 596         if (currentDesc.type() == PropertyDescriptor.DATA &&
 597                 (newDesc.type() == PropertyDescriptor.DATA ||
 598                  newDesc.type() == PropertyDescriptor.GENERIC)) {
 599             if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) {
 600                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
 601                     newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
 602                     if (reject) {
 603                         throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 604                     }
 605                     return false;
 606                 }
 607             }
 608 
 609             final boolean newValue = newDesc.has(VALUE);
 610             final Object value     = newValue ? newDesc.getValue() : currentDesc.getValue();
 611 
 612             if (newValue && property != null) {
 613                 // Temporarily clear flags.
 614                 property = modifyOwnProperty(property, 0);
 615                 set(key, value, 0);
 616                 //this might change the map if we change types of the property
 617                 //hence we need to read it again. note that we should probably
 618                 //have the setter return the new property throughout and in
 619                 //general respect Property return values from modify and add
 620                 //functions - which we don't seem to do at all here :-(
 621                 //There is already a bug filed to generify PropertyAccess so we
 622                 //can have the setter return e.g. a Property
 623                 property = getMap().findProperty(key);
 624             }
 625 
 626             if (property == null) {
 627                 // promoting an arrayData value to actual property
 628                 addOwnProperty(key, propFlags, value);
 629                 checkIntegerKey(key);
 630             } else {
 631                 // Now set the new flags
 632                 modifyOwnProperty(property, propFlags);
 633             }
 634         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
 635                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
 636                     newDesc.type() == PropertyDescriptor.GENERIC)) {
 637             if (!currentDesc.isConfigurable()) {
 638                 if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
 639                     newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
 640                     if (reject) {
 641                         throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 642                     }
 643                     return false;
 644                 }
 645             }
 646             // New set the new features.
 647             modifyOwnProperty(property, propFlags,
 648                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
 649                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
 650         } else {
 651             // changing descriptor type
 652             if (!currentDesc.isConfigurable()) {
 653                 // not configurable can not be made configurable
 654                 if (reject) {
 655                     throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this));
 656                 }
 657                 return false;
 658             }
 659 
 660             propFlags = 0;
 661 
 662             // Preserve only configurable and enumerable from current desc
 663             // if those are not overridden in the new property descriptor.
 664             boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable();
 665             if (!value) {
 666                 propFlags |= Property.NOT_CONFIGURABLE;
 667             }
 668             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
 669             if (!value) {
 670                 propFlags |= Property.NOT_ENUMERABLE;
 671             }
 672 
 673             final int type = newDesc.type();
 674             if (type == PropertyDescriptor.DATA) {
 675                 // get writable from the new descriptor
 676                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
 677                 if (!value) {
 678                     propFlags |= Property.NOT_WRITABLE;
 679                 }
 680 
 681                 // delete the old property
 682                 deleteOwnProperty(property);
 683                 // add new data property
 684                 addOwnProperty(key, propFlags, newDesc.getValue());
 685             } else if (type == PropertyDescriptor.ACCESSOR) {
 686                 if (property == null) {
 687                     addOwnProperty(key, propFlags,
 688                                      newDesc.has(GET) ? newDesc.getGetter() : null,
 689                                      newDesc.has(SET) ? newDesc.getSetter() : null);
 690                 } else {
 691                     // Modify old property with the new features.
 692                     modifyOwnProperty(property, propFlags,
 693                                         newDesc.has(GET) ? newDesc.getGetter() : null,
 694                                         newDesc.has(SET) ? newDesc.getSetter() : null);
 695                 }
 696             }
 697         }
 698 
 699         checkIntegerKey(key);
 700 
 701         return true;
 702     }
 703 
 704     /**
 705      * Almost like defineOwnProperty(int,Object) for arrays this one does
 706      * not add 'gap' elements (like the array one does).
 707      *
 708      * @param index key for property
 709      * @param value value to define
 710      */
 711     public void defineOwnProperty(final int index, final Object value) {
 712         assert isValidArrayIndex(index) : "invalid array index";
 713         final long longIndex = ArrayIndex.toLongIndex(index);
 714         final long oldLength = getArray().length();
 715         if (longIndex >= oldLength) {
 716             setArray(getArray().ensure(longIndex).safeDelete(oldLength, longIndex - 1, false));
 717         }
 718         setArray(getArray().set(index, value, false));
 719     }
 720 
 721     private void checkIntegerKey(final Object key) {
 722         final int index = getArrayIndex(key);
 723 
 724         if (isValidArrayIndex(index)) {
 725             final ArrayData data = getArray();
 726 
 727             if (data.has(index)) {
 728                 setArray(data.delete(index));
 729             }
 730         }
 731     }
 732 
 733     /**
 734       * Add a new property to the object.
 735       *
 736       * @param key          property key
 737       * @param propertyDesc property descriptor for property
 738       */
 739     public final void addOwnProperty(final Object key, final PropertyDescriptor propertyDesc) {
 740         // Already checked that there is no own property with that key.
 741         PropertyDescriptor pdesc = propertyDesc;
 742 
 743         final int propFlags = Property.toFlags(pdesc);
 744 
 745         if (pdesc.type() == PropertyDescriptor.GENERIC) {
 746             final Global global = Context.getGlobal();
 747             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
 748 
 749             dDesc.fillFrom((ScriptObject)pdesc);
 750             pdesc = dDesc;
 751         }
 752 
 753         final int type = pdesc.type();
 754         if (type == PropertyDescriptor.DATA) {
 755             addOwnProperty(key, propFlags, pdesc.getValue());
 756         } else if (type == PropertyDescriptor.ACCESSOR) {
 757             addOwnProperty(key, propFlags,
 758                     pdesc.has(GET) ? pdesc.getGetter() : null,
 759                     pdesc.has(SET) ? pdesc.getSetter() : null);
 760         }
 761 
 762         checkIntegerKey(key);
 763     }
 764 
 765     /**
 766      * Low level property API (not using property descriptors)
 767      * <p>
 768      * Find a property in the prototype hierarchy. Note: this is final and not
 769      * a good idea to override. If you have to, use
 770      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 771      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 772      * overriding way to find array properties
 773      *
 774      * @see jdk.nashorn.internal.objects.NativeArray
 775      *
 776      * @param key  Property key.
 777      * @param deep Whether the search should look up proto chain.
 778      *
 779      * @return FindPropertyData or null if not found.
 780      */
 781     public final FindProperty findProperty(final Object key, final boolean deep) {
 782         return findProperty(key, deep, false, this);
 783     }
 784 
 785     /**
 786      * Low level property API (not using property descriptors)
 787      * <p>
 788      * Find a property in the prototype hierarchy. Note: this is not a good idea
 789      * to override except as it was done in {@link WithObject}.
 790      * If you have to, use
 791      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 792      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 793      * overriding way to find array properties
 794      *
 795      * @see jdk.nashorn.internal.objects.NativeArray
 796      *
 797      * @param key  Property key.
 798      * @param deep true if the search should look up proto chain
 799      * @param isScope true if this is a scope access
 800      * @param start the object on which the lookup was originally initiated
 801      * @return FindPropertyData or null if not found.
 802      */
 803     protected FindProperty findProperty(final Object key, final boolean deep, final boolean isScope, final ScriptObject start) {
 804 
 805         final PropertyMap selfMap  = getMap();
 806         final Property    property = selfMap.findProperty(key);
 807 
 808         if (property != null) {
 809             return new FindProperty(start, this, property);
 810         }
 811 
 812         if (deep) {
 813             final ScriptObject myProto = getProto();
 814             final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, isScope, start);
 815             // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate
 816             // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null.
 817             checkSharedProtoMap();
 818             return find;
 819         }
 820 
 821         return null;
 822     }
 823 
 824     /**
 825      * Low level property API. This is similar to {@link #findProperty(Object, boolean)} but returns a
 826      * {@code boolean} value instead of a {@link FindProperty} object.
 827      * @param key  Property key.
 828      * @param deep Whether the search should look up proto chain.
 829      * @return true if the property was found.
 830      */
 831     boolean hasProperty(final Object key, final boolean deep) {
 832         if (getMap().findProperty(key) != null) {
 833             return true;
 834         }
 835 
 836         if (deep) {
 837             final ScriptObject myProto = getProto();
 838             if (myProto != null) {
 839                 return myProto.hasProperty(key, true);
 840             }
 841         }
 842 
 843         return false;
 844     }
 845 
 846     private SwitchPoint findBuiltinSwitchPoint(final Object key) {
 847         for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
 848             final Property prop = myProto.getMap().findProperty(key);
 849             if (prop != null) {
 850                 final SwitchPoint sp = prop.getBuiltinSwitchPoint();
 851                 if (sp != null && !sp.hasBeenInvalidated()) {
 852                     return sp;
 853                 }
 854             }
 855         }
 856         return null;
 857     }
 858 
 859     /**
 860      * Add a new property to the object.
 861      * <p>
 862      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 863      *
 864      * @param key             Property key.
 865      * @param propertyFlags   Property flags.
 866      * @param getter          Property getter, or null if not defined
 867      * @param setter          Property setter, or null if not defined
 868      *
 869      * @return New property.
 870      */
 871     public final Property addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 872         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
 873     }
 874 
 875     /**
 876      * Add a new property to the object.
 877      * <p>
 878      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 879      *
 880      * @param key             Property key.
 881      * @param propertyFlags   Property flags.
 882      * @param value           Value of property
 883      *
 884      * @return New property.
 885      */
 886     public final Property addOwnProperty(final Object key, final int propertyFlags, final Object value) {
 887         return addSpillProperty(key, propertyFlags, value, true);
 888     }
 889 
 890     /**
 891      * Add a new property to the object.
 892      * <p>
 893      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 894      *
 895      * @param newProperty property to add
 896      *
 897      * @return New property.
 898      */
 899     public final Property addOwnProperty(final Property newProperty) {
 900         PropertyMap oldMap = getMap();
 901         while (true) {
 902             final PropertyMap newMap = oldMap.addProperty(newProperty);
 903             if (!compareAndSetMap(oldMap, newMap)) {
 904                 oldMap = getMap();
 905                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
 906 
 907                 if (oldProperty != null) {
 908                     return oldProperty;
 909                 }
 910             } else {
 911                 return newProperty;
 912             }
 913         }
 914     }
 915 
 916     private void erasePropertyValue(final Property property) {
 917         // Erase the property field value with undefined. If the property is an accessor property
 918         // we don't want to call the setter!!
 919         if (property != null && !property.isAccessorProperty()) {
 920             property.setValue(this, this, UNDEFINED, false);
 921         }
 922     }
 923 
 924     /**
 925      * Delete a property from the object.
 926      *
 927      * @param property Property to delete.
 928      *
 929      * @return true if deleted.
 930      */
 931     public final boolean deleteOwnProperty(final Property property) {
 932         erasePropertyValue(property);
 933         PropertyMap oldMap = getMap();
 934 
 935         while (true) {
 936             final PropertyMap newMap = oldMap.deleteProperty(property);
 937 
 938             if (newMap == null) {
 939                 return false;
 940             }
 941 
 942             if (!compareAndSetMap(oldMap, newMap)) {
 943                 oldMap = getMap();
 944             } else {
 945                 // delete getter and setter function references so that we don't leak
 946                 if (property instanceof UserAccessorProperty) {
 947                     ((UserAccessorProperty)property).setAccessors(this, getMap(), null);
 948                 }
 949 
 950                 invalidateGlobalConstant(property.getKey());
 951                 return true;
 952             }
 953         }
 954 
 955     }
 956 
 957     /**
 958      * Fast initialization functions for ScriptFunctions that are strict, to avoid
 959      * creating setters that probably aren't used. Inject directly into the spill pool
 960      * the defaults for "arguments" and "caller"
 961      *
 962      * @param key           property key
 963      * @param propertyFlags flags
 964      * @param getter        getter for {@link UserAccessorProperty}, null if not present or N/A
 965      * @param setter        setter for {@link UserAccessorProperty}, null if not present or N/A
 966      */
 967     protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 968         final PropertyMap oldMap = getMap();
 969         final int slot = oldMap.getFreeSpillSlot();
 970         ensureSpillSize(slot);
 971         objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
 972         Property    newProperty;
 973         PropertyMap newMap;
 974         do {
 975             newProperty = new UserAccessorProperty(key, propertyFlags, slot);
 976             newMap = oldMap.addProperty(newProperty);
 977         } while (!compareAndSetMap(oldMap, newMap));
 978     }
 979 
 980     /**
 981      * Modify a property in the object
 982      *
 983      * @param oldProperty    property to modify
 984      * @param propertyFlags  new property flags
 985      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
 986      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
 987      *
 988      * @return new property
 989      */
 990     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 991         Property newProperty;
 992 
 993         if (oldProperty instanceof UserAccessorProperty) {
 994             final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
 995             final int slot = uc.getSlot();
 996 
 997             assert uc.getLocalType() == Object.class;
 998             final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
 999             assert gs != null;
1000             //reuse existing getter setter for speed
1001             gs.set(getter, setter);
1002             if (uc.getFlags() == (propertyFlags | Property.IS_ACCESSOR_PROPERTY)) {
1003                 return oldProperty;
1004             }
1005             newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
1006         } else {
1007             // erase old property value and create new user accessor property
1008             erasePropertyValue(oldProperty);
1009             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
1010         }
1011 
1012         return modifyOwnProperty(oldProperty, newProperty);
1013     }
1014 
1015     /**
1016       * Modify a property in the object
1017       *
1018       * @param oldProperty    property to modify
1019       * @param propertyFlags  new property flags
1020       *
1021       * @return new property
1022       */
1023     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
1024         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
1025     }
1026 
1027     /**
1028      * Modify a property in the object, replacing a property with a new one
1029      *
1030      * @param oldProperty   property to replace
1031      * @param newProperty   property to replace it with
1032      *
1033      * @return new property
1034      */
1035     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
1036         if (oldProperty == newProperty) {
1037             return newProperty; //nop
1038         }
1039 
1040         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
1041 
1042         PropertyMap oldMap = getMap();
1043 
1044         while (true) {
1045             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
1046 
1047             if (!compareAndSetMap(oldMap, newMap)) {
1048                 oldMap = getMap();
1049                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
1050 
1051                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
1052                     return oldPropertyLookup;
1053                 }
1054             } else {
1055                 return newProperty;
1056             }
1057         }
1058     }
1059 
1060     /**
1061      * Update getter and setter in an object literal.
1062      *
1063      * @param key    Property key.
1064      * @param getter {@link UserAccessorProperty} defined getter, or null if none
1065      * @param setter {@link UserAccessorProperty} defined setter, or null if none
1066      */
1067     public final void setUserAccessors(final Object key, final ScriptFunction getter, final ScriptFunction setter) {
1068         final Object realKey = JSType.toPropertyKey(key);
1069         final Property oldProperty = getMap().findProperty(realKey);
1070         if (oldProperty instanceof UserAccessorProperty) {
1071             modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter);
1072         } else {
1073             addOwnProperty(newUserAccessors(realKey, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter));
1074         }
1075     }
1076 
1077     private static int getIntValue(final FindProperty find, final int programPoint) {
1078         final MethodHandle getter = find.getGetter(int.class, programPoint, null);
1079         if (getter != null) {
1080             try {
1081                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
1082             } catch (final Error|RuntimeException e) {
1083                 throw e;
1084             } catch (final Throwable e) {
1085                 throw new RuntimeException(e);
1086             }
1087         }
1088 
1089         return UNDEFINED_INT;
1090     }
1091 
1092     private static double getDoubleValue(final FindProperty find, final int programPoint) {
1093         final MethodHandle getter = find.getGetter(double.class, programPoint, null);
1094         if (getter != null) {
1095             try {
1096                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
1097             } catch (final Error|RuntimeException e) {
1098                 throw e;
1099             } catch (final Throwable e) {
1100                 throw new RuntimeException(e);
1101             }
1102         }
1103 
1104         return UNDEFINED_DOUBLE;
1105     }
1106 
1107     /**
1108      * Return methodHandle of value function for call.
1109      *
1110      * @param find      data from find property.
1111      * @param type      method type of function.
1112      * @param bindName  null or name to bind to second argument (property not found method.)
1113      *
1114      * @return value of property as a MethodHandle or null.
1115      */
1116     protected static MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
1117         return getCallMethodHandle(find.getObjectValue(), type, bindName);
1118     }
1119 
1120     /**
1121      * Return methodHandle of value function for call.
1122      *
1123      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
1124      * @param type      method type of function.
1125      * @param bindName  null or name to bind to second argument (property not found method.)
1126      *
1127      * @return value of property as a MethodHandle or null.
1128      */
1129     private static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
1130         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
1131     }
1132 
1133     /**
1134      * Get value using found property.
1135      *
1136      * @param property Found property.
1137      *
1138      * @return Value of property.
1139      */
1140     public final Object getWithProperty(final Property property) {
1141         return new FindProperty(this, this, property).getObjectValue();
1142     }
1143 
1144     /**
1145      * Get a property given a key
1146      *
1147      * @param key property key
1148      *
1149      * @return property for key
1150      */
1151     public final Property getProperty(final String key) {
1152         return getMap().findProperty(key);
1153     }
1154 
1155     /**
1156      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
1157      * Used for argument access in a vararg function using parameter name.
1158      * Returns the argument at a given key (index)
1159      *
1160      * @param key argument index
1161      *
1162      * @return the argument at the given position, or undefined if not present
1163      */
1164     public Object getArgument(final int key) {
1165         return get(key);
1166     }
1167 
1168     /**
1169      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
1170      * Used for argument access in a vararg function using parameter name.
1171      * Returns the argument at a given key (index)
1172      *
1173      * @param key   argument index
1174      * @param value the value to write at the given index
1175      */
1176     public void setArgument(final int key, final Object value) {
1177         set(key, value, 0);
1178     }
1179 
1180     /**
1181      * Return the current context from the object's map.
1182      * @return Current context.
1183      */
1184     protected Context getContext() {
1185         return Context.fromClass(getClass());
1186     }
1187 
1188     /**
1189      * Return the map of an object.
1190      * @return PropertyMap object.
1191      */
1192     public final PropertyMap getMap() {
1193         return map;
1194     }
1195 
1196     /**
1197      * Set the initial map.
1198      * @param map Initial map.
1199      */
1200     public final void setMap(final PropertyMap map) {
1201         this.map = map;
1202     }
1203 
1204     /**
1205      * Conditionally set the new map if the old map is the same.
1206      * @param oldMap Map prior to manipulation.
1207      * @param newMap Replacement map.
1208      * @return true if the operation succeeded.
1209      */
1210     protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
1211         if (oldMap == this.map) {
1212             this.map = newMap;
1213             return true;
1214         }
1215         return false;
1216      }
1217 
1218     /**
1219      * Return the __proto__ of an object.
1220      * @return __proto__ object.
1221      */
1222     public final ScriptObject getProto() {
1223         return proto;
1224     }
1225 
1226     /**
1227      * Get the proto of a specific depth
1228      * @param n depth
1229      * @return proto at given depth
1230      */
1231     public final ScriptObject getProto(final int n) {
1232         assert n > 0;
1233         ScriptObject p = getProto();
1234         for (int i = n; --i > 0;) {
1235             p = p.getProto();
1236         }
1237         return p;
1238     }
1239 
1240     /**
1241      * Set the __proto__ of an object.
1242      * @param newProto new __proto__ to set.
1243      */
1244     public final void setProto(final ScriptObject newProto) {
1245         final ScriptObject oldProto = proto;
1246 
1247         if (oldProto != newProto) {
1248             proto = newProto;
1249 
1250             // Let current listeners know that the prototype has changed
1251             getMap().protoChanged();
1252             // Replace our current allocator map with one that is associated with the new prototype.
1253             setMap(getMap().changeProto(newProto));
1254         }
1255     }
1256 
1257     /**
1258      * Set the initial __proto__ of this object. This should be used instead of
1259      * {@link #setProto} if it is known that the current property map will not be
1260      * used on a new object with any other parent property map, so we can pass over
1261      * property map invalidation/evolution.
1262      *
1263      * @param initialProto the initial __proto__ to set.
1264      */
1265     public void setInitialProto(final ScriptObject initialProto) {
1266         this.proto = initialProto;
1267     }
1268 
1269     /**
1270      * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype.
1271      * @param obj the object literal that needs to have its prototype initialized to the global Object prototype.
1272      */
1273     public static void setGlobalObjectProto(final ScriptObject obj) {
1274         obj.setInitialProto(Global.objectPrototype());
1275     }
1276 
1277     /**
1278      * Set the __proto__ of an object with checks.
1279      * This is the built-in operation [[SetPrototypeOf]]
1280      * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V)
1281      *
1282      * @param newProto Prototype to set.
1283      */
1284     public final void setPrototypeOf(final Object newProto) {
1285         if (newProto == null || newProto instanceof ScriptObject) {
1286             if (! isExtensible()) {
1287                 // okay to set same proto again - even if non-extensible
1288 
1289                 if (newProto == getProto()) {
1290                     return;
1291                 }
1292                 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1293             }
1294 
1295             // check for circularity
1296             ScriptObject p = (ScriptObject)newProto;
1297             while (p != null) {
1298                 if (p == this) {
1299                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1300                 }
1301                 p = p.getProto();
1302             }
1303             setProto((ScriptObject) newProto);
1304         } else {
1305             throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1306         }
1307     }
1308 
1309     /**
1310      * Set the __proto__ of an object from an object literal.
1311      * See ES6 draft spec: B.3.1 __proto__ Property Names in
1312      * Object Initializers. Step 6 handling of "__proto__".
1313      *
1314      * @param newProto Prototype to set.
1315      */
1316     public final void setProtoFromLiteral(final Object newProto) {
1317         if (newProto == null || newProto instanceof ScriptObject) {
1318             setPrototypeOf(newProto);
1319         } else {
1320             // Some non-object, non-null. Then, we need to set
1321             // Object.prototype as the new __proto__
1322             //
1323             // var obj = { __proto__ : 34 };
1324             // print(obj.__proto__ === Object.prototype); // => true
1325             setPrototypeOf(Global.objectPrototype());
1326         }
1327     }
1328 
1329     /**
1330      * return an array of all property keys - all inherited, non-enumerable included.
1331      * This is meant for source code completion by interactive shells or editors.
1332      *
1333      * @return Array of keys, order of properties is undefined.
1334      */
1335     public String[] getAllKeys() {
1336         final Set<String> keys = new HashSet<>();
1337         final Set<String> nonEnumerable = new HashSet<>();
1338         for (ScriptObject self = this; self != null; self = self.getProto()) {
1339             keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable)));
1340         }
1341         return keys.toArray(new String[0]);
1342     }
1343 
1344     /**
1345      * Return an array of own property keys associated with the object.
1346      *
1347      * @param all True if to include non-enumerable keys.
1348      * @return Array of keys.
1349      */
1350     public final String[] getOwnKeys(final boolean all) {
1351         return getOwnKeys(String.class, all, null);
1352     }
1353 
1354     /**
1355      * Return an array of own property keys associated with the object.
1356      *
1357      * @param all True if to include non-enumerable keys.
1358      * @return Array of keys.
1359      */
1360     public final Symbol[] getOwnSymbols(final boolean all) {
1361         return getOwnKeys(Symbol.class, all, null);
1362     }
1363 
1364     /**
1365      * return an array of own property keys associated with the object.
1366      *
1367      * @param <T> the type returned keys.
1368      * @param type the type of keys to return, either {@code String.class} or {@code Symbol.class}.
1369      * @param all True if to include non-enumerable keys.
1370      * @param nonEnumerable set of non-enumerable properties seen already. Used to
1371      *                      filter out shadowed, but enumerable properties from proto children.
1372      * @return Array of keys.
1373      */
1374     @SuppressWarnings("unchecked")
1375     protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) {
1376         final List<Object> keys    = new ArrayList<>();
1377         final PropertyMap  selfMap = this.getMap();
1378 
1379         final ArrayData array  = getArray();
1380 
1381         if (type == String.class) {
1382             for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1383                 keys.add(JSType.toString(iter.next().longValue()));
1384             }
1385         }
1386 
1387         for (final Property property : selfMap.getProperties()) {
1388             final boolean enumerable = property.isEnumerable();
1389             final Object key = property.getKey();
1390             if (!type.isInstance(key)) {
1391                 continue;
1392             }
1393             if (all) {
1394                 keys.add(key);
1395             } else if (enumerable) {
1396                 // either we don't have non-enumerable filter set or filter set
1397                 // does not contain the current property.
1398                 if (nonEnumerable == null || !nonEnumerable.contains(key)) {
1399                     keys.add(key);
1400                 }
1401             } else {
1402                 // store this non-enumerable property for later proto walk
1403                 if (nonEnumerable != null) {
1404                     nonEnumerable.add((T) key);
1405                 }
1406             }
1407         }
1408 
1409         return keys.toArray((T[]) Array.newInstance(type, keys.size()));
1410     }
1411 
1412     /**
1413      * Check if this ScriptObject has array entries. This means that someone has
1414      * set values with numeric keys in the object.
1415      *
1416      * @return true if array entries exists.
1417      */
1418     public boolean hasArrayEntries() {
1419         return getArray().length() > 0 || getMap().containsArrayKeys();
1420     }
1421 
1422     /**
1423      * Return the valid JavaScript type name descriptor
1424      *
1425      * @return "Object"
1426      */
1427     public String getClassName() {
1428         return "Object";
1429     }
1430 
1431     /**
1432      * {@code length} is a well known property. This is its getter.
1433      * Note that this *may* be optimized by other classes
1434      *
1435      * @return length property value for this ScriptObject
1436      */
1437     public Object getLength() {
1438         return get("length");
1439     }
1440 
1441     /**
1442      * Stateless toString for ScriptObjects.
1443      *
1444      * @return string description of this object, e.g. {@code [object Object]}
1445      */
1446     public String safeToString() {
1447         return "[object " + getClassName() + "]";
1448     }
1449 
1450     /**
1451      * Return the default value of the object with a given preferred type hint.
1452      * The preferred type hints are String.class for type String, Number.class
1453      * for type Number. <p>
1454      *
1455      * A <code>hint</code> of null means "no hint".
1456      *
1457      * ECMA 8.12.8 [[DefaultValue]](hint)
1458      *
1459      * @param typeHint the preferred type hint
1460      * @return the default value
1461      */
1462     public Object getDefaultValue(final Class<?> typeHint) {
1463         // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
1464         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1465         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1466         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1467         return Context.getGlobal().getDefaultValue(this, typeHint);
1468     }
1469 
1470     /**
1471      * Checking whether a script object is an instance of another. Used
1472      * in {@link ScriptFunction} for hasInstance implementation, walks
1473      * the proto chain
1474      *
1475      * @param instance instance to check
1476      * @return true if 'instance' is an instance of this object
1477      */
1478     public boolean isInstance(final ScriptObject instance) {
1479         return false;
1480     }
1481 
1482     /**
1483      * Flag this ScriptObject as non extensible
1484      *
1485      * @return the object after being made non extensible
1486      */
1487     public ScriptObject preventExtensions() {
1488         PropertyMap oldMap = getMap();
1489         while (!compareAndSetMap(oldMap,  getMap().preventExtensions())) {
1490             oldMap = getMap();
1491         }
1492 
1493         //invalidate any fast array setters
1494         final ArrayData array = getArray();
1495         assert array != null;
1496         setArray(ArrayData.preventExtension(array));
1497         return this;
1498     }
1499 
1500     /**
1501      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1502      *
1503      * @param obj object to check
1504      *
1505      * @return true if array
1506      */
1507     public static boolean isArray(final Object obj) {
1508         return obj instanceof ScriptObject && ((ScriptObject)obj).isArray();
1509     }
1510 
1511     /**
1512      * Check if this ScriptObject is an array
1513      * @return true if array
1514      */
1515     public final boolean isArray() {
1516         return (flags & IS_ARRAY) != 0;
1517     }
1518 
1519     /**
1520      * Flag this ScriptObject as being an array
1521      */
1522     public final void setIsArray() {
1523         flags |= IS_ARRAY;
1524     }
1525 
1526     /**
1527      * Check if this ScriptObject is an {@code arguments} vector
1528      * @return true if arguments vector
1529      */
1530     public final boolean isArguments() {
1531         return (flags & IS_ARGUMENTS) != 0;
1532     }
1533 
1534     /**
1535      * Flag this ScriptObject as being an {@code arguments} vector
1536      */
1537     public final void setIsArguments() {
1538         flags |= IS_ARGUMENTS;
1539     }
1540 
1541     /**
1542      * Check if this object has non-writable length property
1543      *
1544      * @return {@code true} if 'length' property is non-writable
1545      */
1546     public boolean isLengthNotWritable() {
1547         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1548     }
1549 
1550     /**
1551      * Flag this object as having non-writable length property.
1552      */
1553     public void setIsLengthNotWritable() {
1554         flags |= IS_LENGTH_NOT_WRITABLE;
1555     }
1556 
1557     /**
1558      * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type
1559      * that can handle elementType
1560      * @param elementType elementType
1561      * @return array data
1562      */
1563     public final ArrayData getArray(final Class<?> elementType) {
1564         if (elementType == null) {
1565             return arrayData;
1566         }
1567         final ArrayData newArrayData = arrayData.convert(elementType);
1568         if (newArrayData != arrayData) {
1569             arrayData = newArrayData;
1570         }
1571         return newArrayData;
1572     }
1573 
1574     /**
1575      * Get the {@link ArrayData} for this ScriptObject if it is an array
1576      * @return array data
1577      */
1578     public final ArrayData getArray() {
1579         return arrayData;
1580     }
1581 
1582     /**
1583      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1584      * @param arrayData the array data
1585      */
1586     public final void setArray(final ArrayData arrayData) {
1587         this.arrayData = arrayData;
1588     }
1589 
1590     /**
1591      * Check if this ScriptObject is extensible
1592      * @return true if extensible
1593      */
1594     public boolean isExtensible() {
1595         return getMap().isExtensible();
1596     }
1597 
1598     /**
1599      * ECMAScript 15.2.3.8 - seal implementation
1600      * @return the sealed ScriptObject
1601      */
1602     public ScriptObject seal() {
1603         PropertyMap oldMap = getMap();
1604 
1605         while (true) {
1606             final PropertyMap newMap = getMap().seal();
1607 
1608             if (!compareAndSetMap(oldMap, newMap)) {
1609                 oldMap = getMap();
1610             } else {
1611                 setArray(ArrayData.seal(getArray()));
1612                 return this;
1613             }
1614         }
1615     }
1616 
1617     /**
1618      * Check whether this ScriptObject is sealed
1619      * @return true if sealed
1620      */
1621     public boolean isSealed() {
1622         return getMap().isSealed();
1623     }
1624 
1625     /**
1626      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1627      * @return the frozen ScriptObject
1628      */
1629     public ScriptObject freeze() {
1630         PropertyMap oldMap = getMap();
1631 
1632         while (true) {
1633             final PropertyMap newMap = getMap().freeze();
1634 
1635             if (!compareAndSetMap(oldMap, newMap)) {
1636                 oldMap = getMap();
1637             } else {
1638                 setArray(ArrayData.freeze(getArray()));
1639                 return this;
1640             }
1641         }
1642     }
1643 
1644     /**
1645      * Check whether this ScriptObject is frozen
1646      * @return true if frozen
1647      */
1648     public boolean isFrozen() {
1649         return getMap().isFrozen();
1650     }
1651 
1652     /**
1653      * Check whether this ScriptObject is scope
1654      * @return true if scope
1655      */
1656     public boolean isScope() {
1657         return false;
1658     }
1659 
1660     /**
1661      * Tag this script object as built in
1662      */
1663     public final void setIsBuiltin() {
1664         flags |= IS_BUILTIN;
1665     }
1666 
1667     /**
1668      * Check if this script object is built in
1669      * @return true if build in
1670      */
1671     public final boolean isBuiltin() {
1672         return (flags & IS_BUILTIN) != 0;
1673     }
1674 
1675     /**
1676      * Tag this script object as internal object that should not be visible to script code.
1677      */
1678     public final void setIsInternal() {
1679         flags |= IS_INTERNAL;
1680     }
1681 
1682     /**
1683      * Check if this script object is an internal object that should not be visible to script code.
1684      * @return true if internal
1685      */
1686     public final boolean isInternal() {
1687         return (flags & IS_INTERNAL) != 0;
1688     }
1689 
1690     /**
1691      * Clears the properties from a ScriptObject
1692      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1693      *
1694      * @param strict strict mode or not
1695      */
1696     public void clear(final boolean strict) {
1697         final Iterator<String> iter = propertyIterator();
1698         while (iter.hasNext()) {
1699             delete(iter.next(), strict);
1700         }
1701     }
1702 
1703     /**
1704      * Checks if a property with a given key is present in a ScriptObject
1705      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1706      *
1707      * @param key the key to check for
1708      * @return true if a property with the given key exists, false otherwise
1709      */
1710     public boolean containsKey(final Object key) {
1711         return has(key);
1712     }
1713 
1714     /**
1715      * Checks if a property with a given value is present in a ScriptObject
1716      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1717      *
1718      * @param value value to check for
1719      * @return true if a property with the given value exists, false otherwise
1720      */
1721     public boolean containsValue(final Object value) {
1722         final Iterator<Object> iter = valueIterator();
1723         while (iter.hasNext()) {
1724             if (iter.next().equals(value)) {
1725                 return true;
1726             }
1727         }
1728         return false;
1729     }
1730 
1731     /**
1732      * Returns the set of {@literal <property, value>} entries that make up this
1733      * ScriptObject's properties
1734      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1735      *
1736      * @return an entry set of all the properties in this object
1737      */
1738     public Set<Map.Entry<Object, Object>> entrySet() {
1739         final Iterator<String> iter = propertyIterator();
1740         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1741         while (iter.hasNext()) {
1742             final Object key = iter.next();
1743             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1744         }
1745         return Collections.unmodifiableSet(entries);
1746     }
1747 
1748     /**
1749      * Check whether a ScriptObject contains no properties
1750      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1751      *
1752      * @return true if object has no properties
1753      */
1754     public boolean isEmpty() {
1755         return !propertyIterator().hasNext();
1756     }
1757 
1758     /**
1759      * Return the set of keys (property names) for all properties
1760      * in this ScriptObject
1761      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1762      *
1763      * @return keySet of this ScriptObject
1764      */
1765     public Set<Object> keySet() {
1766         final Iterator<String> iter = propertyIterator();
1767         final Set<Object> keySet = new HashSet<>();
1768         while (iter.hasNext()) {
1769             keySet.add(iter.next());
1770         }
1771         return Collections.unmodifiableSet(keySet);
1772     }
1773 
1774     /**
1775      * Put a property in the ScriptObject
1776      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1777      *
1778      * @param key property key
1779      * @param value property value
1780      * @param strict strict mode or not
1781      * @return oldValue if property with same key existed already
1782      */
1783     public Object put(final Object key, final Object value, final boolean strict) {
1784         final Object oldValue = get(key);
1785         final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1786         set(key, value, scriptObjectFlags);
1787         return oldValue;
1788     }
1789 
1790     /**
1791      * Put several properties in the ScriptObject given a mapping
1792      * of their keys to their values
1793      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1794      *
1795      * @param otherMap a {@literal <key,value>} map of properties to add
1796      * @param strict strict mode or not
1797      */
1798     public void putAll(final Map<?, ?> otherMap, final boolean strict) {
1799         final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1800         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1801             set(entry.getKey(), entry.getValue(), scriptObjectFlags);
1802         }
1803     }
1804 
1805     /**
1806      * Remove a property from the ScriptObject.
1807      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1808      *
1809      * @param key the key of the property
1810      * @param strict strict mode or not
1811      * @return the oldValue of the removed property
1812      */
1813     public Object remove(final Object key, final boolean strict) {
1814         final Object oldValue = get(key);
1815         delete(key, strict);
1816         return oldValue;
1817     }
1818 
1819     /**
1820      * Return the size of the ScriptObject - i.e. the number of properties
1821      * it contains
1822      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1823      *
1824      * @return number of properties in ScriptObject
1825      */
1826     public int size() {
1827         int n = 0;
1828         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1829             n++;
1830         }
1831         return n;
1832     }
1833 
1834     /**
1835      * Return the values of the properties in the ScriptObject
1836      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1837      *
1838      * @return collection of values for the properties in this ScriptObject
1839      */
1840     public Collection<Object> values() {
1841         final List<Object>     values = new ArrayList<>(size());
1842         final Iterator<Object> iter   = valueIterator();
1843         while (iter.hasNext()) {
1844             values.add(iter.next());
1845         }
1846         return Collections.unmodifiableList(values);
1847     }
1848 
1849     /**
1850      * Lookup method that, given a CallSiteDescriptor, looks up the target
1851      * MethodHandle and creates a GuardedInvocation
1852      * with the appropriate guard(s).
1853      *
1854      * @param desc call site descriptor
1855      * @param request the link request
1856      *
1857      * @return GuardedInvocation for the callsite
1858      */
1859     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1860         // NOTE: we support GET:ELEMENT and SET:ELEMENT as JavaScript doesn't distinguish items from properties. Nashorn itself
1861         // emits "GET:PROPERTY|ELEMENT|METHOD:identifier" for "<expr>.<identifier>" and "GET:ELEMENT|PROPERTY|METHOD" for "<expr>[<expr>]", but we are
1862         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1863         // operation has an associated name or not.
1864         switch (NashornCallSiteDescriptor.getStandardOperation(desc)) {
1865         case GET:
1866             return desc.getOperation() instanceof NamedOperation
1867                     ? findGetMethod(desc, request)
1868                     : findGetIndexMethod(desc, request);
1869         case SET:
1870             return desc.getOperation() instanceof NamedOperation
1871                     ? findSetMethod(desc, request)
1872                     : findSetIndexMethod(desc, request);
1873         case CALL:
1874             return findCallMethod(desc, request);
1875         case NEW:
1876             return findNewMethod(desc, request);
1877         default:
1878             return null;
1879         }
1880     }
1881 
1882     /**
1883      * Find the appropriate New method for an invoke dynamic call.
1884      *
1885      * @param desc The invoke dynamic call site descriptor.
1886      * @param request The link request
1887      *
1888      * @return GuardedInvocation to be invoked at call site.
1889      */
1890     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1891         return notAFunction(desc);
1892     }
1893 
1894     /**
1895      * Find the appropriate CALL method for an invoke dynamic call.
1896      * This generates "not a function" always
1897      *
1898      * @param desc    the call site descriptor.
1899      * @param request the link request
1900      *
1901      * @return GuardedInvocation to be invoked at call site.
1902      */
1903     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1904         return notAFunction(desc);
1905     }
1906 
1907     private GuardedInvocation notAFunction(final CallSiteDescriptor desc) {
1908         throw typeError("not.a.function", NashornCallSiteDescriptor.getFunctionErrorMessage(desc, this));
1909     }
1910 
1911     /**
1912      * Test whether this object contains in its prototype chain or is itself a with-object.
1913      * @return true if a with-object was found
1914      */
1915     boolean hasWithScope() {
1916         return false;
1917     }
1918 
1919     /**
1920      * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
1921      * {@code depth} times.
1922      * @param methodHandle a method handle
1923      * @param depth        distance to target prototype
1924      * @return the filtered method handle
1925      */
1926     static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
1927         if (depth == 0) {
1928             return methodHandle;
1929         }
1930         final int listIndex = depth - 1; // We don't need 0-deep walker
1931         MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null;
1932 
1933         if (filter == null) {
1934             filter = addProtoFilter(GETPROTO, depth - 1);
1935             PROTO_FILTERS.add(null);
1936             PROTO_FILTERS.set(listIndex, filter);
1937         }
1938 
1939         return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
1940     }
1941 
1942     /**
1943      * Find the appropriate GET method for an invoke dynamic call.
1944      *
1945      * @param desc     the call site descriptor
1946      * @param request  the link request
1947      *
1948      * @return GuardedInvocation to be invoked at call site.
1949      */
1950     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1951         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
1952 
1953         String name = NashornCallSiteDescriptor.getOperand(desc);
1954         if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
1955             if (Global.isBuiltinFunctionPrototypeApply()) {
1956                 name = "call";
1957             }
1958         }
1959 
1960         if (request.isCallSiteUnstable() || hasWithScope()) {
1961             return findMegaMorphicGetMethod(desc, name, NashornCallSiteDescriptor.isMethodFirstOperation(desc));
1962         }
1963 
1964         final FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this);
1965         MethodHandle mh;
1966 
1967         if (find == null) {
1968             if (!NashornCallSiteDescriptor.isMethodFirstOperation(desc)) {
1969                 return noSuchProperty(desc, request);
1970             } else {
1971                 return noSuchMethod(desc, request);
1972             }
1973         }
1974 
1975         final GlobalConstants globalConstants = getGlobalConstants();
1976         if (globalConstants != null) {
1977             final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
1978             if (cinv != null) {
1979                 return cinv;
1980             }
1981         }
1982 
1983         final Class<?> returnType = desc.getMethodType().returnType();
1984         final Property property   = find.getProperty();
1985 
1986         final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
1987                 NashornCallSiteDescriptor.getProgramPoint(desc) :
1988                 UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
1989 
1990         mh = find.getGetter(returnType, programPoint, request);
1991         // Get the appropriate guard for this callsite and property.
1992         final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck);
1993         final ScriptObject owner = find.getOwner();
1994         final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
1995 
1996         final SwitchPoint[] protoSwitchPoints;
1997 
1998         if (mh == null) {
1999             mh = Lookup.emptyGetter(returnType);
2000             protoSwitchPoints = getProtoSwitchPoints(name, owner);
2001         } else if (!find.isSelf()) {
2002             assert mh.type().returnType().equals(returnType) :
2003                     "return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
2004             if (!property.isAccessorProperty()) {
2005                 // Add a filter that replaces the self object with the prototype owning the property.
2006                 mh = addProtoFilter(mh, find.getProtoChainLength());
2007             }
2008             protoSwitchPoints = getProtoSwitchPoints(name, owner);
2009         } else {
2010             protoSwitchPoints = null;
2011         }
2012 
2013         final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception);
2014         return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
2015     }
2016 
2017     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
2018         Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: ", desc, " ", name + " ", isMethod);
2019         final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc));
2020         final MethodHandle guard   = getScriptObjectGuard(desc.getMethodType(), true);
2021         return new GuardedInvocation(invoker, guard);
2022     }
2023 
2024     @SuppressWarnings("unused")
2025     private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) {
2026         final FindProperty find = findProperty(key, true, isScope, this);
2027         if (find != null) {
2028             // If this is a method invocation, and found property has a different self object then this,
2029             // then return a function bound to the self object. This is the case for functions in with expressions.
2030             final Object value = find.getObjectValue();
2031             if (isMethod && value instanceof ScriptFunction && find.getSelf() != this && !find.getSelf().isInternal()) {
2032                 return ((ScriptFunction) value).createBound(find.getSelf(), ScriptRuntime.EMPTY_ARRAY);
2033             }
2034             return value;
2035         }
2036 
2037         return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT);
2038     }
2039 
2040     // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
2041     @SuppressWarnings("unused")
2042     private void declareAndSet(final String key, final Object value) {
2043         final PropertyMap oldMap = getMap();
2044         final FindProperty find = findProperty(key, false);
2045         assert find != null;
2046 
2047         final Property property = find.getProperty();
2048         assert property != null;
2049         assert property.needsDeclaration();
2050 
2051         final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
2052         setMap(newMap);
2053         set(key, value, 0);
2054     }
2055 
2056     /**
2057      * Find the appropriate GETINDEX method for an invoke dynamic call.
2058      *
2059      * @param desc    the call site descriptor
2060      * @param request the link request
2061      *
2062      * @return GuardedInvocation to be invoked at call site.
2063      */
2064     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2065         final MethodType callType                = desc.getMethodType();
2066         final Class<?>   returnType              = callType.returnType();
2067         final Class<?>   returnClass             = returnType.isPrimitive() ? returnType : Object.class;
2068         final Class<?>   keyClass                = callType.parameterType(1);
2069         final boolean    explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2070 
2071         final String name;
2072         if (returnClass.isPrimitive()) {
2073             //turn e.g. get with a double into getDouble
2074             final String returnTypeName = returnClass.getName();
2075             name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
2076         } else {
2077             name = "get";
2078         }
2079 
2080         final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
2081         return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2082     }
2083 
2084     private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) {
2085         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck);
2086     }
2087 
2088     /**
2089      * Find a handle for a getIndex method
2090      * @param returnType     return type for getter
2091      * @param name           name
2092      * @param elementType    index type for getter
2093      * @param desc           call site descriptor
2094      * @return method handle for getter
2095      */
2096     private static MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
2097         if (!returnType.isPrimitive()) {
2098             return findOwnMH_V(name, returnType, elementType);
2099         }
2100 
2101         return MH.insertArguments(
2102                 findOwnMH_V(name, returnType, elementType, int.class),
2103                 2,
2104                 NashornCallSiteDescriptor.isOptimistic(desc) ?
2105                         NashornCallSiteDescriptor.getProgramPoint(desc) :
2106                         INVALID_PROGRAM_POINT);
2107     }
2108 
2109     /**
2110      * Get a switch point for a property with the given {@code name} that will be invalidated when
2111      * the property definition is changed in this object's prototype chain. Returns {@code null} if
2112      * the property is defined in this object itself.
2113      *
2114      * @param name the property name
2115      * @param owner the property owner, null if property is not defined
2116      * @return a SwitchPoint or null
2117      */
2118     public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) {
2119         if (owner == this || getProto() == null) {
2120             return null;
2121         }
2122 
2123         final Set<SwitchPoint> switchPoints = new HashSet<>();
2124         SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name);
2125 
2126         if (switchPoint == null) {
2127             switchPoint = new SwitchPoint();
2128             for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
2129                 obj.getProto().getMap().addSwitchPoint(name, switchPoint);
2130             }
2131         }
2132 
2133         switchPoints.add(switchPoint);
2134 
2135         for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
2136             final SwitchPoint sharedProtoSwitchPoint = obj.getProto().getMap().getSharedProtoSwitchPoint();
2137             if (sharedProtoSwitchPoint != null && !sharedProtoSwitchPoint.hasBeenInvalidated()) {
2138                 switchPoints.add(sharedProtoSwitchPoint);


2139             }
2140         }
2141 

2142         return switchPoints.toArray(new SwitchPoint[0]);
2143     }
2144 
2145     // Similar to getProtoSwitchPoints method above, but used for additional prototype switchpoints of
2146     // properties that are known not to exist, e.g. the original property name in a __noSuchProperty__ invocation.
2147     final SwitchPoint getProtoSwitchPoint(final String name) {
2148         if (getProto() == null) {
2149             return null;
2150         }
2151 
2152         SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name);
2153 
2154         if (switchPoint == null) {
2155             switchPoint = new SwitchPoint();
2156             for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) {
2157                 obj.getProto().getMap().addSwitchPoint(name, switchPoint);
2158             }
2159         }
2160 
2161         return switchPoint;
2162     }
2163 
2164     private void checkSharedProtoMap() {
2165         // Check if our map has an expected shared prototype property map. If it has, make sure that
2166         // the prototype map has not been invalidated, and that it does match the actual map of the prototype.
2167         if (getMap().isInvalidSharedMapFor(getProto())) {
2168             // Change our own map to one that does not assume a shared prototype map.
2169             setMap(getMap().makeUnsharedCopy());
2170         }
2171     }
2172 
2173     /**
2174      * Find the appropriate SET method for an invoke dynamic call.
2175      *
2176      * @param desc    the call site descriptor
2177      * @param request the link request
2178      *
2179      * @return GuardedInvocation to be invoked at call site.
2180      */
2181     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2182         final String name = NashornCallSiteDescriptor.getOperand(desc);
2183 
2184         if (request.isCallSiteUnstable() || hasWithScope()) {
2185             return findMegaMorphicSetMethod(desc, name);
2186         }
2187 
2188         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2189 
2190         /*
2191          * If doing property set on a scope object, we should stop proto search on the first
2192          * non-scope object. Without this, for example, when assigning "toString" on global scope,
2193          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
2194          *
2195          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
2196          */
2197         FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this);
2198 
2199         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
2200         if (find != null && find.isInheritedOrdinaryProperty()) {
2201             // We should still check if inherited data property is not writable
2202             if (isExtensible() && !find.getProperty().isWritable()) {
2203                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2204             }
2205             // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well.
2206             if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) {
2207                 find = null;
2208             }
2209         }
2210 
2211         if (find != null) {
2212             if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
2213                 if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) {
2214                     throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode.
2215                 }
2216                 // Existing, non-writable data property
2217                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2218             }
2219             if (!find.getProperty().hasNativeSetter()) {
2220                 // Existing accessor property without setter
2221                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.has.no.setter", true);
2222             }
2223         } else {
2224             if (!isExtensible()) {
2225                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false);
2226             }
2227         }
2228 
2229         final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
2230 
2231         final GlobalConstants globalConstants = getGlobalConstants();
2232         if (globalConstants != null) {
2233             final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
2234             if (cinv != null) {
2235                 return cinv;
2236             }
2237         }
2238 
2239         return inv;
2240     }
2241 
2242     private GlobalConstants getGlobalConstants() {
2243         // Avoid hitting getContext() which might be costly for a non-Global unless needed.
2244         return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
2245     }
2246 
2247     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
2248         final String  name = NashornCallSiteDescriptor.getOperand(desc);
2249         if (NashornCallSiteDescriptor.isStrict(desc)) {
2250             throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this));
2251         }
2252         assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
2253         return new GuardedInvocation(
2254                 Lookup.EMPTY_SETTER,
2255                 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
2256                 getProtoSwitchPoints(name, null),
2257                 explicitInstanceOfCheck ? null : ClassCastException.class);
2258     }
2259 
2260     @SuppressWarnings("unused")
2261     private boolean extensionCheck(final boolean isStrict, final String name) {
2262         if (isExtensible()) {
2263             return true; //go on and do the set. this is our guard
2264         } else if (isStrict) {
2265             //throw an error for attempting to do the set in strict mode
2266             throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this));
2267         } else {
2268             //not extensible, non strict - this is a nop
2269             return false;
2270         }
2271     }
2272 
2273     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
2274         Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic setter: ", desc, " ", name);
2275         final MethodType        type = desc.getMethodType().insertParameterTypes(1, Object.class);
2276         //never bother with ClassCastExceptionGuard for megamorphic callsites
2277         final GuardedInvocation inv = findSetIndexMethod(desc, false, type);
2278         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
2279     }
2280 
2281     @SuppressWarnings("unused")
2282     private static Object globalFilter(final Object object) {
2283         ScriptObject sobj = (ScriptObject) object;
2284         while (sobj != null && !(sobj instanceof Global)) {
2285             sobj = sobj.getProto();
2286         }
2287         return sobj;
2288     }
2289 
2290     /**
2291      * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray}
2292      * provides special quick accessor linkage for continuous arrays that are represented as Java arrays
2293      *
2294      * @param desc    call site descriptor
2295      * @param request link request
2296      *
2297      * @return GuardedInvocation to be invoked at call site.
2298      */
2299     protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
2300         return findSetIndexMethod(desc, explicitInstanceOfCheck(desc, request), desc.getMethodType());
2301     }
2302 
2303     /**
2304      * Find the appropriate SETINDEX method for an invoke dynamic call.
2305      *
2306      * @param desc  the call site descriptor
2307      * @param explicitInstanceOfCheck add an explicit instanceof check?
2308      * @param callType the method type at the call site
2309      *
2310      * @return GuardedInvocation to be invoked at call site.
2311      */
2312     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) {
2313         assert callType.parameterCount() == 3;
2314         final Class<?> keyClass   = callType.parameterType(1);
2315         final Class<?> valueClass = callType.parameterType(2);
2316 
2317         MethodHandle methodHandle = findOwnMH_V("set", void.class, keyClass, valueClass, int.class);
2318         methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc));
2319 
2320         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2321     }
2322 
2323     /**
2324      * Fall back if a function property is not found.
2325      * @param desc The call site descriptor
2326      * @param request the link request
2327      * @return GuardedInvocation to be invoked at call site.
2328      */
2329     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2330         final String       name      = NashornCallSiteDescriptor.getOperand(desc);
2331         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
2332         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
2333 
2334         if (find == null) {
2335             return noSuchProperty(desc, request)
2336                     // Add proto switchpoint to switch from no-such-property to no-such-method if it is ever defined.
2337                     .addSwitchPoint(getProtoSwitchPoint(NO_SUCH_METHOD_NAME));
2338         }
2339 
2340         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2341 
2342         final Object value = find.getObjectValue();
2343         if (!(value instanceof ScriptFunction)) {
2344             return createEmptyGetter(desc, explicitInstanceOfCheck, name);
2345         }
2346 
2347         final ScriptFunction func = (ScriptFunction)value;
2348         final Object         thiz = scopeCall && func.isStrict() ? UNDEFINED : this;
2349         // TODO: It'd be awesome if we could bind "name" without binding "this".
2350         // Since we're binding this we must use an identity guard here.
2351         return new GuardedInvocation(
2352                 MH.dropArguments(
2353                         MH.constant(
2354                                 ScriptFunction.class,
2355                                 func.createBound(thiz, new Object[] { name })),
2356                         0,
2357                         Object.class),
2358                 NashornGuards.combineGuards(
2359                         NashornGuards.getIdentityGuard(this),
2360                         NashornGuards.getMapGuard(getMap(), true)))
2361                 // Add a protoype switchpoint for the original name so this gets invalidated if it is ever defined.
2362                 .addSwitchPoint(getProtoSwitchPoint(name));
2363     }
2364 
2365     /**
2366      * Fall back if a property is not found.
2367      * @param desc the call site descriptor.
2368      * @param request the link request
2369      * @return GuardedInvocation to be invoked at call site.
2370      */
2371     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
2372         final String       name        = NashornCallSiteDescriptor.getOperand(desc);
2373         final FindProperty find        = findProperty(NO_SUCH_PROPERTY_NAME, true);
2374         final boolean      scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
2375 
2376         if (find != null) {
2377             final Object   value = find.getObjectValue();
2378             ScriptFunction func  = null;
2379             MethodHandle   mh    = null;
2380 
2381             if (value instanceof ScriptFunction) {
2382                 func = (ScriptFunction)value;
2383                 mh   = getCallMethodHandle(func, desc.getMethodType(), name);
2384             }
2385 
2386             if (mh != null) {
2387                 assert func != null;
2388                 if (scopeAccess && func.isStrict()) {
2389                     mh = bindTo(mh, UNDEFINED);
2390                 }
2391 
2392                 return new GuardedInvocation(
2393                         mh,
2394                         find.isSelf()?
2395                             getKnownFunctionPropertyGuardSelf(
2396                                 getMap(),
2397                                 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2398                                 func)
2399                             :
2400                             //TODO this always does a scriptobject check
2401                             getKnownFunctionPropertyGuardProto(
2402                                 getMap(),
2403                                 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2404                                 find.getProtoChainLength(),
2405                                 func),
2406                         getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()),
2407                         //TODO this doesn't need a ClassCastException as guard always checks script object
2408                         null)
2409                         // Add a protoype switchpoint for the original name so this gets invalidated if it is ever defined.
2410                         .addSwitchPoint(getProtoSwitchPoint(name));
2411             }
2412         }
2413 
2414         if (scopeAccess) {
2415             throw referenceError("not.defined", name);
2416         }
2417 
2418         return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name);
2419     }
2420 
2421     /**
2422      * Invoke fall back if a property is not found.
2423      * @param key Name of property.
2424      * @param isScope is this a scope access?
2425      * @param programPoint program point
2426      * @return Result from call.
2427      */
2428     protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) {
2429         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
2430         final Object func = (find != null)? find.getObjectValue() : null;
2431 
2432         Object ret = UNDEFINED;
2433         if (func instanceof ScriptFunction) {
2434             final ScriptFunction sfunc = (ScriptFunction)func;
2435             final Object self = isScope && sfunc.isStrict()? UNDEFINED : this;
2436             ret = ScriptRuntime.apply(sfunc, self, key);
2437         } else if (isScope) {
2438             throw referenceError("not.defined", key.toString());
2439         }
2440 
2441         if (isValid(programPoint)) {
2442             throw new UnwarrantedOptimismException(ret, programPoint);
2443         }
2444 
2445         return ret;
2446     }
2447 
2448 
2449     /**
2450      * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
2451      * @param name the method name
2452      * @param isScope is this a scope access?
2453      * @return the bound function, or undefined
2454      */
2455     private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) {
2456         final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
2457 
2458         if (find == null) {
2459             return invokeNoSuchProperty(name, isScope, programPoint);
2460         }
2461 
2462         final Object value = find.getObjectValue();
2463         if (!(value instanceof ScriptFunction)) {
2464             if (isScope) {
2465                 throw referenceError("not.defined", name);
2466             }
2467             return UNDEFINED;
2468         }
2469 
2470         final ScriptFunction func = (ScriptFunction)value;
2471         final Object self = isScope && func.isStrict()? UNDEFINED : this;
2472         return func.createBound(self, new Object[] {name});
2473     }
2474 
2475     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) {
2476         if (NashornCallSiteDescriptor.isOptimistic(desc)) {
2477             throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
2478         }
2479 
2480         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
2481                 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null),
2482                 explicitInstanceOfCheck ? null : ClassCastException.class);
2483     }
2484 
2485     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2486         protected T[] values;
2487         protected final ScriptObject object;
2488         private int index;
2489 
2490         ScriptObjectIterator(final ScriptObject object) {
2491             this.object = object;
2492         }
2493 
2494         protected abstract void init();
2495 
2496         @Override
2497         public boolean hasNext() {
2498             if (values == null) {
2499                 init();
2500             }
2501             return index < values.length;
2502         }
2503 
2504         @Override
2505         public T next() {
2506             if (values == null) {
2507                 init();
2508             }
2509             return values[index++];
2510         }
2511 
2512         @Override
2513         public void remove() {
2514             throw new UnsupportedOperationException("remove");
2515         }
2516     }
2517 
2518     private static class KeyIterator extends ScriptObjectIterator<String> {
2519         KeyIterator(final ScriptObject object) {
2520             super(object);
2521         }
2522 
2523         @Override
2524         protected void init() {
2525             final Set<String> keys = new LinkedHashSet<>();
2526             final Set<String> nonEnumerable = new HashSet<>();
2527             for (ScriptObject self = object; self != null; self = self.getProto()) {
2528                 keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable)));
2529             }
2530             this.values = keys.toArray(new String[0]);
2531         }
2532     }
2533 
2534     private static class ValueIterator extends ScriptObjectIterator<Object> {
2535         ValueIterator(final ScriptObject object) {
2536             super(object);
2537         }
2538 
2539         @Override
2540         protected void init() {
2541             final ArrayList<Object> valueList = new ArrayList<>();
2542             final Set<String> nonEnumerable = new HashSet<>();
2543             for (ScriptObject self = object; self != null; self = self.getProto()) {
2544                 for (final String key : self.getOwnKeys(String.class, false, nonEnumerable)) {
2545                     valueList.add(self.get(key));
2546                 }
2547             }
2548             this.values = valueList.toArray(new Object[0]);
2549         }
2550     }
2551 
2552     /**
2553      * Add a spill property for the given key.
2554      * @param key    Property key.
2555      * @param flags  Property flags.
2556      * @return Added property.
2557      */
2558     private Property addSpillProperty(final Object key, final int flags, final Object value, final boolean hasInitialValue) {
2559         final PropertyMap propertyMap = getMap();
2560         final int fieldSlot  = propertyMap.getFreeFieldSlot();
2561         final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
2562 
2563         Property property;
2564         if (fieldSlot > -1) {
2565             property = hasInitialValue ?
2566                 new AccessorProperty(key, propertyFlags, fieldSlot, this, value) :
2567                 new AccessorProperty(key, propertyFlags, getClass(), fieldSlot);
2568             property = addOwnProperty(property);
2569         } else {
2570             final int spillSlot = propertyMap.getFreeSpillSlot();
2571             property = hasInitialValue ?
2572                 new SpillProperty(key, propertyFlags, spillSlot, this, value) :
2573                 new SpillProperty(key, propertyFlags, spillSlot);
2574             property = addOwnProperty(property);
2575             ensureSpillSize(property.getSlot());
2576         }
2577         return property;
2578     }
2579 
2580     /**
2581      * Add a spill entry for the given key.
2582      * @param key Property key.
2583      * @return Setter method handle.
2584      */
2585     MethodHandle addSpill(final Class<?> type, final String key) {
2586         return addSpillProperty(key, 0, null, false).getSetter(type, getMap());
2587     }
2588 
2589     /**
2590      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2591      * fewer parameters than declared and other things that JavaScript allows. This might involve
2592      * creating collectors.
2593      *
2594      * @param methodHandle method handle for invoke
2595      * @param callType     type of the call
2596      *
2597      * @return method handle with adjusted arguments
2598      */
2599     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2600         return pairArguments(methodHandle, callType, null);
2601     }
2602 
2603     /**
2604      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2605      * fewer parameters than declared and other things that JavaScript allows. This might involve
2606      * creating collectors.
2607      *
2608      * Make sure arguments are paired correctly.
2609      * @param methodHandle MethodHandle to adjust.
2610      * @param callType     MethodType of the call site.
2611      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2612      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2613      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2614      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2615      *
2616      * @return method handle with adjusted arguments
2617      */
2618     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2619         final MethodType methodType = methodHandle.type();
2620         if (methodType.equals(callType.changeReturnType(methodType.returnType()))) {
2621             return methodHandle;
2622         }
2623 
2624         final int parameterCount = methodType.parameterCount();
2625         final int callCount      = callType.parameterCount();
2626 
2627         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2628         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 &&
2629                 callType.parameterType(callCount - 1).isArray();
2630 
2631         if (isCalleeVarArg) {
2632             return isCallerVarArg ?
2633                 methodHandle :
2634                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2635         }
2636 
2637         if (isCallerVarArg) {
2638             return adaptHandleToVarArgCallSite(methodHandle, callCount);
2639         }
2640 
2641         if (callCount < parameterCount) {
2642             final int      missingArgs = parameterCount - callCount;
2643             final Object[] fillers     = new Object[missingArgs];
2644 
2645             Arrays.fill(fillers, UNDEFINED);
2646 
2647             if (isCalleeVarArg) {
2648                 fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY;
2649             }
2650 
2651             return MH.insertArguments(
2652                 methodHandle,
2653                 parameterCount - missingArgs,
2654                 fillers);
2655         }
2656 
2657         if (callCount > parameterCount) {
2658             final int discardedArgs = callCount - parameterCount;
2659 
2660             final Class<?>[] discards = new Class<?>[discardedArgs];
2661             Arrays.fill(discards, Object.class);
2662 
2663             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2664         }
2665 
2666         return methodHandle;
2667     }
2668 
2669     static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
2670         final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
2671         return MH.filterArguments(
2672             MH.asSpreader(
2673                 mh,
2674                 Object[].class,
2675                 spreadArgs),
2676             callSiteParamCount - 1,
2677             MH.insertArguments(
2678                 TRUNCATINGFILTER,
2679                 0,
2680                 spreadArgs)
2681             );
2682     }
2683 
2684     @SuppressWarnings("unused")
2685     private static Object[] truncatingFilter(final int n, final Object[] array) {
2686         final int length = array == null ? 0 : array.length;
2687         if (n == length) {
2688             return array == null ? ScriptRuntime.EMPTY_ARRAY : array;
2689         }
2690 
2691         final Object[] newArray = new Object[n];
2692 
2693         if (array != null) {
2694             System.arraycopy(array, 0, newArray, 0, Math.min(n, length));
2695         }
2696 
2697         if (length < n) {
2698             final Object fill = UNDEFINED;
2699 
2700             for (int i = length; i < n; i++) {
2701                 newArray[i] = fill;
2702             }
2703         }
2704 
2705         return newArray;
2706     }
2707 
2708     /**
2709       * Numeric length setter for length property
2710       *
2711       * @param newLength new length to set
2712       */
2713     public final void setLength(final long newLength) {
2714         final ArrayData data = getArray();
2715         final long arrayLength = data.length();
2716         if (newLength == arrayLength) {
2717             return;
2718         }
2719 
2720         if (newLength > arrayLength) {
2721             setArray(data.ensure(newLength - 1).safeDelete(arrayLength, newLength - 1, false));
2722             return;
2723         }
2724 
2725         if (newLength < arrayLength) {
2726            long actualLength = newLength;
2727 
2728            // Check for numeric keys in property map and delete them or adjust length, depending on whether
2729            // they're defined as configurable. See ES5 #15.4.5.2
2730            if (getMap().containsArrayKeys()) {
2731 
2732                for (long l = arrayLength - 1; l >= newLength; l--) {
2733                    final FindProperty find = findProperty(JSType.toString(l), false);
2734 
2735                    if (find != null) {
2736 
2737                        if (find.getProperty().isConfigurable()) {
2738                            deleteOwnProperty(find.getProperty());
2739                        } else {
2740                            actualLength = l + 1;
2741                            break;
2742                        }
2743                    }
2744                }
2745            }
2746 
2747            setArray(data.shrink(actualLength));
2748            data.setLength(actualLength);
2749        }
2750     }
2751 
2752     private int getInt(final int index, final Object key, final int programPoint) {
2753         if (isValidArrayIndex(index)) {
2754             for (ScriptObject object = this; ; ) {
2755                 if (object.getMap().containsArrayKeys()) {
2756                     final FindProperty find = object.findProperty(key, false);
2757 
2758                     if (find != null) {
2759                         return getIntValue(find, programPoint);
2760                     }
2761                 }
2762 
2763                 if ((object = object.getProto()) == null) {
2764                     break;
2765                 }
2766 
2767                 final ArrayData array = object.getArray();
2768 
2769                 if (array.has(index)) {
2770                     return isValid(programPoint) ?
2771                         array.getIntOptimistic(index, programPoint) :
2772                         array.getInt(index);
2773                 }
2774             }
2775         } else {
2776             final FindProperty find = findProperty(key, true);
2777 
2778             if (find != null) {
2779                 return getIntValue(find, programPoint);
2780             }
2781         }
2782 
2783         return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint));
2784     }
2785 
2786     @Override
2787     public int getInt(final Object key, final int programPoint) {
2788         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2789         final int       index        = getArrayIndex(primitiveKey);
2790         final ArrayData array        = getArray();
2791 
2792         if (array.has(index)) {
2793             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2794         }
2795 
2796         return getInt(index, JSType.toPropertyKey(primitiveKey), programPoint);
2797     }
2798 
2799     @Override
2800     public int getInt(final double key, final int programPoint) {
2801         final int       index = getArrayIndex(key);
2802         final ArrayData array = getArray();
2803 
2804         if (array.has(index)) {
2805             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2806         }
2807 
2808         return getInt(index, JSType.toString(key), programPoint);
2809     }
2810 
2811     @Override
2812     public int getInt(final int key, final int programPoint) {
2813         final int       index = getArrayIndex(key);
2814         final ArrayData array = getArray();
2815 
2816         if (array.has(index)) {
2817             return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key);
2818         }
2819 
2820         return getInt(index, JSType.toString(key), programPoint);
2821     }
2822 
2823     private double getDouble(final int index, final Object key, final int programPoint) {
2824         if (isValidArrayIndex(index)) {
2825             for (ScriptObject object = this; ; ) {
2826                 if (object.getMap().containsArrayKeys()) {
2827                     final FindProperty find = object.findProperty(key, false);
2828                     if (find != null) {
2829                         return getDoubleValue(find, programPoint);
2830                     }
2831                 }
2832 
2833                 if ((object = object.getProto()) == null) {
2834                     break;
2835                 }
2836 
2837                 final ArrayData array = object.getArray();
2838 
2839                 if (array.has(index)) {
2840                     return isValid(programPoint) ?
2841                         array.getDoubleOptimistic(index, programPoint) :
2842                         array.getDouble(index);
2843                 }
2844             }
2845         } else {
2846             final FindProperty find = findProperty(key, true);
2847 
2848             if (find != null) {
2849                 return getDoubleValue(find, programPoint);
2850             }
2851         }
2852 
2853         return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT));
2854     }
2855 
2856     @Override
2857     public double getDouble(final Object key, final int programPoint) {
2858         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2859         final int       index        = getArrayIndex(primitiveKey);
2860         final ArrayData array        = getArray();
2861 
2862         if (array.has(index)) {
2863             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2864         }
2865 
2866         return getDouble(index, JSType.toPropertyKey(primitiveKey), programPoint);
2867     }
2868 
2869     @Override
2870     public double getDouble(final double key, final int programPoint) {
2871         final int       index = getArrayIndex(key);
2872         final ArrayData array = getArray();
2873 
2874         if (array.has(index)) {
2875             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2876         }
2877 
2878         return getDouble(index, JSType.toString(key), programPoint);
2879     }
2880 
2881     @Override
2882     public double getDouble(final int key, final int programPoint) {
2883         final int       index = getArrayIndex(key);
2884         final ArrayData array = getArray();
2885 
2886         if (array.has(index)) {
2887             return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key);
2888         }
2889 
2890         return getDouble(index, JSType.toString(key), programPoint);
2891     }
2892 
2893     private Object get(final int index, final Object key) {
2894         if (isValidArrayIndex(index)) {
2895             for (ScriptObject object = this; ; ) {
2896                 if (object.getMap().containsArrayKeys()) {
2897                     final FindProperty find = object.findProperty(key, false);
2898 
2899                     if (find != null) {
2900                         return find.getObjectValue();
2901                     }
2902                 }
2903 
2904                 if ((object = object.getProto()) == null) {
2905                     break;
2906                 }
2907 
2908                 final ArrayData array = object.getArray();
2909 
2910                 if (array.has(index)) {
2911                     return array.getObject(index);
2912                 }
2913             }
2914         } else {
2915             final FindProperty find = findProperty(key, true);
2916 
2917             if (find != null) {
2918                 return find.getObjectValue();
2919             }
2920         }
2921 
2922         return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT);
2923     }
2924 
2925     @Override
2926     public Object get(final Object key) {
2927         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2928         final int       index        = getArrayIndex(primitiveKey);
2929         final ArrayData array        = getArray();
2930 
2931         if (array.has(index)) {
2932             return array.getObject(index);
2933         }
2934 
2935         return get(index, JSType.toPropertyKey(primitiveKey));
2936     }
2937 
2938     @Override
2939     public Object get(final double key) {
2940         final int index = getArrayIndex(key);
2941         final ArrayData array = getArray();
2942 
2943         if (array.has(index)) {
2944             return array.getObject(index);
2945         }
2946 
2947         return get(index, JSType.toString(key));
2948     }
2949 
2950     @Override
2951     public Object get(final int key) {
2952         final int index = getArrayIndex(key);
2953         final ArrayData array = getArray();
2954 
2955         if (array.has(index)) {
2956             return array.getObject(index);
2957         }
2958 
2959         return get(index, JSType.toString(key));
2960     }
2961 
2962     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) {
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, callSiteFlags, key, value);
2968                 return true;
2969             }
2970         }
2971         return false;
2972     }
2973 
2974     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) {
2975          if (getMap().containsArrayKeys()) {
2976             final String       key  = JSType.toString(longIndex);
2977             final FindProperty find = findProperty(key, true);
2978             if (find != null) {
2979                 setObject(find, callSiteFlags, key, value);
2980                 return true;
2981             }
2982         }
2983         return false;
2984     }
2985 
2986     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) {
2987         if (getMap().containsArrayKeys()) {
2988             final String       key  = JSType.toString(longIndex);
2989             final FindProperty find = findProperty(key, true);
2990             if (find != null) {
2991                 setObject(find, callSiteFlags, key, value);
2992                 return true;
2993             }
2994         }
2995         return false;
2996     }
2997 
2998     //value agnostic
2999     private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) {
3000         if (longIndex >= oldLength) {
3001             if (!isExtensible()) {
3002                 if (isStrictFlag(callSiteFlags)) {
3003                     throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this));
3004                 }
3005                 return true;
3006             }
3007             setArray(getArray().ensure(longIndex));
3008         }
3009         return false;
3010     }
3011 
3012     private void doesNotHave(final int index, final int value, final int callSiteFlags) {
3013         final long oldLength = getArray().length();
3014         final long longIndex = ArrayIndex.toLongIndex(index);
3015         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3016             final boolean strict = isStrictFlag(callSiteFlags);
3017             setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict));
3018         }
3019     }
3020 
3021     private void doesNotHave(final int index, final double value, final int callSiteFlags) {
3022         final long oldLength = getArray().length();
3023         final long longIndex = ArrayIndex.toLongIndex(index);
3024         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3025             final boolean strict = isStrictFlag(callSiteFlags);
3026             setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict));
3027         }
3028     }
3029 
3030     private void doesNotHave(final int index, final Object value, final int callSiteFlags) {
3031         final long oldLength = getArray().length();
3032         final long longIndex = ArrayIndex.toLongIndex(index);
3033         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3034             final boolean strict = isStrictFlag(callSiteFlags);
3035             setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict));
3036         }
3037     }
3038 
3039     /**
3040      * This is the most generic of all Object setters. Most of the others use this in some form.
3041      * TODO: should be further specialized
3042      *
3043      * @param find          found property
3044      * @param callSiteFlags callsite flags
3045      * @param key           property key
3046      * @param value         property value
3047      */
3048     public final void setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value) {
3049         FindProperty f = find;
3050 
3051         invalidateGlobalConstant(key);
3052 
3053         if (f != null && f.isInheritedOrdinaryProperty()) {
3054             final boolean isScope = isScopeFlag(callSiteFlags);
3055             // If the start object of the find is not this object it means the property was found inside a
3056             // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set'
3057             // to the 'with' object.
3058             // Note that although a 'set' operation involving a with statement follows scope rules outside
3059             // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists),
3060             // it follows non-scope rules inside the 'with' expression (set is performed on the top level object).
3061             // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object.
3062             if (isScope && f.getSelf() != this) {
3063                 f.getSelf().setObject(null, 0, key, value);
3064                 return;
3065             }
3066             // Setting a property should not modify the property in prototype unless this is a scope callsite
3067             // and the owner is a scope object as well (with the exception of 'with' statement handled above).
3068             if (!isScope || !f.getOwner().isScope()) {
3069                 f = null;
3070             }
3071         }
3072 
3073         if (f != null) {
3074             if (!f.getProperty().isWritable() || !f.getProperty().hasNativeSetter()) {
3075                 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
3076                     throw typeError("assign.constant", key.toString()); // Overwriting ES6 const should throw also in non-strict mode.
3077                 }
3078                 if (isStrictFlag(callSiteFlags)) {
3079                     throw typeError(
3080                             f.getProperty().isAccessorProperty() ? "property.has.no.setter" : "property.not.writable",
3081                             key.toString(), ScriptRuntime.safeToString(this));
3082                 }
3083                 return;
3084             }
3085 
3086             f.setValue(value, isStrictFlag(callSiteFlags));
3087 
3088         } else if (!isExtensible()) {
3089             if (isStrictFlag(callSiteFlags)) {
3090                 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this));
3091             }
3092         } else {
3093             ScriptObject sobj = this;
3094             // undefined scope properties are set in the global object.
3095             if (isScope()) {
3096                 while (sobj != null && !(sobj instanceof Global)) {
3097                     sobj = sobj.getProto();
3098                 }
3099                 assert sobj != null : "no parent global object in scope";
3100             }
3101             //this will unbox any Number object to its primitive type in case the
3102             //property supports primitive types, so it doesn't matter that it comes
3103             //in as an Object.
3104             sobj.addSpillProperty(key, 0, value, true);
3105         }
3106     }
3107 
3108     @Override
3109     public void set(final Object key, final int value, final int callSiteFlags) {
3110         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3111         final int    index        = getArrayIndex(primitiveKey);
3112 
3113         if (isValidArrayIndex(index)) {
3114             final ArrayData data = getArray();
3115             if (data.has(index)) {
3116                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3117             } else {
3118                 doesNotHave(index, value, callSiteFlags);
3119             }
3120 
3121             return;
3122         }
3123 
3124         final Object propName = JSType.toPropertyKey(primitiveKey);
3125         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3126     }
3127 
3128     @Override
3129     public void set(final Object key, final double value, final int callSiteFlags) {
3130         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3131         final int    index        = getArrayIndex(primitiveKey);
3132 
3133         if (isValidArrayIndex(index)) {
3134             final ArrayData data = getArray();
3135             if (data.has(index)) {
3136                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3137             } else {
3138                 doesNotHave(index, value, callSiteFlags);
3139             }
3140 
3141             return;
3142         }
3143 
3144         final Object propName = JSType.toPropertyKey(primitiveKey);
3145         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3146     }
3147 
3148     @Override
3149     public void set(final Object key, final Object value, final int callSiteFlags) {
3150         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3151         final int    index        = getArrayIndex(primitiveKey);
3152 
3153         if (isValidArrayIndex(index)) {
3154             final ArrayData data = getArray();
3155             if (data.has(index)) {
3156                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3157             } else {
3158                 doesNotHave(index, value, callSiteFlags);
3159             }
3160 
3161             return;
3162         }
3163 
3164         final Object propName = JSType.toPropertyKey(primitiveKey);
3165         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3166     }
3167 
3168     @Override
3169     public void set(final double key, final int value, final int callSiteFlags) {
3170         final int index = getArrayIndex(key);
3171 
3172         if (isValidArrayIndex(index)) {
3173             final ArrayData data = getArray();
3174             if (data.has(index)) {
3175                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3176             } else {
3177                 doesNotHave(index, value, callSiteFlags);
3178             }
3179 
3180             return;
3181         }
3182 
3183         final String propName = JSType.toString(key);
3184         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3185     }
3186 
3187     @Override
3188     public void set(final double key, final double value, final int callSiteFlags) {
3189         final int index = getArrayIndex(key);
3190 
3191         if (isValidArrayIndex(index)) {
3192             final ArrayData data = getArray();
3193             if (data.has(index)) {
3194                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3195             } else {
3196                 doesNotHave(index, value, callSiteFlags);
3197             }
3198 
3199             return;
3200         }
3201 
3202         final String propName = JSType.toString(key);
3203         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3204     }
3205 
3206     @Override
3207     public void set(final double key, final Object value, final int callSiteFlags) {
3208         final int index = getArrayIndex(key);
3209 
3210         if (isValidArrayIndex(index)) {
3211             final ArrayData data = getArray();
3212             if (data.has(index)) {
3213                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3214             } else {
3215                 doesNotHave(index, value, callSiteFlags);
3216             }
3217 
3218             return;
3219         }
3220 
3221         final String propName = JSType.toString(key);
3222         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3223     }
3224 
3225     @Override
3226     public void set(final int key, final int value, final int callSiteFlags) {
3227         final int index = getArrayIndex(key);
3228         if (isValidArrayIndex(index)) {
3229             if (getArray().has(index)) {
3230                 final ArrayData data = getArray();
3231                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3232             } else {
3233                 doesNotHave(index, value, callSiteFlags);
3234             }
3235             return;
3236         }
3237 
3238         final String propName = JSType.toString(key);
3239         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3240     }
3241 
3242     @Override
3243     public void set(final int key, final double value, final int callSiteFlags) {
3244         final int index = getArrayIndex(key);
3245 
3246         if (isValidArrayIndex(index)) {
3247             final ArrayData data = getArray();
3248             if (data.has(index)) {
3249                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3250             } else {
3251                 doesNotHave(index, value, callSiteFlags);
3252             }
3253 
3254             return;
3255         }
3256 
3257         final String propName = JSType.toString(key);
3258         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3259     }
3260 
3261     @Override
3262     public void set(final int key, final Object value, final int callSiteFlags) {
3263         final int index = getArrayIndex(key);
3264 
3265         if (isValidArrayIndex(index)) {
3266             final ArrayData data = getArray();
3267             if (data.has(index)) {
3268                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3269             } else {
3270                 doesNotHave(index, value, callSiteFlags);
3271             }
3272 
3273             return;
3274         }
3275 
3276         final String propName = JSType.toString(key);
3277         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3278     }
3279 
3280     @Override
3281     public boolean has(final Object key) {
3282         final Object primitiveKey = JSType.toPrimitive(key);
3283         final int    index        = getArrayIndex(primitiveKey);
3284         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), true);
3285     }
3286 
3287     @Override
3288     public boolean has(final double key) {
3289         final int index = getArrayIndex(key);
3290         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3291     }
3292 
3293     @Override
3294     public boolean has(final int key) {
3295         final int index = getArrayIndex(key);
3296         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3297     }
3298 
3299     private boolean hasArrayProperty(final int index) {
3300         boolean hasArrayKeys = false;
3301 
3302         for (ScriptObject self = this; self != null; self = self.getProto()) {
3303             if (self.getArray().has(index)) {
3304                 return true;
3305             }
3306             hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys();
3307         }
3308 
3309         return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true);
3310     }
3311 
3312     @Override
3313     public boolean hasOwnProperty(final Object key) {
3314         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3315         final int    index        = getArrayIndex(primitiveKey);
3316         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), false);
3317     }
3318 
3319     @Override
3320     public boolean hasOwnProperty(final int key) {
3321         final int index = getArrayIndex(key);
3322         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3323     }
3324 
3325     @Override
3326     public boolean hasOwnProperty(final double key) {
3327         final int index = getArrayIndex(key);
3328         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3329     }
3330 
3331     private boolean hasOwnArrayProperty(final int index) {
3332         return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false);
3333     }
3334 
3335     @Override
3336     public boolean delete(final int key, final boolean strict) {
3337         final int index = getArrayIndex(key);
3338         final ArrayData array = getArray();
3339 
3340         if (array.has(index)) {
3341             if (array.canDelete(index, strict)) {
3342                 setArray(array.delete(index));
3343                 return true;
3344             }
3345             return false;
3346         }
3347         return deleteObject(JSType.toObject(key), strict);
3348     }
3349 
3350     @Override
3351     public boolean delete(final double key, final boolean strict) {
3352         final int index = getArrayIndex(key);
3353         final ArrayData array = getArray();
3354 
3355         if (array.has(index)) {
3356             if (array.canDelete(index, strict)) {
3357                 setArray(array.delete(index));
3358                 return true;
3359             }
3360             return false;
3361         }
3362 
3363         return deleteObject(JSType.toObject(key), strict);
3364     }
3365 
3366     @Override
3367     public boolean delete(final Object key, final boolean strict) {
3368         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
3369         final int       index        = getArrayIndex(primitiveKey);
3370         final ArrayData array        = getArray();
3371 
3372         if (array.has(index)) {
3373             if (array.canDelete(index, strict)) {
3374                 setArray(array.delete(index));
3375                 return true;
3376             }
3377             return false;
3378         }
3379 
3380         return deleteObject(primitiveKey, strict);
3381     }
3382 
3383     private boolean deleteObject(final Object key, final boolean strict) {
3384         final Object propName = JSType.toPropertyKey(key);
3385         final FindProperty find = findProperty(propName, false);
3386 
3387         if (find == null) {
3388             return true;
3389         }
3390 
3391         if (!find.getProperty().isConfigurable()) {
3392             if (strict) {
3393                 throw typeError("cant.delete.property", propName.toString(), ScriptRuntime.safeToString(this));
3394             }
3395             return false;
3396         }
3397 
3398         final Property prop = find.getProperty();
3399         deleteOwnProperty(prop);
3400 
3401         return true;
3402     }
3403 
3404     /**
3405      * Return a shallow copy of this ScriptObject.
3406      * @return a shallow copy.
3407      */
3408     public final ScriptObject copy() {
3409         try {
3410             return clone();
3411         } catch (final CloneNotSupportedException e) {
3412             throw new RuntimeException(e);
3413         }
3414     }
3415 
3416     @Override
3417     protected ScriptObject clone() throws CloneNotSupportedException {
3418         final ScriptObject clone = (ScriptObject) super.clone();
3419         if (objectSpill != null) {
3420             clone.objectSpill = objectSpill.clone();
3421             if (primitiveSpill != null) {
3422                 clone.primitiveSpill = primitiveSpill.clone();
3423             }
3424         }
3425         clone.arrayData = arrayData.copy();
3426         return clone;
3427     }
3428 
3429     /**
3430      * Make a new UserAccessorProperty property. getter and setter functions are stored in
3431      * this ScriptObject and slot values are used in property object.
3432      *
3433      * @param key the property name
3434      * @param propertyFlags attribute flags of the property
3435      * @param getter getter function for the property
3436      * @param setter setter function for the property
3437      * @return the newly created UserAccessorProperty
3438      */
3439     protected final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3440         final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
3441         //property.getSetter(Object.class, getMap());
3442         uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
3443         return uc;
3444     }
3445 
3446     /**
3447      * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise.
3448      * @return {@code true} if dual fields should be used.
3449      */
3450     protected boolean useDualFields() {
3451         return !StructureLoader.isSingleFieldStructure(getClass().getName());
3452     }
3453 
3454     Object ensureSpillSize(final int slot) {
3455         final int oldLength = objectSpill == null ? 0 : objectSpill.length;
3456         if (slot < oldLength) {
3457             return this;
3458         }
3459         final int newLength = alignUp(slot + 1, SPILL_RATE);
3460         final Object[] newObjectSpill    = new Object[newLength];
3461         final long[]   newPrimitiveSpill = useDualFields() ? new long[newLength] : null;
3462 
3463         if (objectSpill != null) {
3464             System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength);
3465             if (primitiveSpill != null && newPrimitiveSpill != null) {
3466                 System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength);
3467             }
3468         }
3469 
3470         this.primitiveSpill = newPrimitiveSpill;
3471         this.objectSpill    = newObjectSpill;
3472 
3473         return this;
3474     }
3475 
3476     private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
3477         return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3478     }
3479 
3480     private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
3481         return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3482     }
3483 
3484     private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3485         return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func);
3486     }
3487 
3488     @SuppressWarnings("unused")
3489     private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3490         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3491             try {
3492                 return getter.invokeExact(self) == func;
3493             } catch (final RuntimeException | Error e) {
3494                 throw e;
3495             } catch (final Throwable t) {
3496                 throw new RuntimeException(t);
3497             }
3498         }
3499 
3500         return false;
3501     }
3502 
3503     private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3504         return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func);
3505     }
3506 
3507     private static ScriptObject getProto(final ScriptObject self, final int depth) {
3508         ScriptObject proto = self;
3509         for (int d = 0; d < depth; d++) {
3510             proto = proto.getProto();
3511             if (proto == null) {
3512                 return null;
3513             }
3514         }
3515 
3516         return proto;
3517     }
3518 
3519     @SuppressWarnings("unused")
3520     private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3521         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3522             final ScriptObject proto = getProto((ScriptObject)self, depth);
3523             if (proto == null) {
3524                 return false;
3525             }
3526             try {
3527                 return getter.invokeExact((Object)proto) == func;
3528             } catch (final RuntimeException | Error e) {
3529                 throw e;
3530             } catch (final Throwable t) {
3531                 throw new RuntimeException(t);
3532             }
3533         }
3534 
3535         return false;
3536     }
3537 
3538     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3539     private static LongAdder count;
3540 
3541     static {
3542         if (Context.DEBUG) {
3543             count = new LongAdder();
3544         }
3545     }
3546     /**
3547      * Get number of {@code ScriptObject} instances created. If not running in debug
3548      * mode this is always 0
3549      *
3550      * @return number of ScriptObjects created
3551      */
3552     public static long getCount() {
3553         return count.longValue();
3554     }
3555 }
--- EOF ---