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