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