src/jdk/nashorn/internal/runtime/ScriptObject.java

Thu, 22 Aug 2013 17:23:50 +0200

author
hannesw
date
Thu, 22 Aug 2013 17:23:50 +0200
changeset 515
8ad9bcb04e6d
parent 513
b7c04b3b01a7
child 524
badc919cd621
permissions
-rw-r--r--

8023531: new RegExp('').toString() should return '/(?:)/'
Reviewed-by: sundar, jlaskey

     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  */
    26 package jdk.nashorn.internal.runtime;
    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;
    41 import java.lang.invoke.MethodHandle;
    42 import java.lang.invoke.MethodHandles;
    43 import java.lang.invoke.MethodType;
    44 import java.util.AbstractMap;
    45 import java.util.ArrayList;
    46 import java.util.Arrays;
    47 import java.util.Collection;
    48 import java.util.Collections;
    49 import java.util.HashSet;
    50 import java.util.Iterator;
    51 import java.util.LinkedHashSet;
    52 import java.util.List;
    53 import java.util.Map;
    54 import java.util.Set;
    55 import jdk.internal.dynalink.CallSiteDescriptor;
    56 import jdk.internal.dynalink.linker.GuardedInvocation;
    57 import jdk.internal.dynalink.linker.LinkRequest;
    58 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    59 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
    60 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
    61 import jdk.nashorn.internal.lookup.Lookup;
    62 import jdk.nashorn.internal.lookup.MethodHandleFactory;
    63 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
    64 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
    65 import jdk.nashorn.internal.runtime.arrays.ArrayData;
    66 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
    67 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    68 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
    69 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
    70 import jdk.nashorn.internal.runtime.linker.NashornGuards;
    72 /**
    73  * Base class for generic JavaScript objects.
    74  * <p>
    75  * Notes:
    76  * <ul>
    77  * <li>The map is used to identify properties in the object.</li>
    78  * <li>If the map is modified then it must be cloned and replaced.  This notifies
    79  *     any code that made assumptions about the object that things have changed.
    80  *     Ex. CallSites that have been validated must check to see if the map has
    81  *     changed (or a map from a different object type) and hence relink the method
    82  *     to call.</li>
    83  * <li>Modifications of the map include adding/deleting attributes or changing a
    84  *     function field value.</li>
    85  * </ul>
    86  */
    88 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
    90     /** Search fall back routine name for "no such method" */
    91     static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
    93     /** Search fall back routine name for "no such property" */
    94     static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
    96     /** Per ScriptObject flag - is this a scope object? */
    97     public static final int IS_SCOPE       = 0b0000_0001;
    99     /** Per ScriptObject flag - is this an array object? */
   100     public static final int IS_ARRAY       = 0b0000_0010;
   102     /** Per ScriptObject flag - is this an arguments object? */
   103     public static final int IS_ARGUMENTS   = 0b0000_0100;
   105     /** Is this a prototype PropertyMap? */
   106     public static final int IS_PROTOTYPE   = 0b0000_1000;
   108     /** Is length property not-writable? */
   109     public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
   111     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
   112     public static final int SPILL_RATE = 8;
   114     /** Map to property information and accessor functions. Ordered by insertion. */
   115     private PropertyMap map;
   117     /** objects proto. */
   118     private ScriptObject proto;
   120     /** Context of the object, lazily cached. */
   121     private Context context;
   123     /** Object flags. */
   124     private int flags;
   126     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
   127     public Object[] spill;
   129     /** Indexed array data. */
   130     private ArrayData arrayData;
   132     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
   133     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   134     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
   135     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
   137     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
   138     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
   140     /** Method handle for getting a function argument at a given index. Used from MapCreator */
   141     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
   143     /** Method handle for setting a function argument at a given index. Used from MapCreator */
   144     public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
   146     /** Method handle for getting the proto of a ScriptObject */
   147     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
   149     /** Method handle for setting the proto of a ScriptObject */
   150     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
   152     /** Method handle for setting the user accessors of a ScriptObject */
   153     public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
   155     /**
   156      * Constructor
   157      */
   158     public ScriptObject() {
   159         this(null);
   160     }
   162     /**
   163     * Constructor
   164     *
   165     * @param map {@link PropertyMap} used to create the initial object
   166     */
   167     public ScriptObject(final PropertyMap map) {
   168         if (Context.DEBUG) {
   169             ScriptObject.count++;
   170         }
   172         this.arrayData = ArrayData.EMPTY_ARRAY;
   173         this.setMap(map == null ? PropertyMap.newMap() : map);
   174     }
   176     /**
   177      * Constructor that directly sets the prototype to {@code proto} and property map to
   178      * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)}
   179      * would do. This should only be used for objects that are always constructed with the
   180      * same combination of prototype and property map.
   181      *
   182      * @param proto the prototype object
   183      * @param map intial {@link PropertyMap}
   184      */
   185     protected ScriptObject(final ScriptObject proto, final PropertyMap map) {
   186         if (Context.DEBUG) {
   187             ScriptObject.count++;
   188         }
   190         this.arrayData = ArrayData.EMPTY_ARRAY;
   191         this.setMap(map == null ? PropertyMap.newMap() : map);
   192         this.proto = proto;
   194         if (proto != null) {
   195             proto.setIsPrototype();
   196         }
   197     }
   199     /**
   200      * Copy all properties from the source object with their receiver bound to the source.
   201      * This function was known as mergeMap
   202      *
   203      * @param source The source object to copy from.
   204      */
   205     public void addBoundProperties(final ScriptObject source) {
   206         addBoundProperties(source, source.getMap().getProperties());
   207     }
   209     /**
   210      * Copy all properties from the array with their receiver bound to the source.
   211      *
   212      * @param source The source object to copy from.
   213      * @param properties The array of properties to copy.
   214      */
   215     public void addBoundProperties(final ScriptObject source, final Property[] properties) {
   216         PropertyMap newMap = this.getMap();
   218         for (final Property property : properties) {
   219             final String key = property.getKey();
   221             if (newMap.findProperty(key) == null) {
   222                 if (property instanceof UserAccessorProperty) {
   223                     final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
   224                     newMap = newMap.addProperty(prop);
   225                 } else {
   226                     newMap = newMap.addPropertyBind((AccessorProperty)property, source);
   227                 }
   228             }
   229         }
   231         this.setMap(newMap);
   232     }
   234     /**
   235      * Copy all properties from the array with their receiver bound to the source.
   236      *
   237      * @param source The source object to copy from.
   238      * @param properties The collection of accessor properties to copy.
   239      */
   240     public void addBoundProperties(final Object source, final AccessorProperty[] properties) {
   241         PropertyMap newMap = this.getMap();
   243         for (final AccessorProperty property : properties) {
   244             final String key = property.getKey();
   246             if (newMap.findProperty(key) == null) {
   247                 newMap = newMap.addPropertyBind(property, source);
   248             }
   249         }
   251         this.setMap(newMap);
   252     }
   254     /**
   255      * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
   256      * first argument in lieu of the bound argument).
   257      * @param methodHandle Method handle to bind to.
   258      * @param receiver     Object to bind.
   259      * @return Bound method handle.
   260      */
   261     static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
   262         return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
   263     }
   265     /**
   266      * Return a property iterator.
   267      * @return Property iterator.
   268      */
   269     public Iterator<String> propertyIterator() {
   270         return new KeyIterator(this);
   271     }
   273     /**
   274      * Return a property value iterator.
   275      * @return Property value iterator.
   276      */
   277     public Iterator<Object> valueIterator() {
   278         return new ValueIterator(this);
   279     }
   281     /**
   282      * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
   283      * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
   284      */
   285     public final boolean isAccessorDescriptor() {
   286         return has(GET) || has(SET);
   287     }
   289     /**
   290      * ECMA 8.10.2 IsDataDescriptor ( Desc )
   291      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
   292      */
   293     public final boolean isDataDescriptor() {
   294         return has(VALUE) || has(WRITABLE);
   295     }
   297     /**
   298      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
   299      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
   300      */
   301     public final boolean isGenericDescriptor() {
   302         return isAccessorDescriptor() || isDataDescriptor();
   303     }
   305     /**
   306       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   307       *
   308       * @return property descriptor
   309       */
   310     public final PropertyDescriptor toPropertyDescriptor() {
   311         final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   313         final PropertyDescriptor desc;
   314         if (isDataDescriptor()) {
   315             if (has(SET) || has(GET)) {
   316                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   317             }
   319             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
   320         } else if (isAccessorDescriptor()) {
   321             if (has(VALUE) || has(WRITABLE)) {
   322                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
   323             }
   325             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
   326         } else {
   327             desc = global.newGenericDescriptor(false, false);
   328         }
   330         return desc.fillFrom(this);
   331     }
   333     /**
   334      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
   335      *
   336      * @param global  global scope object
   337      * @param obj object to create property descriptor from
   338      *
   339      * @return property descriptor
   340      */
   341     public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
   342         if (obj instanceof ScriptObject) {
   343             return ((ScriptObject)obj).toPropertyDescriptor();
   344         }
   346         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
   347     }
   349     /**
   350      * ECMA 8.12.1 [[GetOwnProperty]] (P)
   351      *
   352      * @param key property key
   353      *
   354      * @return Returns the Property Descriptor of the named own property of this
   355      * object, or undefined if absent.
   356      */
   357     public Object getOwnPropertyDescriptor(final String key) {
   358         final Property property = getMap().findProperty(key);
   360         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
   362         if (property != null) {
   363             final ScriptFunction get   = property.getGetterFunction(this);
   364             final ScriptFunction set   = property.getSetterFunction(this);
   366             final boolean configurable = property.isConfigurable();
   367             final boolean enumerable   = property.isEnumerable();
   368             final boolean writable     = property.isWritable();
   370             if (property instanceof UserAccessorProperty) {
   371                 return global.newAccessorDescriptor(
   372                     (get != null) ?
   373                         get :
   374                         UNDEFINED,
   375                     (set != null) ?
   376                         set :
   377                         UNDEFINED,
   378                     configurable,
   379                     enumerable);
   380             }
   382             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
   383         }
   385         final int index = ArrayIndex.getArrayIndex(key);
   386         final ArrayData array = getArray();
   388         if (array.has(index)) {
   389             return array.getDescriptor(global, index);
   390         }
   392         return UNDEFINED;
   393     }
   395     /**
   396      * ECMA 8.12.2 [[GetProperty]] (P)
   397      *
   398      * @param key property key
   399      *
   400      * @return Returns the fully populated Property Descriptor of the named property
   401      * of this object, or undefined if absent.
   402      */
   403     public Object getPropertyDescriptor(final String key) {
   404         final Object res = getOwnPropertyDescriptor(key);
   406         if (res != UNDEFINED) {
   407             return res;
   408         } else if (getProto() != null) {
   409             return getProto().getOwnPropertyDescriptor(key);
   410         } else {
   411             return UNDEFINED;
   412         }
   413     }
   415     /**
   416      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
   417      *
   418      * @param key the property key
   419      * @param propertyDesc the property descriptor
   420      * @param reject is the property extensible - true means new definitions are rejected
   421      *
   422      * @return true if property was successfully defined
   423      */
   424     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   425         final ScriptObject       global  = Context.getGlobalTrusted();
   426         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
   427         final Object             current = getOwnPropertyDescriptor(key);
   428         final String             name    = JSType.toString(key);
   430         if (current == UNDEFINED) {
   431             if (isExtensible()) {
   432                 // add a new own property
   433                 addOwnProperty(key, desc);
   434                 return true;
   435             }
   436             // new property added to non-extensible object
   437             if (reject) {
   438                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
   439             }
   440             return false;
   441         }
   442         // modifying an existing property
   443         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
   444         final PropertyDescriptor newDesc     = desc;
   446         if (newDesc.type() == PropertyDescriptor.GENERIC &&
   447             ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
   448             // every descriptor field is absent
   449             return true;
   450         }
   452         if (currentDesc.equals(newDesc)) {
   453             // every descriptor field of the new is same as the current
   454             return true;
   455         }
   457         if (! currentDesc.isConfigurable()) {
   458             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
   459                 // not configurable can not be made configurable
   460                 if (reject) {
   461                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   462                 }
   463                 return false;
   464             }
   466             if (newDesc.has(ENUMERABLE) &&
   467                 currentDesc.isEnumerable() != newDesc.isEnumerable()) {
   468                 // cannot make non-enumerable as enumerable or vice-versa
   469                 if (reject) {
   470                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   471                 }
   472                 return false;
   473             }
   474         }
   476         int propFlags = Property.mergeFlags(currentDesc, newDesc);
   477         Property property = getMap().findProperty(key);
   479         if (currentDesc.type() == PropertyDescriptor.DATA &&
   480             (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
   481             if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
   482                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
   483                     newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
   484                     if (reject) {
   485                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   486                     }
   487                     return false;
   488                 }
   489             }
   491             final boolean newValue = newDesc.has(VALUE);
   492             final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
   493             if (newValue && property != null) {
   494                 // Temporarily clear flags.
   495                 property = modifyOwnProperty(property, 0);
   496                 set(key, value, false);
   497             }
   499             if (property == null) {
   500                 // promoting an arrayData value to actual property
   501                 addOwnProperty(key, propFlags, value);
   502                 removeArraySlot(key);
   503             } else {
   504                 // Now set the new flags
   505                 modifyOwnProperty(property, propFlags);
   506             }
   507         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
   508                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
   509                     newDesc.type() == PropertyDescriptor.GENERIC)) {
   510             if (! currentDesc.isConfigurable()) {
   511                 if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
   512                     newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
   513                     if (reject) {
   514                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   515                     }
   516                     return false;
   517                 }
   518             }
   520             // New set the new features.
   521             modifyOwnProperty(property, propFlags,
   522                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
   523                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
   524         } else {
   525             // changing descriptor type
   526             if (! currentDesc.isConfigurable()) {
   527                 // not configurable can not be made configurable
   528                 if (reject) {
   529                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
   530                 }
   531                 return false;
   532             }
   534             propFlags = 0;
   536             // Preserve only configurable and enumerable from current desc
   537             // if those are not overridden in the new property descriptor.
   538             boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
   539             if (!value) {
   540                 propFlags |= Property.NOT_CONFIGURABLE;
   541             }
   542             value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
   543             if (!value) {
   544                 propFlags |= Property.NOT_ENUMERABLE;
   545             }
   547             final int type = newDesc.type();
   548             if (type == PropertyDescriptor.DATA) {
   549                 // get writable from the new descriptor
   550                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
   551                 if (! value) {
   552                     propFlags |= Property.NOT_WRITABLE;
   553                 }
   555                 // delete the old property
   556                 deleteOwnProperty(property);
   557                 // add new data property
   558                 addOwnProperty(key, propFlags, newDesc.getValue());
   559             } else if (type == PropertyDescriptor.ACCESSOR) {
   560                 if (property == null) {
   561                     addOwnProperty(key, propFlags,
   562                                      newDesc.has(GET) ? newDesc.getGetter() : null,
   563                                      newDesc.has(SET) ? newDesc.getSetter() : null);
   564                 } else {
   565                     // Modify old property with the new features.
   566                     modifyOwnProperty(property, propFlags,
   567                                         newDesc.has(GET) ? newDesc.getGetter() : null,
   568                                         newDesc.has(SET) ? newDesc.getSetter() : null);
   569                 }
   570             }
   571         }
   573         checkIntegerKey(key);
   575         return true;
   576     }
   578     /**
   579      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
   580      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
   581      * method in such cases. This is because set method uses inherited setters (if any)
   582      * from any object in proto chain such as Array.prototype, Object.prototype.
   583      * This method directly sets a particular element value in the current object.
   584      *
   585      * @param index key for property
   586      * @param value value to define
   587      */
   588     protected final void defineOwnProperty(final int index, final Object value) {
   589         assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
   590         final long longIndex = ArrayIndex.toLongIndex(index);
   591         if (longIndex >= getArray().length()) {
   592             // make array big enough to hold..
   593             setArray(getArray().ensure(longIndex));
   594         }
   595         setArray(getArray().set(index, value, false));
   596     }
   598     private void checkIntegerKey(final String key) {
   599         final int index = ArrayIndex.getArrayIndex(key);
   601         if (ArrayIndex.isValidArrayIndex(index)) {
   602             final ArrayData data = getArray();
   604             if (data.has(index)) {
   605                 setArray(data.delete(index));
   606             }
   607         }
   608     }
   610     private void removeArraySlot(final String key) {
   611         final int index = ArrayIndex.getArrayIndex(key);
   612         final ArrayData array = getArray();
   614         if (array.has(index)) {
   615             setArray(array.delete(index));
   616         }
   617     }
   619     /**
   620       * Add a new property to the object.
   621       *
   622       * @param key          property key
   623       * @param propertyDesc property descriptor for property
   624       */
   625     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
   626         // Already checked that there is no own property with that key.
   627         PropertyDescriptor pdesc = propertyDesc;
   629         final int propFlags = Property.toFlags(pdesc);
   631         if (pdesc.type() == PropertyDescriptor.GENERIC) {
   632             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
   633             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
   635             dDesc.fillFrom((ScriptObject)pdesc);
   636             pdesc = dDesc;
   637         }
   639         final int type = pdesc.type();
   640         if (type == PropertyDescriptor.DATA) {
   641             addOwnProperty(key, propFlags, pdesc.getValue());
   642         } else if (type == PropertyDescriptor.ACCESSOR) {
   643             addOwnProperty(key, propFlags,
   644                     pdesc.has(GET) ? pdesc.getGetter() : null,
   645                     pdesc.has(SET) ? pdesc.getSetter() : null);
   646         }
   648         checkIntegerKey(key);
   649     }
   651     /**
   652      * Low level property API (not using property descriptors)
   653      * <p>
   654      * Find a property in the prototype hierarchy. Note: this is final and not
   655      * a good idea to override. If you have to, use
   656      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   657      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   658      * overriding way to find array properties
   659      *
   660      * @see jdk.nashorn.internal.objects.NativeArray
   661      *
   662      * @param key  Property key.
   663      * @param deep Whether the search should look up proto chain.
   664      *
   665      * @return FindPropertyData or null if not found.
   666      */
   667     public final FindProperty findProperty(final String key, final boolean deep) {
   668         return findProperty(key, deep, false, this);
   669     }
   671     /**
   672      * Low level property API (not using property descriptors)
   673      * <p>
   674      * Find a property in the prototype hierarchy. Note: this is not a good idea
   675      * to override except as it was done in {@link WithObject}.
   676      * If you have to, use
   677      * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
   678      * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
   679      * overriding way to find array properties
   680      *
   681      * @see jdk.nashorn.internal.objects.NativeArray
   682      *
   683      * @param key  Property key.
   684      * @param deep Whether the search should look up proto chain.
   685      * @param stopOnNonScope should a deep search stop on the first non-scope object?
   686      * @param start the object on which the lookup was originally initiated
   687      *
   688      * @return FindPropertyData or null if not found.
   689      */
   690     FindProperty findProperty(final String key, final boolean deep, final boolean stopOnNonScope, final ScriptObject start) {
   691         // if doing deep search, stop search on the first non-scope object if asked to do so
   692         if (stopOnNonScope && start != this && !isScope()) {
   693             return null;
   694         }
   696         final PropertyMap selfMap  = getMap();
   697         final Property    property = selfMap.findProperty(key);
   699         if (property != null) {
   700             return new FindProperty(start, this, property);
   701         }
   703         if (deep) {
   704             final ScriptObject myProto = getProto();
   705             if (myProto != null) {
   706                 return myProto.findProperty(key, deep, stopOnNonScope, start);
   707             }
   708         }
   710         return null;
   711     }
   713     /**
   714      * Add a new property to the object.
   715      * <p>
   716      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   717      *
   718      * @param key             Property key.
   719      * @param propertyFlags   Property flags.
   720      * @param getter          Property getter, or null if not defined
   721      * @param setter          Property setter, or null if not defined
   722      *
   723      * @return New property.
   724      */
   725     public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   726         return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
   727     }
   729     /**
   730      * Add a new property to the object.
   731      * <p>
   732      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   733      *
   734      * @param key             Property key.
   735      * @param propertyFlags   Property flags.
   736      * @param value           Value of property
   737      *
   738      * @return New property.
   739      */
   740     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
   741         final Property property = addSpillProperty(key, propertyFlags);
   742         property.setObjectValue(this, this, value, false);
   743         return property;
   744     }
   746     /**
   747      * Add a new property to the object.
   748      * <p>
   749      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
   750      *
   751      * @param newProperty property to add
   752      *
   753      * @return New property.
   754      */
   755     public final Property addOwnProperty(final Property newProperty) {
   756         PropertyMap oldMap = getMap();
   758         while (true) {
   759             final PropertyMap newMap = oldMap.addProperty(newProperty);
   761             if (!compareAndSetMap(oldMap, newMap)) {
   762                 oldMap = getMap();
   763                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
   765                 if (oldProperty != null) {
   766                     return oldProperty;
   767                 }
   768             } else {
   769                 return newProperty;
   770             }
   771         }
   772     }
   774     private void erasePropertyValue(final Property property) {
   775         // Erase the property field value with undefined. If the property is defined
   776         // by user-defined accessors, we don't want to call the setter!!
   777         if (!(property instanceof UserAccessorProperty)) {
   778             property.setObjectValue(this, this, UNDEFINED, false);
   779         }
   780     }
   782     /**
   783      * Delete a property from the object.
   784      *
   785      * @param property Property to delete.
   786      *
   787      * @return true if deleted.
   788      */
   789     public final boolean deleteOwnProperty(final Property property) {
   790         erasePropertyValue(property);
   791         PropertyMap oldMap = getMap();
   793         while (true) {
   794             final PropertyMap newMap = oldMap.deleteProperty(property);
   796             if (newMap == null) {
   797                 return false;
   798             }
   800             if (!compareAndSetMap(oldMap, newMap)) {
   801                 oldMap = getMap();
   802             } else {
   803                 // delete getter and setter function references so that we don't leak
   804                 if (property instanceof UserAccessorProperty) {
   805                     final UserAccessorProperty uc = (UserAccessorProperty) property;
   806                     setSpill(uc.getGetterSlot(), null);
   807                     setSpill(uc.getSetterSlot(), null);
   808                 }
   809                 return true;
   810             }
   811         }
   812     }
   814     /**
   815      * Modify a property in the object
   816      *
   817      * @param oldProperty    property to modify
   818      * @param propertyFlags  new property flags
   819      * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
   820      * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
   821      *
   822      * @return new property
   823      */
   824     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
   825         Property newProperty;
   826         if (oldProperty instanceof UserAccessorProperty) {
   827             final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
   828             final int getterSlot = uc.getGetterSlot();
   829             final int setterSlot = uc.getSetterSlot();
   830             setSpill(getterSlot, getter);
   831             setSpill(setterSlot, setter);
   833             // if just flipping getter and setter with new functions, no need to change property or map
   834             if (uc.flags == propertyFlags) {
   835                 return oldProperty;
   836             }
   838             newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
   839         } else {
   840             // erase old property value and create new user accessor property
   841             erasePropertyValue(oldProperty);
   842             newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
   843         }
   845         notifyPropertyModified(this, oldProperty, newProperty);
   847         return modifyOwnProperty(oldProperty, newProperty);
   848     }
   850     /**
   851       * Modify a property in the object
   852       *
   853       * @param oldProperty    property to modify
   854       * @param propertyFlags  new property flags
   855       *
   856       * @return new property
   857       */
   858     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
   859         return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
   860     }
   862     /**
   863      * Modify a property in the object, replacing a property with a new one
   864      *
   865      * @param oldProperty   property to replace
   866      * @param newProperty   property to replace it with
   867      *
   868      * @return new property
   869      */
   870     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
   871         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
   873         PropertyMap oldMap = getMap();
   875         while (true) {
   876             final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
   878             if (!compareAndSetMap(oldMap, newMap)) {
   879                 oldMap = getMap();
   880                 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
   882                 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
   883                     return oldPropertyLookup;
   884                 }
   885             } else {
   886                 return newProperty;
   887             }
   888         }
   889     }
   891     /**
   892      * Update getter and setter in an object literal.
   893      *
   894      * @param key    Property key.
   895      * @param getter {@link UserAccessorProperty} defined getter, or null if none
   896      * @param setter {@link UserAccessorProperty} defined setter, or null if none
   897      */
   898     public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
   899         final Property oldProperty = getMap().findProperty(key);
   900         if (oldProperty instanceof UserAccessorProperty) {
   901             modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter);
   902         } else {
   903             addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter));
   904         }
   905     }
   907     private static int getIntValue(final FindProperty find) {
   908         final MethodHandle getter = find.getGetter(int.class);
   909         if (getter != null) {
   910             try {
   911                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
   912             } catch (final Error|RuntimeException e) {
   913                 throw e;
   914             } catch (final Throwable e) {
   915                 throw new RuntimeException(e);
   916             }
   917         }
   919         return ObjectClassGenerator.UNDEFINED_INT;
   920     }
   922     private static long getLongValue(final FindProperty find) {
   923         final MethodHandle getter = find.getGetter(long.class);
   924         if (getter != null) {
   925             try {
   926                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
   927             } catch (final Error|RuntimeException e) {
   928                 throw e;
   929             } catch (final Throwable e) {
   930                 throw new RuntimeException(e);
   931             }
   932         }
   934         return ObjectClassGenerator.UNDEFINED_LONG;
   935     }
   937     private static double getDoubleValue(final FindProperty find) {
   938         final MethodHandle getter = find.getGetter(double.class);
   939         if (getter != null) {
   940             try {
   941                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
   942             } catch (final Error|RuntimeException e) {
   943                 throw e;
   944             } catch (final Throwable e) {
   945                 throw new RuntimeException(e);
   946             }
   947         }
   949         return ObjectClassGenerator.UNDEFINED_DOUBLE;
   950     }
   952     /**
   953       * Get the object value of a property
   954       *
   955       * @param find {@link FindProperty} lookup result
   956       *
   957       * @return the value of the property
   958       */
   959     protected static Object getObjectValue(final FindProperty find) {
   960         return find.getObjectValue();
   961     }
   963     /**
   964      * Return methodHandle of value function for call.
   965      *
   966      * @param find      data from find property.
   967      * @param type      method type of function.
   968      * @param bindName  null or name to bind to second argument (property not found method.)
   969      *
   970      * @return value of property as a MethodHandle or null.
   971      */
   972     protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
   973         return getCallMethodHandle(getObjectValue(find), type, bindName);
   974     }
   976     /**
   977      * Return methodHandle of value function for call.
   978      *
   979      * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
   980      * @param type      method type of function.
   981      * @param bindName  null or name to bind to second argument (property not found method.)
   982      *
   983      * @return value of property as a MethodHandle or null.
   984      */
   985     protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
   986         return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
   987     }
   989     /**
   990      * Get value using found property.
   991      *
   992      * @param property Found property.
   993      *
   994      * @return Value of property.
   995      */
   996     public final Object getWithProperty(final Property property) {
   997         return getObjectValue(new FindProperty(this, this, property));
   998     }
  1000     /**
  1001      * Get a property given a key
  1003      * @param key property key
  1005      * @return property for key
  1006      */
  1007     public final Property getProperty(final String key) {
  1008         return getMap().findProperty(key);
  1011     /**
  1012      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1013      * Used for argument access in a vararg function using parameter name.
  1014      * Returns the argument at a given key (index)
  1016      * @param key argument index
  1018      * @return the argument at the given position, or undefined if not present
  1019      */
  1020     public Object getArgument(final int key) {
  1021         return get(key);
  1024     /**
  1025      * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
  1026      * Used for argument access in a vararg function using parameter name.
  1027      * Returns the argument at a given key (index)
  1029      * @param key   argument index
  1030      * @param value the value to write at the given index
  1031      */
  1032     public void setArgument(final int key, final Object value) {
  1033         set(key, value, false);
  1036     /**
  1037      * Return true if the script object context is strict
  1038      * @return true if strict context
  1039      */
  1040     public final boolean isStrictContext() {
  1041         return getContext()._strict;
  1044     /**
  1045      * Checks if this object belongs to the given context
  1046      * @param ctx context to check against
  1047      * @return true if this object belongs to the given context
  1048      */
  1049     public final boolean isOfContext(final Context ctx) {
  1050         return context == ctx;
  1053     /**
  1054      * Return the current context from the object's map.
  1055      * @return Current context.
  1056      */
  1057     protected final Context getContext() {
  1058         if (context == null) {
  1059             context = Context.fromClass(getClass());
  1061         return context;
  1064     /**
  1065      * Set the current context.
  1066      * @param ctx context instance to set
  1067      */
  1068     protected final void setContext(final Context ctx) {
  1069         ctx.getClass();
  1070         this.context = ctx;
  1073     /**
  1074      * Return the map of an object.
  1075      * @return PropertyMap object.
  1076      */
  1077     public final PropertyMap getMap() {
  1078         return map;
  1081     /**
  1082      * Set the initial map.
  1083      * @param map Initial map.
  1084      */
  1085     public final void setMap(final PropertyMap map) {
  1086         this.map = map;
  1089     /**
  1090      * Conditionally set the new map if the old map is the same.
  1091      * @param oldMap Map prior to manipulation.
  1092      * @param newMap Replacement map.
  1093      * @return true if the operation succeeded.
  1094      */
  1095     protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
  1096         final boolean update = oldMap == this.map;
  1098         if (update) {
  1099             this.map = newMap;
  1102         return update;
  1105     /**
  1106      * Return the __proto__ of an object.
  1107      * @return __proto__ object.
  1108      */
  1109     public final ScriptObject getProto() {
  1110         return proto;
  1113     /**
  1114      * Set the __proto__ of an object.
  1115      * @param newProto new __proto__ to set.
  1116      */
  1117     public synchronized final void setProto(final ScriptObject newProto) {
  1118         final ScriptObject oldProto = proto;
  1119         map = map.changeProto(oldProto, newProto);
  1121         if (newProto != null) {
  1122             newProto.setIsPrototype();
  1125         proto = newProto;
  1127         if (isPrototype()) {
  1128             // tell listeners that my __proto__ has been changed
  1129             notifyProtoChanged(this, oldProto, newProto);
  1131             if (oldProto != null) {
  1132                 oldProto.removePropertyListener(this);
  1135             if (newProto != null) {
  1136                 newProto.addPropertyListener(this);
  1141     /**
  1142      * Set the __proto__ of an object with checks.
  1143      * @param newProto Prototype to set.
  1144      */
  1145     public final void setProtoCheck(final Object newProto) {
  1146         if (!isExtensible()) {
  1147             throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
  1150         if (newProto == null || newProto instanceof ScriptObject) {
  1151             // check for circularity
  1152             ScriptObject proto = (ScriptObject)newProto;
  1153             while (proto != null) {
  1154                 if (proto == this) {
  1155                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
  1157                 proto = proto.getProto();
  1159             setProto((ScriptObject)newProto);
  1160         } else {
  1161             final ScriptObject global = Context.getGlobalTrusted();
  1162             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
  1164             if (newProtoObject instanceof ScriptObject) {
  1165                 setProto((ScriptObject)newProtoObject);
  1166             } else {
  1167                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
  1172     /**
  1173      * return an array of own property keys associated with the object.
  1175      * @param all True if to include non-enumerable keys.
  1176      * @return Array of keys.
  1177      */
  1178     public String[] getOwnKeys(final boolean all) {
  1179         final List<Object> keys    = new ArrayList<>();
  1180         final PropertyMap  selfMap = this.getMap();
  1182         final ArrayData array  = getArray();
  1183         final long length      = array.length();
  1185         for (long i = 0; i < length; i = array.nextIndex(i)) {
  1186             if (array.has((int)i)) {
  1187                 keys.add(JSType.toString(i));
  1191         for (final Property property : selfMap.getProperties()) {
  1192             if (all || property.isEnumerable()) {
  1193                 keys.add(property.getKey());
  1197         return keys.toArray(new String[keys.size()]);
  1200     /**
  1201      * Check if this ScriptObject has array entries. This means that someone has
  1202      * set values with numeric keys in the object.
  1204      * Note: this can be O(n) up to the array length
  1206      * @return true if array entries exists.
  1207      */
  1208     public boolean hasArrayEntries() {
  1209         final ArrayData array = getArray();
  1210         final long length = array.length();
  1212         for (long i = 0; i < length; i++) {
  1213             if (array.has((int)i)) {
  1214                 return true;
  1218         return false;
  1221     /**
  1222      * Return the valid JavaScript type name descriptor
  1224      * @return "Object"
  1225      */
  1226     public String getClassName() {
  1227         return "Object";
  1230     /**
  1231      * {@code length} is a well known property. This is its getter.
  1232      * Note that this *may* be optimized by other classes
  1234      * @return length property value for this ScriptObject
  1235      */
  1236     public Object getLength() {
  1237         return get("length");
  1240     /**
  1241      * Stateless toString for ScriptObjects.
  1243      * @return string description of this object, e.g. {@code [object Object]}
  1244      */
  1245     public String safeToString() {
  1246         return "[object " + getClassName() + "]";
  1249     /**
  1250      * Return the default value of the object with a given preferred type hint.
  1251      * The preferred type hints are String.class for type String, Number.class
  1252      * for type Number. <p>
  1254      * A <code>hint</code> of null means "no hint".
  1256      * ECMA 8.12.8 [[DefaultValue]](hint)
  1258      * @param typeHint the preferred type hint
  1259      * @return the default value
  1260      */
  1261     public Object getDefaultValue(final Class<?> typeHint) {
  1262         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
  1263         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
  1264         // are being executed in a long-running program, we move the code and their associated dynamic call sites
  1265         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
  1266         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
  1269     /**
  1270      * Checking whether a script object is an instance of another. Used
  1271      * in {@link ScriptFunction} for hasInstance implementation, walks
  1272      * the proto chain
  1274      * @param instance instace to check
  1275      * @return true if 'instance' is an instance of this object
  1276      */
  1277     public boolean isInstance(final ScriptObject instance) {
  1278         return false;
  1281     /**
  1282      * Flag this ScriptObject as non extensible
  1284      * @return the object after being made non extensible
  1285      */
  1286     public ScriptObject preventExtensions() {
  1287         PropertyMap oldMap = getMap();
  1289         while (true) {
  1290             final PropertyMap newMap = getMap().preventExtensions();
  1292             if (!compareAndSetMap(oldMap, newMap)) {
  1293                 oldMap = getMap();
  1294             } else {
  1295                 return this;
  1300     /**
  1301      * Check whether if an Object (not just a ScriptObject) represents JavaScript array
  1303      * @param obj object to check
  1305      * @return true if array
  1306      */
  1307     public static boolean isArray(final Object obj) {
  1308         return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
  1311     /**
  1312      * Check if this ScriptObject is an array
  1313      * @return true if array
  1314      */
  1315     public final boolean isArray() {
  1316         return (flags & IS_ARRAY) != 0;
  1319     /**
  1320      * Flag this ScriptObject as being an array
  1321      */
  1322     public final void setIsArray() {
  1323         flags |= IS_ARRAY;
  1326     /**
  1327      * Check if this ScriptObject is an {@code arguments} vector
  1328      * @return true if arguments vector
  1329      */
  1330     public final boolean isArguments() {
  1331         return (flags & IS_ARGUMENTS) != 0;
  1334     /**
  1335      * Flag this ScriptObject as being an {@code arguments} vector
  1336      */
  1337     public final void setIsArguments() {
  1338         flags |= IS_ARGUMENTS;
  1341     /**
  1342      * Check if this object is a prototype
  1344      * @return {@code true} if is prototype
  1345      */
  1346     public final boolean isPrototype() {
  1347         return (flags & IS_PROTOTYPE) != 0;
  1350     /**
  1351      * Flag this object as having a prototype.
  1352      */
  1353     public final void setIsPrototype() {
  1354         if (proto != null && !isPrototype()) {
  1355             proto.addPropertyListener(this);
  1357         flags |= IS_PROTOTYPE;
  1360     /**
  1361      * Check if this object has non-writable length property
  1363      * @return {@code true} if 'length' property is non-writable
  1364      */
  1365     public final boolean isLengthNotWritable() {
  1366         return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
  1369     /**
  1370      * Flag this object as having non-writable length property
  1371      */
  1372     public void setIsLengthNotWritable() {
  1373         flags |= IS_LENGTH_NOT_WRITABLE;
  1376     /**
  1377      * Get the {@link ArrayData} for this ScriptObject if it is an array
  1378      * @return array data
  1379      */
  1380     public final ArrayData getArray() {
  1381         return arrayData;
  1384     /**
  1385      * Set the {@link ArrayData} for this ScriptObject if it is to be an array
  1386      * @param arrayData the array data
  1387      */
  1388     public final void setArray(final ArrayData arrayData) {
  1389         this.arrayData = arrayData;
  1392     /**
  1393      * Check if this ScriptObject is extensible
  1394      * @return true if extensible
  1395      */
  1396     public boolean isExtensible() {
  1397         return getMap().isExtensible();
  1400     /**
  1401      * ECMAScript 15.2.3.8 - seal implementation
  1402      * @return the sealed ScriptObject
  1403      */
  1404     public ScriptObject seal() {
  1405         PropertyMap oldMap = getMap();
  1407         while (true) {
  1408             final PropertyMap newMap = getMap().seal();
  1410             if (!compareAndSetMap(oldMap, newMap)) {
  1411                 oldMap = getMap();
  1412             } else {
  1413                 setArray(ArrayData.seal(getArray()));
  1414                 return this;
  1419     /**
  1420      * Check whether this ScriptObject is sealed
  1421      * @return true if sealed
  1422      */
  1423     public boolean isSealed() {
  1424         return getMap().isSealed();
  1427     /**
  1428      * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
  1429      * @return the frozen ScriptObject
  1430      */
  1431     public ScriptObject freeze() {
  1432         PropertyMap oldMap = getMap();
  1434         while (true) {
  1435             final PropertyMap newMap = getMap().freeze();
  1437             if (!compareAndSetMap(oldMap, newMap)) {
  1438                 oldMap = getMap();
  1439             } else {
  1440                 setArray(ArrayData.freeze(getArray()));
  1441                 return this;
  1446     /**
  1447      * Check whether this ScriptObject is frozen
  1448      * @return true if frozen
  1449      */
  1450     public boolean isFrozen() {
  1451         return getMap().isFrozen();
  1455     /**
  1456      * Flag this ScriptObject as scope
  1457      */
  1458     public final void setIsScope() {
  1459         if (Context.DEBUG) {
  1460             scopeCount++;
  1462         flags |= IS_SCOPE;
  1465     /**
  1466      * Check whether this ScriptObject is scope
  1467      * @return true if scope
  1468      */
  1469     public final boolean isScope() {
  1470         return (flags & IS_SCOPE) != 0;
  1473     /**
  1474      * Clears the properties from a ScriptObject
  1475      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1476      */
  1477     public void clear() {
  1478         final boolean strict = isStrictContext();
  1479         final Iterator<String> iter = propertyIterator();
  1480         while (iter.hasNext()) {
  1481             delete(iter.next(), strict);
  1485     /**
  1486      * Checks if a property with a given key is present in a ScriptObject
  1487      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1489      * @param key the key to check for
  1490      * @return true if a property with the given key exists, false otherwise
  1491      */
  1492     public boolean containsKey(final Object key) {
  1493         return has(key);
  1496     /**
  1497      * Checks if a property with a given value is present in a ScriptObject
  1498      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1500      * @param value value to check for
  1501      * @return true if a property with the given value exists, false otherwise
  1502      */
  1503     public boolean containsValue(final Object value) {
  1504         final Iterator<Object> iter = valueIterator();
  1505         while (iter.hasNext()) {
  1506             if (iter.next().equals(value)) {
  1507                 return true;
  1510         return false;
  1513     /**
  1514      * Returns the set of {@literal <property, value>} entries that make up this
  1515      * ScriptObject's properties
  1516      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1518      * @return an entry set of all the properties in this object
  1519      */
  1520     public Set<Map.Entry<Object, Object>> entrySet() {
  1521         final Iterator<String> iter = propertyIterator();
  1522         final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
  1523         while (iter.hasNext()) {
  1524             final Object key = iter.next();
  1525             entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
  1527         return Collections.unmodifiableSet(entries);
  1530     /**
  1531      * Check whether a ScriptObject contains no properties
  1532      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1534      * @return true if object has no properties
  1535      */
  1536     public boolean isEmpty() {
  1537         return !propertyIterator().hasNext();
  1540     /**
  1541      * Return the set of keys (property names) for all properties
  1542      * in this ScriptObject
  1543      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1545      * @return keySet of this ScriptObject
  1546      */
  1547     public Set<Object> keySet() {
  1548         final Iterator<String> iter = propertyIterator();
  1549         final Set<Object> keySet = new HashSet<>();
  1550         while (iter.hasNext()) {
  1551             keySet.add(iter.next());
  1553         return Collections.unmodifiableSet(keySet);
  1556     /**
  1557      * Put a property in the ScriptObject
  1558      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1560      * @param key property key
  1561      * @param value property value
  1562      * @return oldValue if property with same key existed already
  1563      */
  1564     public Object put(final Object key, final Object value) {
  1565         final Object oldValue = get(key);
  1566         set(key, value, isStrictContext());
  1567         return oldValue;
  1570     /**
  1571      * Put several properties in the ScriptObject given a mapping
  1572      * of their keys to their values
  1573      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1575      * @param otherMap a {@literal <key,value>} map of properties to add
  1576      */
  1577     public void putAll(final Map<?, ?> otherMap) {
  1578         final boolean strict = isStrictContext();
  1579         for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
  1580             set(entry.getKey(), entry.getValue(), strict);
  1584     /**
  1585      * Remove a property from the ScriptObject.
  1586      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1588      * @param key the key of the property
  1589      * @return the oldValue of the removed property
  1590      */
  1591     public Object remove(final Object key) {
  1592         final Object oldValue = get(key);
  1593         delete(key, isStrictContext());
  1594         return oldValue;
  1597     /**
  1598      * Delete a property from the ScriptObject.
  1599      * (to help ScriptObjectMirror implementation)
  1601      * @param key the key of the property
  1602      * @return if the delete was successful or not
  1603      */
  1604     public boolean delete(final Object key) {
  1605         return delete(key, isStrictContext());
  1608     /**
  1609      * Return the size of the ScriptObject - i.e. the number of properties
  1610      * it contains
  1611      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1613      * @return number of properties in ScriptObject
  1614      */
  1615     public int size() {
  1616         int n = 0;
  1617         for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
  1618             n++;
  1620         return n;
  1623     /**
  1624      * Return the values of the properties in the ScriptObject
  1625      * (java.util.Map-like method to help ScriptObjectMirror implementation)
  1627      * @return collection of values for the properties in this ScriptObject
  1628      */
  1629     public Collection<Object> values() {
  1630         final List<Object>     values = new ArrayList<>(size());
  1631         final Iterator<Object> iter   = valueIterator();
  1632         while (iter.hasNext()) {
  1633             values.add(iter.next());
  1635         return Collections.unmodifiableList(values);
  1638     /**
  1639      * Lookup method that, given a CallSiteDescriptor, looks up the target
  1640      * MethodHandle and creates a GuardedInvocation
  1641      * with the appropriate guard(s).
  1643      * @param desc call site descriptor
  1644      * @param request the link request
  1646      * @return GuardedInvocation for the callsite
  1647      */
  1648     public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
  1649         final int c = desc.getNameTokenCount();
  1650         // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
  1651         // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
  1652         // care about them, and just link to whatever is the first operation.
  1653         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
  1654         // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
  1655         // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
  1656         // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
  1657         // operation has an associated name or not.
  1658         switch (operator) {
  1659         case "getProp":
  1660         case "getElem":
  1661         case "getMethod":
  1662             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
  1663         case "setProp":
  1664         case "setElem":
  1665             return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
  1666         case "call":
  1667             return findCallMethod(desc, request);
  1668         case "new":
  1669             return findNewMethod(desc);
  1670         case "callMethod":
  1671             return findCallMethodMethod(desc, request);
  1672         default:
  1673             return null;
  1677     /**
  1678      * Find the appropriate New method for an invoke dynamic call.
  1680      * @param desc The invoke dynamic call site descriptor.
  1682      * @return GuardedInvocation to be invoked at call site.
  1683      */
  1684     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
  1685         return notAFunction();
  1688     /**
  1689      * Find the appropriate CALL method for an invoke dynamic call.
  1690      * This generates "not a function" always
  1692      * @param desc    the call site descriptor.
  1693      * @param request the link request
  1695      * @return GuardedInvocation to be invoed at call site.
  1696      */
  1697     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1698         return notAFunction();
  1701     private GuardedInvocation notAFunction() {
  1702         throw typeError("not.a.function", ScriptRuntime.safeToString(this));
  1705     /**
  1706      * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
  1707      * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
  1708      * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
  1709      * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
  1711      * @param desc    the call site descriptor.
  1712      * @param request the link request
  1714      * @return GuardedInvocation to be invoked at call site.
  1715      */
  1716     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1717         // R(P0, P1, ...)
  1718         final MethodType callType = desc.getMethodType();
  1719         // use type Object(P0) for the getter
  1720         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
  1721         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
  1723         // Object(P0) => Object(P0, P1, ...)
  1724         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
  1725         // R(Object, P0, P1, ...)
  1726         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
  1727         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
  1728         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
  1731     /**
  1732      * Find the appropriate GET method for an invoke dynamic call.
  1734      * @param desc     the call site descriptor
  1735      * @param request  the link request
  1736      * @param operator operator for get: getProp, getMethod, getElem etc
  1738      * @return GuardedInvocation to be invoked at call site.
  1739      */
  1740     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
  1741         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1742         final FindProperty find = findProperty(name, true);
  1744         MethodHandle methodHandle;
  1746         if (find == null) {
  1747             if ("getProp".equals(operator)) {
  1748                 return noSuchProperty(desc, request);
  1749             } else if ("getMethod".equals(operator)) {
  1750                 return noSuchMethod(desc, request);
  1751             } else if ("getElem".equals(operator)) {
  1752                 return createEmptyGetter(desc, name);
  1754             throw new AssertionError(); // never invoked with any other operation
  1757         if (request.isCallSiteUnstable()) {
  1758             return findMegaMorphicGetMethod(desc, name);
  1761         final Class<?> returnType = desc.getMethodType().returnType();
  1762         final Property property = find.getProperty();
  1763         methodHandle = find.getGetter(returnType);
  1765         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
  1766         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
  1768         if (methodHandle != null) {
  1769             assert methodHandle.type().returnType().equals(returnType);
  1770             if (find.isSelf()) {
  1771                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
  1772                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
  1775             final ScriptObject prototype = find.getOwner();
  1777             if (!property.hasGetterFunction(prototype)) {
  1778                 methodHandle = bindTo(methodHandle, prototype);
  1780             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
  1783         assert !NashornCallSiteDescriptor.isFastScope(desc);
  1784         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
  1787     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
  1788         final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
  1789         final GuardedInvocation inv = findGetIndexMethod(mhType);
  1791         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1794     /**
  1795      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1797      * @param desc    the call site descriptor
  1798      * @param request the link request
  1800      * @return GuardedInvocation to be invoked at call site.
  1801      */
  1802     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1803         return findGetIndexMethod(desc.getMethodType());
  1806     /**
  1807      * Find the appropriate GETINDEX method for an invoke dynamic call.
  1809      * @param callType the call site method type
  1810      * @return GuardedInvocation to be invoked at call site.
  1811      */
  1812     private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
  1813         final Class<?> returnClass = callType.returnType();
  1814         final Class<?> keyClass    = callType.parameterType(1);
  1816         String name = "get";
  1817         if (returnClass.isPrimitive()) {
  1818             //turn e.g. get with a double into getDouble
  1819             final String returnTypeName = returnClass.getName();
  1820             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
  1823         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
  1826     private static MethodHandle getScriptObjectGuard(final MethodType type) {
  1827         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
  1830     /**
  1831      * Find the appropriate SET method for an invoke dynamic call.
  1833      * @param desc    the call site descriptor
  1834      * @param request the link request
  1836      * @return GuardedInvocation to be invoked at call site.
  1837      */
  1838     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1839         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1840         if(request.isCallSiteUnstable()) {
  1841             return findMegaMorphicSetMethod(desc, name);
  1844         final boolean scope = isScope();
  1845         /*
  1846          * If doing property set on a scope object, we should stop proto search on the first
  1847          * non-scope object. Without this, for example, when assigning "toString" on global scope,
  1848          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
  1850          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
  1851          */
  1852         FindProperty find = findProperty(name, true, scope, this);
  1853         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
  1854         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
  1855             // We should still check if inherited data property is not writable
  1856             if (isExtensible() && !find.getProperty().isWritable()) {
  1857                 return createEmptySetMethod(desc, "property.not.writable", false);
  1859             // Otherwise, forget the found property
  1860             find = null;
  1863         if (find != null) {
  1864             if(!find.getProperty().isWritable()) {
  1865                 // Existing, non-writable property
  1866                 return createEmptySetMethod(desc, "property.not.writable", true);
  1868         } else if (!isExtensible()) {
  1869             // Non-existing property on a non-extensible object
  1870             return createEmptySetMethod(desc, "object.non.extensible", false);
  1873         return new SetMethodCreator(this, find, desc).createGuardedInvocation();
  1876     private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
  1877         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  1878         if (NashornCallSiteDescriptor.isStrict(desc)) {
  1879                throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
  1881            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
  1882            final PropertyMap myMap = getMap();
  1883            return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
  1886     @SuppressWarnings("unused")
  1887     private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
  1888         final ScriptObject obj = (ScriptObject)self;
  1889         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1890         if (!obj.isExtensible()) {
  1891             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1892         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1893             setter.invokeExact(self, value);
  1894         } else {
  1895             obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1899     @SuppressWarnings("unused")
  1900     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1901         final ScriptObject obj = (ScriptObject)self;
  1902         if (obj.trySetSpill(desc, oldMap, newMap, value)) {
  1903             obj.spill[index] = value;
  1907     private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
  1908         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1909         if (!isExtensible() && isStrict) {
  1910             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
  1911         } else if (compareAndSetMap(oldMap, newMap)) {
  1912             return true;
  1913         } else {
  1914             set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
  1915             return false;
  1919     @SuppressWarnings("unused")
  1920     private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
  1921         final ScriptObject obj      = (ScriptObject)self;
  1922         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1924         if (!obj.isExtensible()) {
  1925             if (isStrict) {
  1926                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1928         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1929             obj.spill = new Object[SPILL_RATE];
  1930             obj.spill[index] = value;
  1931         } else {
  1932             obj.set(desc.getNameToken(2), value, isStrict);
  1936     @SuppressWarnings("unused")
  1937     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) {
  1938         final ScriptObject obj      = (ScriptObject)self;
  1939         final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
  1941         if (!obj.isExtensible()) {
  1942             if (isStrict) {
  1943                 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
  1945         } else if (obj.compareAndSetMap(oldMap, newMap)) {
  1946             final int oldLength = obj.spill.length;
  1947             final Object[] newSpill = new Object[newLength];
  1948             System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
  1949             obj.spill = newSpill;
  1950             obj.spill[index] = value;
  1951         } else {
  1952             obj.set(desc.getNameToken(2), value, isStrict);
  1956     private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
  1957         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
  1958         final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
  1959         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
  1962     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
  1963         return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
  1966     /**
  1967      * Find the appropriate SETINDEX method for an invoke dynamic call.
  1969      * @param callType the method type at the call site
  1970      * @param isStrict are we in strict mode?
  1972      * @return GuardedInvocation to be invoked at call site.
  1973      */
  1974     private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
  1975         assert callType.parameterCount() == 3;
  1977         final Class<?>   keyClass   = callType.parameterType(1);
  1978         final Class<?>   valueClass = callType.parameterType(2);
  1980         MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
  1981         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
  1983         return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
  1986     /**
  1987      * Fall back if a function property is not found.
  1988      * @param desc The call site descriptor
  1989      * @param request the link request
  1990      * @return GuardedInvocation to be invoked at call site.
  1991      */
  1992     public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
  1993         final String       name      = desc.getNameToken(2);
  1994         final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
  1995         final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
  1997         if (find == null) {
  1998             return noSuchProperty(desc, request);
  2001         final Object value = getObjectValue(find);
  2002         if (! (value instanceof ScriptFunction)) {
  2003             return createEmptyGetter(desc, name);
  2006         final ScriptFunction func = (ScriptFunction)value;
  2007         final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
  2008         // TODO: It'd be awesome if we could bind "name" without binding "this".
  2009         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
  2010                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
  2011                 null, NashornGuards.getMapGuard(getMap()));
  2014     /**
  2015      * Fall back if a property is not found.
  2016      * @param desc the call site descriptor.
  2017      * @param request the link request
  2018      * @return GuardedInvocation to be invoked at call site.
  2019      */
  2020     public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
  2021         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
  2022         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  2023         final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
  2025         if (find != null) {
  2026             final Object   value        = getObjectValue(find);
  2027             ScriptFunction func         = null;
  2028             MethodHandle   methodHandle = null;
  2030             if (value instanceof ScriptFunction) {
  2031                 func = (ScriptFunction)value;
  2032                 methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
  2035             if (methodHandle != null) {
  2036                 if (scopeAccess && func.isStrict()) {
  2037                     methodHandle = bindTo(methodHandle, UNDEFINED);
  2039                 return new GuardedInvocation(methodHandle,
  2040                         find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
  2041                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
  2045         if (scopeAccess) {
  2046             throw referenceError("not.defined", name);
  2049         return createEmptyGetter(desc, name);
  2051     /**
  2052      * Invoke fall back if a property is not found.
  2053      * @param name Name of property.
  2054      * @return Result from call.
  2055      */
  2056     private Object invokeNoSuchProperty(final String name) {
  2057         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
  2059         if (find != null) {
  2060             final Object func = getObjectValue(find);
  2062             if (func instanceof ScriptFunction) {
  2063                 return ScriptRuntime.apply((ScriptFunction)func, this, name);
  2067         return UNDEFINED;
  2070     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
  2071         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
  2074     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
  2075         protected T[] values;
  2076         protected final ScriptObject object;
  2077         private int index;
  2079         ScriptObjectIterator(final ScriptObject object) {
  2080             this.object = object;
  2083         protected abstract void init();
  2085         @Override
  2086         public boolean hasNext() {
  2087             if (values == null) {
  2088                 init();
  2090             return index < values.length;
  2093         @Override
  2094         public T next() {
  2095             if (values == null) {
  2096                 init();
  2098             return values[index++];
  2101         @Override
  2102         public void remove() {
  2103             throw new UnsupportedOperationException();
  2107     private static class KeyIterator extends ScriptObjectIterator<String> {
  2108         KeyIterator(final ScriptObject object) {
  2109             super(object);
  2112         @Override
  2113         protected void init() {
  2114             final Set<String> keys = new LinkedHashSet<>();
  2115             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2116                 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
  2118             this.values = keys.toArray(new String[keys.size()]);
  2122     private static class ValueIterator extends ScriptObjectIterator<Object> {
  2123         ValueIterator(final ScriptObject object) {
  2124             super(object);
  2127         @Override
  2128         protected void init() {
  2129             final ArrayList<Object> valueList = new ArrayList<>();
  2130             for (ScriptObject self = object; self != null; self = self.getProto()) {
  2131                 for (final String key : self.getOwnKeys(false)) {
  2132                     valueList.add(self.get(key));
  2135             this.values = valueList.toArray(new Object[valueList.size()]);
  2139     /**
  2140      * Add a spill property for the given key.
  2141      * @param key           Property key.
  2142      * @param propertyFlags Property flags.
  2143      * @return Added property.
  2144      */
  2145     private Property addSpillProperty(final String key, final int propertyFlags) {
  2146         int fieldCount   = getMap().getFieldCount();
  2147         int fieldMaximum = getMap().getFieldMaximum();
  2148         Property property;
  2150         if (fieldCount < fieldMaximum) {
  2151             property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
  2152             notifyPropertyAdded(this, property);
  2153             property = addOwnProperty(property);
  2154         } else {
  2155             int i = getMap().getSpillLength();
  2156             property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
  2157             notifyPropertyAdded(this, property);
  2158             property = addOwnProperty(property);
  2159             i = property.getSlot();
  2161             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
  2163             if (spill == null || newLength > spill.length) {
  2164                 final Object[] newSpill = new Object[newLength];
  2166                 if (spill != null) {
  2167                     System.arraycopy(spill, 0, newSpill, 0, spill.length);
  2170                 spill = newSpill;
  2174         return property;
  2178     /**
  2179      * Add a spill entry for the given key.
  2180      * @param key Property key.
  2181      * @return Setter method handle.
  2182      */
  2183     MethodHandle addSpill(final String key) {
  2184         final Property spillProperty = addSpillProperty(key, 0);
  2185         final Class<?> type = Object.class;
  2186         return spillProperty.getSetter(type, getMap()); //TODO specfields
  2189     /**
  2190      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2191      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2192      * creating collectors.
  2194      * @param methodHandle method handle for invoke
  2195      * @param callType     type of the call
  2197      * @return method handle with adjusted arguments
  2198      */
  2199     protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
  2200         return pairArguments(methodHandle, callType, null);
  2203     /**
  2204      * Make sure arguments are paired correctly, with respect to more parameters than declared,
  2205      * fewer parameters than declared and other things that JavaScript allows. This might involve
  2206      * creating collectors.
  2208      * Make sure arguments are paired correctly.
  2209      * @param methodHandle MethodHandle to adjust.
  2210      * @param callType     MethodType of the call site.
  2211      * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
  2212      * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
  2213      * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
  2214      * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
  2216      * @return method handle with adjusted arguments
  2217      */
  2218     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
  2220         final MethodType methodType = methodHandle.type();
  2221         if (methodType.equals(callType)) {
  2222             return methodHandle;
  2225         final int parameterCount = methodType.parameterCount();
  2226         final int callCount      = callType.parameterCount();
  2228         final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
  2229         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
  2230                 callType.parameterType(callCount - 1).isArray());
  2232         if (callCount < parameterCount) {
  2233             final int      missingArgs = parameterCount - callCount;
  2234             final Object[] fillers     = new Object[missingArgs];
  2236             Arrays.fill(fillers, UNDEFINED);
  2238             if (isCalleeVarArg) {
  2239                 fillers[missingArgs - 1] = new Object[0];
  2242             return MH.insertArguments(
  2243                 methodHandle,
  2244                 parameterCount - missingArgs,
  2245                 fillers);
  2248         if (isCalleeVarArg) {
  2249             return isCallerVarArg ?
  2250                 methodHandle :
  2251                 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
  2254         if (isCallerVarArg) {
  2255             final int spreadArgs = parameterCount - callCount + 1;
  2256             return MH.filterArguments(
  2257                 MH.asSpreader(
  2258                     methodHandle,
  2259                     Object[].class,
  2260                     spreadArgs),
  2261                 callCount - 1,
  2262                 MH.insertArguments(
  2263                     TRUNCATINGFILTER,
  2264                     0,
  2265                     spreadArgs)
  2266                 );
  2269         if (callCount > parameterCount) {
  2270             final int discardedArgs = callCount - parameterCount;
  2272             final Class<?>[] discards = new Class<?>[discardedArgs];
  2273             Arrays.fill(discards, Object.class);
  2275             return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
  2278         return methodHandle;
  2281     @SuppressWarnings("unused")
  2282     private static Object[] truncatingFilter(final int n, final Object[] array) {
  2283         final int length = array == null ? 0 : array.length;
  2284         if (n == length) {
  2285             return array == null ? new Object[0] : array;
  2288         final Object[] newArray = new Object[n];
  2290         if (array != null) {
  2291             for (int i = 0; i < n && i < length; i++) {
  2292                 newArray[i] = array[i];
  2296         if (length < n) {
  2297             final Object fill = UNDEFINED;
  2299             for (int i = length; i < n; i++) {
  2300                 newArray[i] = fill;
  2304         return newArray;
  2307     /**
  2308       * Numeric length setter for length property
  2310       * @param newLength new length to set
  2311       */
  2312     public final void setLength(final long newLength) {
  2313        final long arrayLength = getArray().length();
  2314        if (newLength == arrayLength) {
  2315            return;
  2318        final boolean isStrict = isStrictContext();
  2320        if (newLength > arrayLength) {
  2321            setArray(getArray().ensure(newLength - 1));
  2322             if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
  2323                setArray(getArray().delete(arrayLength, (newLength - 1)));
  2325            return;
  2328        if (newLength < arrayLength) {
  2329            setArray(getArray().shrink(newLength));
  2330            getArray().setLength(newLength);
  2334     private int getInt(final int index, final String key) {
  2335         if (ArrayIndex.isValidArrayIndex(index)) {
  2336              for (ScriptObject object = this; ; ) {
  2337                 final FindProperty find = object.findProperty(key, false, false, this);
  2339                 if (find != null) {
  2340                     return getIntValue(find);
  2343                 if ((object = object.getProto()) == null) {
  2344                     break;
  2347                 final ArrayData array = object.getArray();
  2349                 if (array.has(index)) {
  2350                     return array.getInt(index);
  2353         } else {
  2354             final FindProperty find = findProperty(key, true);
  2356             if (find != null) {
  2357                 return getIntValue(find);
  2361         return JSType.toInt32(invokeNoSuchProperty(key));
  2364     @Override
  2365     public int getInt(final Object key) {
  2366         final int index = ArrayIndex.getArrayIndex(key);
  2367         final ArrayData array = getArray();
  2369         if (array.has(index)) {
  2370             return array.getInt(index);
  2373         return getInt(index, JSType.toString(key));
  2376     @Override
  2377     public int getInt(final double key) {
  2378         final int index = ArrayIndex.getArrayIndex(key);
  2379         final ArrayData array = getArray();
  2381         if (array.has(index)) {
  2382             return array.getInt(index);
  2385         return getInt(index, JSType.toString(key));
  2388     @Override
  2389     public int getInt(final long key) {
  2390         final int index = ArrayIndex.getArrayIndex(key);
  2391         final ArrayData array = getArray();
  2393         if (array.has(index)) {
  2394             return array.getInt(index);
  2397         return getInt(index, JSType.toString(key));
  2400     @Override
  2401     public int getInt(final int key) {
  2402         final ArrayData array = getArray();
  2404         if (array.has(key)) {
  2405             return array.getInt(key);
  2408         return getInt(key, JSType.toString(key));
  2411     private long getLong(final int index, final String key) {
  2412         if (ArrayIndex.isValidArrayIndex(index)) {
  2413             for (ScriptObject object = this; ; ) {
  2414                 final FindProperty find = object.findProperty(key, false, false, this);
  2416                 if (find != null) {
  2417                     return getLongValue(find);
  2420                 if ((object = object.getProto()) == null) {
  2421                     break;
  2424                 final ArrayData array = object.getArray();
  2426                 if (array.has(index)) {
  2427                     return array.getLong(index);
  2430         } else {
  2431             final FindProperty find = findProperty(key, true);
  2433             if (find != null) {
  2434                 return getLongValue(find);
  2438         return JSType.toLong(invokeNoSuchProperty(key));
  2441     @Override
  2442     public long getLong(final Object key) {
  2443         final int index = ArrayIndex.getArrayIndex(key);
  2444         final ArrayData array = getArray();
  2446         if (array.has(index)) {
  2447             return array.getLong(index);
  2450         return getLong(index, JSType.toString(key));
  2453     @Override
  2454     public long getLong(final double key) {
  2455         final int index = ArrayIndex.getArrayIndex(key);
  2456         final ArrayData array = getArray();
  2458         if (array.has(index)) {
  2459             return array.getLong(index);
  2462         return getLong(index, JSType.toString(key));
  2465     @Override
  2466     public long getLong(final long key) {
  2467         final int index = ArrayIndex.getArrayIndex(key);
  2468         final ArrayData array = getArray();
  2470         if (array.has(index)) {
  2471             return array.getLong(index);
  2474         return getLong(index, JSType.toString(key));
  2477     @Override
  2478     public long getLong(final int key) {
  2479         final ArrayData array = getArray();
  2481         if (array.has(key)) {
  2482             return array.getLong(key);
  2485         return getLong(key, JSType.toString(key));
  2488     private double getDouble(final int index, final String key) {
  2489         if (ArrayIndex.isValidArrayIndex(index)) {
  2490             for (ScriptObject object = this; ; ) {
  2491                 final FindProperty find = object.findProperty(key, false, false, this);
  2493                 if (find != null) {
  2494                     return getDoubleValue(find);
  2497                 if ((object = object.getProto()) == null) {
  2498                     break;
  2501                 final ArrayData array = object.getArray();
  2503                 if (array.has(index)) {
  2504                     return array.getDouble(index);
  2507         } else {
  2508             final FindProperty find = findProperty(key, true);
  2510             if (find != null) {
  2511                 return getDoubleValue(find);
  2515         return JSType.toNumber(invokeNoSuchProperty(key));
  2518     @Override
  2519     public double getDouble(final Object key) {
  2520         final int index = ArrayIndex.getArrayIndex(key);
  2521         final ArrayData array = getArray();
  2523         if (array.has(index)) {
  2524             return array.getDouble(index);
  2527         return getDouble(index, JSType.toString(key));
  2530     @Override
  2531     public double getDouble(final double key) {
  2532         final int index = ArrayIndex.getArrayIndex(key);
  2533         final ArrayData array = getArray();
  2535         if (array.has(index)) {
  2536             return array.getDouble(index);
  2539         return getDouble(index, JSType.toString(key));
  2542     @Override
  2543     public double getDouble(final long key) {
  2544         final int index = ArrayIndex.getArrayIndex(key);
  2545         final ArrayData array = getArray();
  2547         if (array.has(index)) {
  2548             return array.getDouble(index);
  2551         return getDouble(index, JSType.toString(key));
  2554     @Override
  2555     public double getDouble(final int key) {
  2556         final ArrayData array = getArray();
  2558         if (array.has(key)) {
  2559             return array.getDouble(key);
  2562         return getDouble(key, JSType.toString(key));
  2565     private Object get(final int index, final String key) {
  2566         if (ArrayIndex.isValidArrayIndex(index)) {
  2567             for (ScriptObject object = this; ; ) {
  2568                 final FindProperty find = object.findProperty(key, false, false, this);
  2570                 if (find != null) {
  2571                     return getObjectValue(find);
  2574                 if ((object = object.getProto()) == null) {
  2575                     break;
  2578                 final ArrayData array = object.getArray();
  2580                 if (array.has(index)) {
  2581                     return array.getObject(index);
  2584         } else {
  2585             final FindProperty find = findProperty(key, true);
  2587             if (find != null) {
  2588                 return getObjectValue(find);
  2592         return invokeNoSuchProperty(key);
  2595     @Override
  2596     public Object get(final Object key) {
  2597         final int index = ArrayIndex.getArrayIndex(key);
  2598         final ArrayData array = getArray();
  2600         if (array.has(index)) {
  2601             return array.getObject(index);
  2604         return get(index, JSType.toString(key));
  2607     @Override
  2608     public Object get(final double key) {
  2609         final int index = ArrayIndex.getArrayIndex(key);
  2610         final ArrayData array = getArray();
  2612         if (array.has(index)) {
  2613             return array.getObject(index);
  2616         return get(index, JSType.toString(key));
  2619     @Override
  2620     public Object get(final long key) {
  2621         final int index = ArrayIndex.getArrayIndex(key);
  2622         final ArrayData array = getArray();
  2624         if (array.has(index)) {
  2625             return array.getObject(index);
  2628         return get(index, JSType.toString(key));
  2631     @Override
  2632     public Object get(final int key) {
  2633         final ArrayData array = getArray();
  2635         if (array.has(key)) {
  2636             return array.getObject(key);
  2639         return get(key, JSType.toString(key));
  2642     /**
  2643      * Handle when an array doesn't have a slot - possibly grow and/or convert array.
  2645      * @param index  key as index
  2646      * @param value  element value
  2647      * @param strict are we in strict mode
  2648      */
  2649     private void doesNotHave(final int index, final Object value, final boolean strict) {
  2650         final long oldLength = getArray().length();
  2651         final long longIndex = index & JSType.MAX_UINT;
  2653         if (!getArray().has(index)) {
  2654             final String key = JSType.toString(longIndex);
  2655             final FindProperty find = findProperty(key, true);
  2657             if (find != null) {
  2658                 setObject(find, strict, key, value);
  2659                 return;
  2663         if (longIndex >= oldLength) {
  2664             if (!isExtensible()) {
  2665                 if (strict) {
  2666                     throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
  2668                 return;
  2670             setArray(getArray().ensure(longIndex));
  2673         if (value instanceof Integer) {
  2674             setArray(getArray().set(index, (int)value, strict));
  2675         } else if (value instanceof Long) {
  2676             setArray(getArray().set(index, (long)value, strict));
  2677         } else if (value instanceof Double) {
  2678             setArray(getArray().set(index, (double)value, strict));
  2679         } else {
  2680             setArray(getArray().set(index, value, strict));
  2683         if (longIndex > oldLength) {
  2684             ArrayData array = getArray();
  2686             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
  2687                 array = array.delete(oldLength, (longIndex - 1));
  2690             setArray(array);
  2694     /**
  2695      * This is the most generic of all Object setters. Most of the others use this in some form.
  2696      * TODO: should be further specialized
  2698      * @param find    found property
  2699      * @param strict  are we in strict mode
  2700      * @param key     property key
  2701      * @param value   property value
  2702      */
  2703     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
  2704         FindProperty f = find;
  2706         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
  2707             f = null;
  2710         if (f != null) {
  2711             if (!f.getProperty().isWritable()) {
  2712                 if (strict) {
  2713                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
  2716                 return;
  2719             f.setObjectValue(value, strict);
  2721         } else if (!isExtensible()) {
  2722             if (strict) {
  2723                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
  2725         } else {
  2726             spill(key, value);
  2730     private void spill(final String key, final Object value) {
  2731         addSpillProperty(key, 0).setObjectValue(this, this, value, false);
  2735     @Override
  2736     public void set(final Object key, final int value, final boolean strict) {
  2737         final int index = ArrayIndex.getArrayIndex(key);
  2739         if (ArrayIndex.isValidArrayIndex(index)) {
  2740             if (getArray().has(index)) {
  2741                 setArray(getArray().set(index, value, strict));
  2742             } else {
  2743                 doesNotHave(index, value, strict);
  2746             return;
  2749         set(key, JSType.toObject(value), strict);
  2752     @Override
  2753     public void set(final Object key, final long value, final boolean strict) {
  2754         final int index = ArrayIndex.getArrayIndex(key);
  2756         if (ArrayIndex.isValidArrayIndex(index)) {
  2757             if (getArray().has(index)) {
  2758                 setArray(getArray().set(index, value, strict));
  2759             } else {
  2760                 doesNotHave(index, value, strict);
  2763             return;
  2766         set(key, JSType.toObject(value), strict);
  2769     @Override
  2770     public void set(final Object key, final double value, final boolean strict) {
  2771         final int index = ArrayIndex.getArrayIndex(key);
  2773         if (ArrayIndex.isValidArrayIndex(index)) {
  2774             if (getArray().has(index)) {
  2775                 setArray(getArray().set(index, value, strict));
  2776             } else {
  2777                 doesNotHave(index, value, strict);
  2780             return;
  2783         set(key, JSType.toObject(value), strict);
  2786     @Override
  2787     public void set(final Object key, final Object value, final boolean strict) {
  2788         final int index = ArrayIndex.getArrayIndex(key);
  2790         if (ArrayIndex.isValidArrayIndex(index)) {
  2791             if (getArray().has(index)) {
  2792                 setArray(getArray().set(index, value, strict));
  2793             } else {
  2794                 doesNotHave(index, value, strict);
  2797             return;
  2800         final String       propName = JSType.toString(key);
  2801         final FindProperty find     = findProperty(propName, true);
  2803         setObject(find, strict, propName, value);
  2806     @Override
  2807     public void set(final double key, final int value, final boolean strict) {
  2808         final int index = ArrayIndex.getArrayIndex(key);
  2810         if (ArrayIndex.isValidArrayIndex(index)) {
  2811             if (getArray().has(index)) {
  2812                 setArray(getArray().set(index, value, strict));
  2813             } else {
  2814                 doesNotHave(index, value, strict);
  2817             return;
  2820         set(JSType.toObject(key), JSType.toObject(value), strict);
  2823     @Override
  2824     public void set(final double key, final long value, final boolean strict) {
  2825         final int index = ArrayIndex.getArrayIndex(key);
  2827         if (ArrayIndex.isValidArrayIndex(index)) {
  2828             if (getArray().has(index)) {
  2829                 setArray(getArray().set(index, value, strict));
  2830             } else {
  2831                 doesNotHave(index, value, strict);
  2834             return;
  2837         set(JSType.toObject(key), JSType.toObject(value), strict);
  2840     @Override
  2841     public void set(final double key, final double value, final boolean strict) {
  2842         final int index = ArrayIndex.getArrayIndex(key);
  2844         if (ArrayIndex.isValidArrayIndex(index)) {
  2845             if (getArray().has(index)) {
  2846                 setArray(getArray().set(index, value, strict));
  2847             } else {
  2848                 doesNotHave(index, value, strict);
  2851             return;
  2854         set(JSType.toObject(key), JSType.toObject(value), strict);
  2857     @Override
  2858     public void set(final double key, final Object value, final boolean strict) {
  2859         final int index = ArrayIndex.getArrayIndex(key);
  2861         if (ArrayIndex.isValidArrayIndex(index)) {
  2862             if (getArray().has(index)) {
  2863                 setArray(getArray().set(index, value, strict));
  2864             } else {
  2865                 doesNotHave(index, value, strict);
  2868             return;
  2871         set(JSType.toObject(key), value, strict);
  2874     @Override
  2875     public void set(final long key, final int value, final boolean strict) {
  2876         final int index = ArrayIndex.getArrayIndex(key);
  2878         if (ArrayIndex.isValidArrayIndex(index)) {
  2879             if (getArray().has(index)) {
  2880                 setArray(getArray().set(index, value, strict));
  2881             } else {
  2882                 doesNotHave(index, value, strict);
  2885             return;
  2888         set(JSType.toObject(key), JSType.toObject(value), strict);
  2891     @Override
  2892     public void set(final long key, final long value, final boolean strict) {
  2893         final int index = ArrayIndex.getArrayIndex(key);
  2895         if (ArrayIndex.isValidArrayIndex(index)) {
  2896             if (getArray().has(index)) {
  2897                 setArray(getArray().set(index, value, strict));
  2898             } else {
  2899                 doesNotHave(index, value, strict);
  2902             return;
  2905         set(JSType.toObject(key), JSType.toObject(value), strict);
  2908     @Override
  2909     public void set(final long key, final double value, final boolean strict) {
  2910         final int index = ArrayIndex.getArrayIndex(key);
  2912         if (ArrayIndex.isValidArrayIndex(index)) {
  2913             if (getArray().has(index)) {
  2914                 setArray(getArray().set(index, value, strict));
  2915             } else {
  2916                 doesNotHave(index, value, strict);
  2919             return;
  2922         set(JSType.toObject(key), JSType.toObject(value), strict);
  2925     @Override
  2926     public void set(final long key, final Object value, final boolean strict) {
  2927         final int index = ArrayIndex.getArrayIndex(key);
  2929         if (ArrayIndex.isValidArrayIndex(index)) {
  2930             if (getArray().has(index)) {
  2931                 setArray(getArray().set(index, value, strict));
  2932             } else {
  2933                 doesNotHave(index, value, strict);
  2936             return;
  2939         set(JSType.toObject(key), value, strict);
  2942     @Override
  2943     public void set(final int key, final int value, final boolean strict) {
  2944         final int index = ArrayIndex.getArrayIndex(key);
  2946         if (ArrayIndex.isValidArrayIndex(index)) {
  2947             if (getArray().has(index)) {
  2948                 setArray(getArray().set(index, value, strict));
  2949             } else {
  2950                 doesNotHave(index, value, strict);
  2953             return;
  2956         set(JSType.toObject(key), JSType.toObject(value), strict);
  2959     @Override
  2960     public void set(final int key, final long value, final boolean strict) {
  2961         final int index = ArrayIndex.getArrayIndex(key);
  2963         if (ArrayIndex.isValidArrayIndex(index)) {
  2964             if (getArray().has(index)) {
  2965                 setArray(getArray().set(index, value, strict));
  2966             } else {
  2967                 doesNotHave(index, value, strict);
  2970             return;
  2973         set(JSType.toObject(key), JSType.toObject(value), strict);
  2976     @Override
  2977     public void set(final int key, final double value, final boolean strict) {
  2978         final int index = ArrayIndex.getArrayIndex(key);
  2980         if (ArrayIndex.isValidArrayIndex(index)) {
  2981             if (getArray().has(index)) {
  2982                 setArray(getArray().set(index, value, strict));
  2983             } else {
  2984                 doesNotHave(index, value, strict);
  2987             return;
  2990         set(JSType.toObject(key), JSType.toObject(value), strict);
  2993     @Override
  2994     public void set(final int key, final Object value, final boolean strict) {
  2995         final int index = ArrayIndex.getArrayIndex(key);
  2997         if (ArrayIndex.isValidArrayIndex(index)) {
  2998             if (getArray().has(index)) {
  2999                 setArray(getArray().set(index, value, strict));
  3000             } else {
  3001                 doesNotHave(index, value, strict);
  3004             return;
  3007         set(JSType.toObject(key), value, strict);
  3010     @Override
  3011     public boolean has(final Object key) {
  3012         final int index = ArrayIndex.getArrayIndex(key);
  3014         if (ArrayIndex.isValidArrayIndex(index)) {
  3015             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3016                 if (self.getArray().has(index)) {
  3017                     return true;
  3022         final FindProperty find = findProperty(JSType.toString(key), true);
  3024         return find != null;
  3027     @Override
  3028     public boolean has(final double key) {
  3029         final int index = ArrayIndex.getArrayIndex(key);
  3031         if (ArrayIndex.isValidArrayIndex(index)) {
  3032             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3033                 if (self.getArray().has(index)) {
  3034                     return true;
  3039         final FindProperty find = findProperty(JSType.toString(key), true);
  3041         return find != null;
  3044     @Override
  3045     public boolean has(final long key) {
  3046         final int index = ArrayIndex.getArrayIndex(key);
  3048         if (ArrayIndex.isValidArrayIndex(index)) {
  3049             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3050                 if (self.getArray().has(index)) {
  3051                     return true;
  3056         final FindProperty find = findProperty(JSType.toString(key), true);
  3058         return find != null;
  3061     @Override
  3062     public boolean has(final int key) {
  3063         final int index = ArrayIndex.getArrayIndex(key);
  3065         if (ArrayIndex.isValidArrayIndex(index)) {
  3066             for (ScriptObject self = this; self != null; self = self.getProto()) {
  3067                 if (self.getArray().has(index)) {
  3068                     return true;
  3073         final FindProperty find = findProperty(JSType.toString(key), true);
  3075         return find != null;
  3078     @Override
  3079     public boolean hasOwnProperty(final Object key) {
  3080         final int index = ArrayIndex.getArrayIndex(key);
  3082         if (getArray().has(index)) {
  3083             return true;
  3086         final FindProperty find = findProperty(JSType.toString(key), false);
  3088         return find != null;
  3091     @Override
  3092     public boolean hasOwnProperty(final int key) {
  3093         final int index = ArrayIndex.getArrayIndex(key);
  3095         if (getArray().has(index)) {
  3096             return true;
  3099         final FindProperty find = findProperty(JSType.toString(key), false);
  3101         return find != null;
  3104     @Override
  3105     public boolean hasOwnProperty(final long key) {
  3106         final int index = ArrayIndex.getArrayIndex(key);
  3108         if (getArray().has(index)) {
  3109             return true;
  3112         final FindProperty find = findProperty(JSType.toString(key), false);
  3114         return find != null;
  3117     @Override
  3118     public boolean hasOwnProperty(final double key) {
  3119         final int index = ArrayIndex.getArrayIndex(key);
  3121         if (getArray().has(index)) {
  3122             return true;
  3125         final FindProperty find = findProperty(JSType.toString(key), false);
  3127         return find != null;
  3130     @Override
  3131     public boolean delete(final int key, final boolean strict) {
  3132         final int index = ArrayIndex.getArrayIndex(key);
  3133         final ArrayData array = getArray();
  3135         if (array.has(index)) {
  3136             if (array.canDelete(index, strict)) {
  3137                 setArray(array.delete(index));
  3138                 return true;
  3140             return false;
  3143         return deleteObject(JSType.toObject(key), strict);
  3146     @Override
  3147     public boolean delete(final long key, final boolean strict) {
  3148         final int index = ArrayIndex.getArrayIndex(key);
  3149         final ArrayData array = getArray();
  3151         if (array.has(index)) {
  3152             if (array.canDelete(index, strict)) {
  3153                 setArray(array.delete(index));
  3154                 return true;
  3156             return false;
  3159         return deleteObject(JSType.toObject(key), strict);
  3162     @Override
  3163     public boolean delete(final double key, final boolean strict) {
  3164         final int index = ArrayIndex.getArrayIndex(key);
  3165         final ArrayData array = getArray();
  3167         if (array.has(index)) {
  3168             if (array.canDelete(index, strict)) {
  3169                 setArray(array.delete(index));
  3170                 return true;
  3172             return false;
  3175         return deleteObject(JSType.toObject(key), strict);
  3178     @Override
  3179     public boolean delete(final Object key, final boolean strict) {
  3180         final int index = ArrayIndex.getArrayIndex(key);
  3181         final ArrayData array = getArray();
  3183         if (array.has(index)) {
  3184             if (array.canDelete(index, strict)) {
  3185                 setArray(array.delete(index));
  3186                 return true;
  3188             return false;
  3191         return deleteObject(key, strict);
  3194     private boolean deleteObject(final Object key, final boolean strict) {
  3195         final String propName = JSType.toString(key);
  3196         final FindProperty find = findProperty(propName, false);
  3198         if (find == null) {
  3199             return true;
  3202         if (!find.getProperty().isConfigurable()) {
  3203             if (strict) {
  3204                 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
  3206             return false;
  3209         final Property prop = find.getProperty();
  3210         notifyPropertyDeleted(this, prop);
  3211         deleteOwnProperty(prop);
  3213         return true;
  3216     /**
  3217      * Make a new UserAccessorProperty property. getter and setter functions are stored in
  3218      * this ScriptObject and slot values are used in property object.
  3220      * @param key the property name
  3221      * @param propertyFlags attribute flags of the property
  3222      * @param getter getter function for the property
  3223      * @param setter setter function for the property
  3224      * @return the newly created UserAccessorProperty
  3225      */
  3226     protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
  3227         final UserAccessorProperty property = getMap().newUserAccessors(key, propertyFlags);
  3228         setSpill(property.getGetterSlot(), getter);
  3229         setSpill(property.getSetterSlot(), setter);
  3231         return property;
  3234     /**
  3235      * Write a value to a spill slot
  3236      * @param slot  the slot index
  3237      * @param value the value
  3238      */
  3239     protected final void setSpill(final int slot, final Object value) {
  3240         if (spill == null) {
  3241             // create new spill.
  3242             spill = new Object[Math.max(slot + 1, SPILL_RATE)];
  3243         } else if (slot >= spill.length) {
  3244             // grow spill as needed
  3245             final Object[] newSpill = new Object[slot + 1];
  3246             System.arraycopy(spill, 0, newSpill, 0, spill.length);
  3247             spill = newSpill;
  3250         spill[slot] = value;
  3253     /**
  3254      * Get a value from a spill slot
  3255      * @param slot the slot index
  3256      * @return the value in the spill slot with the given index
  3257      */
  3258     protected Object getSpill(final int slot) {
  3259         return spill != null && slot < spill.length ? spill[slot] : null;
  3262     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
  3263         final Class<?>   own = ScriptObject.class;
  3264         final MethodType mt  = MH.type(rtype, types);
  3265         try {
  3266             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
  3267         } catch (final MethodHandleFactory.LookupException e) {
  3268             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
  3272     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3273         return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
  3276     @SuppressWarnings("unused")
  3277     private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
  3278         if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
  3279             try {
  3280                 return getter.invokeExact(where) == func;
  3281             } catch (final RuntimeException | Error e) {
  3282                 throw e;
  3283             } catch (final Throwable t) {
  3284                 throw new RuntimeException(t);
  3288         return false;
  3291     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
  3292     private static int count;
  3294     /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
  3295     private static int scopeCount;
  3297     /**
  3298      * Get number of {@code ScriptObject} instances created. If not running in debug
  3299      * mode this is always 0
  3301      * @return number of ScriptObjects created
  3302      */
  3303     public static int getCount() {
  3304         return count;
  3307     /**
  3308      * Get number of scope {@code ScriptObject} instances created. If not running in debug
  3309      * mode this is always 0
  3311      * @return number of scope ScriptObjects created
  3312      */
  3313     public static int getScopeCount() {
  3314         return scopeCount;

mercurial