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