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