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