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