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