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