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