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