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 inside object literals. ES6 draft. */
  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     /** Method handle to retrive prototype of this object */
 134     public static final MethodHandle GETPROTO           = findOwnMH("getProto", ScriptObject.class);
 135     static final MethodHandle MEGAMORPHIC_GET    = findOwnMH("megamorphicGet", Object.class, String.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_FROM_LITERAL    = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", 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      * This is the built-in operation [[SetPrototypeOf]]
1129      * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V)
1130      *
1131      * @param newProto Prototype to set.
1132      */
1133     public final void setPrototypeOf(final Object newProto) {
1134         if (newProto == null || newProto instanceof ScriptObject) {
1135             if (!isExtensible()) {
1136                 // okay to set same proto again - even if non-extensible
1137                 if (newProto == getProto()) {
1138                     return;
1139                 }
1140                 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1141             }
1142 
1143             // check for circularity
1144             ScriptObject p = (ScriptObject)newProto;
1145             while (p != null) {
1146                 if (p == this) {
1147                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1148                 }
1149                 p = p.getProto();
1150             }
1151             setProto((ScriptObject)newProto);
1152         } else {
1153             throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1154         }
1155     }
1156 
1157     /**
1158      * Set the __proto__ of an object from an object literal.
1159      * See ES6 draft spec: B.3.1 __proto__ Property Names in
1160      * Object Initializers. Step 6 handling of "__proto__".
1161      *
1162      * @param newProto Prototype to set.
1163      */
1164     public final void setProtoFromLiteral(final Object newProto) {
1165         if (newProto == null || newProto instanceof ScriptObject) {
1166             setPrototypeOf(newProto);
1167         } else {
1168             // Some non-object, non-null. Then, we need to set
1169             // Object.prototype as the new __proto__
1170             //
1171             // var obj = { __proto__ : 34 };
1172             // print(obj.__proto__ === Object.prototype); // => true
1173             setPrototypeOf(Global.objectPrototype());
1174         }
1175     }
1176 
1177     /**
1178      * return an array of own property keys associated with the object.
1179      *
1180      * @param all True if to include non-enumerable keys.
1181      * @return Array of keys.
1182      */
1183     public String[] getOwnKeys(final boolean all) {
1184         final List<Object> keys    = new ArrayList<>();
1185         final PropertyMap  selfMap = this.getMap();
1186 
1187         final ArrayData array  = getArray();
1188         final long length      = array.length();
1189 
1190         for (long i = 0; i < length; i = array.nextIndex(i)) {
1191             if (array.has((int)i)) {
1192                 keys.add(JSType.toString(i));
1193             }
1194         }
1195 
1196         for (final Property property : selfMap.getProperties()) {
1197             if (all || property.isEnumerable()) {
1198                 keys.add(property.getKey());
1199             }
1200         }
1201 
1202         return keys.toArray(new String[keys.size()]);
1203     }
1204 
1205     /**
1206      * Check if this ScriptObject has array entries. This means that someone has
1207      * set values with numeric keys in the object.
1208      *
1209      * @return true if array entries exists.
1210      */
1211     public boolean hasArrayEntries() {
1212         return getArray().length() > 0 || getMap().containsArrayKeys();
1213     }
1214 
1215     /**
1216      * Return the valid JavaScript type name descriptor
1217      *
1218      * @return "Object"
1219      */
1220     public String getClassName() {
1221         return "Object";
1222     }
1223 
1224     /**
1225      * {@code length} is a well known property. This is its getter.
1226      * Note that this *may* be optimized by other classes
1227      *
1228      * @return length property value for this ScriptObject
1229      */
1230     public Object getLength() {
1231         return get("length");
1232     }
1233 
1234     /**
1235      * Stateless toString for ScriptObjects.
1236      *
1237      * @return string description of this object, e.g. {@code [object Object]}
1238      */
1239     public String safeToString() {
1240         return "[object " + getClassName() + "]";
1241     }
1242 
1243     /**
1244      * Return the default value of the object with a given preferred type hint.
1245      * The preferred type hints are String.class for type String, Number.class
1246      * for type Number. <p>
1247      *
1248      * A <code>hint</code> of null means "no hint".
1249      *
1250      * ECMA 8.12.8 [[DefaultValue]](hint)
1251      *
1252      * @param typeHint the preferred type hint
1253      * @return the default value
1254      */
1255     public Object getDefaultValue(final Class<?> typeHint) {
1256         // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
1257         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1258         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1259         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1260         return Context.getGlobal().getDefaultValue(this, typeHint);
1261     }
1262 
1263     /**
1264      * Checking whether a script object is an instance of another. Used
1265      * in {@link ScriptFunction} for hasInstance implementation, walks
1266      * the proto chain
1267      *
1268      * @param instance instace to check
1269      * @return true if 'instance' is an instance of this object
1270      */
1271     public boolean isInstance(final ScriptObject instance) {
1272         return false;
1273     }
1274 
1275     /**
1276      * Flag this ScriptObject as non extensible
1277      *
1278      * @return the object after being made non extensible
1279      */
1280     public ScriptObject preventExtensions() {
1281         PropertyMap oldMap = getMap();
1282 
1283         while (true) {
1284             final PropertyMap newMap = getMap().preventExtensions();
1285 
1286             if (!compareAndSetMap(oldMap, newMap)) {
1287                 oldMap = getMap();
1288             } else {
1289                 return this;
1290             }
1291         }
1292     }
1293 
1294     /**
1295      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1296      *
1297      * @param obj object to check
1298      *
1299      * @return true if array
1300      */
1301     public static boolean isArray(final Object obj) {
1302         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
1303     }
1304 
1305     /**
1306      * Check if this ScriptObject is an array
1307      * @return true if array
1308      */
1309     public final boolean isArray() {
1310         return (flags & IS_ARRAY) != 0;
1311     }
1312 
1313     /**
1314      * Flag this ScriptObject as being an array
1315      */
1316     public final void setIsArray() {
1317         flags |= IS_ARRAY;
1318     }
1319 
1320     /**
1321      * Check if this ScriptObject is an {@code arguments} vector
1322      * @return true if arguments vector
1323      */
1324     public final boolean isArguments() {
1325         return (flags & IS_ARGUMENTS) != 0;
1326     }
1327 
1328     /**
1329      * Flag this ScriptObject as being an {@code arguments} vector
1330      */
1331     public final void setIsArguments() {
1332         flags |= IS_ARGUMENTS;
1333     }
1334 
1335     /**
1336      * Check if this object has non-writable length property
1337      *
1338      * @return {@code true} if 'length' property is non-writable
1339      */
1340     public final boolean isLengthNotWritable() {
1341         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1342     }
1343 
1344     /**
1345      * Flag this object as having non-writable length property
1346      */
1347     public void setIsLengthNotWritable() {
1348         flags |= IS_LENGTH_NOT_WRITABLE;
1349     }
1350 
1351     /**
1352      * Get the {@link ArrayData} for this ScriptObject if it is an array
1353      * @return array data
1354      */
1355     public final ArrayData getArray() {
1356         return arrayData;
1357     }
1358 
1359     /**
1360      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1361      * @param arrayData the array data
1362      */
1363     public final void setArray(final ArrayData arrayData) {
1364         this.arrayData = arrayData;
1365     }
1366 
1367     /**
1368      * Check if this ScriptObject is extensible
1369      * @return true if extensible
1370      */
1371     public boolean isExtensible() {
1372         return getMap().isExtensible();
1373     }
1374 
1375     /**
1376      * ECMAScript 15.2.3.8 - seal implementation
1377      * @return the sealed ScriptObject
1378      */
1379     public ScriptObject seal() {
1380         PropertyMap oldMap = getMap();
1381 
1382         while (true) {
1383             final PropertyMap newMap = getMap().seal();
1384 
1385             if (!compareAndSetMap(oldMap, newMap)) {
1386                 oldMap = getMap();
1387             } else {
1388                 setArray(ArrayData.seal(getArray()));
1389                 return this;
1390             }
1391         }
1392     }
1393 
1394     /**
1395      * Check whether this ScriptObject is sealed
1396      * @return true if sealed
1397      */
1398     public boolean isSealed() {
1399         return getMap().isSealed();
1400     }
1401 
1402     /**
1403      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1404      * @return the frozen ScriptObject
1405      */
1406     public ScriptObject freeze() {
1407         PropertyMap oldMap = getMap();
1408 
1409         while (true) {
1410             final PropertyMap newMap = getMap().freeze();
1411 
1412             if (!compareAndSetMap(oldMap, newMap)) {
1413                 oldMap = getMap();
1414             } else {
1415                 setArray(ArrayData.freeze(getArray()));
1416                 return this;
1417             }
1418         }
1419     }
1420 
1421     /**
1422      * Check whether this ScriptObject is frozen
1423      * @return true if frozen
1424      */
1425     public boolean isFrozen() {
1426         return getMap().isFrozen();
1427     }
1428 
1429 
1430     /**
1431      * Flag this ScriptObject as scope
1432      */
1433     public final void setIsScope() {
1434         if (Context.DEBUG) {
1435             scopeCount++;
1436         }
1437         flags |= IS_SCOPE;
1438     }
1439 
1440     /**
1441      * Check whether this ScriptObject is scope
1442      * @return true if scope
1443      */
1444     public final boolean isScope() {
1445         return (flags & IS_SCOPE) != 0;
1446     }
1447 
1448     /**
1449      * Clears the properties from a ScriptObject
1450      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1451      *
1452      * @param strict strict mode or not
1453      */
1454     public void clear(final boolean strict) {
1455         final Iterator<String> iter = propertyIterator();
1456         while (iter.hasNext()) {
1457             delete(iter.next(), strict);
1458         }
1459     }
1460 
1461     /**
1462      * Checks if a property with a given key is present in a ScriptObject
1463      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1464      *
1465      * @param key the key to check for
1466      * @return true if a property with the given key exists, false otherwise
1467      */
1468     public boolean containsKey(final Object key) {
1469         return has(key);
1470     }
1471 
1472     /**
1473      * Checks if a property with a given value is present in a ScriptObject
1474      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1475      *
1476      * @param value value to check for
1477      * @return true if a property with the given value exists, false otherwise
1478      */
1479     public boolean containsValue(final Object value) {
1480         final Iterator<Object> iter = valueIterator();
1481         while (iter.hasNext()) {
1482             if (iter.next().equals(value)) {
1483                 return true;
1484             }
1485         }
1486         return false;
1487     }
1488 
1489     /**
1490      * Returns the set of {@literal <property, value>} entries that make up this
1491      * ScriptObject's properties
1492      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1493      *
1494      * @return an entry set of all the properties in this object
1495      */
1496     public Set<Map.Entry<Object, Object>> entrySet() {
1497         final Iterator<String> iter = propertyIterator();
1498         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1499         while (iter.hasNext()) {
1500             final Object key = iter.next();
1501             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1502         }
1503         return Collections.unmodifiableSet(entries);
1504     }
1505 
1506     /**
1507      * Check whether a ScriptObject contains no properties
1508      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1509      *
1510      * @return true if object has no properties
1511      */
1512     public boolean isEmpty() {
1513         return !propertyIterator().hasNext();
1514     }
1515 
1516     /**
1517      * Return the set of keys (property names) for all properties
1518      * in this ScriptObject
1519      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1520      *
1521      * @return keySet of this ScriptObject
1522      */
1523     public Set<Object> keySet() {
1524         final Iterator<String> iter = propertyIterator();
1525         final Set<Object> keySet = new HashSet<>();
1526         while (iter.hasNext()) {
1527             keySet.add(iter.next());
1528         }
1529         return Collections.unmodifiableSet(keySet);
1530     }
1531 
1532     /**
1533      * Put a property in the ScriptObject
1534      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1535      *
1536      * @param key property key
1537      * @param value property value
1538      * @param strict strict mode or not
1539      * @return oldValue if property with same key existed already
1540      */
1541     public Object put(final Object key, final Object value, final boolean strict) {
1542         final Object oldValue = get(key);
1543         set(key, value, strict);
1544         return oldValue;
1545     }
1546 
1547     /**
1548      * Put several properties in the ScriptObject given a mapping
1549      * of their keys to their values
1550      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1551      *
1552      * @param otherMap a {@literal <key,value>} map of properties to add
1553      * @param strict strict mode or not
1554      */
1555     public void putAll(final Map<?, ?> otherMap, final boolean strict) {
1556         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1557             set(entry.getKey(), entry.getValue(), strict);
1558         }
1559     }
1560 
1561     /**
1562      * Remove a property from the ScriptObject.
1563      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1564      *
1565      * @param key the key of the property
1566      * @param strict strict mode or not
1567      * @return the oldValue of the removed property
1568      */
1569     public Object remove(final Object key, final boolean strict) {
1570         final Object oldValue = get(key);
1571         delete(key, strict);
1572         return oldValue;
1573     }
1574 
1575     /**
1576      * Return the size of the ScriptObject - i.e. the number of properties
1577      * it contains
1578      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1579      *
1580      * @return number of properties in ScriptObject
1581      */
1582     public int size() {
1583         int n = 0;
1584         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1585             n++;
1586         }
1587         return n;
1588     }
1589 
1590     /**
1591      * Return the values of the properties in the ScriptObject
1592      * (java.util.Map-like method to help ScriptObjectMirror implementation)
1593      *
1594      * @return collection of values for the properties in this ScriptObject
1595      */
1596     public Collection<Object> values() {
1597         final List<Object>     values = new ArrayList<>(size());
1598         final Iterator<Object> iter   = valueIterator();
1599         while (iter.hasNext()) {
1600             values.add(iter.next());
1601         }
1602         return Collections.unmodifiableList(values);
1603     }
1604 
1605     /**
1606      * Lookup method that, given a CallSiteDescriptor, looks up the target
1607      * MethodHandle and creates a GuardedInvocation
1608      * with the appropriate guard(s).
1609      *
1610      * @param desc call site descriptor
1611      * @param request the link request
1612      *
1613      * @return GuardedInvocation for the callsite
1614      */
1615     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1616         final int c = desc.getNameTokenCount();
1617         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
1618         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
1619         // care about them, and just link to whatever is the first operation.
1620         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
1621         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
1622         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
1623         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1624         // operation has an associated name or not.
1625         switch (operator) {
1626         case "getProp":
1627         case "getElem":
1628         case "getMethod":
1629             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
1630         case "setProp":
1631         case "setElem":
1632             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
1633         case "call":
1634             return findCallMethod(desc, request);
1635         case "new":
1636             return findNewMethod(desc);
1637         case "callMethod":
1638             return findCallMethodMethod(desc, request);
1639         default:
1640             return null;
1641         }
1642     }
1643 
1644     /**
1645      * Find the appropriate New method for an invoke dynamic call.
1646      *
1647      * @param desc The invoke dynamic call site descriptor.
1648      *
1649      * @return GuardedInvocation to be invoked at call site.
1650      */
1651     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
1652         return notAFunction();
1653     }
1654 
1655     /**
1656      * Find the appropriate CALL method for an invoke dynamic call.
1657      * This generates "not a function" always
1658      *
1659      * @param desc    the call site descriptor.
1660      * @param request the link request
1661      *
1662      * @return GuardedInvocation to be invoed at call site.
1663      */
1664     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1665         return notAFunction();
1666     }
1667 
1668     private GuardedInvocation notAFunction() {
1669         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
1670     }
1671 
1672     /**
1673      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
1674      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
1675      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
1676      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
1677      *
1678      * @param desc    the call site descriptor.
1679      * @param request the link request
1680      *
1681      * @return GuardedInvocation to be invoked at call site.
1682      */
1683     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1684         // R(P0, P1, ...)
1685         final MethodType callType = desc.getMethodType();
1686         // use type Object(P0) for the getter
1687         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1688         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1689 
1690         // Object(P0) => Object(P0, P1, ...)
1691         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1692         // R(Object, P0, P1, ...)
1693         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1694         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1695         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1696     }
1697 
1698     /**
1699      * Test whether this object contains in its prototype chain or is itself a with-object.
1700      * @return true if a with-object was found
1701      */
1702     final boolean hasWithScope() {
1703         if (isScope()) {
1704             for (ScriptObject obj = this; obj != null; obj = obj.getProto()) {
1705                 if (obj instanceof WithObject) {
1706                     return true;
1707                 }
1708             }
1709         }
1710         return false;
1711     }
1712 
1713     /**
1714      * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
1715      * {@code depth} times.
1716      * @param methodHandle a method handle
1717      * @param depth        distance to target prototype
1718      * @return the filtered method handle
1719      */
1720     static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
1721         if (depth == 0) {
1722             return methodHandle;
1723         }
1724         final int listIndex = depth - 1; // We don't need 0-deep walker
1725         MethodHandle filter = listIndex < protoFilters.size() ? protoFilters.get(listIndex) : null;
1726 
1727         if(filter == null) {
1728             filter = addProtoFilter(GETPROTO, depth - 1);
1729             protoFilters.add(null);
1730             protoFilters.set(listIndex, filter);
1731         }
1732 
1733         return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
1734     }
1735 
1736     /**
1737      * Find the appropriate GET method for an invoke dynamic call.
1738      *
1739      * @param desc     the call site descriptor
1740      * @param request  the link request
1741      * @param operator operator for get: getProp, getMethod, getElem etc
1742      *
1743      * @return GuardedInvocation to be invoked at call site.
1744      */
1745     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1746         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1747         if (request.isCallSiteUnstable() || hasWithScope()) {
1748             return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
1749         }
1750 
1751         final FindProperty find = findProperty(name, true);
1752         MethodHandle methodHandle;
1753 
1754         if (find == null) {
1755             if ("getProp".equals(operator)) {
1756                 return noSuchProperty(desc, request);
1757             } else if ("getMethod".equals(operator)) {
1758                 return noSuchMethod(desc, request);
1759             } else if ("getElem".equals(operator)) {
1760                 return createEmptyGetter(desc, name);
1761             }
1762             throw new AssertionError(); // never invoked with any other operation
1763         }
1764 
1765         final Class<?> returnType = desc.getMethodType().returnType();
1766         final Property property = find.getProperty();
1767         methodHandle = find.getGetter(returnType);
1768 
1769         // Get the appropriate guard for this callsite and property.
1770         final MethodHandle guard = NashornGuards.getGuard(this, property, desc);
1771         final ScriptObject owner = find.getOwner();
1772 
1773         if (methodHandle != null) {
1774             assert methodHandle.type().returnType().equals(returnType);
1775             if (find.isSelf()) {
1776                 return new GuardedInvocation(methodHandle, guard);
1777             }
1778 
1779             if (!property.hasGetterFunction(owner)) {
1780                 // Add a filter that replaces the self object with the prototype owning the property.
1781                 methodHandle = addProtoFilter(methodHandle, find.getProtoChainLength());
1782             }
1783             return new GuardedInvocation(methodHandle, guard == null ? null : getProtoSwitchPoint(name, owner), guard);
1784         }
1785 
1786         assert !NashornCallSiteDescriptor.isFastScope(desc);
1787         return new GuardedInvocation(Lookup.emptyGetter(returnType), getProtoSwitchPoint(name, owner), guard);
1788     }
1789 
1790     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name,
1791                                                               final boolean isMethod) {
1792         final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
1793         final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
1794         return new GuardedInvocation(invoker, guard);
1795     }
1796 
1797     @SuppressWarnings("unused")
1798     private Object megamorphicGet(final String key, final boolean isMethod) {
1799         final FindProperty find = findProperty(key, true);
1800 
1801         if (find != null) {
1802             return find.getObjectValue();
1803         }
1804 
1805         return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
1806     }
1807 
1808     /**
1809      * Find the appropriate GETINDEX method for an invoke dynamic call.
1810      *
1811      * @param desc    the call site descriptor
1812      * @param request the link request
1813      *
1814      * @return GuardedInvocation to be invoked at call site.
1815      */
1816     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1817         return findGetIndexMethod(desc.getMethodType());
1818     }
1819 
1820     /**
1821      * Find the appropriate GETINDEX method for an invoke dynamic call.
1822      *
1823      * @param callType the call site method type
1824      * @return GuardedInvocation to be invoked at call site.
1825      */
1826     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
1827         final Class<?> returnClass = callType.returnType();
1828         final Class<?> keyClass    = callType.parameterType(1);
1829 
1830         String name = "get";
1831         if (returnClass.isPrimitive()) {
1832             //turn e.g. get with a double into getDouble
1833             final String returnTypeName = returnClass.getName();
1834             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
1835         }
1836 
1837         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
1838     }
1839 
1840     private static MethodHandle getScriptObjectGuard(final MethodType type) {
1841         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
1842     }
1843 
1844     /**
1845      * Get a switch point for a property with the given {@code name} that will be invalidated when
1846      * the property definition is changed in this object's prototype chain. Returns {@code null} if
1847      * the property is defined in this object itself.
1848      *
1849      * @param name the property name
1850      * @param owner the property owner, null if property is not defined
1851      * @return a SwitchPoint or null
1852      */
1853     public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
1854         if (owner == this || getProto() == null) {
1855             return null;
1856         }
1857 
1858         for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
1859             ScriptObject parent = obj.getProto();
1860             parent.getMap().addListener(name, obj.getMap());
1861         }
1862 
1863         return getMap().getSwitchPoint(name);
1864     }
1865 
1866     /**
1867      * Find the appropriate SET method for an invoke dynamic call.
1868      *
1869      * @param desc    the call site descriptor
1870      * @param request the link request
1871      *
1872      * @return GuardedInvocation to be invoked at call site.
1873      */
1874     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1875         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1876         if (request.isCallSiteUnstable() || hasWithScope()) {
1877             return findMegaMorphicSetMethod(desc, name);
1878         }
1879 
1880         final boolean scope = isScope();
1881         /*
1882          * If doing property set on a scope object, we should stop proto search on the first
1883          * non-scope object. Without this, for example, when assigning "toString" on global scope,
1884          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
1885          *
1886          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
1887          */
1888         FindProperty find = findProperty(name, true, scope, this);
1889 
1890         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
1891         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
1892             // We should still check if inherited data property is not writable
1893             if (isExtensible() && !find.getProperty().isWritable()) {
1894                 return createEmptySetMethod(desc, "property.not.writable", false);
1895             }
1896             // Otherwise, forget the found property
1897             find = null;
1898         }
1899 
1900         if (find != null) {
1901             if(!find.getProperty().isWritable()) {
1902                 // Existing, non-writable property
1903                 return createEmptySetMethod(desc, "property.not.writable", true);
1904             }
1905         } else {
1906             if (! isExtensible()) {
1907                 return createEmptySetMethod(desc, "object.non.extensible", false);
1908             }
1909         }
1910 
1911         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
1912     }
1913 
1914     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
1915         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1916         if (NashornCallSiteDescriptor.isStrict(desc)) {
1917                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
1918            }
1919            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
1920            return new GuardedInvocation(Lookup.EMPTY_SETTER, getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
1921     }
1922 
1923     @SuppressWarnings("unused")
1924     private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
1925         final ScriptObject obj = (ScriptObject)self;
1926         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1927         if (!obj.isExtensible()) {
1928             if (isStrict) {
1929                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1930             }
1931         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1932             setter.invokeExact(self, value);
1933         } else {
1934             obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1935         }
1936     }
1937 
1938     @SuppressWarnings("unused")
1939     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1940         final ScriptObject obj = (ScriptObject)self;
1941         if (obj.trySetSpill(desc, oldMap, newMap, value)) {
1942             obj.spill[index] = value;
1943         }
1944     }
1945 
1946     private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
1947         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
1948         if (!isExtensible() && isStrict) {
1949             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
1950         } else if (compareAndSetMap(oldMap, newMap)) {
1951             return true;
1952         } else {
1953             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
1954             return false;
1955         }
1956     }
1957 
1958     @SuppressWarnings("unused")
1959     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
1960         final ScriptObject obj      = (ScriptObject)self;
1961         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
1962 
1963         if (!obj.isExtensible()) {
1964             if (isStrict) {
1965                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1966             }
1967         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1968             obj.spill = new Object[SPILL_RATE];
1969             obj.spill[index] = value;
1970         } else {
1971             obj.set(desc.getNameToken(2), value, isStrict);
1972         }
1973     }
1974 
1975     @SuppressWarnings("unused")
1976     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) {
1977         final ScriptObject obj      = (ScriptObject)self;
1978         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
1979 
1980         if (!obj.isExtensible()) {
1981             if (isStrict) {
1982                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
1983             }
1984         } else if (obj.compareAndSetMap(oldMap, newMap)) {
1985             final int oldLength = obj.spill.length;
1986             final Object[] newSpill = new Object[newLength];
1987             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
1988             obj.spill = newSpill;
1989             obj.spill[index] = value;
1990         } else {
1991             obj.set(desc.getNameToken(2), value, isStrict);
1992         }
1993     }
1994 
1995     @SuppressWarnings("unused")
1996     private static Object globalFilter(final Object object) {
1997         ScriptObject sobj = (ScriptObject) object;
1998         while (sobj != null && !(sobj instanceof Global)) {
1999             sobj = sobj.getProto();
2000         }
2001         return sobj;
2002     }
2003 
2004     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
2005         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
2006         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
2007         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
2008     }
2009 
2010     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
2011         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
2012     }
2013 
2014     /**
2015      * Find the appropriate SETINDEX method for an invoke dynamic call.
2016      *
2017      * @param callType the method type at the call site
2018      * @param isStrict are we in strict mode?
2019      *
2020      * @return GuardedInvocation to be invoked at call site.
2021      */
2022     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
2023         assert callType.parameterCount() == 3;
2024 
2025         final Class<?>   keyClass   = callType.parameterType(1);
2026         final Class<?>   valueClass = callType.parameterType(2);
2027 
2028         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
2029         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
2030 
2031         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
2032     }
2033 
2034     /**
2035      * Fall back if a function property is not found.
2036      * @param desc The call site descriptor
2037      * @param request the link request
2038      * @return GuardedInvocation to be invoked at call site.
2039      */
2040     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2041         final String       name      = desc.getNameToken(2);
2042         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
2043         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
2044 
2045         if (find == null) {
2046             return noSuchProperty(desc, request);
2047         }
2048 
2049         final Object value = find.getObjectValue();
2050         if (! (value instanceof ScriptFunction)) {
2051             return createEmptyGetter(desc, name);
2052         }
2053 
2054         final ScriptFunction func = (ScriptFunction)value;
2055         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
2056         // TODO: It'd be awesome if we could bind "name" without binding "this".
2057         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
2058                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
2059                 null, NashornGuards.getMapGuard(getMap()));
2060     }
2061 
2062     /**
2063      * Fall back if a property is not found.
2064      * @param desc the call site descriptor.
2065      * @param request the link request
2066      * @return GuardedInvocation to be invoked at call site.
2067      */
2068     @SuppressWarnings("null")
2069     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
2070         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2071         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
2072         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
2073 
2074         if (find != null) {
2075             final Object   value        = find.getObjectValue();
2076             ScriptFunction func         = null;
2077             MethodHandle   methodHandle = null;
2078 
2079             if (value instanceof ScriptFunction) {
2080                 func = (ScriptFunction)value;
2081                 methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
2082             }
2083 
2084             if (methodHandle != null) {
2085                 if (scopeAccess && func.isStrict()) {
2086                     methodHandle = bindTo(methodHandle, UNDEFINED);
2087                 }
2088                 return new GuardedInvocation(methodHandle,
2089                         getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
2090                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
2091             }
2092         }
2093 
2094         if (scopeAccess) {
2095             throw referenceError("not.defined", name);
2096         }
2097 
2098         return createEmptyGetter(desc, name);
2099     }
2100 
2101     /**
2102      * Invoke fall back if a property is not found.
2103      * @param name Name of property.
2104      * @return Result from call.
2105      */
2106     protected Object invokeNoSuchProperty(final String name) {
2107         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
2108 
2109         if (find != null) {
2110             final Object func = find.getObjectValue();
2111 
2112             if (func instanceof ScriptFunction) {
2113                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
2114             }
2115         }
2116 
2117         return UNDEFINED;
2118     }
2119 
2120     /**
2121      * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
2122      * @param name the method name
2123      * @return the bound function, or undefined
2124      */
2125     private Object getNoSuchMethod(final String name) {
2126         final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
2127 
2128         if (find == null) {
2129             return invokeNoSuchProperty(name);
2130         }
2131 
2132         final Object value = find.getObjectValue();
2133         if (! (value instanceof ScriptFunction)) {
2134             return UNDEFINED;
2135         }
2136 
2137         return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
2138     }
2139 
2140     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
2141         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
2142                 getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
2143     }
2144 
2145     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2146         protected T[] values;
2147         protected final ScriptObject object;
2148         private int index;
2149 
2150         ScriptObjectIterator(final ScriptObject object) {
2151             this.object = object;
2152         }
2153 
2154         protected abstract void init();
2155 
2156         @Override
2157         public boolean hasNext() {
2158             if (values == null) {
2159                 init();
2160             }
2161             return index < values.length;
2162         }
2163 
2164         @Override
2165         public T next() {
2166             if (values == null) {
2167                 init();
2168             }
2169             return values[index++];
2170         }
2171 
2172         @Override
2173         public void remove() {
2174             throw new UnsupportedOperationException();
2175         }
2176     }
2177 
2178     private static class KeyIterator extends ScriptObjectIterator<String> {
2179         KeyIterator(final ScriptObject object) {
2180             super(object);
2181         }
2182 
2183         @Override
2184         protected void init() {
2185             final Set<String> keys = new LinkedHashSet<>();
2186             for (ScriptObject self = object; self != null; self = self.getProto()) {
2187                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
2188             }
2189             this.values = keys.toArray(new String[keys.size()]);
2190         }
2191     }
2192 
2193     private static class ValueIterator extends ScriptObjectIterator<Object> {
2194         ValueIterator(final ScriptObject object) {
2195             super(object);
2196         }
2197 
2198         @Override
2199         protected void init() {
2200             final ArrayList<Object> valueList = new ArrayList<>();
2201             for (ScriptObject self = object; self != null; self = self.getProto()) {
2202                 for (final String key : self.getOwnKeys(false)) {
2203                     valueList.add(self.get(key));
2204                 }
2205             }
2206             this.values = valueList.toArray(new Object[valueList.size()]);
2207         }
2208     }
2209 
2210     /**
2211      * Add a spill property for the given key.
2212      * @param key           Property key.
2213      * @param propertyFlags Property flags.
2214      * @return Added property.
2215      */
2216     private Property addSpillProperty(final String key, final int propertyFlags) {
2217         int fieldCount   = getMap().getFieldCount();
2218         int fieldMaximum = getMap().getFieldMaximum();
2219         Property property;
2220 
2221         if (fieldCount < fieldMaximum) {
2222             property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
2223             property = addOwnProperty(property);
2224         } else {
2225             int i = getMap().getSpillLength();
2226             property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
2227             property = addOwnProperty(property);
2228             i = property.getSlot();
2229 
2230             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
2231 
2232             if (spill == null || newLength > spill.length) {
2233                 final Object[] newSpill = new Object[newLength];
2234 
2235                 if (spill != null) {
2236                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
2237                 }
2238 
2239                 spill = newSpill;
2240             }
2241         }
2242 
2243         return property;
2244     }
2245 
2246 
2247     /**
2248      * Add a spill entry for the given key.
2249      * @param key Property key.
2250      * @return Setter method handle.
2251      */
2252     MethodHandle addSpill(final String key) {
2253         final Property spillProperty = addSpillProperty(key, 0);
2254         final Class<?> type = Object.class;
2255         return spillProperty.getSetter(type, getMap()); //TODO specfields
2256     }
2257 
2258     /**
2259      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2260      * fewer parameters than declared and other things that JavaScript allows. This might involve
2261      * creating collectors.
2262      *
2263      * @param methodHandle method handle for invoke
2264      * @param callType     type of the call
2265      *
2266      * @return method handle with adjusted arguments
2267      */
2268     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2269         return pairArguments(methodHandle, callType, null);
2270     }
2271 
2272     /**
2273      * Make sure arguments are paired correctly, with respect to more parameters than declared,
2274      * fewer parameters than declared and other things that JavaScript allows. This might involve
2275      * creating collectors.
2276      *
2277      * Make sure arguments are paired correctly.
2278      * @param methodHandle MethodHandle to adjust.
2279      * @param callType     MethodType of the call site.
2280      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2281      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2282      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2283      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2284      *
2285      * @return method handle with adjusted arguments
2286      */
2287     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2288 
2289         final MethodType methodType = methodHandle.type();
2290         if (methodType.equals(callType)) {
2291             return methodHandle;
2292         }
2293 
2294         final int parameterCount = methodType.parameterCount();
2295         final int callCount      = callType.parameterCount();
2296 
2297         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2298         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
2299                 callType.parameterType(callCount - 1).isArray());
2300 
2301         if (callCount < parameterCount) {
2302             final int      missingArgs = parameterCount - callCount;
2303             final Object[] fillers     = new Object[missingArgs];
2304 
2305             Arrays.fill(fillers, UNDEFINED);
2306 
2307             if (isCalleeVarArg) {
2308                 fillers[missingArgs - 1] = new Object[0];
2309             }
2310 
2311             return MH.insertArguments(
2312                 methodHandle,
2313                 parameterCount - missingArgs,
2314                 fillers);
2315         }
2316 
2317         if (isCalleeVarArg) {
2318             return isCallerVarArg ?
2319                 methodHandle :
2320                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2321         }
2322 
2323         if (isCallerVarArg) {
2324             final int spreadArgs = parameterCount - callCount + 1;
2325             return MH.filterArguments(
2326                 MH.asSpreader(
2327                     methodHandle,
2328                     Object[].class,
2329                     spreadArgs),
2330                 callCount - 1,
2331                 MH.insertArguments(
2332                     TRUNCATINGFILTER,
2333                     0,
2334                     spreadArgs)
2335                 );
2336         }
2337 
2338         if (callCount > parameterCount) {
2339             final int discardedArgs = callCount - parameterCount;
2340 
2341             final Class<?>[] discards = new Class<?>[discardedArgs];
2342             Arrays.fill(discards, Object.class);
2343 
2344             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2345         }
2346 
2347         return methodHandle;
2348     }
2349 
2350     @SuppressWarnings("unused")
2351     private static Object[] truncatingFilter(final int n, final Object[] array) {
2352         final int length = array == null ? 0 : array.length;
2353         if (n == length) {
2354             return array == null ? new Object[0] : array;
2355         }
2356 
2357         final Object[] newArray = new Object[n];
2358 
2359         if (array != null) {
2360             for (int i = 0; i < n && i < length; i++) {
2361                 newArray[i] = array[i];
2362             }
2363         }
2364 
2365         if (length < n) {
2366             final Object fill = UNDEFINED;
2367 
2368             for (int i = length; i < n; i++) {
2369                 newArray[i] = fill;
2370             }
2371         }
2372 
2373         return newArray;
2374     }
2375 
2376     /**
2377       * Numeric length setter for length property
2378       *
2379       * @param newLength new length to set
2380       */
2381     public final void setLength(final long newLength) {
2382        final long arrayLength = getArray().length();
2383        if (newLength == arrayLength) {
2384            return;
2385        }
2386 
2387        if (newLength > arrayLength) {
2388            setArray(getArray().ensure(newLength - 1));
2389             if (getArray().canDelete(arrayLength, (newLength - 1), false)) {
2390                setArray(getArray().delete(arrayLength, (newLength - 1)));
2391            }
2392            return;
2393        }
2394 
2395        if (newLength < arrayLength) {
2396            long actualLength = newLength;
2397 
2398            // Check for numeric keys in property map and delete them or adjust length, depending on whether
2399            // they're defined as configurable. See ES5 #15.4.5.2
2400            if (getMap().containsArrayKeys()) {
2401 
2402                for (long l = arrayLength - 1; l >= newLength; l--) {
2403                    final FindProperty find = findProperty(JSType.toString(l), false);
2404 
2405                    if (find != null) {
2406 
2407                        if (find.getProperty().isConfigurable()) {
2408                            deleteOwnProperty(find.getProperty());
2409                        } else {
2410                            actualLength = l + 1;
2411                            break;
2412                        }
2413                    }
2414                }
2415            }
2416 
2417            setArray(getArray().shrink(actualLength));
2418            getArray().setLength(actualLength);
2419        }
2420     }
2421 
2422     private int getInt(final int index, final String key) {
2423         if (isValidArrayIndex(index)) {
2424             for (ScriptObject object = this; ; ) {
2425                 if (object.getMap().containsArrayKeys()) {
2426                     final FindProperty find = object.findProperty(key, false, false, this);
2427 
2428                     if (find != null) {
2429                         return getIntValue(find);
2430                     }
2431                 }
2432 
2433                 if ((object = object.getProto()) == null) {
2434                     break;
2435                 }
2436 
2437                 final ArrayData array = object.getArray();
2438 
2439                 if (array.has(index)) {
2440                     return array.getInt(index);
2441                 }
2442             }
2443         } else {
2444             final FindProperty find = findProperty(key, true);
2445 
2446             if (find != null) {
2447                 return getIntValue(find);
2448             }
2449         }
2450 
2451         return JSType.toInt32(invokeNoSuchProperty(key));
2452     }
2453 
2454     @Override
2455     public int getInt(final Object key) {
2456         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2457         final int index = getArrayIndex(primitiveKey);
2458         final ArrayData array = getArray();
2459 
2460         if (array.has(index)) {
2461             return array.getInt(index);
2462         }
2463 
2464         return getInt(index, JSType.toString(primitiveKey));
2465     }
2466 
2467     @Override
2468     public int getInt(final double key) {
2469         final int index = getArrayIndex(key);
2470         final ArrayData array = getArray();
2471 
2472         if (array.has(index)) {
2473             return array.getInt(index);
2474         }
2475 
2476         return getInt(index, JSType.toString(key));
2477     }
2478 
2479     @Override
2480     public int getInt(final long key) {
2481         final int index = getArrayIndex(key);
2482         final ArrayData array = getArray();
2483 
2484         if (array.has(index)) {
2485             return array.getInt(index);
2486         }
2487 
2488         return getInt(index, JSType.toString(key));
2489     }
2490 
2491     @Override
2492     public int getInt(final int key) {
2493         final int index = getArrayIndex(key);
2494         final ArrayData array = getArray();
2495 
2496         if (array.has(index)) {
2497             return array.getInt(index);
2498         }
2499 
2500         return getInt(index, JSType.toString(key));
2501     }
2502 
2503     private long getLong(final int index, final String key) {
2504         if (isValidArrayIndex(index)) {
2505             for (ScriptObject object = this; ; ) {
2506                 if (object.getMap().containsArrayKeys()) {
2507                     final FindProperty find = object.findProperty(key, false, false, this);
2508 
2509                     if (find != null) {
2510                         return getLongValue(find);
2511                     }
2512                 }
2513 
2514                 if ((object = object.getProto()) == null) {
2515                     break;
2516                 }
2517 
2518                 final ArrayData array = object.getArray();
2519 
2520                 if (array.has(index)) {
2521                     return array.getLong(index);
2522                 }
2523             }
2524         } else {
2525             final FindProperty find = findProperty(key, true);
2526 
2527             if (find != null) {
2528                 return getLongValue(find);
2529             }
2530         }
2531 
2532         return JSType.toLong(invokeNoSuchProperty(key));
2533     }
2534 
2535     @Override
2536     public long getLong(final Object key) {
2537         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2538         final int index = getArrayIndex(primitiveKey);
2539         final ArrayData array = getArray();
2540 
2541         if (array.has(index)) {
2542             return array.getLong(index);
2543         }
2544 
2545         return getLong(index, JSType.toString(primitiveKey));
2546     }
2547 
2548     @Override
2549     public long getLong(final double key) {
2550         final int index = getArrayIndex(key);
2551         final ArrayData array = getArray();
2552 
2553         if (array.has(index)) {
2554             return array.getLong(index);
2555         }
2556 
2557         return getLong(index, JSType.toString(key));
2558     }
2559 
2560     @Override
2561     public long getLong(final long key) {
2562         final int index = getArrayIndex(key);
2563         final ArrayData array = getArray();
2564 
2565         if (array.has(index)) {
2566             return array.getLong(index);
2567         }
2568 
2569         return getLong(index, JSType.toString(key));
2570     }
2571 
2572     @Override
2573     public long getLong(final int key) {
2574         final int index = getArrayIndex(key);
2575         final ArrayData array = getArray();
2576 
2577         if (array.has(index)) {
2578             return array.getLong(index);
2579         }
2580 
2581         return getLong(index, JSType.toString(key));
2582     }
2583 
2584     private double getDouble(final int index, final String key) {
2585         if (isValidArrayIndex(index)) {
2586             for (ScriptObject object = this; ; ) {
2587                 if (object.getMap().containsArrayKeys()) {
2588                     final FindProperty find = object.findProperty(key, false, false, this);
2589 
2590                     if (find != null) {
2591                         return getDoubleValue(find);
2592                     }
2593                 }
2594 
2595                 if ((object = object.getProto()) == null) {
2596                     break;
2597                 }
2598 
2599                 final ArrayData array = object.getArray();
2600 
2601                 if (array.has(index)) {
2602                     return array.getDouble(index);
2603                 }
2604             }
2605         } else {
2606             final FindProperty find = findProperty(key, true);
2607 
2608             if (find != null) {
2609                 return getDoubleValue(find);
2610             }
2611         }
2612 
2613         return JSType.toNumber(invokeNoSuchProperty(key));
2614     }
2615 
2616     @Override
2617     public double getDouble(final Object key) {
2618         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2619         final int index = getArrayIndex(primitiveKey);
2620         final ArrayData array = getArray();
2621 
2622         if (array.has(index)) {
2623             return array.getDouble(index);
2624         }
2625 
2626         return getDouble(index, JSType.toString(primitiveKey));
2627     }
2628 
2629     @Override
2630     public double getDouble(final double key) {
2631         final int index = getArrayIndex(key);
2632         final ArrayData array = getArray();
2633 
2634         if (array.has(index)) {
2635             return array.getDouble(index);
2636         }
2637 
2638         return getDouble(index, JSType.toString(key));
2639     }
2640 
2641     @Override
2642     public double getDouble(final long key) {
2643         final int index = getArrayIndex(key);
2644         final ArrayData array = getArray();
2645 
2646         if (array.has(index)) {
2647             return array.getDouble(index);
2648         }
2649 
2650         return getDouble(index, JSType.toString(key));
2651     }
2652 
2653     @Override
2654     public double getDouble(final int key) {
2655         final int index = getArrayIndex(key);
2656         final ArrayData array = getArray();
2657 
2658         if (array.has(index)) {
2659             return array.getDouble(index);
2660         }
2661 
2662         return getDouble(index, JSType.toString(key));
2663     }
2664 
2665     private Object get(final int index, final String key) {
2666         if (isValidArrayIndex(index)) {
2667             for (ScriptObject object = this; ; ) {
2668                 if (object.getMap().containsArrayKeys()) {
2669                     final FindProperty find = object.findProperty(key, false, false, this);
2670 
2671                     if (find != null) {
2672                         return find.getObjectValue();
2673                     }
2674                 }
2675 
2676                 if ((object = object.getProto()) == null) {
2677                     break;
2678                 }
2679 
2680                 final ArrayData array = object.getArray();
2681 
2682                 if (array.has(index)) {
2683                     return array.getObject(index);
2684                 }
2685             }
2686         } else {
2687             final FindProperty find = findProperty(key, true);
2688 
2689             if (find != null) {
2690                 return find.getObjectValue();
2691             }
2692         }
2693 
2694         return invokeNoSuchProperty(key);
2695     }
2696 
2697     @Override
2698     public Object get(final Object key) {
2699         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2700         final int index = getArrayIndex(primitiveKey);
2701         final ArrayData array = getArray();
2702 
2703         if (array.has(index)) {
2704             return array.getObject(index);
2705         }
2706 
2707         return get(index, JSType.toString(primitiveKey));
2708     }
2709 
2710     @Override
2711     public Object get(final double key) {
2712         final int index = getArrayIndex(key);
2713         final ArrayData array = getArray();
2714 
2715         if (array.has(index)) {
2716             return array.getObject(index);
2717         }
2718 
2719         return get(index, JSType.toString(key));
2720     }
2721 
2722     @Override
2723     public Object get(final long key) {
2724         final int index = getArrayIndex(key);
2725         final ArrayData array = getArray();
2726 
2727         if (array.has(index)) {
2728             return array.getObject(index);
2729         }
2730 
2731         return get(index, JSType.toString(key));
2732     }
2733 
2734     @Override
2735     public Object get(final int key) {
2736         final int index = getArrayIndex(key);
2737         final ArrayData array = getArray();
2738 
2739         if (array.has(index)) {
2740             return array.getObject(index);
2741         }
2742 
2743         return get(index, JSType.toString(key));
2744     }
2745 
2746     /**
2747      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
2748      *
2749      * @param index  key as index
2750      * @param value  element value
2751      * @param strict are we in strict mode
2752      */
2753     private void doesNotHave(final int index, final Object value, final boolean strict) {
2754         final long longIndex = ArrayIndex.toLongIndex(index);
2755         if (getMap().containsArrayKeys()) {
2756             final String key = JSType.toString(longIndex);
2757             final FindProperty find = findProperty(key, true);
2758 
2759             if (find != null) {
2760                 setObject(find, strict, key, value);
2761                 return;
2762             }
2763         }
2764 
2765         setValueAtArrayIndex(longIndex, index, value, strict);
2766     }
2767 
2768     /**
2769      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
2770      *
2771      * @param index  key as index
2772      * @param value  element value
2773      * @param strict are we in strict mode
2774      */
2775     private void setValueAtArrayIndex(final long longIndex, final int index, final Object value, final boolean strict) {
2776         final long oldLength = getArray().length();
2777         if (longIndex >= oldLength) {
2778             if (!isExtensible()) {
2779                 if (strict) {
2780                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
2781                 }
2782                 return;
2783             }
2784             setArray(getArray().ensure(longIndex));
2785         }
2786 
2787         if (value instanceof Integer) {
2788             setArray(getArray().set(index, (int)value, strict));
2789         } else if (value instanceof Long) {
2790             setArray(getArray().set(index, (long)value, strict));
2791         } else if (value instanceof Double) {
2792             setArray(getArray().set(index, (double)value, strict));
2793         } else {
2794             setArray(getArray().set(index, value, strict));
2795         }
2796 
2797         if (longIndex > oldLength) {
2798             ArrayData array = getArray();
2799 
2800             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
2801                 array = array.delete(oldLength, (longIndex - 1));
2802             }
2803 
2804             setArray(array);
2805         }
2806     }
2807 
2808     /**
2809      * This is the most generic of all Object setters. Most of the others use this in some form.
2810      * TODO: should be further specialized
2811      *
2812      * @param find    found property
2813      * @param strict  are we in strict mode
2814      * @param key     property key
2815      * @param value   property value
2816      */
2817     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
2818         FindProperty f = find;
2819 
2820         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty) && !isScope()) {
2821             // Setting a property should not modify the property in prototype unless this is a scope object.
2822             f = null;
2823         }
2824 
2825         if (f != null) {
2826             if (!f.getProperty().isWritable()) {
2827                 if (strict) {
2828                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
2829                 }
2830 
2831                 return;
2832             }
2833 
2834             f.setObjectValue(value, strict);
2835 
2836         } else if (!isExtensible()) {
2837             if (strict) {
2838                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
2839             }
2840         } else {
2841             ScriptObject sobj = this;
2842             // undefined scope properties are set in the global object.
2843             if (isScope()) {
2844                 while (sobj != null && !(sobj instanceof Global)) {
2845                     sobj = sobj.getProto();
2846                 }
2847                 assert sobj != null : "no parent global object in scope";
2848             }
2849             sobj.spill(key, value);
2850         }
2851     }
2852 
2853     private void spill(final String key, final Object value) {
2854         addSpillProperty(key, 0).setObjectValue(this, this, value, false);
2855     }
2856 
2857 
2858     @Override
2859     public void set(final Object key, final int value, final boolean strict) {
2860         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2861         final int index = getArrayIndex(primitiveKey);
2862 
2863         if (isValidArrayIndex(index)) {
2864             if (getArray().has(index)) {
2865                 setArray(getArray().set(index, value, strict));
2866             } else {
2867                 doesNotHave(index, value, strict);
2868             }
2869 
2870             return;
2871         }
2872 
2873         final String propName = JSType.toString(primitiveKey);
2874         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2875     }
2876 
2877     @Override
2878     public void set(final Object key, final long value, final boolean strict) {
2879         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2880         final int index = getArrayIndex(primitiveKey);
2881 
2882         if (isValidArrayIndex(index)) {
2883             if (getArray().has(index)) {
2884                 setArray(getArray().set(index, value, strict));
2885             } else {
2886                 doesNotHave(index, value, strict);
2887             }
2888 
2889             return;
2890         }
2891 
2892         final String propName = JSType.toString(primitiveKey);
2893         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2894     }
2895 
2896     @Override
2897     public void set(final Object key, final double value, final boolean strict) {
2898         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2899         final int index = getArrayIndex(primitiveKey);
2900 
2901         if (isValidArrayIndex(index)) {
2902             if (getArray().has(index)) {
2903                 setArray(getArray().set(index, value, strict));
2904             } else {
2905                 doesNotHave(index, value, strict);
2906             }
2907 
2908             return;
2909         }
2910 
2911         final String propName = JSType.toString(primitiveKey);
2912         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2913     }
2914 
2915     @Override
2916     public void set(final Object key, final Object value, final boolean strict) {
2917         final Object primitiveKey = JSType.toPrimitive(key, String.class);
2918         final int index = getArrayIndex(primitiveKey);
2919 
2920         if (isValidArrayIndex(index)) {
2921             if (getArray().has(index)) {
2922                 setArray(getArray().set(index, value, strict));
2923             } else {
2924                 doesNotHave(index, value, strict);
2925             }
2926 
2927             return;
2928         }
2929 
2930         final String propName = JSType.toString(primitiveKey);
2931         setObject(findProperty(propName, true), strict, propName, value);
2932     }
2933 
2934     @Override
2935     public void set(final double key, final int value, final boolean strict) {
2936         final int index = getArrayIndex(key);
2937 
2938         if (isValidArrayIndex(index)) {
2939             if (getArray().has(index)) {
2940                 setArray(getArray().set(index, value, strict));
2941             } else {
2942                 doesNotHave(index, value, strict);
2943             }
2944 
2945             return;
2946         }
2947 
2948         final String propName = JSType.toString(key);
2949         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2950     }
2951 
2952     @Override
2953     public void set(final double key, final long value, final boolean strict) {
2954         final int index = getArrayIndex(key);
2955 
2956         if (isValidArrayIndex(index)) {
2957             if (getArray().has(index)) {
2958                 setArray(getArray().set(index, value, strict));
2959             } else {
2960                 doesNotHave(index, value, strict);
2961             }
2962 
2963             return;
2964         }
2965 
2966         final String propName = JSType.toString(key);
2967         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2968     }
2969 
2970     @Override
2971     public void set(final double key, final double value, final boolean strict) {
2972         final int index = getArrayIndex(key);
2973 
2974         if (isValidArrayIndex(index)) {
2975             if (getArray().has(index)) {
2976                 setArray(getArray().set(index, value, strict));
2977             } else {
2978                 doesNotHave(index, value, strict);
2979             }
2980 
2981             return;
2982         }
2983 
2984         final String propName = JSType.toString(key);
2985         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
2986     }
2987 
2988     @Override
2989     public void set(final double key, final Object value, final boolean strict) {
2990         final int index = getArrayIndex(key);
2991 
2992         if (isValidArrayIndex(index)) {
2993             if (getArray().has(index)) {
2994                 setArray(getArray().set(index, value, strict));
2995             } else {
2996                 doesNotHave(index, value, strict);
2997             }
2998 
2999             return;
3000         }
3001 
3002         final String propName = JSType.toString(key);
3003         setObject(findProperty(propName, true), strict, propName, value);
3004     }
3005 
3006     @Override
3007     public void set(final long key, final int value, final boolean strict) {
3008         final int index = getArrayIndex(key);
3009 
3010         if (isValidArrayIndex(index)) {
3011             if (getArray().has(index)) {
3012                 setArray(getArray().set(index, value, strict));
3013             } else {
3014                 doesNotHave(index, value, strict);
3015             }
3016 
3017             return;
3018         }
3019 
3020         final String propName = JSType.toString(key);
3021         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3022     }
3023 
3024     @Override
3025     public void set(final long key, final long value, final boolean strict) {
3026         final int index = getArrayIndex(key);
3027 
3028         if (isValidArrayIndex(index)) {
3029             if (getArray().has(index)) {
3030                 setArray(getArray().set(index, value, strict));
3031             } else {
3032                 doesNotHave(index, value, strict);
3033             }
3034 
3035             return;
3036         }
3037 
3038         final String propName = JSType.toString(key);
3039         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3040     }
3041 
3042     @Override
3043     public void set(final long key, final double value, final boolean strict) {
3044         final int index = getArrayIndex(key);
3045 
3046         if (isValidArrayIndex(index)) {
3047             if (getArray().has(index)) {
3048                 setArray(getArray().set(index, value, strict));
3049             } else {
3050                 doesNotHave(index, value, strict);
3051             }
3052 
3053             return;
3054         }
3055 
3056         final String propName = JSType.toString(key);
3057         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3058     }
3059 
3060     @Override
3061     public void set(final long key, final Object value, final boolean strict) {
3062         final int index = getArrayIndex(key);
3063 
3064         if (isValidArrayIndex(index)) {
3065             if (getArray().has(index)) {
3066                 setArray(getArray().set(index, value, strict));
3067             } else {
3068                 doesNotHave(index, value, strict);
3069             }
3070 
3071             return;
3072         }
3073 
3074         final String propName = JSType.toString(key);
3075         setObject(findProperty(propName, true), strict, propName, value);
3076     }
3077 
3078     @Override
3079     public void set(final int key, final int value, final boolean strict) {
3080         final int index = getArrayIndex(key);
3081 
3082         if (isValidArrayIndex(index)) {
3083             if (getArray().has(index)) {
3084                 setArray(getArray().set(index, value, strict));
3085             } else {
3086                 doesNotHave(index, value, strict);
3087             }
3088 
3089             return;
3090         }
3091 
3092         final String propName = JSType.toString(key);
3093         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3094     }
3095 
3096     @Override
3097     public void set(final int key, final long value, final boolean strict) {
3098         final int index = getArrayIndex(key);
3099 
3100         if (isValidArrayIndex(index)) {
3101             if (getArray().has(index)) {
3102                 setArray(getArray().set(index, value, strict));
3103             } else {
3104                 doesNotHave(index, value, strict);
3105             }
3106 
3107             return;
3108         }
3109 
3110         final String propName = JSType.toString(key);
3111         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3112     }
3113 
3114     @Override
3115     public void set(final int key, final double value, final boolean strict) {
3116         final int index = getArrayIndex(key);
3117 
3118         if (isValidArrayIndex(index)) {
3119             if (getArray().has(index)) {
3120                 setArray(getArray().set(index, value, strict));
3121             } else {
3122                 doesNotHave(index, value, strict);
3123             }
3124 
3125             return;
3126         }
3127 
3128         final String propName = JSType.toString(key);
3129         setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
3130     }
3131 
3132     @Override
3133     public void set(final int key, final Object value, final boolean strict) {
3134         final int index = getArrayIndex(key);
3135 
3136         if (isValidArrayIndex(index)) {
3137             if (getArray().has(index)) {
3138                 setArray(getArray().set(index, value, strict));
3139             } else {
3140                 doesNotHave(index, value, strict);
3141             }
3142 
3143             return;
3144         }
3145 
3146         final String propName = JSType.toString(key);
3147         setObject(findProperty(propName, true), strict, propName, value);
3148     }
3149 
3150     @Override
3151     public boolean has(final Object key) {
3152         final Object primitiveKey = JSType.toPrimitive(key);
3153         final int index = getArrayIndex(primitiveKey);
3154         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
3155     }
3156 
3157     @Override
3158     public boolean has(final double key) {
3159         final int index = getArrayIndex(key);
3160         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3161     }
3162 
3163     @Override
3164     public boolean has(final long key) {
3165         final int index = getArrayIndex(key);
3166         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3167     }
3168 
3169     @Override
3170     public boolean has(final int key) {
3171         final int index = getArrayIndex(key);
3172         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3173     }
3174 
3175     private boolean hasArrayProperty(final int index) {
3176         boolean hasArrayKeys = false;
3177 
3178         for (ScriptObject self = this; self != null; self = self.getProto()) {
3179             if (self.getArray().has(index)) {
3180                 return true;
3181             }
3182             hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys();
3183         }
3184 
3185         return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true);
3186     }
3187 
3188     @Override
3189     public boolean hasOwnProperty(final Object key) {
3190         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3191         final int index = getArrayIndex(primitiveKey);
3192         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
3193     }
3194 
3195     @Override
3196     public boolean hasOwnProperty(final int key) {
3197         final int index = getArrayIndex(key);
3198         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3199     }
3200 
3201     @Override
3202     public boolean hasOwnProperty(final long key) {
3203         final int index = getArrayIndex(key);
3204         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3205     }
3206 
3207     @Override
3208     public boolean hasOwnProperty(final double key) {
3209         final int index = getArrayIndex(key);
3210         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3211     }
3212 
3213     private boolean hasOwnArrayProperty(final int index) {
3214         return getArray().has(index) || (getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false));
3215     }
3216 
3217     @Override
3218     public boolean delete(final int key, final boolean strict) {
3219         final int index = getArrayIndex(key);
3220         final ArrayData array = getArray();
3221 
3222         if (array.has(index)) {
3223             if (array.canDelete(index, strict)) {
3224                 setArray(array.delete(index));
3225                 return true;
3226             }
3227             return false;
3228         }
3229 
3230         return deleteObject(JSType.toObject(key), strict);
3231     }
3232 
3233     @Override
3234     public boolean delete(final long key, final boolean strict) {
3235         final int index = getArrayIndex(key);
3236         final ArrayData array = getArray();
3237 
3238         if (array.has(index)) {
3239             if (array.canDelete(index, strict)) {
3240                 setArray(array.delete(index));
3241                 return true;
3242             }
3243             return false;
3244         }
3245 
3246         return deleteObject(JSType.toObject(key), strict);
3247     }
3248 
3249     @Override
3250     public boolean delete(final double key, final boolean strict) {
3251         final int index = getArrayIndex(key);
3252         final ArrayData array = getArray();
3253 
3254         if (array.has(index)) {
3255             if (array.canDelete(index, strict)) {
3256                 setArray(array.delete(index));
3257                 return true;
3258             }
3259             return false;
3260         }
3261 
3262         return deleteObject(JSType.toObject(key), strict);
3263     }
3264 
3265     @Override
3266     public boolean delete(final Object key, final boolean strict) {
3267         final Object primitiveKey = JSType.toPrimitive(key, String.class);
3268         final int index = getArrayIndex(primitiveKey);
3269         final ArrayData array = getArray();
3270 
3271         if (array.has(index)) {
3272             if (array.canDelete(index, strict)) {
3273                 setArray(array.delete(index));
3274                 return true;
3275             }
3276             return false;
3277         }
3278 
3279         return deleteObject(primitiveKey, strict);
3280     }
3281 
3282     private boolean deleteObject(final Object key, final boolean strict) {
3283         final String propName = JSType.toString(key);
3284         final FindProperty find = findProperty(propName, false);
3285 
3286         if (find == null) {
3287             return true;
3288         }
3289 
3290         if (!find.getProperty().isConfigurable()) {
3291             if (strict) {
3292                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
3293             }
3294             return false;
3295         }
3296 
3297         final Property prop = find.getProperty();
3298         deleteOwnProperty(prop);
3299 
3300         return true;
3301     }
3302 
3303     /**
3304      * Make a new UserAccessorProperty property. getter and setter functions are stored in
3305      * this ScriptObject and slot values are used in property object.
3306      *
3307      * @param key the property name
3308      * @param propertyFlags attribute flags of the property
3309      * @param getter getter function for the property
3310      * @param setter setter function for the property
3311      * @return the newly created UserAccessorProperty
3312      */
3313     protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3314         final UserAccessorProperty property = getMap().newUserAccessors(key, propertyFlags);
3315         setSpill(property.getGetterSlot(), getter);
3316         setSpill(property.getSetterSlot(), setter);
3317 
3318         return property;
3319     }
3320 
3321     /**
3322      * Write a value to a spill slot
3323      * @param slot  the slot index
3324      * @param value the value
3325      */
3326     protected final void setSpill(final int slot, final Object value) {
3327         if (spill == null) {
3328             // create new spill.
3329             spill = new Object[Math.max(slot + 1, SPILL_RATE)];
3330         } else if (slot >= spill.length) {
3331             // grow spill as needed
3332             final Object[] newSpill = new Object[slot + 1];
3333             System.arraycopy(spill, 0, newSpill, 0, spill.length);
3334             spill = newSpill;
3335         }
3336 
3337         spill[slot] = value;
3338     }
3339 
3340     /**
3341      * Get a value from a spill slot
3342      * @param slot the slot index
3343      * @return the value in the spill slot with the given index
3344      */
3345     protected Object getSpill(final int slot) {
3346         return spill != null && slot < spill.length ? spill[slot] : null;
3347     }
3348 
3349     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
3350         final Class<?>   own = ScriptObject.class;
3351         final MethodType mt  = MH.type(rtype, types);
3352         try {
3353             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
3354         } catch (final MethodHandleFactory.LookupException e) {
3355             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
3356         }
3357     }
3358 
3359     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3360         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
3361     }
3362 
3363     @SuppressWarnings("unused")
3364     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
3365         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3366             try {
3367                 return getter.invokeExact(where) == func;
3368             } catch (final RuntimeException | Error e) {
3369                 throw e;
3370             } catch (final Throwable t) {
3371                 throw new RuntimeException(t);
3372             }
3373         }
3374 
3375         return false;
3376     }
3377 
3378     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3379     private static int count;
3380 
3381     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
3382     private static int scopeCount;
3383 
3384     /**
3385      * Get number of {@code ScriptObject} instances created. If not running in debug
3386      * mode this is always 0
3387      *
3388      * @return number of ScriptObjects created
3389      */
3390     public static int getCount() {
3391         return count;
3392     }
3393 
3394     /**
3395      * Get number of scope {@code ScriptObject} instances created. If not running in debug
3396      * mode this is always 0
3397      *
3398      * @return number of scope ScriptObjects created
3399      */
3400     public static int getScopeCount() {
3401         return scopeCount;
3402     }
3403 
3404 }