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             final String key = property.getKey();
 308             final Property oldProp = newMap.findProperty(key);
 309             if (oldProp == null) {
 310                 if (property instanceof UserAccessorProperty) {
 311                     // Note: we copy accessor functions to this object which is semantically different from binding.
 312                     final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
 313                     newMap = newMap.addPropertyNoHistory(prop);
 314                 } else {
 315                     newMap = newMap.addPropertyBind((AccessorProperty)property, source);
 316                 }
 317             } else {
 318                 // See ECMA section 10.5 Declaration Binding Instantiation
 319                 // step 5 processing each function declaration.
 320                 if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
 321                      if (oldProp instanceof UserAccessorProperty ||
 322                          !(oldProp.isWritable() && oldProp.isEnumerable())) {
 323                          throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
 324                      }
 325                 }
 326             }
 327         }
 328 
 329         this.setMap(newMap);
 330     }
 331 
 332     /**
 333      * Copy all properties from the array with their receiver bound to the source.
 334      *
 335      * @param source The source object to copy from.
 336      * @param properties The collection of accessor properties to copy.
 337      */
 338     public void addBoundProperties(final Object source, final AccessorProperty[] properties) {
 339         PropertyMap newMap = this.getMap();
 340 
 341         for (final AccessorProperty property : properties) {
 342             final String key = property.getKey();
 343 
 344             if (newMap.findProperty(key) == null) {
 345                 newMap = newMap.addPropertyBind(property, source);
 346             }
 347         }
 348 
 349         this.setMap(newMap);
 350     }
 351 
 352     /**
 353      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
 354      * first argument in lieu of the bound argument).
 355      * @param methodHandle Method handle to bind to.
 356      * @param receiver     Object to bind.
 357      * @return Bound method handle.
 358      */
 359     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
 360         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
 361     }
 362 
 363     /**
 364      * Return a property iterator.
 365      * @return Property iterator.
 366      */
 367     public Iterator<String> propertyIterator() {
 368         return new KeyIterator(this);
 369     }
 370 
 371     /**
 372      * Return a property value iterator.
 373      * @return Property value iterator.
 374      */
 375     public Iterator<Object> valueIterator() {
 376         return new ValueIterator(this);
 377     }
 378 
 379     /**
 380      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
 381      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
 382      */
 383     public final boolean isAccessorDescriptor() {
 384         return has(GET) || has(SET);
 385     }
 386 
 387     /**
 388      * ECMA 8.10.2 IsDataDescriptor ( Desc )
 389      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
 390      */
 391     public final boolean isDataDescriptor() {
 392         return has(VALUE) || has(WRITABLE);
 393     }
 394 
 395     /**
 396      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
 397      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
 398      */
 399     public final boolean isGenericDescriptor() {
 400         return isAccessorDescriptor() || isDataDescriptor();
 401     }
 402 
 403     /**
 404       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 405       *
 406       * @return property descriptor
 407       */
 408     public final PropertyDescriptor toPropertyDescriptor() {
 409         final Global global = Context.getGlobal();
 410 
 411         final PropertyDescriptor desc;
 412         if (isDataDescriptor()) {
 413             if (has(SET) || has(GET)) {
 414                 throw typeError(global, "inconsistent.property.descriptor");
 415             }
 416 
 417             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
 418         } else if (isAccessorDescriptor()) {
 419             if (has(VALUE) || has(WRITABLE)) {
 420                 throw typeError(global, "inconsistent.property.descriptor");
 421             }
 422 
 423             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
 424         } else {
 425             desc = global.newGenericDescriptor(false, false);
 426         }
 427 
 428         return desc.fillFrom(this);
 429     }
 430 
 431     /**
 432      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 433      *
 434      * @param global  global scope object
 435      * @param obj object to create property descriptor from
 436      *
 437      * @return property descriptor
 438      */
 439     public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) {
 440         if (obj instanceof ScriptObject) {
 441             return ((ScriptObject)obj).toPropertyDescriptor();
 442         }
 443 
 444         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
 445     }
 446 
 447     /**
 448      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 449      *
 450      * @param key property key
 451      *
 452      * @return Returns the Property Descriptor of the named own property of this
 453      * object, or undefined if absent.
 454      */
 455     public Object getOwnPropertyDescriptor(final String key) {
 456         final Property property = getMap().findProperty(key);
 457 
 458         final Global global = Context.getGlobal();
 459 
 460         if (property != null) {
 461             final ScriptFunction get   = property.getGetterFunction(this);
 462             final ScriptFunction set   = property.getSetterFunction(this);
 463 
 464             final boolean configurable = property.isConfigurable();
 465             final boolean enumerable   = property.isEnumerable();
 466             final boolean writable     = property.isWritable();
 467 
 468             if (property instanceof UserAccessorProperty) {
 469                 return global.newAccessorDescriptor(
 470                     get != null ?
 471                         get :
 472                         UNDEFINED,
 473                     set != null ?
 474                         set :
 475                         UNDEFINED,
 476                     configurable,
 477                     enumerable);
 478             }
 479 
 480             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
 481         }
 482 
 483         final int index = getArrayIndex(key);
 484         final ArrayData array = getArray();
 485 
 486         if (array.has(index)) {
 487             return array.getDescriptor(global, index);
 488         }
 489 
 490         return UNDEFINED;
 491     }
 492 
 493     /**
 494      * ECMA 8.12.2 [[GetProperty]] (P)
 495      *
 496      * @param key property key
 497      *
 498      * @return Returns the fully populated Property Descriptor of the named property
 499      * of this object, or undefined if absent.
 500      */
 501     public Object getPropertyDescriptor(final String key) {
 502         final Object res = getOwnPropertyDescriptor(key);
 503 
 504         if (res != UNDEFINED) {
 505             return res;
 506         } else if (getProto() != null) {
 507             return getProto().getOwnPropertyDescriptor(key);
 508         } else {
 509             return UNDEFINED;
 510         }
 511     }
 512 
 513     private void invalidateGlobalConstant(final String key) {




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