1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
  29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
  30 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  31 import static jdk.nashorn.internal.lookup.Lookup.MH;
  32 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
  33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  34 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
  35 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
  36 import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
  37 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
  38 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
  39 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
  40 import static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
  41 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
  42 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
  43 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  44 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  45 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
  46 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
  47 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
  48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag;
  49 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag;
  50 import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
  51 
  52 import java.lang.invoke.MethodHandle;
  53 import java.lang.invoke.MethodHandles;
  54 import java.lang.invoke.MethodType;
  55 import java.lang.invoke.SwitchPoint;
  56 import java.util.AbstractMap;
  57 import java.util.ArrayList;
  58 import java.util.Arrays;
  59 import java.util.Collection;
  60 import java.util.Collections;
  61 import java.util.HashSet;
  62 import java.util.Iterator;
  63 import java.util.LinkedHashSet;
  64 import java.util.List;
  65 import java.util.Map;
  66 import java.util.Set;
  67 import jdk.internal.dynalink.CallSiteDescriptor;
  68 import jdk.internal.dynalink.linker.GuardedInvocation;
  69 import jdk.internal.dynalink.linker.LinkRequest;
  70 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
  71 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  72 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  73 import jdk.nashorn.internal.codegen.types.Type;
  74 import jdk.nashorn.internal.lookup.Lookup;
  75 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
  76 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
  77 import jdk.nashorn.internal.objects.Global;
  78 import jdk.nashorn.internal.objects.NativeArray;
  79 import jdk.nashorn.internal.runtime.arrays.ArrayData;
  80 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
  81 import jdk.nashorn.internal.runtime.linker.Bootstrap;
  82 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
  83 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  84 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  85 
  86 /**
  87  * Base class for generic JavaScript objects.
  88  * <p>
  89  * Notes:
  90  * <ul>
  91  * <li>The map is used to identify properties in the object.</li>
  92  * <li>If the map is modified then it must be cloned and replaced.  This notifies
  93  *     any code that made assumptions about the object that things have changed.
  94  *     Ex. CallSites that have been validated must check to see if the map has
  95  *     changed (or a map from a different object type) and hence relink the method
  96  *     to call.</li>
  97  * <li>Modifications of the map include adding/deleting attributes or changing a
  98  *     function field value.</li>
  99  * </ul>
 100  */
 101 
 102 public abstract class ScriptObject implements PropertyAccess, Cloneable {
 103     /** __proto__ special property name inside object literals. ES6 draft. */
 104     public static final String PROTO_PROPERTY_NAME   = "__proto__";
 105 
 106     /** Search fall back routine name for "no such method" */
 107     public static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
 108 
 109     /** Search fall back routine name for "no such property" */
 110     public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
 111 



 112     /** Per ScriptObject flag - is this an array object? */
 113     public static final int IS_ARRAY               = 1 << 0;
 114 
 115     /** Per ScriptObject flag - is this an arguments object? */
 116     public static final int IS_ARGUMENTS           = 1 << 1;
 117 
 118     /** Is length property not-writable? */
 119     public static final int IS_LENGTH_NOT_WRITABLE = 1 << 2;
 120 
 121     /** Is this a builtin object? */
 122     public static final int IS_BUILTIN             = 1 << 3;
 123 
 124     /**
 125      * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
 126      * {@link ScriptObject#objectSpill} when full
 127      */
 128     public static final int SPILL_RATE = 8;
 129 
 130     /** Map to property information and accessor functions. Ordered by insertion. */
 131     private PropertyMap map;
 132 
 133     /** objects proto. */
 134     private ScriptObject proto;
 135 
 136     /** Object flags. */
 137     private int flags;
 138 
 139     /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */
 140     protected long[]   primitiveSpill;
 141 
 142     /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
 143     protected Object[] objectSpill;
 144 
 145     /** Indexed array data. */
 146     private ArrayData arrayData;
 147 
 148     /** Method handle to retrieve prototype of this object */
 149     public static final MethodHandle GETPROTO      = findOwnMH_V("getProto", ScriptObject.class);
 150 
 151     static final MethodHandle MEGAMORPHIC_GET    = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class);
 152     static final MethodHandle GLOBALFILTER       = findOwnMH_S("globalFilter", Object.class, Object.class);
 153     static final MethodHandle DECLARE_AND_SET    = findOwnMH_V("declareAndSet", void.class, String.class, Object.class);
 154 
 155     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class);
 156     private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class);
 157     private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class);
 158 
 159     private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>();
 160 
 161     /** Method handle for getting the array data */
 162     public static final Call GET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
 163 
 164     /** Method handle for getting a function argument at a given index. Used from MapCreator */
 165     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
 166 
 167     /** Method handle for setting a function argument at a given index. Used from MapCreator */
 168     public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
 169 
 170     /** Method handle for getting the proto of a ScriptObject */
 171     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 172 
 173     /** Method handle for getting the proto of a ScriptObject */
 174     public static final Call GET_PROTO_DEPTH    = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class);
 175 
 176     /** Method handle for setting the proto of a ScriptObject */
 177     public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class);
 178 
 179     /** Method handle for setting the proto of a ScriptObject after checking argument */
 180     public static final Call SET_PROTO_FROM_LITERAL    = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class);
 181 
 182     /** Method handle for setting the user accessors of a ScriptObject */
 183     //TODO fastpath this
 184     public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
 185 
 186     static final MethodHandle[] SET_SLOW = new MethodHandle[] {
 187         findOwnMH_V("set", void.class, Object.class, int.class, int.class),
 188         findOwnMH_V("set", void.class, Object.class, long.class, int.class),
 189         findOwnMH_V("set", void.class, Object.class, double.class, int.class),
 190         findOwnMH_V("set", void.class, Object.class, Object.class, int.class)
 191     };
 192 
 193     /** Method handle to reset the map of this ScriptObject */
 194     public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class);
 195 
 196     static final MethodHandle CAS_MAP           = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class);
 197     static final MethodHandle EXTENSION_CHECK   = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class);
 198     static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class);
 199 
 200     /**
 201      * Constructor
 202      */
 203     public ScriptObject() {
 204         this(null);
 205     }
 206 
 207     /**
 208     * Constructor
 209     *
 210     * @param map {@link PropertyMap} used to create the initial object
 211     */
 212     public ScriptObject(final PropertyMap map) {
 213         if (Context.DEBUG) {
 214             ScriptObject.count++;
 215         }
 216         this.arrayData = ArrayData.EMPTY_ARRAY;
 217         this.setMap(map == null ? PropertyMap.newMap() : map);
 218     }
 219 
 220     /**
 221      * Constructor that directly sets the prototype to {@code proto} and property map to
 222      * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)}
 223      * would do. This should only be used for objects that are always constructed with the
 224      * same combination of prototype and property map.
 225      *
 226      * @param proto the prototype object
 227      * @param map intial {@link PropertyMap}
 228      */
 229     protected ScriptObject(final ScriptObject proto, final PropertyMap map) {
 230         this(map);
 231         this.proto = proto;
 232     }
 233 
 234     /**
 235      * Constructor used to instantiate spill properties directly. Used from
 236      * SpillObjectCreator.
 237      *
 238      * @param map            property maps
 239      * @param primitiveSpill primitive spills
 240      * @param objectSpill    reference spills
 241      */
 242     public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
 243         this(map);
 244         this.primitiveSpill = primitiveSpill;
 245         this.objectSpill    = objectSpill;
 246         assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
 247     }
 248 
 249     /**
 250      * Check whether this is a global object
 251      * @return true if global
 252      */
 253     protected boolean isGlobal() {
 254         return false;
 255     }
 256 
 257     private static int alignUp(final int size, final int alignment) {
 258         return size + alignment - 1 & ~(alignment - 1);
 259     }
 260 
 261     /**
 262      * Given a number of properties, return the aligned to SPILL_RATE
 263      * buffer size required for the smallest spill pool needed to
 264      * house them
 265      * @param nProperties number of properties
 266      * @return property buffer length, a multiple of SPILL_RATE
 267      */
 268     public static int spillAllocationLength(final int nProperties) {
 269         return alignUp(nProperties, SPILL_RATE);
 270     }
 271 
 272     /**
 273      * Copy all properties from the source object with their receiver bound to the source.
 274      * This function was known as mergeMap
 275      *
 276      * @param source The source object to copy from.
 277      */
 278     public void addBoundProperties(final ScriptObject source) {
 279         addBoundProperties(source, source.getMap().getProperties());
 280     }
 281 
 282     /**
 283      * Copy all properties from the array with their receiver bound to the source.
 284      *
 285      * @param source The source object to copy from.
 286      * @param properties The array of properties to copy.
 287      */
 288     public void addBoundProperties(final ScriptObject source, final Property[] properties) {
 289         PropertyMap newMap = this.getMap();
 290 
 291         for (final Property property : properties) {
 292             newMap = addBoundProperty(newMap, source, property);
 293         }
 294 
 295         this.setMap(newMap);
 296     }
 297 
 298     /**
 299      * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the
 300      * new interim property map.
 301      *
 302      * @param propMap the property map
 303      * @param source the source object
 304      * @param property the property to be added
 305      * @return the new property map
 306      */
 307     protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) {
 308         PropertyMap newMap = propMap;
 309         final String key = property.getKey();
 310         final Property oldProp = newMap.findProperty(key);
 311         if (oldProp == null) {
 312             if (property instanceof UserAccessorProperty) {
 313                 // Note: we copy accessor functions to this object which is semantically different from binding.
 314                 final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
 315                 newMap = newMap.addPropertyNoHistory(prop);
 316             } else {
 317                 newMap = newMap.addPropertyBind((AccessorProperty)property, source);
 318             }
 319         } else {
 320             // See ECMA section 10.5 Declaration Binding Instantiation
 321             // step 5 processing each function declaration.
 322             if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
 323                 if (oldProp instanceof UserAccessorProperty ||
 324                         !(oldProp.isWritable() && oldProp.isEnumerable())) {
 325                     throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
 326                 }
 327             }
 328         }
 329         return 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     /**
 514      * Invalidate any existing global constant method handles that may exist for {@code key}.
 515      * @param key the property name
 516      */
 517     protected void invalidateGlobalConstant(final String key) {
 518         final GlobalConstants globalConstants = getGlobalConstants();
 519         if (globalConstants != null) {
 520             globalConstants.delete(key);
 521         }
 522     }
 523 
 524     /**
 525      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
 526      *
 527      * @param key the property key
 528      * @param propertyDesc the property descriptor
 529      * @param reject is the property extensible - true means new definitions are rejected
 530      *
 531      * @return true if property was successfully defined
 532      */
 533     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
 534         final Global             global  = Context.getGlobal();
 535         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
 536         final Object             current = getOwnPropertyDescriptor(key);
 537         final String             name    = JSType.toString(key);
 538 
 539         invalidateGlobalConstant(key);
 540 
 541         if (current == UNDEFINED) {
 542             if (isExtensible()) {
 543                 // add a new own property
 544                 addOwnProperty(key, desc);
 545                 return true;
 546             }
 547             // new property added to non-extensible object
 548             if (reject) {
 549                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
 550             }
 551             return false;
 552         }
 553 
 554         // modifying an existing property
 555         final PropertyDescriptor currentDesc = (PropertyDescriptor)current;
 556         final PropertyDescriptor newDesc     = desc;
 557 
 558         if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) {
 559             // every descriptor field is absent
 560             return true;
 561         }
 562 
 563         if (newDesc.hasAndEquals(currentDesc)) {
 564             // every descriptor field of the new is same as the current
 565             return true;
 566         }
 567 
 568         if (!currentDesc.isConfigurable()) {
 569             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
 570                 // not configurable can not be made configurable
 571                 if (reject) {
 572                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 573                 }
 574                 return false;
 575             }
 576 
 577             if (newDesc.has(ENUMERABLE) &&
 578                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
 579                 // cannot make non-enumerable as enumerable or vice-versa
 580                 if (reject) {
 581                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 582                 }
 583                 return false;
 584             }
 585         }
 586 
 587         int propFlags = Property.mergeFlags(currentDesc, newDesc);
 588         Property property = getMap().findProperty(key);
 589 
 590         if (currentDesc.type() == PropertyDescriptor.DATA &&
 591                 (newDesc.type() == PropertyDescriptor.DATA ||
 592                  newDesc.type() == PropertyDescriptor.GENERIC)) {
 593             if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) {
 594                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
 595                     newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
 596                     if (reject) {
 597                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 598                     }
 599                     return false;
 600                 }
 601             }
 602 
 603             final boolean newValue = newDesc.has(VALUE);
 604             final Object value     = newValue ? newDesc.getValue() : currentDesc.getValue();
 605 
 606             if (newValue && property != null) {
 607                 // Temporarily clear flags.
 608                 property = modifyOwnProperty(property, 0);
 609                 set(key, value, 0);
 610                 //this might change the map if we change types of the property
 611                 //hence we need to read it again. note that we should probably
 612                 //have the setter return the new property throughout and in
 613                 //general respect Property return values from modify and add
 614                 //functions - which we don't seem to do at all here :-(
 615                 //There is already a bug filed to generify PropertyAccess so we
 616                 //can have the setter return e.g. a Property
 617                 property = getMap().findProperty(key);
 618             }
 619 
 620             if (property == null) {
 621                 // promoting an arrayData value to actual property
 622                 addOwnProperty(key, propFlags, value);
 623                 checkIntegerKey(key);
 624             } else {
 625                 // Now set the new flags
 626                 modifyOwnProperty(property, propFlags);
 627             }
 628         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
 629                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
 630                     newDesc.type() == PropertyDescriptor.GENERIC)) {
 631             if (!currentDesc.isConfigurable()) {
 632                 if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
 633                     newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
 634                     if (reject) {
 635                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 636                     }
 637                     return false;
 638                 }
 639             }
 640             // New set the new features.
 641             modifyOwnProperty(property, propFlags,
 642                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
 643                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
 644         } else {
 645             // changing descriptor type
 646             if (!currentDesc.isConfigurable()) {
 647                 // not configurable can not be made configurable
 648                 if (reject) {
 649                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
 650                 }
 651                 return false;
 652             }
 653 
 654             propFlags = 0;
 655 
 656             // Preserve only configurable and enumerable from current desc
 657             // if those are not overridden in the new property descriptor.
 658             boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable();
 659             if (!value) {
 660                 propFlags |= Property.NOT_CONFIGURABLE;
 661             }
 662             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
 663             if (!value) {
 664                 propFlags |= Property.NOT_ENUMERABLE;
 665             }
 666 
 667             final int type = newDesc.type();
 668             if (type == PropertyDescriptor.DATA) {
 669                 // get writable from the new descriptor
 670                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
 671                 if (!value) {
 672                     propFlags |= Property.NOT_WRITABLE;
 673                 }
 674 
 675                 // delete the old property
 676                 deleteOwnProperty(property);
 677                 // add new data property
 678                 addOwnProperty(key, propFlags, newDesc.getValue());
 679             } else if (type == PropertyDescriptor.ACCESSOR) {
 680                 if (property == null) {
 681                     addOwnProperty(key, propFlags,
 682                                      newDesc.has(GET) ? newDesc.getGetter() : null,
 683                                      newDesc.has(SET) ? newDesc.getSetter() : null);
 684                 } else {
 685                     // Modify old property with the new features.
 686                     modifyOwnProperty(property, propFlags,
 687                                         newDesc.has(GET) ? newDesc.getGetter() : null,
 688                                         newDesc.has(SET) ? newDesc.getSetter() : null);
 689                 }
 690             }
 691         }
 692 
 693         checkIntegerKey(key);
 694 
 695         return true;
 696     }
 697 
 698     /**
 699      * Almost like defineOwnProperty(int,Object) for arrays this one does
 700      * not add 'gap' elements (like the array one does).
 701      *
 702      * @param index key for property
 703      * @param value value to define
 704      */
 705     public void defineOwnProperty(final int index, final Object value) {
 706         assert isValidArrayIndex(index) : "invalid array index";
 707         final long longIndex = ArrayIndex.toLongIndex(index);
 708         final long oldLength = getArray().length();
 709         if (longIndex >= oldLength) {
 710             setArray(getArray().ensure(longIndex));
 711             doesNotHaveEnsureDelete(longIndex, oldLength, false);
 712         }
 713         setArray(getArray().set(index, value, false));
 714     }
 715 
 716     private void checkIntegerKey(final String key) {
 717         final int index = getArrayIndex(key);
 718 
 719         if (isValidArrayIndex(index)) {
 720             final ArrayData data = getArray();
 721 
 722             if (data.has(index)) {
 723                 setArray(data.delete(index));
 724             }
 725         }
 726     }
 727 
 728     /**
 729       * Add a new property to the object.
 730       *
 731       * @param key          property key
 732       * @param propertyDesc property descriptor for property
 733       */
 734     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
 735         // Already checked that there is no own property with that key.
 736         PropertyDescriptor pdesc = propertyDesc;
 737 
 738         final int propFlags = Property.toFlags(pdesc);
 739 
 740         if (pdesc.type() == PropertyDescriptor.GENERIC) {
 741             final Global global = Context.getGlobal();
 742             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
 743 
 744             dDesc.fillFrom((ScriptObject)pdesc);
 745             pdesc = dDesc;
 746         }
 747 
 748         final int type = pdesc.type();
 749         if (type == PropertyDescriptor.DATA) {
 750             addOwnProperty(key, propFlags, pdesc.getValue());
 751         } else if (type == PropertyDescriptor.ACCESSOR) {
 752             addOwnProperty(key, propFlags,
 753                     pdesc.has(GET) ? pdesc.getGetter() : null,
 754                     pdesc.has(SET) ? pdesc.getSetter() : null);
 755         }
 756 
 757         checkIntegerKey(key);
 758     }
 759 
 760     /**
 761      * Low level property API (not using property descriptors)
 762      * <p>
 763      * Find a property in the prototype hierarchy. Note: this is final and not
 764      * a good idea to override. If you have to, use
 765      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 766      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 767      * overriding way to find array properties
 768      *
 769      * @see jdk.nashorn.internal.objects.NativeArray
 770      *
 771      * @param key  Property key.
 772      * @param deep Whether the search should look up proto chain.
 773      *
 774      * @return FindPropertyData or null if not found.
 775      */
 776     public final FindProperty findProperty(final String key, final boolean deep) {
 777         return findProperty(key, deep, this);
 778     }
 779 
 780     /**
 781      * Low level property API (not using property descriptors)
 782      * <p>
 783      * Find a property in the prototype hierarchy. Note: this is not a good idea
 784      * to override except as it was done in {@link WithObject}.
 785      * If you have to, use
 786      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
 787      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
 788      * overriding way to find array properties
 789      *
 790      * @see jdk.nashorn.internal.objects.NativeArray
 791      *
 792      * @param key  Property key.
 793      * @param deep Whether the search should look up proto chain.
 794      * @param start the object on which the lookup was originally initiated
 795      *
 796      * @return FindPropertyData or null if not found.
 797      */
 798     protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
 799 
 800         final PropertyMap selfMap  = getMap();
 801         final Property    property = selfMap.findProperty(key);
 802 
 803         if (property != null) {
 804             return new FindProperty(start, this, property);
 805         }
 806 
 807         if (deep) {
 808             final ScriptObject myProto = getProto();
 809             if (myProto != null) {
 810                 return myProto.findProperty(key, deep, start);
 811             }
 812         }
 813 
 814         return null;
 815     }
 816 
 817     /**
 818      * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
 819      * {@code boolean} value instead of a {@link FindProperty} object.
 820      * @param key  Property key.
 821      * @param deep Whether the search should look up proto chain.
 822      * @return true if the property was found.
 823      */
 824     boolean hasProperty(final String key, final boolean deep) {
 825         if (getMap().findProperty(key) != null) {
 826             return true;
 827         }
 828 
 829         if (deep) {
 830             final ScriptObject myProto = getProto();
 831             if (myProto != null) {
 832                 return myProto.hasProperty(key, deep);
 833             }
 834         }
 835 
 836         return false;
 837     }
 838 
 839     private SwitchPoint findBuiltinSwitchPoint(final String key) {
 840         for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
 841             final Property prop = myProto.getMap().findProperty(key);
 842             if (prop != null) {
 843                 final SwitchPoint sp = prop.getBuiltinSwitchPoint();
 844                 if (sp != null && !sp.hasBeenInvalidated()) {
 845                     return sp;
 846                 }
 847             }
 848         }
 849         return null;
 850     }
 851 
 852     /**
 853      * Add a new property to the object.
 854      * <p>
 855      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 856      *
 857      * @param key             Property key.
 858      * @param propertyFlags   Property flags.
 859      * @param getter          Property getter, or null if not defined
 860      * @param setter          Property setter, or null if not defined
 861      *
 862      * @return New property.
 863      */
 864     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 865         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
 866     }
 867 
 868     /**
 869      * Add a new property to the object.
 870      * <p>
 871      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 872      *
 873      * @param key             Property key.
 874      * @param propertyFlags   Property flags.
 875      * @param value           Value of property
 876      *
 877      * @return New property.
 878      */
 879     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
 880         return addSpillProperty(key, propertyFlags, value, true);
 881     }
 882 
 883     /**
 884      * Add a new property to the object.
 885      * <p>
 886      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
 887      *
 888      * @param newProperty property to add
 889      *
 890      * @return New property.
 891      */
 892     public final Property addOwnProperty(final Property newProperty) {
 893         PropertyMap oldMap = getMap();
 894         while (true) {
 895             final PropertyMap newMap = oldMap.addProperty(newProperty);
 896             if (!compareAndSetMap(oldMap, newMap)) {
 897                 oldMap = getMap();
 898                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
 899 
 900                 if (oldProperty != null) {
 901                     return oldProperty;
 902                 }
 903             } else {
 904                 return newProperty;
 905             }
 906         }
 907     }
 908 
 909     private void erasePropertyValue(final Property property) {
 910         // Erase the property field value with undefined. If the property is defined
 911         // by user-defined accessors, we don't want to call the setter!!
 912         if (!(property instanceof UserAccessorProperty)) {
 913             assert property != null;
 914             property.setValue(this, this, UNDEFINED, false);
 915         }
 916     }
 917 
 918     /**
 919      * Delete a property from the object.
 920      *
 921      * @param property Property to delete.
 922      *
 923      * @return true if deleted.
 924      */
 925     public final boolean deleteOwnProperty(final Property property) {
 926         erasePropertyValue(property);
 927         PropertyMap oldMap = getMap();
 928 
 929         while (true) {
 930             final PropertyMap newMap = oldMap.deleteProperty(property);
 931 
 932             if (newMap == null) {
 933                 return false;
 934             }
 935 
 936             if (!compareAndSetMap(oldMap, newMap)) {
 937                 oldMap = getMap();
 938             } else {
 939                 // delete getter and setter function references so that we don't leak
 940                 if (property instanceof UserAccessorProperty) {
 941                     ((UserAccessorProperty)property).setAccessors(this, getMap(), null);
 942                 }
 943 
 944                 invalidateGlobalConstant(property.getKey());
 945                 return true;
 946             }
 947         }
 948 
 949     }
 950 
 951     /**
 952      * Fast initialization functions for ScriptFunctions that are strict, to avoid
 953      * creating setters that probably aren't used. Inject directly into the spill pool
 954      * the defaults for "arguments" and "caller"
 955      *
 956      * @param key           property key
 957      * @param propertyFlags flags
 958      * @param getter        getter for {@link UserAccessorProperty}, null if not present or N/A
 959      * @param setter        setter for {@link UserAccessorProperty}, null if not present or N/A
 960      */
 961     protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 962         final PropertyMap oldMap = getMap();
 963         final int slot = oldMap.getFreeSpillSlot();
 964         ensureSpillSize(slot);
 965         objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
 966         Property    newProperty;
 967         PropertyMap newMap;
 968         do {
 969             newProperty = new UserAccessorProperty(key, propertyFlags, slot);
 970             newMap = oldMap.addProperty(newProperty);
 971         } while (!compareAndSetMap(oldMap, newMap));
 972     }
 973 
 974     /**
 975      * Modify a property in the object
 976      *
 977      * @param oldProperty    property to modify
 978      * @param propertyFlags  new property flags
 979      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
 980      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
 981      *
 982      * @return new property
 983      */
 984     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
 985         Property newProperty;
 986 
 987         if (oldProperty instanceof UserAccessorProperty) {
 988             final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
 989             final int slot = uc.getSlot();
 990 
 991             assert uc.getLocalType() == Object.class;
 992             final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
 993             assert gs != null;
 994             //reuse existing getter setter for speed
 995             gs.set(getter, setter);
 996             if (uc.getFlags() == propertyFlags) {
 997                 return oldProperty;
 998             }
 999             newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
1000         } else {
1001             // erase old property value and create new user accessor property
1002             erasePropertyValue(oldProperty);
1003             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
1004         }
1005 
1006         return modifyOwnProperty(oldProperty, newProperty);
1007     }
1008 
1009     /**
1010       * Modify a property in the object
1011       *
1012       * @param oldProperty    property to modify
1013       * @param propertyFlags  new property flags
1014       *
1015       * @return new property
1016       */
1017     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
1018         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
1019     }
1020 
1021     /**
1022      * Modify a property in the object, replacing a property with a new one
1023      *
1024      * @param oldProperty   property to replace
1025      * @param newProperty   property to replace it with
1026      *
1027      * @return new property
1028      */
1029     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
1030         if (oldProperty == newProperty) {
1031             return newProperty; //nop
1032         }
1033 
1034         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
1035 
1036         PropertyMap oldMap = getMap();
1037 
1038         while (true) {
1039             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
1040 
1041             if (!compareAndSetMap(oldMap, newMap)) {
1042                 oldMap = getMap();
1043                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
1044 
1045                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
1046                     return oldPropertyLookup;
1047                 }
1048             } else {
1049                 return newProperty;
1050             }
1051         }
1052     }
1053 
1054     /**
1055      * Update getter and setter in an object literal.
1056      *
1057      * @param key    Property key.
1058      * @param getter {@link UserAccessorProperty} defined getter, or null if none
1059      * @param setter {@link UserAccessorProperty} defined setter, or null if none
1060      */
1061     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
1062         final Property oldProperty = getMap().findProperty(key);
1063         if (oldProperty instanceof UserAccessorProperty) {
1064             modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter);
1065         } else {
1066             addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter));
1067         }
1068     }
1069 
1070     private static int getIntValue(final FindProperty find, final int programPoint) {
1071         final MethodHandle getter = find.getGetter(int.class, programPoint, null);
1072         if (getter != null) {
1073             try {
1074                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
1075             } catch (final Error|RuntimeException e) {
1076                 throw e;
1077             } catch (final Throwable e) {
1078                 throw new RuntimeException(e);
1079             }
1080         }
1081 
1082         return UNDEFINED_INT;
1083     }
1084 
1085     private static long getLongValue(final FindProperty find, final int programPoint) {
1086         final MethodHandle getter = find.getGetter(long.class, programPoint, null);
1087         if (getter != null) {
1088             try {
1089                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
1090             } catch (final Error|RuntimeException e) {
1091                 throw e;
1092             } catch (final Throwable e) {
1093                 throw new RuntimeException(e);
1094             }
1095         }
1096 
1097         return UNDEFINED_LONG;
1098     }
1099 
1100     private static double getDoubleValue(final FindProperty find, final int programPoint) {
1101         final MethodHandle getter = find.getGetter(double.class, programPoint, null);
1102         if (getter != null) {
1103             try {
1104                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
1105             } catch (final Error|RuntimeException e) {
1106                 throw e;
1107             } catch (final Throwable e) {
1108                 throw new RuntimeException(e);
1109             }
1110         }
1111 
1112         return UNDEFINED_DOUBLE;
1113     }
1114 
1115     /**
1116      * Return methodHandle of value function for call.
1117      *
1118      * @param find      data from find property.
1119      * @param type      method type of function.
1120      * @param bindName  null or name to bind to second argument (property not found method.)
1121      *
1122      * @return value of property as a MethodHandle or null.
1123      */
1124     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
1125         return getCallMethodHandle(find.getObjectValue(), type, bindName);
1126     }
1127 
1128     /**
1129      * Return methodHandle of value function for call.
1130      *
1131      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
1132      * @param type      method type of function.
1133      * @param bindName  null or name to bind to second argument (property not found method.)
1134      *
1135      * @return value of property as a MethodHandle or null.
1136      */
1137     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
1138         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
1139     }
1140 
1141     /**
1142      * Get value using found property.
1143      *
1144      * @param property Found property.
1145      *
1146      * @return Value of property.
1147      */
1148     public final Object getWithProperty(final Property property) {
1149         return new FindProperty(this, this, property).getObjectValue();
1150     }
1151 
1152     /**
1153      * Get a property given a key
1154      *
1155      * @param key property key
1156      *
1157      * @return property for key
1158      */
1159     public final Property getProperty(final String key) {
1160         return getMap().findProperty(key);
1161     }
1162 
1163     /**
1164      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
1165      * Used for argument access in a vararg function using parameter name.
1166      * Returns the argument at a given key (index)
1167      *
1168      * @param key argument index
1169      *
1170      * @return the argument at the given position, or undefined if not present
1171      */
1172     public Object getArgument(final int key) {
1173         return get(key);
1174     }
1175 
1176     /**
1177      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
1178      * Used for argument access in a vararg function using parameter name.
1179      * Returns the argument at a given key (index)
1180      *
1181      * @param key   argument index
1182      * @param value the value to write at the given index
1183      */
1184     public void setArgument(final int key, final Object value) {
1185         set(key, value, 0);
1186     }
1187 
1188     /**
1189      * Return the current context from the object's map.
1190      * @return Current context.
1191      */
1192     protected Context getContext() {
1193         return Context.fromClass(getClass());
1194     }
1195 
1196     /**
1197      * Return the map of an object.
1198      * @return PropertyMap object.
1199      */
1200     public final PropertyMap getMap() {
1201         return map;
1202     }
1203 
1204     /**
1205      * Set the initial map.
1206      * @param map Initial map.
1207      */
1208     public final void setMap(final PropertyMap map) {
1209         this.map = map;
1210     }
1211 
1212     /**
1213      * Conditionally set the new map if the old map is the same.
1214      * @param oldMap Map prior to manipulation.
1215      * @param newMap Replacement map.
1216      * @return true if the operation succeeded.
1217      */
1218     protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
1219         if (oldMap == this.map) {
1220             this.map = newMap;
1221             return true;
1222         }
1223         return false;
1224      }
1225 
1226     /**
1227      * Return the __proto__ of an object.
1228      * @return __proto__ object.
1229      */
1230     public final ScriptObject getProto() {
1231         return proto;
1232     }
1233 
1234     /**
1235      * Get the proto of a specific depth
1236      * @param n depth
1237      * @return proto at given depth
1238      */
1239     public final ScriptObject getProto(final int n) {
1240         assert n > 0;
1241         ScriptObject p = getProto();
1242         for (int i = n; i-- > 0;) {
1243             p = p.getProto();
1244         }
1245         return p;
1246     }
1247 
1248     /**
1249      * Set the __proto__ of an object.
1250      * @param newProto new __proto__ to set.
1251      */
1252     public final void setProto(final ScriptObject newProto) {
1253         final ScriptObject oldProto = proto;
1254 
1255         if (oldProto != newProto) {
1256             proto = newProto;
1257 
1258             // Let current listeners know that the protototype has changed and set our map
1259             final PropertyListeners listeners = getMap().getListeners();
1260             if (listeners != null) {
1261                 listeners.protoChanged();
1262             }
1263             // Replace our current allocator map with one that is associated with the new prototype.
1264             setMap(getMap().changeProto(newProto));
1265         }
1266     }
1267 
1268     /**
1269      * Set the initial __proto__ of this object. This should be used instead of
1270      * {@link #setProto} if it is known that the current property map will not be
1271      * used on a new object with any other parent property map, so we can pass over
1272      * property map invalidation/evolution.
1273      *
1274      * @param initialProto the initial __proto__ to set.
1275      */
1276     public void setInitialProto(final ScriptObject initialProto) {
1277         this.proto = initialProto;
1278     }
1279 
1280     /**
1281      * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype.
1282      * @param obj the object literal that needs to have its prototype initialized to the global Object prototype.
1283      */
1284     public static void setGlobalObjectProto(final ScriptObject obj) {
1285         obj.setInitialProto(Global.objectPrototype());
1286     }
1287 
1288     /**
1289      * Set the __proto__ of an object with checks.
1290      * This is the built-in operation [[SetPrototypeOf]]
1291      * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V)
1292      *
1293      * @param newProto Prototype to set.
1294      */
1295     public final void setPrototypeOf(final Object newProto) {
1296         if (newProto == null || newProto instanceof ScriptObject) {
1297             if (! isExtensible()) {
1298                 // okay to set same proto again - even if non-extensible
1299 
1300                 if (newProto == getProto()) {
1301                     return;
1302                 }
1303                 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1304             }
1305 
1306             // check for circularity
1307             ScriptObject p = (ScriptObject)newProto;
1308             while (p != null) {
1309                 if (p == this) {
1310                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1311                 }
1312                 p = p.getProto();
1313             }
1314             setProto((ScriptObject)newProto);
1315         } else {
1316             throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1317         }
1318     }
1319 
1320     /**
1321      * Set the __proto__ of an object from an object literal.
1322      * See ES6 draft spec: B.3.1 __proto__ Property Names in
1323      * Object Initializers. Step 6 handling of "__proto__".
1324      *
1325      * @param newProto Prototype to set.
1326      */
1327     public final void setProtoFromLiteral(final Object newProto) {
1328         if (newProto == null || newProto instanceof ScriptObject) {
1329             setPrototypeOf(newProto);
1330         } else {
1331             // Some non-object, non-null. Then, we need to set
1332             // Object.prototype as the new __proto__
1333             //
1334             // var obj = { __proto__ : 34 };
1335             // print(obj.__proto__ === Object.prototype); // => true
1336             setPrototypeOf(Global.objectPrototype());
1337         }
1338     }
1339 
1340     /**
1341      * return an array of own property keys associated with the object.
1342      *
1343      * @param all True if to include non-enumerable keys.
1344      * @return Array of keys.
1345      */
1346     public final String[] getOwnKeys(final boolean all) {
1347         return getOwnKeys(all, null);
1348     }
1349 
1350     /**
1351      * return an array of own property keys associated with the object.
1352      *
1353      * @param all True if to include non-enumerable keys.
1354      * @param nonEnumerable set of non-enumerable properties seen already.Used
1355        to filter out shadowed, but enumerable properties from proto children.
1356      * @return Array of keys.
1357      */
1358     protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
1359         final List<Object> keys    = new ArrayList<>();
1360         final PropertyMap  selfMap = this.getMap();
1361 
1362         final ArrayData array  = getArray();
1363 
1364         for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1365             keys.add(JSType.toString(iter.next().longValue()));
1366         }
1367 
1368         for (final Property property : selfMap.getProperties()) {
1369             final boolean enumerable = property.isEnumerable();
1370             final String key = property.getKey();
1371             if (all) {
1372                 keys.add(key);
1373             } else if (enumerable) {
1374                 // either we don't have non-enumerable filter set or filter set
1375                 // does not contain the current property.
1376                 if (nonEnumerable == null || !nonEnumerable.contains(key)) {
1377                     keys.add(key);
1378                 }
1379             } else {
1380                 // store this non-enumerable property for later proto walk
1381                 if (nonEnumerable != null) {
1382                     nonEnumerable.add(key);
1383                 }
1384             }
1385         }
1386 
1387         return keys.toArray(new String[keys.size()]);
1388     }
1389 
1390     /**
1391      * Check if this ScriptObject has array entries. This means that someone has
1392      * set values with numeric keys in the object.
1393      *
1394      * @return true if array entries exists.
1395      */
1396     public boolean hasArrayEntries() {
1397         return getArray().length() > 0 || getMap().containsArrayKeys();
1398     }
1399 
1400     /**
1401      * Return the valid JavaScript type name descriptor
1402      *
1403      * @return "Object"
1404      */
1405     public String getClassName() {
1406         return "Object";
1407     }
1408 
1409     /**
1410      * {@code length} is a well known property. This is its getter.
1411      * Note that this *may* be optimized by other classes
1412      *
1413      * @return length property value for this ScriptObject
1414      */
1415     public Object getLength() {
1416         return get("length");
1417     }
1418 
1419     /**
1420      * Stateless toString for ScriptObjects.
1421      *
1422      * @return string description of this object, e.g. {@code [object Object]}
1423      */
1424     public String safeToString() {
1425         return "[object " + getClassName() + "]";
1426     }
1427 
1428     /**
1429      * Return the default value of the object with a given preferred type hint.
1430      * The preferred type hints are String.class for type String, Number.class
1431      * for type Number. <p>
1432      *
1433      * A <code>hint</code> of null means "no hint".
1434      *
1435      * ECMA 8.12.8 [[DefaultValue]](hint)
1436      *
1437      * @param typeHint the preferred type hint
1438      * @return the default value
1439      */
1440     public Object getDefaultValue(final Class<?> typeHint) {
1441         // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
1442         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1443         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1444         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1445         return Context.getGlobal().getDefaultValue(this, typeHint);
1446     }
1447 
1448     /**
1449      * Checking whether a script object is an instance of another. Used
1450      * in {@link ScriptFunction} for hasInstance implementation, walks
1451      * the proto chain
1452      *
1453      * @param instance instace to check
1454      * @return true if 'instance' is an instance of this object
1455      */
1456     public boolean isInstance(final ScriptObject instance) {
1457         return false;
1458     }
1459 
1460     /**
1461      * Flag this ScriptObject as non extensible
1462      *
1463      * @return the object after being made non extensible
1464      */
1465     public ScriptObject preventExtensions() {
1466         PropertyMap oldMap = getMap();
1467         while (!compareAndSetMap(oldMap,  getMap().preventExtensions())) {
1468             oldMap = getMap();
1469         }
1470 
1471         //invalidate any fast array setters
1472         final ArrayData array = getArray();
1473         assert array != null;
1474         setArray(ArrayData.preventExtension(array));
1475         return this;
1476     }
1477 
1478     /**
1479      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1480      *
1481      * @param obj object to check
1482      *
1483      * @return true if array
1484      */
1485     public static boolean isArray(final Object obj) {
1486         return obj instanceof ScriptObject && ((ScriptObject)obj).isArray();
1487     }
1488 
1489     /**
1490      * Check if this ScriptObject is an array
1491      * @return true if array
1492      */
1493     public final boolean isArray() {
1494         return (flags & IS_ARRAY) != 0;
1495     }
1496 
1497     /**
1498      * Flag this ScriptObject as being an array
1499      */
1500     public final void setIsArray() {
1501         flags |= IS_ARRAY;
1502     }
1503 
1504     /**
1505      * Check if this ScriptObject is an {@code arguments} vector
1506      * @return true if arguments vector
1507      */
1508     public final boolean isArguments() {
1509         return (flags & IS_ARGUMENTS) != 0;
1510     }
1511 
1512     /**
1513      * Flag this ScriptObject as being an {@code arguments} vector
1514      */
1515     public final void setIsArguments() {
1516         flags |= IS_ARGUMENTS;
1517     }
1518 
1519     /**
1520      * Check if this object has non-writable length property
1521      *
1522      * @return {@code true} if 'length' property is non-writable
1523      */
1524     public boolean isLengthNotWritable() {
1525         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1526     }
1527 
1528     /**
1529      * Flag this object as having non-writable length property.
1530      */
1531     public void setIsLengthNotWritable() {
1532         flags |= IS_LENGTH_NOT_WRITABLE;
1533     }
1534 
1535     /**
1536      * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type
1537      * that can handle elementType
1538      * @param elementType elementType
1539      * @return array data
1540      */
1541     public final ArrayData getArray(final Class<?> elementType) {
1542         if (elementType == null) {
1543             return arrayData;
1544         }
1545         final ArrayData newArrayData = arrayData.convert(elementType);
1546         if (newArrayData != arrayData) {
1547             arrayData = newArrayData;
1548         }
1549         return newArrayData;
1550     }
1551 
1552     /**
1553      * Get the {@link ArrayData} for this ScriptObject if it is an array
1554      * @return array data
1555      */
1556     public final ArrayData getArray() {
1557         return arrayData;
1558     }
1559 
1560     /**
1561      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1562      * @param arrayData the array data
1563      */
1564     public final void setArray(final ArrayData arrayData) {
1565         this.arrayData = arrayData;
1566     }
1567 
1568     /**
1569      * Check if this ScriptObject is extensible
1570      * @return true if extensible
1571      */
1572     public boolean isExtensible() {
1573         return getMap().isExtensible();
1574     }
1575 
1576     /**
1577      * ECMAScript 15.2.3.8 - seal implementation
1578      * @return the sealed ScriptObject
1579      */
1580     public ScriptObject seal() {
1581         PropertyMap oldMap = getMap();
1582 
1583         while (true) {
1584             final PropertyMap newMap = getMap().seal();
1585 
1586             if (!compareAndSetMap(oldMap, newMap)) {
1587                 oldMap = getMap();
1588             } else {
1589                 setArray(ArrayData.seal(getArray()));
1590                 return this;
1591             }
1592         }
1593     }
1594 
1595     /**
1596      * Check whether this ScriptObject is sealed
1597      * @return true if sealed
1598      */
1599     public boolean isSealed() {
1600         return getMap().isSealed();
1601     }
1602 
1603     /**
1604      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1605      * @return the frozen ScriptObject
1606      */
1607     public ScriptObject freeze() {
1608         PropertyMap oldMap = getMap();
1609 
1610         while (true) {
1611             final PropertyMap newMap = getMap().freeze();
1612 
1613             if (!compareAndSetMap(oldMap, newMap)) {
1614                 oldMap = getMap();
1615             } else {
1616                 setArray(ArrayData.freeze(getArray()));
1617                 return this;
1618             }
1619         }
1620     }
1621 
1622     /**
1623      * Check whether this ScriptObject is frozen
1624      * @return true if frozen
1625      */
1626     public boolean isFrozen() {
1627         return getMap().isFrozen();
1628     }
1629 











