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