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