1630     /**
1631      * Check whether this ScriptObject is scope
1632      * @return true if scope
1633      */
1634     public boolean isScope() {
1635         return false;
1636     }
1637 
1638     /**
1639      * Tag this script object as built in
1640      */
1641     public final void setIsBuiltin() {
1642         flags |= IS_BUILTIN;
1643     }
1644 
1645     /**
1646      * Check if this script object is built in
1647      * @return true if build in
1648      */
1649     public final boolean isBuiltin() {
1650         return (flags & IS_BUILTIN) != 0;
1651     }
1652 
1653     /**
1654      * Clears the properties from a ScriptObject
1655      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1656      *
1657      * @param strict strict mode or not
1658      */
1659     public void clear(final boolean strict) {
1660         final Iterator<String> iter = propertyIterator();
1661         while (iter.hasNext()) {
1662             delete(iter.next(), strict);
1663         }
1664     }
1665 
1666     /**
1667      * Checks if a property with a given key is present in a ScriptObject
1668      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1669      *
1670      * @param key the key to check for
1671      * @return true if a property with the given key exists, false otherwise
1672      */
1673     public boolean containsKey(final Object key) {
1674         return has(key);
1675     }
1676 
1677     /**
1678      * Checks if a property with a given value is present in a ScriptObject
1679      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1680      *
1681      * @param value value to check for
1682      * @return true if a property with the given value exists, false otherwise
1683      */
1684     public boolean containsValue(final Object value) {
1685         final Iterator<Object> iter = valueIterator();
1686         while (iter.hasNext()) {
1687             if (iter.next().equals(value)) {
1688                 return true;
1689             }
1690         }
1691         return false;
1692     }
1693 
1694     /**
1695      * Returns the set of {@literal <property, value>} entries that make up this
1696      * ScriptObject's properties
1697      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1698      *
1699      * @return an entry set of all the properties in this object
1700      */
1701     public Set<Map.Entry<Object, Object>> entrySet() {
1702         final Iterator<String> iter = propertyIterator();
1703         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1704         while (iter.hasNext()) {
1705             final Object key = iter.next();
1706             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1707         }
1708         return Collections.unmodifiableSet(entries);
1709     }
1710 
1711     /**
1712      * Check whether a ScriptObject contains no properties
1713      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1714      *
1715      * @return true if object has no properties
1716      */
1717     public boolean isEmpty() {
1718         return !propertyIterator().hasNext();
1719     }
1720 
1721     /**
1722      * Return the set of keys (property names) for all properties
1723      * in this ScriptObject
1724      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1725      *
1726      * @return keySet of this ScriptObject
1727      */
1728     public Set<Object> keySet() {
1729         final Iterator<String> iter = propertyIterator();
1730         final Set<Object> keySet = new HashSet<>();
1731         while (iter.hasNext()) {
1732             keySet.add(iter.next());
1733         }
1734         return Collections.unmodifiableSet(keySet);
1735     }
1736 
1737     /**
1738      * Put a property in the ScriptObject
1739      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1740      *
1741      * @param key property key
1742      * @param value property value
1743      * @param strict strict mode or not
1744      * @return oldValue if property with same key existed already
1745      */
1746     public Object put(final Object key, final Object value, final boolean strict) {
1747         final Object oldValue = get(key);
1748         final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1749         set(key, value, scriptObjectFlags);
1750         return oldValue;
1751     }
1752 
1753     /**
1754      * Put several properties in the ScriptObject given a mapping
1755      * of their keys to their values
1756      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1757      *
1758      * @param otherMap a {@literal <key,value>} map of properties to add
1759      * @param strict strict mode or not
1760      */
1761     public void putAll(final Map<?, ?> otherMap, final boolean strict) {
1762         final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1763         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1764             set(entry.getKey(), entry.getValue(), scriptObjectFlags);
1765         }
1766     }
1767 
1768     /**
1769      * Remove a property from the ScriptObject.
1770      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1771      *
1772      * @param key the key of the property
1773      * @param strict strict mode or not
1774      * @return the oldValue of the removed property
1775      */
1776     public Object remove(final Object key, final boolean strict) {
1777         final Object oldValue = get(key);
1778         delete(key, strict);
1779         return oldValue;
1780     }
1781 
1782     /**
1783      * Return the size of the ScriptObject - i.e. the number of properties
1784      * it contains
1785      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1786      *
1787      * @return number of properties in ScriptObject
1788      */
1789     public int size() {
1790         int n = 0;
1791         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1792             n++;
1793         }
1794         return n;
1795     }
1796 
1797     /**
1798      * Return the values of the properties in the ScriptObject
1799      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1800      *
1801      * @return collection of values for the properties in this ScriptObject
1802      */
1803     public Collection<Object> values() {
1804         final List<Object>     values = new ArrayList<>(size());
1805         final Iterator<Object> iter   = valueIterator();
1806         while (iter.hasNext()) {
1807             values.add(iter.next());
1808         }
1809         return Collections.unmodifiableList(values);
1810     }
1811 
1812     /**
1813      * Lookup method that, given a CallSiteDescriptor, looks up the target
1814      * MethodHandle and creates a GuardedInvocation
1815      * with the appropriate guard(s).
1816      *
1817      * @param desc call site descriptor
1818      * @param request the link request
1819      *
1820      * @return GuardedInvocation for the callsite
1821      */
1822     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1823         final int c = desc.getNameTokenCount();
1824         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
1825         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
1826         // care about them, and just link to whatever is the first operation.
1827         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
1828         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
1829         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
1830         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1831         // operation has an associated name or not.
1832         switch (operator) {
1833         case "getProp":
1834         case "getElem":
1835         case "getMethod":
1836             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
1837         case "setProp":
1838         case "setElem":
1839             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc, request);
1840         case "call":
1841             return findCallMethod(desc, request);
1842         case "new":
1843             return findNewMethod(desc, request);
1844         case "callMethod":
1845             return findCallMethodMethod(desc, request);
1846         default:
1847             return null;
1848         }
1849     }
1850 
1851     /**
1852      * Find the appropriate New method for an invoke dynamic call.
1853      *
1854      * @param desc The invoke dynamic call site descriptor.
1855      * @param request The link request
1856      *
1857      * @return GuardedInvocation to be invoked at call site.
1858      */
1859     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1860         return notAFunction();
1861     }
1862 
1863     /**
1864      * Find the appropriate CALL method for an invoke dynamic call.
1865      * This generates "not a function" always
1866      *
1867      * @param desc    the call site descriptor.
1868      * @param request the link request
1869      *
1870      * @return GuardedInvocation to be invoed at call site.
1871      */
1872     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1873         return notAFunction();
1874     }
1875 
1876     private GuardedInvocation notAFunction() {
1877         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
1878     }
1879 
1880     /**
1881      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
1882      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
1883      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
1884      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
1885      *
1886      * @param desc    the call site descriptor.
1887      * @param request the link request
1888      *
1889      * @return GuardedInvocation to be invoked at call site.
1890      */
1891     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1892         // R(P0, P1, ...)
1893         final MethodType callType = desc.getMethodType();
1894         // use type Object(P0) for the getter
1895         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1896         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1897 
1898         // Object(P0) => Object(P0, P1, ...)
1899         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1900         // R(Object, P0, P1, ...)
1901         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1902         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1903         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1904     }
1905 
1906     /**
1907      * Test whether this object contains in its prototype chain or is itself a with-object.
1908      * @return true if a with-object was found
1909      */
1910     boolean hasWithScope() {







1911         return false;
1912     }
1913 
1914     /**
1915      * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
1916      * {@code depth} times.
1917      * @param methodHandle a method handle
1918      * @param depth        distance to target prototype
1919      * @return the filtered method handle
1920      */
1921     static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
1922         if (depth == 0) {
1923             return methodHandle;
1924         }
1925         final int listIndex = depth - 1; // We don't need 0-deep walker
1926         MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null;
1927 
1928         if (filter == null) {
1929             filter = addProtoFilter(GETPROTO, depth - 1);
1930             PROTO_FILTERS.add(null);
1931             PROTO_FILTERS.set(listIndex, filter);
1932         }
1933 
1934         return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
1935     }
1936 
1937     /**
1938      * Find the appropriate GET method for an invoke dynamic call.
1939      *
1940      * @param desc     the call site descriptor
1941      * @param request  the link request
1942      * @param operator operator for get: getProp, getMethod, getElem etc
1943      *
1944      * @return GuardedInvocation to be invoked at call site.
1945      */
1946     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1947         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
1948 
1949         String name;
1950         name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1951         if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
1952             if (Global.isBuiltinFunctionPrototypeApply()) {
1953                 name = "call";
1954             }
1955         }
1956 
1957         if (request.isCallSiteUnstable() || hasWithScope()) {
1958             return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
1959         }
1960 
1961         final FindProperty find = findProperty(name, true);
1962         MethodHandle mh;
1963 
1964         if (find == null) {
1965             switch (operator) {
1966             case "getElem": // getElem only gets here if element name is constant, so treat it like a property access
1967             case "getProp":
1968                 return noSuchProperty(desc, request);
1969             case "getMethod":
1970                 return noSuchMethod(desc, request);
1971             default:
1972                 throw new AssertionError(operator); // never invoked with any other operation
1973             }
1974         }
1975 
1976         final GlobalConstants globalConstants = getGlobalConstants();
1977         if (globalConstants != null) {
1978             final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
1979             if (cinv != null) {
1980                 return cinv;
1981             }
1982         }
1983 
1984         final Class<?> returnType = desc.getMethodType().returnType();
1985         final Property property   = find.getProperty();
1986 
1987         final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
1988                 NashornCallSiteDescriptor.getProgramPoint(desc) :
1989                 UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
1990 
1991         mh = find.getGetter(returnType, programPoint, request);
1992         // Get the appropriate guard for this callsite and property.
1993         final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck);
1994         final ScriptObject owner = find.getOwner();
1995         final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
1996 
1997         final SwitchPoint protoSwitchPoint;
1998 
1999         if (mh == null) {
2000             mh = Lookup.emptyGetter(returnType);
2001             protoSwitchPoint = getProtoSwitchPoint(name, owner);
2002         } else if (!find.isSelf()) {
2003             assert mh.type().returnType().equals(returnType) :
2004                     "return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
2005             if (!(property instanceof UserAccessorProperty)) {
2006                 // Add a filter that replaces the self object with the prototype owning the property.
2007                 mh = addProtoFilter(mh, find.getProtoChainLength());
2008             }
2009             protoSwitchPoint = getProtoSwitchPoint(name, owner);
2010         } else {
2011             protoSwitchPoint = null;
2012         }
2013 
2014         final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
2015         return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
2016     }
2017 
2018     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
2019         Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod);
2020         final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
2021         final MethodHandle guard   = getScriptObjectGuard(desc.getMethodType(), true);
2022         return new GuardedInvocation(invoker, guard);
2023     }
2024 
2025     @SuppressWarnings("unused")
2026     private Object megamorphicGet(final String key, final boolean isMethod) {
2027         final FindProperty find = findProperty(key, true);
2028         if (find != null) {
2029             return find.getObjectValue();
2030         }
2031 
2032         return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
2033     }
2034 
2035     // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
2036     @SuppressWarnings("unused")
2037     private void declareAndSet(final String key, final Object value) {
2038         final PropertyMap oldMap = getMap();
2039         final FindProperty find = findProperty(key, false);
2040         assert find != null;
2041 
2042         final Property property = find.getProperty();
2043         assert property != null;
2044         assert property.needsDeclaration();
2045 
2046         final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
2047         setMap(newMap);
2048         set(key, value, 0);
2049     }
2050 
2051     /**
2052      * Find the appropriate GETINDEX method for an invoke dynamic call.
2053      *
2054      * @param desc    the call site descriptor
2055      * @param request the link request
2056      *
2057      * @return GuardedInvocation to be invoked at call site.
2058      */
2059     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2060         final MethodType callType                = desc.getMethodType();
2061         final Class<?>   returnType              = callType.returnType();
2062         final Class<?>   returnClass             = returnType.isPrimitive() ? returnType : Object.class;
2063         final Class<?>   keyClass                = callType.parameterType(1);
2064         final boolean    explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2065 
2066         final String name;
2067         if (returnClass.isPrimitive()) {
2068             //turn e.g. get with a double into getDouble
2069             final String returnTypeName = returnClass.getName();
2070             name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
2071         } else {
2072             name = "get";
2073         }
2074 
2075         final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
2076         return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2077     }
2078 
2079     private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) {
2080         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck);
2081     }
2082 
2083     /**
2084      * Find a handle for a getIndex method
2085      * @param returnType     return type for getter
2086      * @param name           name
2087      * @param elementType    index type for getter
2088      * @param desc           call site descriptor
2089      * @return method handle for getter
2090      */
2091     protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
2092         if (!returnType.isPrimitive()) {
2093             return findOwnMH_V(getClass(), name, returnType, elementType);
2094         }
2095 
2096         return MH.insertArguments(
2097                 findOwnMH_V(getClass(), name, returnType, elementType, int.class),
2098                 2,
2099                 NashornCallSiteDescriptor.isOptimistic(desc) ?
2100                         NashornCallSiteDescriptor.getProgramPoint(desc) :
2101                         INVALID_PROGRAM_POINT);
2102     }
2103 
2104     /**
2105      * Get a switch point for a property with the given {@code name} that will be invalidated when
2106      * the property definition is changed in this object's prototype chain. Returns {@code null} if
2107      * the property is defined in this object itself.
2108      *
2109      * @param name the property name
2110      * @param owner the property owner, null if property is not defined
2111      * @return a SwitchPoint or null
2112      */
2113     public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
2114         if (owner == this || getProto() == null) {
2115             return null;
2116         }
2117 
2118         for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
2119             final ScriptObject parent = obj.getProto();
2120             parent.getMap().addListener(name, obj.getMap());
2121         }
2122 
2123         return getMap().getSwitchPoint(name);
2124     }
2125 
2126     /**
2127      * Find the appropriate SET method for an invoke dynamic call.
2128      *
2129      * @param desc    the call site descriptor
2130      * @param request the link request
2131      *
2132      * @return GuardedInvocation to be invoked at call site.
2133      */
2134     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2135         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2136 
2137         if (request.isCallSiteUnstable() || hasWithScope()) {
2138             return findMegaMorphicSetMethod(desc, name);
2139         }
2140 
2141         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2142 
2143         /*
2144          * If doing property set on a scope object, we should stop proto search on the first
2145          * non-scope object. Without this, for example, when assigning "toString" on global scope,
2146          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
2147          *
2148          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
2149          */
2150         FindProperty find = findProperty(name, true, this);
2151 
2152         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
2153         if (find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
2154             // We should still check if inherited data property is not writable
2155             if (isExtensible() && !find.getProperty().isWritable()) {
2156                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2157             }
2158             // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well.
2159             if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) {
2160                 find = null;
2161             }
2162         }
2163 
2164         if (find != null) {
2165             if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
2166                 if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) {
2167                     throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode.
2168                 }
2169                 // Existing, non-writable property
2170                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2171             }
2172         } else {
2173             if (!isExtensible()) {
2174                 return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false);
2175             }
2176         }
2177 
2178         final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
2179 
2180         final GlobalConstants globalConstants = getGlobalConstants();
2181         if (globalConstants != null) {
2182             final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
2183             if (cinv != null) {
2184                 return cinv;
2185             }
2186         }
2187 
2188         return inv;
2189     }
2190 
2191     private GlobalConstants getGlobalConstants() {
2192         // Avoid hitting getContext() which might be costly for a non-Global unless needed.
2193         return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
2194     }
2195 
2196     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
2197         final String  name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2198         if (NashornCallSiteDescriptor.isStrict(desc)) {
2199             throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this));
2200         }
2201         assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
2202         return new GuardedInvocation(
2203                 Lookup.EMPTY_SETTER,
2204                 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
2205                 getProtoSwitchPoint(name, null),
2206                 explicitInstanceOfCheck ? null : ClassCastException.class);
2207     }
2208 
2209     @SuppressWarnings("unused")
2210     private boolean extensionCheck(final boolean isStrict, final String name) {
2211         if (isExtensible()) {
2212             return true; //go on and do the set. this is our guard
2213         } else if (isStrict) {
2214             //throw an error for attempting to do the set in strict mode
2215             throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this));
2216         } else {
2217             //not extensible, non strict - this is a nop
2218             return false;
2219         }
2220     }
2221 
2222     private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
2223         final MethodType        type = desc.getMethodType().insertParameterTypes(1, Object.class);
2224         //never bother with ClassCastExceptionGuard for megamorphic callsites
2225         final GuardedInvocation inv = findSetIndexMethod(getClass(), desc, false, type);
2226         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
2227     }
2228 
2229     @SuppressWarnings("unused")
2230     private static Object globalFilter(final Object object) {
2231         ScriptObject sobj = (ScriptObject) object;
2232         while (sobj != null && !(sobj instanceof Global)) {
2233             sobj = sobj.getProto();
2234         }
2235         return sobj;
2236     }
2237 
2238     /**
2239      * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray}
2240      * provides special quick accessor linkage for continuous arrays that are represented as Java arrays
2241      *
2242      * @param desc    call site descriptor
2243      * @param request link request
2244      *
2245      * @return GuardedInvocation to be invoked at call site.
2246      */
2247     protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
2248         return findSetIndexMethod(getClass(), desc, explicitInstanceOfCheck(desc, request), desc.getMethodType());
2249     }
2250 
2251     /**
2252      * Find the appropriate SETINDEX method for an invoke dynamic call.
2253      *
2254      * @param clazz the receiver class
2255      * @param desc  the call site descriptor
2256      * @param explicitInstanceOfCheck add an explicit instanceof check?
2257      * @param callType the method type at the call site
2258      *
2259      * @return GuardedInvocation to be invoked at call site.
2260      */
2261     private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) {
2262         assert callType.parameterCount() == 3;
2263         final Class<?> keyClass   = callType.parameterType(1);
2264         final Class<?> valueClass = callType.parameterType(2);
2265 
2266         MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, int.class);
2267         methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc));
2268 
2269         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2270     }
2271 
2272     /**
2273      * Fall back if a function property is not found.
2274      * @param desc The call site descriptor
2275      * @param request the link request
2276      * @return GuardedInvocation to be invoked at call site.
2277      */
2278     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2279         final String       name      = desc.getNameToken(2);
2280         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
2281         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
2282 
2283         if (find == null) {
2284             return noSuchProperty(desc, request);
2285         }
2286 
2287         final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2288 
2289         final Object value = find.getObjectValue();
2290         if (!(value instanceof ScriptFunction)) {
2291             return createEmptyGetter(desc, explicitInstanceOfCheck, name);
2292         }
2293 
2294         final ScriptFunction func = (ScriptFunction)value;
2295         final Object         thiz = scopeCall && func.isStrict() ? UNDEFINED : this;
2296         // TODO: It'd be awesome if we could bind "name" without binding "this".
2297         // Since we're binding this we must use an identity guard here.
2298         return new GuardedInvocation(
2299                 MH.dropArguments(
2300                         MH.constant(
2301                                 ScriptFunction.class,
2302                                 func.makeBoundFunction(thiz, new Object[] { name })),
2303                         0,
2304                         Object.class),
2305                 NashornGuards.combineGuards(
2306                         NashornGuards.getIdentityGuard(this),
2307                         NashornGuards.getMapGuard(getMap(), true)));
2308     }
2309 
2310     /**
2311      * Fall back if a property is not found.
2312      * @param desc the call site descriptor.
2313      * @param request the link request
2314      * @return GuardedInvocation to be invoked at call site.
2315      */
2316     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
2317         final String       name        = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2318         final FindProperty find        = findProperty(NO_SUCH_PROPERTY_NAME, true);
2319         final boolean      scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
2320 
2321         if (find != null) {
2322             final Object   value = find.getObjectValue();
2323             ScriptFunction func  = null;
2324             MethodHandle   mh    = null;
2325 
2326             if (value instanceof ScriptFunction) {
2327                 func = (ScriptFunction)value;
2328                 mh   = getCallMethodHandle(func, desc.getMethodType(), name);
2329             }
2330 
2331             if (mh != null) {
2332                 assert func != null;
2333                 if (scopeAccess && func.isStrict()) {
2334                     mh = bindTo(mh, UNDEFINED);
2335                 }
2336 
2337                 return new GuardedInvocation(
2338                         mh,
2339                         find.isSelf()?
2340                             getKnownFunctionPropertyGuardSelf(
2341                                 getMap(),
2342                                 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2343                                 func)
2344                             :
2345                             //TODO this always does a scriptobject check
2346                             getKnownFunctionPropertyGuardProto(
2347                                 getMap(),
2348                                 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2349                                 find.getProtoChainLength(),
2350                                 func),
2351                         getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
2352                         //TODO this doesn't need a ClassCastException as guard always checks script object
2353                         null);
2354             }
2355         }
2356 
2357         if (scopeAccess) {
2358             throw referenceError("not.defined", name);
2359         }
2360 
2361         return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name);
2362     }
2363 
2364     /**
2365      * Invoke fall back if a property is not found.
2366      * @param name Name of property.
2367      * @param programPoint program point
2368      * @return Result from call.
2369      */
2370     protected Object invokeNoSuchProperty(final String name, final int programPoint) {
2371         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
2372 
2373         Object ret = UNDEFINED;
2374 
2375         if (find != null) {
2376             final Object func = find.getObjectValue();
2377 
2378             if (func instanceof ScriptFunction) {
2379                 ret = ScriptRuntime.apply((ScriptFunction)func, this, name);
2380             }
2381         }
2382 
2383         if (isValid(programPoint)) {
2384             throw new UnwarrantedOptimismException(ret, programPoint);
2385         }
2386 
2387         return ret;
2388     }
2389 
2390 
2391     /**
2392      * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
2393      * @param name the method name
2394      * @return the bound function, or undefined
2395      */
2396     private Object getNoSuchMethod(final String name, final int programPoint) {
2397         final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
2398 
2399         if (find == null) {
2400             return invokeNoSuchProperty(name, programPoint);
2401         }
2402 
2403         final Object value = find.getObjectValue();
2404         if (!(value instanceof ScriptFunction)) {
2405             return UNDEFINED;
2406         }
2407 
2408         return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
2409     }
2410 
2411     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) {
2412         if (NashornCallSiteDescriptor.isOptimistic(desc)) {
2413             throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
2414         }
2415 
2416         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
2417                 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null),
2418                 explicitInstanceOfCheck ? null : ClassCastException.class);
2419     }
2420 
2421     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2422         protected T[] values;
2423         protected final ScriptObject object;
2424         private int index;
2425 
2426         ScriptObjectIterator(final ScriptObject object) {
2427             this.object = object;
2428         }
2429 
2430         protected abstract void init();
2431 
2432         @Override
2433         public boolean hasNext() {
2434             if (values == null) {
2435                 init();
2436             }
2437             return index < values.length;
2438         }
2439 
2440         @Override
2441         public T next() {
2442             if (values == null) {
2443                 init();
2444             }
2445             return values[index++];
2446         }
2447 
2448         @Override
2449         public void remove() {
2450             throw new UnsupportedOperationException("remove");
2451         }
2452     }
2453 
2454     private static class KeyIterator extends ScriptObjectIterator<String> {
2455         KeyIterator(final ScriptObject object) {
2456             super(object);
2457         }
2458 
2459         @Override
2460         protected void init() {
2461             final Set<String> keys = new LinkedHashSet<>();
2462             final Set<String> nonEnumerable = new HashSet<>();
2463             for (ScriptObject self = object; self != null; self = self.getProto()) {
2464                 keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable)));
2465             }
2466             this.values = keys.toArray(new String[keys.size()]);
2467         }
2468     }
2469 
2470     private static class ValueIterator extends ScriptObjectIterator<Object> {
2471         ValueIterator(final ScriptObject object) {
2472             super(object);
2473         }
2474 
2475         @Override
2476         protected void init() {
2477             final ArrayList<Object> valueList = new ArrayList<>();
2478             final Set<String> nonEnumerable = new HashSet<>();
2479             for (ScriptObject self = object; self != null; self = self.getProto()) {
2480                 for (final String key : self.getOwnKeys(false, nonEnumerable)) {
2481                     valueList.add(self.get(key));
2482                 }
2483             }
2484             this.values = valueList.toArray(new Object[valueList.size()]);
2485         }
2486     }
2487 
2488     /**
2489      * Add a spill property for the given key.
2490      * @param key    Property key.
2491      * @param flags  Property flags.
2492      * @return Added property.
2493      */
2494     private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
2495         final PropertyMap propertyMap = getMap();
2496         final int fieldSlot  = propertyMap.getFreeFieldSlot();
2497         final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
2498 
2499         Property property;
2500         if (fieldSlot > -1) {
2501             property = hasInitialValue ?
2502                 new AccessorProperty(key, propertyFlags, fieldSlot, this, value) :
2503                 new AccessorProperty(key, propertyFlags, getClass(), fieldSlot);
2504             property = addOwnProperty(property);
2505         } else {
2506             final int spillSlot = propertyMap.getFreeSpillSlot();
2507             property = hasInitialValue ?
2508                 new SpillProperty(key, propertyFlags, spillSlot, this, value) :
2509                 new SpillProperty(key, propertyFlags, spillSlot);
2510             property = addOwnProperty(property);
2511             ensureSpillSize(property.getSlot());
2512         }
2513         return property;
2514     }
2515 
2516     /**
2517      * Add a spill entry for the given key.
2518      * @param key Property key.
2519      * @return Setter method handle.
2520      */
2521     MethodHandle addSpill(final Class<?> type, final String key) {
2522         return addSpillProperty(key, 0, null, false).getSetter(type, getMap());
2523     }
2524 
2525     /**
2526      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2527      * fewer parameters than declared and other things that JavaScript allows. This might involve
2528      * creating collectors.
2529      *
2530      * @param methodHandle method handle for invoke
2531      * @param callType     type of the call
2532      *
2533      * @return method handle with adjusted arguments
2534      */
2535     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2536         return pairArguments(methodHandle, callType, null);
2537     }
2538 
2539     /**
2540      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2541      * fewer parameters than declared and other things that JavaScript allows. This might involve
2542      * creating collectors.
2543      *
2544      * Make sure arguments are paired correctly.
2545      * @param methodHandle MethodHandle to adjust.
2546      * @param callType     MethodType of the call site.
2547      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2548      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2549      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2550      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2551      *
2552      * @return method handle with adjusted arguments
2553      */
2554     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2555         final MethodType methodType = methodHandle.type();
2556         if (methodType.equals(callType.changeReturnType(methodType.returnType()))) {
2557             return methodHandle;
2558         }
2559 
2560         final int parameterCount = methodType.parameterCount();
2561         final int callCount      = callType.parameterCount();
2562 
2563         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2564         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 &&
2565                 callType.parameterType(callCount - 1).isArray();
2566 
2567         if (isCalleeVarArg) {
2568             return isCallerVarArg ?
2569                 methodHandle :
2570                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2571         }
2572 
2573         if (isCallerVarArg) {
2574             return adaptHandleToVarArgCallSite(methodHandle, callCount);
2575         }
2576 
2577         if (callCount < parameterCount) {
2578             final int      missingArgs = parameterCount - callCount;
2579             final Object[] fillers     = new Object[missingArgs];
2580 
2581             Arrays.fill(fillers, UNDEFINED);
2582 
2583             if (isCalleeVarArg) {
2584                 fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY;
2585             }
2586 
2587             return MH.insertArguments(
2588                 methodHandle,
2589                 parameterCount - missingArgs,
2590                 fillers);
2591         }
2592 
2593         if (callCount > parameterCount) {
2594             final int discardedArgs = callCount - parameterCount;
2595 
2596             final Class<?>[] discards = new Class<?>[discardedArgs];
2597             Arrays.fill(discards, Object.class);
2598 
2599             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2600         }
2601 
2602         return methodHandle;
2603     }
2604 
2605     static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
2606         final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
2607         return MH.filterArguments(
2608             MH.asSpreader(
2609                 mh,
2610                 Object[].class,
2611                 spreadArgs),
2612             callSiteParamCount - 1,
2613             MH.insertArguments(
2614                 TRUNCATINGFILTER,
2615                 0,
2616                 spreadArgs)
2617             );
2618     }
2619 
2620     @SuppressWarnings("unused")
2621     private static Object[] truncatingFilter(final int n, final Object[] array) {
2622         final int length = array == null ? 0 : array.length;
2623         if (n == length) {
2624             return array == null ? ScriptRuntime.EMPTY_ARRAY : array;
2625         }
2626 
2627         final Object[] newArray = new Object[n];
2628 
2629         if (array != null) {
2630             System.arraycopy(array, 0, newArray, 0, Math.min(n, length));
2631         }
2632 
2633         if (length < n) {
2634             final Object fill = UNDEFINED;
2635 
2636             for (int i = length; i < n; i++) {
2637                 newArray[i] = fill;
2638             }
2639         }
2640 
2641         return newArray;
2642     }
2643 
2644     /**
2645       * Numeric length setter for length property
2646       *
2647       * @param newLength new length to set
2648       */
2649     public final void setLength(final long newLength) {
2650         ArrayData data = getArray();
2651         final long arrayLength = data.length();
2652         if (newLength == arrayLength) {
2653             return;
2654         }
2655 
2656         if (newLength > arrayLength) {
2657             data = data.ensure(newLength - 1);
2658             if (data.canDelete(arrayLength, newLength - 1, false)) {
2659                data = data.delete(arrayLength, newLength - 1);
2660             }
2661             setArray(data);
2662             return;
2663         }
2664 
2665         if (newLength < arrayLength) {
2666            long actualLength = newLength;
2667 
2668            // Check for numeric keys in property map and delete them or adjust length, depending on whether
2669            // they're defined as configurable. See ES5 #15.4.5.2
2670            if (getMap().containsArrayKeys()) {
2671 
2672                for (long l = arrayLength - 1; l >= newLength; l--) {
2673                    final FindProperty find = findProperty(JSType.toString(l), false);
2674 
2675                    if (find != null) {
2676 
2677                        if (find.getProperty().isConfigurable()) {
2678                            deleteOwnProperty(find.getProperty());
2679                        } else {
2680                            actualLength = l + 1;
2681                            break;
2682                        }
2683                    }
2684                }
2685            }
2686 
2687            setArray(data.shrink(actualLength));
2688            data.setLength(actualLength);
2689        }
2690     }
2691 
2692     private int getInt(final int index, final String key, final int programPoint) {
2693         if (isValidArrayIndex(index)) {
2694             for (ScriptObject object = this; ; ) {
2695                 if (object.getMap().containsArrayKeys()) {
2696                     final FindProperty find = object.findProperty(key, false, this);
2697 
2698                     if (find != null) {
2699                         return getIntValue(find, programPoint);
2700                     }
2701                 }
2702 
2703                 if ((object = object.getProto()) == null) {
2704                     break;
2705                 }
2706 
2707                 final ArrayData array = object.getArray();
2708 
2709                 if (array.has(index)) {
2710                     return isValid(programPoint) ?
2711                         array.getIntOptimistic(index, programPoint) :
2712                         array.getInt(index);
2713                 }
2714             }
2715         } else {
2716             final FindProperty find = findProperty(key, true);
2717 
2718             if (find != null) {
2719                 return getIntValue(find, programPoint);
2720             }
2721         }
2722 
2723         return JSType.toInt32(invokeNoSuchProperty(key, programPoint));
2724     }
2725 
2726     @Override
2727     public int getInt(final Object key, final int programPoint) {
2728         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2729         final int       index        = getArrayIndex(primitiveKey);
2730         final ArrayData array        = getArray();
2731 
2732         if (array.has(index)) {
2733             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2734         }
2735 
2736         return getInt(index, JSType.toString(primitiveKey), programPoint);
2737     }
2738 
2739     @Override
2740     public int getInt(final double key, final int programPoint) {
2741         final int       index = getArrayIndex(key);
2742         final ArrayData array = getArray();
2743 
2744         if (array.has(index)) {
2745             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2746         }
2747 
2748         return getInt(index, JSType.toString(key), programPoint);
2749     }
2750 
2751     @Override
2752     public int getInt(final long key, final int programPoint) {
2753         final int       index = getArrayIndex(key);
2754         final ArrayData array = getArray();
2755 
2756         if (array.has(index)) {
2757             return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2758         }
2759 
2760         return getInt(index, JSType.toString(key), programPoint);
2761     }
2762 
2763     @Override
2764     public int getInt(final int key, final int programPoint) {
2765         final int       index = getArrayIndex(key);
2766         final ArrayData array = getArray();
2767 
2768         if (array.has(index)) {
2769             return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key);
2770         }
2771 
2772         return getInt(index, JSType.toString(key), programPoint);
2773     }
2774 
2775     private long getLong(final int index, final String key, final int programPoint) {
2776         if (isValidArrayIndex(index)) {
2777             for (ScriptObject object = this; ; ) {
2778                 if (object.getMap().containsArrayKeys()) {
2779                     final FindProperty find = object.findProperty(key, false, this);
2780                     if (find != null) {
2781                         return getLongValue(find, programPoint);
2782                     }
2783                 }
2784 
2785                 if ((object = object.getProto()) == null) {
2786                     break;
2787                 }
2788 
2789                 final ArrayData array = object.getArray();
2790 
2791                 if (array.has(index)) {
2792                     return isValid(programPoint) ?
2793                         array.getLongOptimistic(index, programPoint) :
2794                         array.getLong(index);
2795                 }
2796             }
2797         } else {
2798             final FindProperty find = findProperty(key, true);
2799 
2800             if (find != null) {
2801                 return getLongValue(find, programPoint);
2802             }
2803         }
2804 
2805         return JSType.toLong(invokeNoSuchProperty(key, programPoint));
2806     }
2807 
2808     @Override
2809     public long getLong(final Object key, final int programPoint) {
2810         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2811         final int       index        = getArrayIndex(primitiveKey);
2812         final ArrayData array        = getArray();
2813 
2814         if (array.has(index)) {
2815             return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2816         }
2817 
2818         return getLong(index, JSType.toString(primitiveKey), programPoint);
2819     }
2820 
2821     @Override
2822     public long getLong(final double key, final int programPoint) {
2823         final int       index = getArrayIndex(key);
2824         final ArrayData array = getArray();
2825 
2826         if (array.has(index)) {
2827             return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2828         }
2829 
2830         return getLong(index, JSType.toString(key), programPoint);
2831     }
2832 
2833     @Override
2834     public long getLong(final long key, final int programPoint) {
2835         final int       index = getArrayIndex(key);
2836         final ArrayData array = getArray();
2837 
2838         if (array.has(index)) {
2839             return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2840         }
2841 
2842         return getLong(index, JSType.toString(key), programPoint);
2843     }
2844 
2845     @Override
2846     public long getLong(final int key, final int programPoint) {
2847         final int       index = getArrayIndex(key);
2848         final ArrayData array = getArray();
2849 
2850         if (array.has(index)) {
2851             return isValid(programPoint) ? array.getLongOptimistic(key, programPoint) : array.getLong(key);
2852         }
2853 
2854         return getLong(index, JSType.toString(key), programPoint);
2855     }
2856 
2857     private double getDouble(final int index, final String key, final int programPoint) {
2858         if (isValidArrayIndex(index)) {
2859             for (ScriptObject object = this; ; ) {
2860                 if (object.getMap().containsArrayKeys()) {
2861                     final FindProperty find = object.findProperty(key, false, this);
2862                     if (find != null) {
2863                         return getDoubleValue(find, programPoint);
2864                     }
2865                 }
2866 
2867                 if ((object = object.getProto()) == null) {
2868                     break;
2869                 }
2870 
2871                 final ArrayData array = object.getArray();
2872 
2873                 if (array.has(index)) {
2874                     return isValid(programPoint) ?
2875                         array.getDoubleOptimistic(index, programPoint) :
2876                         array.getDouble(index);
2877                 }
2878             }
2879         } else {
2880             final FindProperty find = findProperty(key, true);
2881 
2882             if (find != null) {
2883                 return getDoubleValue(find, programPoint);
2884             }
2885         }
2886 
2887         return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT));
2888     }
2889 
2890     @Override
2891     public double getDouble(final Object key, final int programPoint) {
2892         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2893         final int       index        = getArrayIndex(primitiveKey);
2894         final ArrayData array        = getArray();
2895 
2896         if (array.has(index)) {
2897             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2898         }
2899 
2900         return getDouble(index, JSType.toString(primitiveKey), programPoint);
2901     }
2902 
2903     @Override
2904     public double getDouble(final double key, final int programPoint) {
2905         final int       index = getArrayIndex(key);
2906         final ArrayData array = getArray();
2907 
2908         if (array.has(index)) {
2909             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2910         }
2911 
2912         return getDouble(index, JSType.toString(key), programPoint);
2913     }
2914 
2915     @Override
2916     public double getDouble(final long key, final int programPoint) {
2917         final int       index = getArrayIndex(key);
2918         final ArrayData array = getArray();
2919 
2920         if (array.has(index)) {
2921             return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2922         }
2923 
2924         return getDouble(index, JSType.toString(key), programPoint);
2925     }
2926 
2927     @Override
2928     public double getDouble(final int key, final int programPoint) {
2929         final int       index = getArrayIndex(key);
2930         final ArrayData array = getArray();
2931 
2932         if (array.has(index)) {
2933             return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key);
2934         }
2935 
2936         return getDouble(index, JSType.toString(key), programPoint);
2937     }
2938 
2939     private Object get(final int index, final String key) {
2940         if (isValidArrayIndex(index)) {
2941             for (ScriptObject object = this; ; ) {
2942                 if (object.getMap().containsArrayKeys()) {
2943                     final FindProperty find = object.findProperty(key, false, this);
2944 
2945                     if (find != null) {
2946                         return find.getObjectValue();
2947                     }
2948                 }
2949 
2950                 if ((object = object.getProto()) == null) {
2951                     break;
2952                 }
2953 
2954                 final ArrayData array = object.getArray();
2955 
2956                 if (array.has(index)) {
2957                     return array.getObject(index);
2958                 }
2959             }
2960         } else {
2961             final FindProperty find = findProperty(key, true);
2962 
2963             if (find != null) {
2964                 return find.getObjectValue();
2965             }
2966         }
2967 
2968         return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
2969     }
2970 
2971     @Override
2972     public Object get(final Object key) {
2973         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2974         final int       index        = getArrayIndex(primitiveKey);
2975         final ArrayData array        = getArray();
2976 
2977         if (array.has(index)) {
2978             return array.getObject(index);
2979         }
2980 
2981         return get(index, JSType.toString(primitiveKey));
2982     }
2983 
2984     @Override
2985     public Object get(final double key) {
2986         final int index = getArrayIndex(key);
2987         final ArrayData array = getArray();
2988 
2989         if (array.has(index)) {
2990             return array.getObject(index);
2991         }
2992 
2993         return get(index, JSType.toString(key));
2994     }
2995 
2996     @Override
2997     public Object get(final long key) {
2998         final int index = getArrayIndex(key);
2999         final ArrayData array = getArray();
3000 
3001         if (array.has(index)) {
3002             return array.getObject(index);
3003         }
3004 
3005         return get(index, JSType.toString(key));
3006     }
3007 
3008     @Override
3009     public Object get(final int key) {
3010         final int index = getArrayIndex(key);
3011         final ArrayData array = getArray();
3012 
3013         if (array.has(index)) {
3014             return array.getObject(index);
3015         }
3016 
3017         return get(index, JSType.toString(key));
3018     }
3019 
3020     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) {
3021         if (getMap().containsArrayKeys()) {
3022             final String       key  = JSType.toString(longIndex);
3023             final FindProperty find = findProperty(key, true);
3024             if (find != null) {
3025                 setObject(find, callSiteFlags, key, value);
3026                 return true;
3027             }
3028         }
3029         return false;
3030     }
3031 
3032     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final int callSiteFlags) {
3033         if (getMap().containsArrayKeys()) {
3034             final String       key  = JSType.toString(longIndex);
3035             final FindProperty find = findProperty(key, true);
3036             if (find != null) {
3037                 setObject(find, callSiteFlags, key, value);
3038                 return true;
3039             }
3040         }
3041         return false;
3042     }
3043 
3044     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) {
3045          if (getMap().containsArrayKeys()) {
3046             final String       key  = JSType.toString(longIndex);
3047             final FindProperty find = findProperty(key, true);
3048             if (find != null) {
3049                 setObject(find, callSiteFlags, key, value);
3050                 return true;
3051             }
3052         }
3053         return false;
3054     }
3055 
3056     private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) {
3057         if (getMap().containsArrayKeys()) {
3058             final String       key  = JSType.toString(longIndex);
3059             final FindProperty find = findProperty(key, true);
3060             if (find != null) {
3061                 setObject(find, callSiteFlags, key, value);
3062                 return true;
3063             }
3064         }
3065         return false;
3066     }
3067 
3068     //value agnostic
3069     private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) {
3070         if (longIndex >= oldLength) {
3071             if (!isExtensible()) {
3072                 if (isStrictFlag(callSiteFlags)) {
3073                     throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this));
3074                 }
3075                 return true;
3076             }
3077             setArray(getArray().ensure(longIndex));
3078         }
3079         return false;
3080     }
3081 
3082     private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) {
3083         if (longIndex > oldLength) {
3084             ArrayData array = getArray();
3085             if (array.canDelete(oldLength, longIndex - 1, strict)) {
3086                 array = array.delete(oldLength, longIndex - 1);
3087             }
3088             setArray(array);
3089         }
3090     }
3091 
3092     private void doesNotHave(final int index, final int value, final int callSiteFlags) {
3093         final long oldLength = getArray().length();
3094         final long longIndex = ArrayIndex.toLongIndex(index);
3095         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3096             final boolean strict = isStrictFlag(callSiteFlags);
3097             setArray(getArray().set(index, value, strict));
3098             doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3099         }
3100     }
3101 
3102     private void doesNotHave(final int index, final long value, final int callSiteFlags) {
3103         final long oldLength = getArray().length();
3104         final long longIndex = ArrayIndex.toLongIndex(index);
3105         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3106             final boolean strict = isStrictFlag(callSiteFlags);
3107             setArray(getArray().set(index, value, strict));
3108             doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3109         }
3110     }
3111 
3112     private void doesNotHave(final int index, final double value, final int callSiteFlags) {
3113         final long oldLength = getArray().length();
3114         final long longIndex = ArrayIndex.toLongIndex(index);
3115         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3116             final boolean strict = isStrictFlag(callSiteFlags);
3117             setArray(getArray().set(index, value, strict));
3118             doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3119         }
3120     }
3121 
3122     private void doesNotHave(final int index, final Object value, final int callSiteFlags) {
3123         final long oldLength = getArray().length();
3124         final long longIndex = ArrayIndex.toLongIndex(index);
3125         if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3126             final boolean strict = isStrictFlag(callSiteFlags);
3127             setArray(getArray().set(index, value, strict));
3128             doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3129         }
3130     }
3131 
3132     /**
3133      * This is the most generic of all Object setters. Most of the others use this in some form.
3134      * TODO: should be further specialized
3135      *
3136      * @param find          found property
3137      * @param callSiteFlags callsite flags
3138      * @param key           property key
3139      * @param value         property value
3140      */
3141     public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) {
3142         FindProperty f = find;
3143 
3144         invalidateGlobalConstant(key);
3145 
3146         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
3147             final boolean isScope = isScopeFlag(callSiteFlags);
3148             // If the start object of the find is not this object it means the property was found inside a
3149             // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set'
3150             // to the 'with' object.
3151             // Note that although a 'set' operation involving a with statement follows scope rules outside
3152             // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists),
3153             // it follows non-scope rules inside the 'with' expression (set is performed on the top level object).
3154             // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object.
3155             if (isScope && f.getSelf() != this) {
3156                 f.getSelf().setObject(null, 0, key, value);
3157                 return;
3158             }
3159             // Setting a property should not modify the property in prototype unless this is a scope callsite
3160             // and the owner is a scope object as well (with the exception of 'with' statement handled above).
3161             if (!isScope || !f.getOwner().isScope()) {
3162                 f = null;
3163             }
3164         }
3165 
3166         if (f != null) {
3167             if (!f.getProperty().isWritable()) {
3168                 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
3169                     throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode.
3170                 }
3171                 if (isStrictFlag(callSiteFlags)) {
3172                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
3173                 }
3174                 return;
3175             }
3176 
3177             f.setValue(value, isStrictFlag(callSiteFlags));
3178 
3179         } else if (!isExtensible()) {
3180             if (isStrictFlag(callSiteFlags)) {
3181                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
3182             }
3183         } else {
3184             ScriptObject sobj = this;
3185             // undefined scope properties are set in the global object.
3186             if (isScope()) {
3187                 while (sobj != null && !(sobj instanceof Global)) {
3188                     sobj = sobj.getProto();
3189                 }
3190                 assert sobj != null : "no parent global object in scope";
3191             }
3192             //this will unbox any Number object to its primitive type in case the
3193             //property supports primitive types, so it doesn't matter that it comes
3194             //in as an Object.
3195             sobj.addSpillProperty(key, 0, value, true);
3196         }
3197     }
3198 
3199     @Override
3200     public void set(final Object key, final int value, final int callSiteFlags) {
3201         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3202         final int    index        = getArrayIndex(primitiveKey);
3203 
3204         if (isValidArrayIndex(index)) {
3205             final ArrayData data = getArray();
3206             if (data.has(index)) {
3207                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3208             } else {
3209                 doesNotHave(index, value, callSiteFlags);
3210             }
3211 
3212             return;
3213         }
3214 
3215         final String propName = JSType.toString(primitiveKey);
3216         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3217     }
3218 
3219     @Override
3220     public void set(final Object key, final long value, final int callSiteFlags) {
3221         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3222         final int    index        = getArrayIndex(primitiveKey);
3223 
3224         if (isValidArrayIndex(index)) {
3225             final ArrayData data = getArray();
3226             if (data.has(index)) {
3227                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3228             } else {
3229                 doesNotHave(index, value, callSiteFlags);
3230             }
3231 
3232             return;
3233         }
3234 
3235         final String propName = JSType.toString(primitiveKey);
3236         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3237     }
3238 
3239     @Override
3240     public void set(final Object key, final double value, final int callSiteFlags) {
3241         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3242         final int    index        = getArrayIndex(primitiveKey);
3243 
3244         if (isValidArrayIndex(index)) {
3245             final ArrayData data = getArray();
3246             if (data.has(index)) {
3247                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3248             } else {
3249                 doesNotHave(index, value, callSiteFlags);
3250             }
3251 
3252             return;
3253         }
3254 
3255         final String propName = JSType.toString(primitiveKey);
3256         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3257     }
3258 
3259     @Override
3260     public void set(final Object key, final Object value, final int callSiteFlags) {
3261         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3262         final int    index        = getArrayIndex(primitiveKey);
3263 
3264         if (isValidArrayIndex(index)) {
3265             final ArrayData data = getArray();
3266             if (data.has(index)) {
3267                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3268             } else {
3269                 doesNotHave(index, value, callSiteFlags);
3270             }
3271 
3272             return;
3273         }
3274 
3275         final String propName = JSType.toString(primitiveKey);
3276         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3277     }
3278 
3279     @Override
3280     public void set(final double key, final int value, final int callSiteFlags) {
3281         final int index = getArrayIndex(key);
3282 
3283         if (isValidArrayIndex(index)) {
3284             final ArrayData data = getArray();
3285             if (data.has(index)) {
3286                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3287             } else {
3288                 doesNotHave(index, value, callSiteFlags);
3289             }
3290 
3291             return;
3292         }
3293 
3294         final String propName = JSType.toString(key);
3295         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3296     }
3297 
3298     @Override
3299     public void set(final double key, final long value, final int callSiteFlags) {
3300         final int index = getArrayIndex(key);
3301 
3302         if (isValidArrayIndex(index)) {
3303             final ArrayData data = getArray();
3304             if (data.has(index)) {
3305                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3306             } else {
3307                 doesNotHave(index, value, callSiteFlags);
3308             }
3309 
3310             return;
3311         }
3312 
3313         final String propName = JSType.toString(key);
3314         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3315     }
3316 
3317     @Override
3318     public void set(final double key, final double value, final int callSiteFlags) {
3319         final int index = getArrayIndex(key);
3320 
3321         if (isValidArrayIndex(index)) {
3322             final ArrayData data = getArray();
3323             if (data.has(index)) {
3324                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3325             } else {
3326                 doesNotHave(index, value, callSiteFlags);
3327             }
3328 
3329             return;
3330         }
3331 
3332         final String propName = JSType.toString(key);
3333         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3334     }
3335 
3336     @Override
3337     public void set(final double key, final Object value, final int callSiteFlags) {
3338         final int index = getArrayIndex(key);
3339 
3340         if (isValidArrayIndex(index)) {
3341             final ArrayData data = getArray();
3342             if (data.has(index)) {
3343                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3344             } else {
3345                 doesNotHave(index, value, callSiteFlags);
3346             }
3347 
3348             return;
3349         }
3350 
3351         final String propName = JSType.toString(key);
3352         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3353     }
3354 
3355     @Override
3356     public void set(final long key, final int value, final int callSiteFlags) {
3357         final int index = getArrayIndex(key);
3358 
3359         if (isValidArrayIndex(index)) {
3360             final ArrayData data = getArray();
3361             if (data.has(index)) {
3362                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3363             } else {
3364                 doesNotHave(index, value, callSiteFlags);
3365             }
3366 
3367             return;
3368         }
3369 
3370         final String propName = JSType.toString(key);
3371         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3372     }
3373 
3374     @Override
3375     public void set(final long key, final long value, final int callSiteFlags) {
3376         final int index = getArrayIndex(key);
3377 
3378         if (isValidArrayIndex(index)) {
3379             final ArrayData data = getArray();
3380             if (data.has(index)) {
3381                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3382             } else {
3383                 doesNotHave(index, value, callSiteFlags);
3384             }
3385 
3386             return;
3387         }
3388 
3389         final String propName = JSType.toString(key);
3390         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3391     }
3392 
3393     @Override
3394     public void set(final long key, final double value, final int callSiteFlags) {
3395         final int index = getArrayIndex(key);
3396 
3397         if (isValidArrayIndex(index)) {
3398             final ArrayData data = getArray();
3399             if (data.has(index)) {
3400                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3401             } else {
3402                 doesNotHave(index, value, callSiteFlags);
3403             }
3404 
3405             return;
3406         }
3407 
3408         final String propName = JSType.toString(key);
3409         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3410     }
3411 
3412     @Override
3413     public void set(final long key, final Object value, final int callSiteFlags) {
3414         final int index = getArrayIndex(key);
3415 
3416         if (isValidArrayIndex(index)) {
3417             final ArrayData data = getArray();
3418             if (data.has(index)) {
3419                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3420             } else {
3421                 doesNotHave(index, value, callSiteFlags);
3422             }
3423 
3424             return;
3425         }
3426 
3427         final String propName = JSType.toString(key);
3428         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3429     }
3430 
3431     @Override
3432     public void set(final int key, final int value, final int callSiteFlags) {
3433         final int index = getArrayIndex(key);
3434         if (isValidArrayIndex(index)) {
3435             if (getArray().has(index)) {
3436                 final ArrayData data = getArray();
3437                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3438             } else {
3439                 doesNotHave(index, value, callSiteFlags);
3440             }
3441             return;
3442         }
3443 
3444         final String propName = JSType.toString(key);
3445         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3446     }
3447 
3448     @Override
3449     public void set(final int key, final long value, final int callSiteFlags) {
3450         final int index = getArrayIndex(key);
3451 
3452         if (isValidArrayIndex(index)) {
3453             final ArrayData data = getArray();
3454             if (data.has(index)) {
3455                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3456             } else {
3457                 doesNotHave(index, value, callSiteFlags);
3458             }
3459 
3460             return;
3461         }
3462 
3463         final String propName = JSType.toString(key);
3464         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3465     }
3466 
3467     @Override
3468     public void set(final int key, final double value, final int callSiteFlags) {
3469         final int index = getArrayIndex(key);
3470 
3471         if (isValidArrayIndex(index)) {
3472             final ArrayData data = getArray();
3473             if (data.has(index)) {
3474                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3475             } else {
3476                 doesNotHave(index, value, callSiteFlags);
3477             }
3478 
3479             return;
3480         }
3481 
3482         final String propName = JSType.toString(key);
3483         setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3484     }
3485 
3486     @Override
3487     public void set(final int key, final Object value, final int callSiteFlags) {
3488         final int index = getArrayIndex(key);
3489 
3490         if (isValidArrayIndex(index)) {
3491             final ArrayData data = getArray();
3492             if (data.has(index)) {
3493                 setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3494             } else {
3495                 doesNotHave(index, value, callSiteFlags);
3496             }
3497 
3498             return;
3499         }
3500 
3501         final String propName = JSType.toString(key);
3502         setObject(findProperty(propName, true), callSiteFlags, propName, value);
3503     }
3504 
3505     @Override
3506     public boolean has(final Object key) {
3507         final Object primitiveKey = JSType.toPrimitive(key);
3508         final int    index        = getArrayIndex(primitiveKey);
3509         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
3510     }
3511 
3512     @Override
3513     public boolean has(final double key) {
3514         final int index = getArrayIndex(key);
3515         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3516     }
3517 
3518     @Override
3519     public boolean has(final long key) {
3520         final int index = getArrayIndex(key);
3521         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3522     }
3523 
3524     @Override
3525     public boolean has(final int key) {
3526         final int index = getArrayIndex(key);
3527         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3528     }
3529 
3530     private boolean hasArrayProperty(final int index) {
3531         boolean hasArrayKeys = false;
3532 
3533         for (ScriptObject self = this; self != null; self = self.getProto()) {
3534             if (self.getArray().has(index)) {
3535                 return true;
3536             }
3537             hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys();
3538         }
3539 
3540         return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true);
3541     }
3542 
3543     @Override
3544     public boolean hasOwnProperty(final Object key) {
3545         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3546         final int    index        = getArrayIndex(primitiveKey);
3547         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
3548     }
3549 
3550     @Override
3551     public boolean hasOwnProperty(final int key) {
3552         final int index = getArrayIndex(key);
3553         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3554     }
3555 
3556     @Override
3557     public boolean hasOwnProperty(final long key) {
3558         final int index = getArrayIndex(key);
3559         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3560     }
3561 
3562     @Override
3563     public boolean hasOwnProperty(final double key) {
3564         final int index = getArrayIndex(key);
3565         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3566     }
3567 
3568     private boolean hasOwnArrayProperty(final int index) {
3569         return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false);
3570     }
3571 
3572     @Override
3573     public boolean delete(final int key, final boolean strict) {
3574         final int index = getArrayIndex(key);
3575         final ArrayData array = getArray();
3576 
3577         if (array.has(index)) {
3578             if (array.canDelete(index, strict)) {
3579                 setArray(array.delete(index));
3580                 return true;
3581             }
3582             return false;
3583         }
3584         return deleteObject(JSType.toObject(key), strict);
3585     }
3586 
3587     @Override
3588     public boolean delete(final long key, final boolean strict) {
3589         final int index = getArrayIndex(key);
3590         final ArrayData array = getArray();
3591 
3592         if (array.has(index)) {
3593             if (array.canDelete(index, strict)) {
3594                 setArray(array.delete(index));
3595                 return true;
3596             }
3597             return false;
3598         }
3599 
3600         return deleteObject(JSType.toObject(key), strict);
3601     }
3602 
3603     @Override
3604     public boolean delete(final double key, final boolean strict) {
3605         final int index = getArrayIndex(key);
3606         final ArrayData array = getArray();
3607 
3608         if (array.has(index)) {
3609             if (array.canDelete(index, strict)) {
3610                 setArray(array.delete(index));
3611                 return true;
3612             }
3613             return false;
3614         }
3615 
3616         return deleteObject(JSType.toObject(key), strict);
3617     }
3618 
3619     @Override
3620     public boolean delete(final Object key, final boolean strict) {
3621         final Object    primitiveKey = JSType.toPrimitive(key, String.class);
3622         final int       index        = getArrayIndex(primitiveKey);
3623         final ArrayData array        = getArray();
3624 
3625         if (array.has(index)) {
3626             if (array.canDelete(index, strict)) {
3627                 setArray(array.delete(index));
3628                 return true;
3629             }
3630             return false;
3631         }
3632 
3633         return deleteObject(primitiveKey, strict);
3634     }
3635 
3636     private boolean deleteObject(final Object key, final boolean strict) {
3637         final String propName = JSType.toString(key);
3638         final FindProperty find = findProperty(propName, false);
3639 
3640         if (find == null) {
3641             return true;
3642         }
3643 
3644         if (!find.getProperty().isConfigurable()) {
3645             if (strict) {
3646                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
3647             }
3648             return false;
3649         }
3650 
3651         final Property prop = find.getProperty();
3652         deleteOwnProperty(prop);
3653 
3654         return true;
3655     }
3656 
3657     /**
3658      * Return a shallow copy of this ScriptObject.
3659      * @return a shallow copy.
3660      */
3661     public final ScriptObject copy() {
3662         try {
3663             return clone();
3664         } catch (final CloneNotSupportedException e) {
3665             throw new RuntimeException(e);
3666         }
3667     }
3668 
3669     @Override
3670     protected ScriptObject clone() throws CloneNotSupportedException {
3671         final ScriptObject clone = (ScriptObject) super.clone();
3672         if (objectSpill != null) {
3673             clone.objectSpill = objectSpill.clone();
3674             if (primitiveSpill != null) {
3675                 clone.primitiveSpill = primitiveSpill.clone();
3676             }
3677         }
3678         clone.arrayData = arrayData.copy();
3679         return clone;
3680     }
3681 
3682     /**
3683      * Make a new UserAccessorProperty property. getter and setter functions are stored in
3684      * this ScriptObject and slot values are used in property object.
3685      *
3686      * @param key the property name
3687      * @param propertyFlags attribute flags of the property
3688      * @param getter getter function for the property
3689      * @param setter setter function for the property
3690      * @return the newly created UserAccessorProperty
3691      */
3692     protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3693         final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
3694         //property.getSetter(Object.class, getMap());
3695         uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
3696         return uc;
3697     }
3698 
3699     /**
3700      * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise.
3701      * @return {@code true} if dual fields should be used.
3702      */
3703     protected boolean useDualFields() {
3704         return !StructureLoader.isSingleFieldStructure(getClass().getName());
3705     }
3706 
3707     Object ensureSpillSize(final int slot) {
3708         final int oldLength = objectSpill == null ? 0 : objectSpill.length;
3709         if (slot < oldLength) {
3710             return this;
3711         }
3712         final int newLength = alignUp(slot + 1, SPILL_RATE);
3713         final Object[] newObjectSpill    = new Object[newLength];
3714         final long[]   newPrimitiveSpill = useDualFields() ? new long[newLength] : null;
3715 
3716         if (objectSpill != null) {
3717             System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength);
3718             if (primitiveSpill != null && newPrimitiveSpill != null) {
3719                 System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength);
3720             }
3721         }
3722 
3723         this.primitiveSpill = newPrimitiveSpill;
3724         this.objectSpill    = newObjectSpill;
3725 
3726         return this;
3727     }
3728 
3729     private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) {
3730         // TODO: figure out how can it work for NativeArray$Prototype etc.
3731         return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3732     }
3733 
3734     private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
3735         return findOwnMH_V(ScriptObject.class, name, rtype, types);
3736     }
3737 
3738     private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
3739         return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3740     }
3741 
3742     private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3743         return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func);
3744     }
3745 
3746     @SuppressWarnings("unused")
3747     private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3748         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3749             try {
3750                 return getter.invokeExact(self) == func;
3751             } catch (final RuntimeException | Error e) {
3752                 throw e;
3753             } catch (final Throwable t) {
3754                 throw new RuntimeException(t);
3755             }
3756         }
3757 
3758         return false;
3759     }
3760 
3761     private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3762         return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func);
3763     }
3764 
3765     private static ScriptObject getProto(final ScriptObject self, final int depth) {
3766         ScriptObject proto = self;
3767         for (int d = 0; d < depth; d++) {
3768             proto = proto.getProto();
3769             if (proto == null) {
3770                 return null;
3771             }
3772         }
3773 
3774         return proto;
3775     }
3776 
3777     @SuppressWarnings("unused")
3778     private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3779         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3780             final ScriptObject proto = getProto((ScriptObject)self, depth);
3781             if (proto == null) {
3782                 return false;
3783             }
3784             try {
3785                 return getter.invokeExact((Object)proto) == func;
3786             } catch (final RuntimeException | Error e) {
3787                 throw e;
3788             } catch (final Throwable t) {
3789                 throw new RuntimeException(t);
3790             }
3791         }
3792 
3793         return false;
3794     }
3795 
3796     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3797     private static int count;
3798 



3799     /**
3800      * Get number of {@code ScriptObject} instances created. If not running in debug
3801      * mode this is always 0
3802      *
3803      * @return number of ScriptObjects created
3804      */
3805     public static int getCount() {
3806         return count;
3807     }











3808 }
--- EOF ---