ScriptObject.java revision 1315:2e50107b1738
154359Sroberto/*
254359Sroberto * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
354359Sroberto * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
482498Sroberto *
554359Sroberto * This code is free software; you can redistribute it and/or modify it
654359Sroberto * under the terms of the GNU General Public License version 2 only, as
754359Sroberto * published by the Free Software Foundation.  Oracle designates this
854359Sroberto * particular file as subject to the "Classpath" exception as provided
9106163Sroberto * by Oracle in the LICENSE file that accompanied this code.
1082498Sroberto *
1182498Sroberto * This code is distributed in the hope that it will be useful, but WITHOUT
1282498Sroberto * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13182007Sroberto * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1482498Sroberto * version 2 for more details (a copy is included in the LICENSE file that
15132451Sroberto * accompanied this code).
16182007Sroberto *
17182007Sroberto * You should have received a copy of the GNU General Public License version
18182007Sroberto * 2 along with this work; if not, write to the Free Software Foundation,
19182007Sroberto * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20132451Sroberto *
21132451Sroberto * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2254359Sroberto * or visit www.oracle.com if you need additional information or have any
2354359Sroberto * questions.
2454359Sroberto */
2554359Sroberto
2654359Srobertopackage jdk.nashorn.internal.runtime;
2754359Sroberto
2854359Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
29200576Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
30200576Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
31200576Srobertoimport static jdk.nashorn.internal.lookup.Lookup.MH;
32106163Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
33200576Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
34200576Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
35200576Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
3654359Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
3754359Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
38200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
39200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
40200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.SET;
41200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
42200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
43200576Srobertoimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
44200576Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
4554359Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
4654359Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
4754359Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
4854359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag;
4954359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag;
5054359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
5154359Sroberto
5254359Srobertoimport java.lang.invoke.MethodHandle;
5354359Srobertoimport java.lang.invoke.MethodHandles;
5454359Srobertoimport java.lang.invoke.MethodType;
5554359Srobertoimport java.lang.invoke.SwitchPoint;
5654359Srobertoimport java.util.AbstractMap;
5754359Srobertoimport java.util.ArrayList;
5854359Srobertoimport java.util.Arrays;
5954359Srobertoimport java.util.Collection;
6054359Srobertoimport java.util.Collections;
6154359Srobertoimport java.util.HashSet;
6254359Srobertoimport java.util.Iterator;
6354359Srobertoimport java.util.LinkedHashSet;
6454359Srobertoimport java.util.List;
6554359Srobertoimport java.util.Map;
6654359Srobertoimport java.util.Set;
6754359Srobertoimport jdk.internal.dynalink.CallSiteDescriptor;
6854359Srobertoimport jdk.internal.dynalink.linker.GuardedInvocation;
6954359Srobertoimport jdk.internal.dynalink.linker.LinkRequest;
7054359Srobertoimport jdk.internal.dynalink.support.CallSiteDescriptorFactory;
7154359Srobertoimport jdk.nashorn.internal.codegen.CompilerConstants.Call;
7254359Srobertoimport jdk.nashorn.internal.codegen.ObjectClassGenerator;
7354359Srobertoimport jdk.nashorn.internal.codegen.types.Type;
7454359Srobertoimport jdk.nashorn.internal.lookup.Lookup;
7554359Srobertoimport jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
7654359Srobertoimport jdk.nashorn.internal.objects.DataPropertyDescriptor;
7754359Srobertoimport jdk.nashorn.internal.objects.Global;
7854359Srobertoimport jdk.nashorn.internal.objects.NativeArray;
7954359Srobertoimport jdk.nashorn.internal.runtime.arrays.ArrayData;
8054359Srobertoimport jdk.nashorn.internal.runtime.arrays.ArrayIndex;
8154359Srobertoimport jdk.nashorn.internal.runtime.linker.Bootstrap;
8282498Srobertoimport jdk.nashorn.internal.runtime.linker.LinkerCallSite;
8354359Srobertoimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
8454359Srobertoimport jdk.nashorn.internal.runtime.linker.NashornGuards;
8554359Sroberto
8654359Sroberto/**
8754359Sroberto * Base class for generic JavaScript objects.
8854359Sroberto * <p>
8954359Sroberto * Notes:
9054359Sroberto * <ul>
9154359Sroberto * <li>The map is used to identify properties in the object.</li>
9254359Sroberto * <li>If the map is modified then it must be cloned and replaced.  This notifies
9354359Sroberto *     any code that made assumptions about the object that things have changed.
9454359Sroberto *     Ex. CallSites that have been validated must check to see if the map has
9582498Sroberto *     changed (or a map from a different object type) and hence relink the method
9654359Sroberto *     to call.</li>
9754359Sroberto * <li>Modifications of the map include adding/deleting attributes or changing a
9854359Sroberto *     function field value.</li>
9982498Sroberto * </ul>
10054359Sroberto */
10154359Sroberto
102182007Srobertopublic abstract class ScriptObject implements PropertyAccess, Cloneable {
103132451Sroberto    /** __proto__ special property name inside object literals. ES6 draft. */
104132451Sroberto    public static final String PROTO_PROPERTY_NAME   = "__proto__";
105132451Sroberto
106182007Sroberto    /** Search fall back routine name for "no such method" */
107182007Sroberto    public static final String NO_SUCH_METHOD_NAME   = "__noSuchMethod__";
108182007Sroberto
109132451Sroberto    /** Search fall back routine name for "no such property" */
110182007Sroberto    public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
11182498Sroberto
11254359Sroberto    /** Per ScriptObject flag - is this a scope object? */
11354359Sroberto    public static final int IS_SCOPE       = 1 << 0;
11454359Sroberto
11554359Sroberto    /** Per ScriptObject flag - is this an array object? */
11654359Sroberto    public static final int IS_ARRAY       = 1 << 1;
11754359Sroberto
11854359Sroberto    /** Per ScriptObject flag - is this an arguments object? */
11954359Sroberto    public static final int IS_ARGUMENTS   = 1 << 2;
12054359Sroberto
12154359Sroberto    /** Is length property not-writable? */
12282498Sroberto    public static final int IS_LENGTH_NOT_WRITABLE = 1 << 3;
12382498Sroberto
12482498Sroberto    /** Is this a builtin object? */
12582498Sroberto    public static final int IS_BUILTIN = 1 << 4;
12654359Sroberto
12754359Sroberto    /**
128182007Sroberto     * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
129182007Sroberto     * {@link ScriptObject#objectSpill} when full
130182007Sroberto     */
131182007Sroberto    public static final int SPILL_RATE = 8;
13254359Sroberto
13354359Sroberto    /** Map to property information and accessor functions. Ordered by insertion. */
13454359Sroberto    private PropertyMap map;
13554359Sroberto
13682498Sroberto    /** objects proto. */
13754359Sroberto    private ScriptObject proto;
13882498Sroberto
13982498Sroberto    /** Object flags. */
14082498Sroberto    private int flags;
14182498Sroberto
14282498Sroberto    /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */
143182007Sroberto    protected long[]   primitiveSpill;
14454359Sroberto
14554359Sroberto    /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
14654359Sroberto    protected Object[] objectSpill;
147182007Sroberto
148182007Sroberto    /** Indexed array data. */
14954359Sroberto    private ArrayData arrayData;
150182007Sroberto
151182007Sroberto    /** Method handle to retrieve prototype of this object */
152132451Sroberto    public static final MethodHandle GETPROTO      = findOwnMH_V("getProto", ScriptObject.class);
153132451Sroberto
15454359Sroberto    static final MethodHandle MEGAMORPHIC_GET    = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class);
15554359Sroberto    static final MethodHandle GLOBALFILTER       = findOwnMH_S("globalFilter", Object.class, Object.class);
156182007Sroberto    static final MethodHandle DECLARE_AND_SET    = findOwnMH_V("declareAndSet", void.class, String.class, Object.class);
15754359Sroberto
158182007Sroberto    private static final MethodHandle TRUNCATINGFILTER   = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class);
159182007Sroberto    private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class);
160132451Sroberto    private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class);
161132451Sroberto
162132451Sroberto    private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>();
163132451Sroberto
164132451Sroberto    /** Method handle for getting the array data */
165132451Sroberto    public static final Call GET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
166132451Sroberto
167132451Sroberto    /** Method handle for getting a function argument at a given index. Used from MapCreator */
168182007Sroberto    public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
169132451Sroberto
17054359Sroberto    /** Method handle for setting a function argument at a given index. Used from MapCreator */
17154359Sroberto    public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
17254359Sroberto
17354359Sroberto    /** Method handle for getting the proto of a ScriptObject */
17454359Sroberto    public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
17554359Sroberto
17654359Sroberto    /** Method handle for getting the proto of a ScriptObject */
17754359Sroberto    public static final Call GET_PROTO_DEPTH    = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class);
17854359Sroberto
17954359Sroberto    /** Method handle for setting the proto of a ScriptObject */
18054359Sroberto    public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class);
181182007Sroberto
182182007Sroberto    /** Method handle for setting the proto of a ScriptObject after checking argument */
18354359Sroberto    public static final Call SET_PROTO_FROM_LITERAL    = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class);
18454359Sroberto
18554359Sroberto    /** Method handle for setting the user accessors of a ScriptObject */
18654359Sroberto    //TODO fastpath this
18754359Sroberto    public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
18854359Sroberto
18954359Sroberto    static final MethodHandle[] SET_SLOW = new MethodHandle[] {
19054359Sroberto        findOwnMH_V("set", void.class, Object.class, int.class, int.class),
19154359Sroberto        findOwnMH_V("set", void.class, Object.class, long.class, int.class),
19254359Sroberto        findOwnMH_V("set", void.class, Object.class, double.class, int.class),
19354359Sroberto        findOwnMH_V("set", void.class, Object.class, Object.class, int.class)
19454359Sroberto    };
19554359Sroberto
19654359Sroberto    /** Method handle to reset the map of this ScriptObject */
19754359Sroberto    public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class);
198132451Sroberto
19954359Sroberto    static final MethodHandle CAS_MAP           = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class);
20054359Sroberto    static final MethodHandle EXTENSION_CHECK   = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class);
201132451Sroberto    static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class);
20254359Sroberto
20354359Sroberto    /**
20454359Sroberto     * Constructor
20554359Sroberto     */
20654359Sroberto    public ScriptObject() {
20754359Sroberto        this(null);
208182007Sroberto    }
209182007Sroberto
21054359Sroberto    /**
211182007Sroberto    * Constructor
212182007Sroberto    *
213182007Sroberto    * @param map {@link PropertyMap} used to create the initial object
214182007Sroberto    */
215182007Sroberto    public ScriptObject(final PropertyMap map) {
216182007Sroberto        if (Context.DEBUG) {
217182007Sroberto            ScriptObject.count++;
218182007Sroberto        }
219182007Sroberto        this.arrayData = ArrayData.EMPTY_ARRAY;
220182007Sroberto        this.setMap(map == null ? PropertyMap.newMap() : map);
221182007Sroberto    }
222182007Sroberto
223182007Sroberto    /**
224182007Sroberto     * Constructor that directly sets the prototype to {@code proto} and property map to
225182007Sroberto     * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)}
226182007Sroberto     * would do. This should only be used for objects that are always constructed with the
227182007Sroberto     * same combination of prototype and property map.
228182007Sroberto     *
229182007Sroberto     * @param proto the prototype object
230182007Sroberto     * @param map intial {@link PropertyMap}
231182007Sroberto     */
232182007Sroberto    protected ScriptObject(final ScriptObject proto, final PropertyMap map) {
233182007Sroberto        this(map);
234182007Sroberto        this.proto = proto;
235182007Sroberto    }
236182007Sroberto
237182007Sroberto    /**
238182007Sroberto     * Constructor used to instantiate spill properties directly. Used from
239182007Sroberto     * SpillObjectCreator.
240182007Sroberto     *
241182007Sroberto     * @param map            property maps
242182007Sroberto     * @param primitiveSpill primitive spills
243182007Sroberto     * @param objectSpill    reference spills
244182007Sroberto     */
245182007Sroberto    public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
246182007Sroberto        this(map);
247182007Sroberto        this.primitiveSpill = primitiveSpill;
248182007Sroberto        this.objectSpill    = objectSpill;
249182007Sroberto        assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
250182007Sroberto    }
251182007Sroberto
252182007Sroberto    /**
253182007Sroberto     * Check whether this is a global object
254182007Sroberto     * @return true if global
255182007Sroberto     */
256182007Sroberto    protected boolean isGlobal() {
257182007Sroberto        return false;
258182007Sroberto    }
259182007Sroberto
260182007Sroberto    private static int alignUp(final int size, final int alignment) {
261182007Sroberto        return size + alignment - 1 & ~(alignment - 1);
262182007Sroberto    }
263182007Sroberto
264182007Sroberto    /**
265182007Sroberto     * Given a number of properties, return the aligned to SPILL_RATE
266182007Sroberto     * buffer size required for the smallest spill pool needed to
267182007Sroberto     * house them
268182007Sroberto     * @param nProperties number of properties
269182007Sroberto     * @return property buffer length, a multiple of SPILL_RATE
270182007Sroberto     */
271182007Sroberto    public static int spillAllocationLength(final int nProperties) {
272182007Sroberto        return alignUp(nProperties, SPILL_RATE);
273182007Sroberto    }
274182007Sroberto
275182007Sroberto    /**
276182007Sroberto     * Copy all properties from the source object with their receiver bound to the source.
277182007Sroberto     * This function was known as mergeMap
278182007Sroberto     *
279182007Sroberto     * @param source The source object to copy from.
280182007Sroberto     */
281182007Sroberto    public void addBoundProperties(final ScriptObject source) {
282182007Sroberto        addBoundProperties(source, source.getMap().getProperties());
283182007Sroberto    }
284182007Sroberto
285182007Sroberto    /**
286182007Sroberto     * Copy all properties from the array with their receiver bound to the source.
287182007Sroberto     *
288182007Sroberto     * @param source The source object to copy from.
289132451Sroberto     * @param properties The array of properties to copy.
290132451Sroberto     */
291132451Sroberto    public void addBoundProperties(final ScriptObject source, final Property[] properties) {
292132451Sroberto        PropertyMap newMap = this.getMap();
293132451Sroberto
294132451Sroberto        for (final Property property : properties) {
295132451Sroberto            newMap = addBoundProperty(newMap, source, property);
296132451Sroberto        }
297132451Sroberto
298132451Sroberto        this.setMap(newMap);
29954359Sroberto    }
30054359Sroberto
30154359Sroberto    /**
302182007Sroberto     * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the
30354359Sroberto     * new interim property map.
30454359Sroberto     *
30554359Sroberto     * @param propMap the property map
30654359Sroberto     * @param source the source object
30754359Sroberto     * @param property the property to be added
30854359Sroberto     * @return the new property map
30954359Sroberto     */
31054359Sroberto    protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) {
311182007Sroberto        PropertyMap newMap = propMap;
312182007Sroberto        final String key = property.getKey();
313132451Sroberto        final Property oldProp = newMap.findProperty(key);
31454359Sroberto        if (oldProp == null) {
31554359Sroberto            if (property instanceof UserAccessorProperty) {
31654359Sroberto                // Note: we copy accessor functions to this object which is semantically different from binding.
31754359Sroberto                final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
31854359Sroberto                newMap = newMap.addPropertyNoHistory(prop);
31954359Sroberto            } else {
32054359Sroberto                newMap = newMap.addPropertyBind((AccessorProperty)property, source);
32154359Sroberto            }
32254359Sroberto        } else {
32354359Sroberto            // See ECMA section 10.5 Declaration Binding Instantiation
32454359Sroberto            // step 5 processing each function declaration.
32554359Sroberto            if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
32654359Sroberto                if (oldProp instanceof UserAccessorProperty ||
32754359Sroberto                        !(oldProp.isWritable() && oldProp.isEnumerable())) {
32854359Sroberto                    throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
32954359Sroberto                }
33054359Sroberto            }
33154359Sroberto        }
33254359Sroberto        return newMap;
33354359Sroberto    }
33454359Sroberto
33554359Sroberto    /**
33654359Sroberto     * Copy all properties from the array with their receiver bound to the source.
33754359Sroberto     *
33854359Sroberto     * @param source The source object to copy from.
33954359Sroberto     * @param properties The collection of accessor properties to copy.
34054359Sroberto     */
34154359Sroberto    public void addBoundProperties(final Object source, final AccessorProperty[] properties) {
34254359Sroberto        PropertyMap newMap = this.getMap();
34354359Sroberto
34454359Sroberto        for (final AccessorProperty property : properties) {
34554359Sroberto            final String key = property.getKey();
34654359Sroberto
34754359Sroberto            if (newMap.findProperty(key) == null) {
34854359Sroberto                newMap = newMap.addPropertyBind(property, source);
34954359Sroberto            }
35054359Sroberto        }
35154359Sroberto
35254359Sroberto        this.setMap(newMap);
35354359Sroberto    }
35454359Sroberto
35554359Sroberto    /**
35682498Sroberto     * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the
35782498Sroberto     * first argument in lieu of the bound argument).
35882498Sroberto     * @param methodHandle Method handle to bind to.
35982498Sroberto     * @param receiver     Object to bind.
36082498Sroberto     * @return Bound method handle.
36182498Sroberto     */
36282498Sroberto    static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) {
36382498Sroberto        return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0));
36482498Sroberto    }
36582498Sroberto
36654359Sroberto    /**
36782498Sroberto     * Return a property iterator.
36856746Sroberto     * @return Property iterator.
36956746Sroberto     */
37082498Sroberto    public Iterator<String> propertyIterator() {
37182498Sroberto        return new KeyIterator(this);
37254359Sroberto    }
37354359Sroberto
37454359Sroberto    /**
37554359Sroberto     * Return a property value iterator.
37654359Sroberto     * @return Property value iterator.
37754359Sroberto     */
37854359Sroberto    public Iterator<Object> valueIterator() {
37954359Sroberto        return new ValueIterator(this);
38054359Sroberto    }
38154359Sroberto
38254359Sroberto    /**
38354359Sroberto     * ECMA 8.10.1 IsAccessorDescriptor ( Desc )
38454359Sroberto     * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter
38554359Sroberto     */
38654359Sroberto    public final boolean isAccessorDescriptor() {
38754359Sroberto        return has(GET) || has(SET);
38854359Sroberto    }
38954359Sroberto
39082498Sroberto    /**
39154359Sroberto     * ECMA 8.10.2 IsDataDescriptor ( Desc )
39282498Sroberto     * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
39382498Sroberto     */
39482498Sroberto    public final boolean isDataDescriptor() {
39582498Sroberto        return has(VALUE) || has(WRITABLE);
39654359Sroberto    }
39754359Sroberto
39854359Sroberto    /**
39954359Sroberto     * ECMA 8.10.3 IsGenericDescriptor ( Desc )
40054359Sroberto     * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
40154359Sroberto     */
40254359Sroberto    public final boolean isGenericDescriptor() {
40354359Sroberto        return isAccessorDescriptor() || isDataDescriptor();
40482498Sroberto    }
40554359Sroberto
40682498Sroberto    /**
40782498Sroberto      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
40854359Sroberto      *
40954359Sroberto      * @return property descriptor
41054359Sroberto      */
41182498Sroberto    public final PropertyDescriptor toPropertyDescriptor() {
41254359Sroberto        final Global global = Context.getGlobal();
41382498Sroberto
41482498Sroberto        final PropertyDescriptor desc;
41582498Sroberto        if (isDataDescriptor()) {
41682498Sroberto            if (has(SET) || has(GET)) {
41782498Sroberto                throw typeError(global, "inconsistent.property.descriptor");
41854359Sroberto            }
41954359Sroberto
42054359Sroberto            desc = global.newDataDescriptor(UNDEFINED, false, false, false);
42154359Sroberto        } else if (isAccessorDescriptor()) {
42282498Sroberto            if (has(VALUE) || has(WRITABLE)) {
42354359Sroberto                throw typeError(global, "inconsistent.property.descriptor");
42482498Sroberto            }
42582498Sroberto
42682498Sroberto            desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
42754359Sroberto        } else {
42854359Sroberto            desc = global.newGenericDescriptor(false, false);
42954359Sroberto        }
43082498Sroberto
43154359Sroberto        return desc.fillFrom(this);
43282498Sroberto    }
43382498Sroberto
43482498Sroberto    /**
43554359Sroberto     * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
43654359Sroberto     *
43754359Sroberto     * @param global  global scope object
43856746Sroberto     * @param obj object to create property descriptor from
43954359Sroberto     *
44054359Sroberto     * @return property descriptor
44154359Sroberto     */
44254359Sroberto    public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) {
44354359Sroberto        if (obj instanceof ScriptObject) {
44454359Sroberto            return ((ScriptObject)obj).toPropertyDescriptor();
44554359Sroberto        }
44654359Sroberto
44754359Sroberto        throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
44854359Sroberto    }
44954359Sroberto
45054359Sroberto    /**
45154359Sroberto     * ECMA 8.12.1 [[GetOwnProperty]] (P)
45254359Sroberto     *
45354359Sroberto     * @param key property key
45454359Sroberto     *
455182007Sroberto     * @return Returns the Property Descriptor of the named own property of this
456182007Sroberto     * object, or undefined if absent.
45754359Sroberto     */
45854359Sroberto    public Object getOwnPropertyDescriptor(final String key) {
459182007Sroberto        final Property property = getMap().findProperty(key);
460182007Sroberto
461182007Sroberto        final Global global = Context.getGlobal();
462182007Sroberto
463182007Sroberto        if (property != null) {
464182007Sroberto            final ScriptFunction get   = property.getGetterFunction(this);
465182007Sroberto            final ScriptFunction set   = property.getSetterFunction(this);
466182007Sroberto
467182007Sroberto            final boolean configurable = property.isConfigurable();
468182007Sroberto            final boolean enumerable   = property.isEnumerable();
469182007Sroberto            final boolean writable     = property.isWritable();
470182007Sroberto
471182007Sroberto            if (property instanceof UserAccessorProperty) {
472182007Sroberto                return global.newAccessorDescriptor(
473182007Sroberto                    get != null ?
474182007Sroberto                        get :
47554359Sroberto                        UNDEFINED,
47654359Sroberto                    set != null ?
47782498Sroberto                        set :
47854359Sroberto                        UNDEFINED,
47954359Sroberto                    configurable,
48054359Sroberto                    enumerable);
48154359Sroberto            }
48254359Sroberto
48354359Sroberto            return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
48454359Sroberto        }
48554359Sroberto
48654359Sroberto        final int index = getArrayIndex(key);
487106163Sroberto        final ArrayData array = getArray();
48854359Sroberto
48954359Sroberto        if (array.has(index)) {
49054359Sroberto            return array.getDescriptor(global, index);
49154359Sroberto        }
49254359Sroberto
49354359Sroberto        return UNDEFINED;
49454359Sroberto    }
495182007Sroberto
49654359Sroberto    /**
49754359Sroberto     * ECMA 8.12.2 [[GetProperty]] (P)
49854359Sroberto     *
49954359Sroberto     * @param key property key
50054359Sroberto     *
501182007Sroberto     * @return Returns the fully populated Property Descriptor of the named property
502182007Sroberto     * of this object, or undefined if absent.
503182007Sroberto     */
504182007Sroberto    public Object getPropertyDescriptor(final String key) {
505182007Sroberto        final Object res = getOwnPropertyDescriptor(key);
50654359Sroberto
50754359Sroberto        if (res != UNDEFINED) {
50854359Sroberto            return res;
50954359Sroberto        } else if (getProto() != null) {
510182007Sroberto            return getProto().getOwnPropertyDescriptor(key);
511182007Sroberto        } else {
512182007Sroberto            return UNDEFINED;
513182007Sroberto        }
514132451Sroberto    }
515132451Sroberto
516182007Sroberto    /**
517132451Sroberto     * Invalidate any existing global constant method handles that may exist for {@code key}.
518182007Sroberto     * @param key the property name
519182007Sroberto     */
520182007Sroberto    protected void invalidateGlobalConstant(final String key) {
521182007Sroberto        final GlobalConstants globalConstants = getGlobalConstants();
522182007Sroberto        if (globalConstants != null) {
523182007Sroberto            globalConstants.delete(key);
524182007Sroberto        }
525182007Sroberto    }
526182007Sroberto
527182007Sroberto    /**
528182007Sroberto     * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
529182007Sroberto     *
530182007Sroberto     * @param key the property key
531182007Sroberto     * @param propertyDesc the property descriptor
532182007Sroberto     * @param reject is the property extensible - true means new definitions are rejected
533182007Sroberto     *
534182007Sroberto     * @return true if property was successfully defined
535182007Sroberto     */
536182007Sroberto    public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
537182007Sroberto        final Global             global  = Context.getGlobal();
538182007Sroberto        final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
539182007Sroberto        final Object             current = getOwnPropertyDescriptor(key);
540182007Sroberto        final String             name    = JSType.toString(key);
541182007Sroberto
542182007Sroberto        invalidateGlobalConstant(key);
543182007Sroberto
544182007Sroberto        if (current == UNDEFINED) {
545182007Sroberto            if (isExtensible()) {
546182007Sroberto                // add a new own property
547182007Sroberto                addOwnProperty(key, desc);
548182007Sroberto                return true;
549182007Sroberto            }
550182007Sroberto            // new property added to non-extensible object
551182007Sroberto            if (reject) {
552182007Sroberto                throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
553182007Sroberto            }
554182007Sroberto            return false;
555182007Sroberto        }
556182007Sroberto
557182007Sroberto        // modifying an existing property
55882498Sroberto        final PropertyDescriptor currentDesc = (PropertyDescriptor)current;
559182007Sroberto        final PropertyDescriptor newDesc     = desc;
560182007Sroberto
561182007Sroberto        if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) {
562182007Sroberto            // every descriptor field is absent
563182007Sroberto            return true;
564182007Sroberto        }
565182007Sroberto
566182007Sroberto        if (newDesc.hasAndEquals(currentDesc)) {
56782498Sroberto            // every descriptor field of the new is same as the current
56882498Sroberto            return true;
56954359Sroberto        }
57054359Sroberto
571182007Sroberto        if (!currentDesc.isConfigurable()) {
572182007Sroberto            if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
573182007Sroberto                // not configurable can not be made configurable
574182007Sroberto                if (reject) {
575182007Sroberto                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
576182007Sroberto                }
577182007Sroberto                return false;
578182007Sroberto            }
579182007Sroberto
580182007Sroberto            if (newDesc.has(ENUMERABLE) &&
58154359Sroberto                currentDesc.isEnumerable() != newDesc.isEnumerable()) {
58254359Sroberto                // cannot make non-enumerable as enumerable or vice-versa
58354359Sroberto                if (reject) {
58454359Sroberto                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
58554359Sroberto                }
586182007Sroberto                return false;
58754359Sroberto            }
588182007Sroberto        }
58954359Sroberto
590182007Sroberto        int propFlags = Property.mergeFlags(currentDesc, newDesc);
59154359Sroberto        Property property = getMap().findProperty(key);
59254359Sroberto
59382498Sroberto        if (currentDesc.type() == PropertyDescriptor.DATA &&
59454359Sroberto                (newDesc.type() == PropertyDescriptor.DATA ||
59582498Sroberto                 newDesc.type() == PropertyDescriptor.GENERIC)) {
59654359Sroberto            if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) {
59754359Sroberto                if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
59854359Sroberto                    newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
59954359Sroberto                    if (reject) {
60054359Sroberto                        throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
60154359Sroberto                    }
60254359Sroberto                    return false;
60354359Sroberto                }
60454359Sroberto            }
605132451Sroberto
60654359Sroberto            final boolean newValue = newDesc.has(VALUE);
60754359Sroberto            final Object value     = newValue ? newDesc.getValue() : currentDesc.getValue();
60854359Sroberto
60954359Sroberto            if (newValue && property != null) {
61054359Sroberto                // Temporarily clear flags.
61154359Sroberto                property = modifyOwnProperty(property, 0);
61254359Sroberto                set(key, value, 0);
61354359Sroberto                //this might change the map if we change types of the property
61454359Sroberto                //hence we need to read it again. note that we should probably
61582498Sroberto                //have the setter return the new property throughout and in
61654359Sroberto                //general respect Property return values from modify and add
61782498Sroberto                //functions - which we don't seem to do at all here :-(
61854359Sroberto                //There is already a bug filed to generify PropertyAccess so we
61982498Sroberto                //can have the setter return e.g. a Property
62054359Sroberto                property = getMap().findProperty(key);
62154359Sroberto            }
62254359Sroberto
62354359Sroberto            if (property == null) {
62454359Sroberto                // promoting an arrayData value to actual property
62554359Sroberto                addOwnProperty(key, propFlags, value);
62654359Sroberto                checkIntegerKey(key);
62754359Sroberto            } else {
62854359Sroberto                // Now set the new flags
62954359Sroberto                modifyOwnProperty(property, propFlags);
63054359Sroberto            }
63154359Sroberto        } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
63254359Sroberto                   (newDesc.type() == PropertyDescriptor.ACCESSOR ||
63354359Sroberto                    newDesc.type() == PropertyDescriptor.GENERIC)) {
63454359Sroberto            if (!currentDesc.isConfigurable()) {
63554359Sroberto                if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
63654359Sroberto                    newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
63754359Sroberto                    if (reject) {
63854359Sroberto                        throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
63954359Sroberto                    }
64054359Sroberto                    return false;
64154359Sroberto                }
64254359Sroberto            }
64354359Sroberto            // New set the new features.
64454359Sroberto            modifyOwnProperty(property, propFlags,
64554359Sroberto                                      newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
64654359Sroberto                                      newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
64754359Sroberto        } else {
64854359Sroberto            // changing descriptor type
64954359Sroberto            if (!currentDesc.isConfigurable()) {
65054359Sroberto                // not configurable can not be made configurable
65154359Sroberto                if (reject) {
65254359Sroberto                    throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
65354359Sroberto                }
65454359Sroberto                return false;
65554359Sroberto            }
65654359Sroberto
65754359Sroberto            propFlags = 0;
65854359Sroberto
65954359Sroberto            // Preserve only configurable and enumerable from current desc
66054359Sroberto            // if those are not overridden in the new property descriptor.
66154359Sroberto            boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable();
66254359Sroberto            if (!value) {
66354359Sroberto                propFlags |= Property.NOT_CONFIGURABLE;
66454359Sroberto            }
66554359Sroberto            value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable();
66654359Sroberto            if (!value) {
66754359Sroberto                propFlags |= Property.NOT_ENUMERABLE;
66854359Sroberto            }
66954359Sroberto
67054359Sroberto            final int type = newDesc.type();
67182498Sroberto            if (type == PropertyDescriptor.DATA) {
67282498Sroberto                // get writable from the new descriptor
67354359Sroberto                value = newDesc.has(WRITABLE) && newDesc.isWritable();
67482498Sroberto                if (!value) {
67554359Sroberto                    propFlags |= Property.NOT_WRITABLE;
67654359Sroberto                }
677182007Sroberto
67854359Sroberto                // delete the old property
67954359Sroberto                deleteOwnProperty(property);
68054359Sroberto                // add new data property
68154359Sroberto                addOwnProperty(key, propFlags, newDesc.getValue());
68254359Sroberto            } else if (type == PropertyDescriptor.ACCESSOR) {
68354359Sroberto                if (property == null) {
68454359Sroberto                    addOwnProperty(key, propFlags,
68554359Sroberto                                     newDesc.has(GET) ? newDesc.getGetter() : null,
68654359Sroberto                                     newDesc.has(SET) ? newDesc.getSetter() : null);
68754359Sroberto                } else {
68854359Sroberto                    // Modify old property with the new features.
68954359Sroberto                    modifyOwnProperty(property, propFlags,
690182007Sroberto                                        newDesc.has(GET) ? newDesc.getGetter() : null,
69154359Sroberto                                        newDesc.has(SET) ? newDesc.getSetter() : null);
69254359Sroberto                }
69354359Sroberto            }
69454359Sroberto        }
69554359Sroberto
69654359Sroberto        checkIntegerKey(key);
69754359Sroberto
69854359Sroberto        return true;
69954359Sroberto    }
700132451Sroberto
70154359Sroberto    /**
702132451Sroberto     * Almost like defineOwnProperty(int,Object) for arrays this one does
703132451Sroberto     * not add 'gap' elements (like the array one does).
704132451Sroberto     *
705132451Sroberto     * @param index key for property
706132451Sroberto     * @param value value to define
707132451Sroberto     */
708182007Sroberto    public void defineOwnProperty(final int index, final Object value) {
709132451Sroberto        assert isValidArrayIndex(index) : "invalid array index";
710182007Sroberto        final long longIndex = ArrayIndex.toLongIndex(index);
711132451Sroberto        final long oldLength = getArray().length();
712132451Sroberto        if (longIndex >= oldLength) {
713132451Sroberto            setArray(getArray().ensure(longIndex));
714132451Sroberto            doesNotHaveEnsureDelete(longIndex, oldLength, false);
715132451Sroberto        }
716132451Sroberto        setArray(getArray().set(index, value, false));
717132451Sroberto    }
718182007Sroberto
719182007Sroberto    private void checkIntegerKey(final String key) {
720182007Sroberto        final int index = getArrayIndex(key);
721182007Sroberto
722182007Sroberto        if (isValidArrayIndex(index)) {
723182007Sroberto            final ArrayData data = getArray();
724182007Sroberto
725182007Sroberto            if (data.has(index)) {
726182007Sroberto                setArray(data.delete(index));
727182007Sroberto            }
728182007Sroberto        }
729182007Sroberto    }
730132451Sroberto
731132451Sroberto    /**
732132451Sroberto      * Add a new property to the object.
73354359Sroberto      *
73454359Sroberto      * @param key          property key
73554359Sroberto      * @param propertyDesc property descriptor for property
73654359Sroberto      */
73754359Sroberto    public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
73854359Sroberto        // Already checked that there is no own property with that key.
73954359Sroberto        PropertyDescriptor pdesc = propertyDesc;
74054359Sroberto
74154359Sroberto        final int propFlags = Property.toFlags(pdesc);
74254359Sroberto
743182007Sroberto        if (pdesc.type() == PropertyDescriptor.GENERIC) {
74454359Sroberto            final Global global = Context.getGlobal();
74554359Sroberto            final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
74654359Sroberto
74754359Sroberto            dDesc.fillFrom((ScriptObject)pdesc);
74854359Sroberto            pdesc = dDesc;
74954359Sroberto        }
75054359Sroberto
75154359Sroberto        final int type = pdesc.type();
75254359Sroberto        if (type == PropertyDescriptor.DATA) {
75354359Sroberto            addOwnProperty(key, propFlags, pdesc.getValue());
75454359Sroberto        } else if (type == PropertyDescriptor.ACCESSOR) {
75554359Sroberto            addOwnProperty(key, propFlags,
75682498Sroberto                    pdesc.has(GET) ? pdesc.getGetter() : null,
75754359Sroberto                    pdesc.has(SET) ? pdesc.getSetter() : null);
75854359Sroberto        }
75954359Sroberto
76054359Sroberto        checkIntegerKey(key);
76154359Sroberto    }
76282498Sroberto
76354359Sroberto    /**
76482498Sroberto     * Low level property API (not using property descriptors)
76554359Sroberto     * <p>
76654359Sroberto     * Find a property in the prototype hierarchy. Note: this is final and not
76754359Sroberto     * a good idea to override. If you have to, use
76854359Sroberto     * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
76954359Sroberto     * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
77054359Sroberto     * overriding way to find array properties
77154359Sroberto     *
77254359Sroberto     * @see jdk.nashorn.internal.objects.NativeArray
77354359Sroberto     *
77454359Sroberto     * @param key  Property key.
77554359Sroberto     * @param deep Whether the search should look up proto chain.
77654359Sroberto     *
77754359Sroberto     * @return FindPropertyData or null if not found.
77854359Sroberto     */
77954359Sroberto    public final FindProperty findProperty(final String key, final boolean deep) {
78054359Sroberto        return findProperty(key, deep, this);
78154359Sroberto    }
78254359Sroberto
78354359Sroberto    /**
78454359Sroberto     * Low level property API (not using property descriptors)
78554359Sroberto     * <p>
78654359Sroberto     * Find a property in the prototype hierarchy. Note: this is not a good idea
78754359Sroberto     * to override except as it was done in {@link WithObject}.
78854359Sroberto     * If you have to, use
78954359Sroberto     * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or
79054359Sroberto     * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the
79154359Sroberto     * overriding way to find array properties
79254359Sroberto     *
79354359Sroberto     * @see jdk.nashorn.internal.objects.NativeArray
79454359Sroberto     *
79554359Sroberto     * @param key  Property key.
79654359Sroberto     * @param deep Whether the search should look up proto chain.
79754359Sroberto     * @param start the object on which the lookup was originally initiated
79854359Sroberto     *
79954359Sroberto     * @return FindPropertyData or null if not found.
80054359Sroberto     */
80154359Sroberto    protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) {
80256746Sroberto
80354359Sroberto        final PropertyMap selfMap  = getMap();
80454359Sroberto        final Property    property = selfMap.findProperty(key);
80554359Sroberto
80654359Sroberto        if (property != null) {
80754359Sroberto            return new FindProperty(start, this, property);
808182007Sroberto        }
809182007Sroberto
81054359Sroberto        if (deep) {
81154359Sroberto            final ScriptObject myProto = getProto();
81254359Sroberto            if (myProto != null) {
81354359Sroberto                return myProto.findProperty(key, deep, start);
81454359Sroberto            }
81554359Sroberto        }
81654359Sroberto
81754359Sroberto        return null;
81854359Sroberto    }
81954359Sroberto
82054359Sroberto    /**
82154359Sroberto     * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
82254359Sroberto     * {@code boolean} value instead of a {@link FindProperty} object.
82354359Sroberto     * @param key  Property key.
82482498Sroberto     * @param deep Whether the search should look up proto chain.
82554359Sroberto     * @return true if the property was found.
82654359Sroberto     */
82782498Sroberto    boolean hasProperty(final String key, final boolean deep) {
82854359Sroberto        if (getMap().findProperty(key) != null) {
82954359Sroberto            return true;
83054359Sroberto        }
831182007Sroberto
832182007Sroberto        if (deep) {
83354359Sroberto            final ScriptObject myProto = getProto();
834182007Sroberto            if (myProto != null) {
83554359Sroberto                return myProto.hasProperty(key, deep);
836182007Sroberto            }
837182007Sroberto        }
838132451Sroberto
83982498Sroberto        return false;
840132451Sroberto    }
84154359Sroberto
84254359Sroberto    private SwitchPoint findBuiltinSwitchPoint(final String key) {
843182007Sroberto        for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
844182007Sroberto            final Property prop = myProto.getMap().findProperty(key);
845182007Sroberto            if (prop != null) {
846182007Sroberto                final SwitchPoint sp = prop.getBuiltinSwitchPoint();
847182007Sroberto                if (sp != null && !sp.hasBeenInvalidated()) {
848182007Sroberto                    return sp;
849182007Sroberto                }
850182007Sroberto            }
851182007Sroberto        }
852182007Sroberto        return null;
853182007Sroberto    }
854182007Sroberto
855182007Sroberto    /**
856182007Sroberto     * Add a new property to the object.
857182007Sroberto     * <p>
858182007Sroberto     * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
859182007Sroberto     *
860182007Sroberto     * @param key             Property key.
861182007Sroberto     * @param propertyFlags   Property flags.
862182007Sroberto     * @param getter          Property getter, or null if not defined
863182007Sroberto     * @param setter          Property setter, or null if not defined
864182007Sroberto     *
865182007Sroberto     * @return New property.
866182007Sroberto     */
867182007Sroberto    public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
868182007Sroberto        return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
869182007Sroberto    }
870182007Sroberto
871182007Sroberto    /**
872182007Sroberto     * Add a new property to the object.
873182007Sroberto     * <p>
874182007Sroberto     * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
875132451Sroberto     *
87654359Sroberto     * @param key             Property key.
877182007Sroberto     * @param propertyFlags   Property flags.
878182007Sroberto     * @param value           Value of property
879182007Sroberto     *
880182007Sroberto     * @return New property.
881182007Sroberto     */
882182007Sroberto    public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
883182007Sroberto        return addSpillProperty(key, propertyFlags, value, true);
884182007Sroberto    }
885182007Sroberto
886182007Sroberto    /**
887182007Sroberto     * Add a new property to the object.
888182007Sroberto     * <p>
889182007Sroberto     * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
890182007Sroberto     *
891182007Sroberto     * @param newProperty property to add
892182007Sroberto     *
893182007Sroberto     * @return New property.
894182007Sroberto     */
895182007Sroberto    public final Property addOwnProperty(final Property newProperty) {
896182007Sroberto        PropertyMap oldMap = getMap();
897182007Sroberto        while (true) {
898182007Sroberto            final PropertyMap newMap = oldMap.addProperty(newProperty);
899182007Sroberto            if (!compareAndSetMap(oldMap, newMap)) {
900182007Sroberto                oldMap = getMap();
901182007Sroberto                final Property oldProperty = oldMap.findProperty(newProperty.getKey());
902182007Sroberto
903182007Sroberto                if (oldProperty != null) {
904182007Sroberto                    return oldProperty;
905182007Sroberto                }
906182007Sroberto            } else {
907182007Sroberto                return newProperty;
908182007Sroberto            }
909182007Sroberto        }
910182007Sroberto    }
911182007Sroberto
912182007Sroberto    private void erasePropertyValue(final Property property) {
913182007Sroberto        // Erase the property field value with undefined. If the property is defined
914182007Sroberto        // by user-defined accessors, we don't want to call the setter!!
915182007Sroberto        if (!(property instanceof UserAccessorProperty)) {
916182007Sroberto            assert property != null;
917182007Sroberto            property.setValue(this, this, UNDEFINED, false);
918182007Sroberto        }
919182007Sroberto    }
920182007Sroberto
921182007Sroberto    /**
922182007Sroberto     * Delete a property from the object.
923182007Sroberto     *
924182007Sroberto     * @param property Property to delete.
925182007Sroberto     *
926182007Sroberto     * @return true if deleted.
927182007Sroberto     */
928182007Sroberto    public final boolean deleteOwnProperty(final Property property) {
92954359Sroberto        erasePropertyValue(property);
93054359Sroberto        PropertyMap oldMap = getMap();
931182007Sroberto
932182007Sroberto        while (true) {
933182007Sroberto            final PropertyMap newMap = oldMap.deleteProperty(property);
934182007Sroberto
935182007Sroberto            if (newMap == null) {
936182007Sroberto                return false;
937182007Sroberto            }
938182007Sroberto
939182007Sroberto            if (!compareAndSetMap(oldMap, newMap)) {
940182007Sroberto                oldMap = getMap();
941182007Sroberto            } else {
942182007Sroberto                // delete getter and setter function references so that we don't leak
943182007Sroberto                if (property instanceof UserAccessorProperty) {
944182007Sroberto                    ((UserAccessorProperty)property).setAccessors(this, getMap(), null);
945182007Sroberto                }
946182007Sroberto
947182007Sroberto                invalidateGlobalConstant(property.getKey());
948182007Sroberto                return true;
949182007Sroberto            }
950182007Sroberto        }
951182007Sroberto
952182007Sroberto    }
953182007Sroberto
954182007Sroberto    /**
955182007Sroberto     * Fast initialization functions for ScriptFunctions that are strict, to avoid
956182007Sroberto     * creating setters that probably aren't used. Inject directly into the spill pool
957182007Sroberto     * the defaults for "arguments" and "caller"
958182007Sroberto     *
959182007Sroberto     * @param key           property key
960182007Sroberto     * @param propertyFlags flags
961182007Sroberto     * @param getter        getter for {@link UserAccessorProperty}, null if not present or N/A
96254359Sroberto     * @param setter        setter for {@link UserAccessorProperty}, null if not present or N/A
96354359Sroberto     */
96454359Sroberto    protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
96554359Sroberto        final PropertyMap oldMap = getMap();
96654359Sroberto        final int slot = oldMap.getFreeSpillSlot();
96754359Sroberto        ensureSpillSize(slot);
96854359Sroberto        objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
96954359Sroberto        Property    newProperty;
97054359Sroberto        PropertyMap newMap;
97154359Sroberto        do {
97254359Sroberto            newProperty = new UserAccessorProperty(key, propertyFlags, slot);
97354359Sroberto            newMap = oldMap.addProperty(newProperty);
97454359Sroberto        } while (!compareAndSetMap(oldMap, newMap));
97554359Sroberto    }
97654359Sroberto
97754359Sroberto    /**
97854359Sroberto     * Modify a property in the object
97954359Sroberto     *
98054359Sroberto     * @param oldProperty    property to modify
98154359Sroberto     * @param propertyFlags  new property flags
98282498Sroberto     * @param getter         getter for {@link UserAccessorProperty}, null if not present or N/A
98354359Sroberto     * @param setter         setter for {@link UserAccessorProperty}, null if not present or N/A
984182007Sroberto     *
985200576Sroberto     * @return new property
98682498Sroberto     */
98754359Sroberto    public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
988182007Sroberto        Property newProperty;
98954359Sroberto
99054359Sroberto        if (oldProperty instanceof UserAccessorProperty) {
99154359Sroberto            final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
99282498Sroberto            final int slot = uc.getSlot();
99354359Sroberto
99454359Sroberto            assert uc.getLocalType() == Object.class;
99554359Sroberto            final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
99654359Sroberto            assert gs != null;
99754359Sroberto            //reuse existing getter setter for speed
99882498Sroberto            gs.set(getter, setter);
99954359Sroberto            if (uc.getFlags() == propertyFlags) {
100054359Sroberto                return oldProperty;
100154359Sroberto            }
100254359Sroberto            newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
100354359Sroberto        } else {
100454359Sroberto            // erase old property value and create new user accessor property
100554359Sroberto            erasePropertyValue(oldProperty);
1006182007Sroberto            newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
100754359Sroberto        }
100854359Sroberto
100954359Sroberto        return modifyOwnProperty(oldProperty, newProperty);
101054359Sroberto    }
101182498Sroberto
101254359Sroberto    /**
101382498Sroberto      * Modify a property in the object
101454359Sroberto      *
101554359Sroberto      * @param oldProperty    property to modify
101654359Sroberto      * @param propertyFlags  new property flags
101754359Sroberto      *
101854359Sroberto      * @return new property
101954359Sroberto      */
102054359Sroberto    public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
102154359Sroberto        return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
102282498Sroberto    }
102354359Sroberto
102454359Sroberto    /**
102582498Sroberto     * Modify a property in the object, replacing a property with a new one
102654359Sroberto     *
102754359Sroberto     * @param oldProperty   property to replace
102854359Sroberto     * @param newProperty   property to replace it with
102954359Sroberto     *
103054359Sroberto     * @return new property
103154359Sroberto     */
103254359Sroberto    private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
103354359Sroberto        if (oldProperty == newProperty) {
103454359Sroberto            return newProperty; //nop
1035182007Sroberto        }
1036132451Sroberto
1037182007Sroberto        assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
1038182007Sroberto
1039132451Sroberto        PropertyMap oldMap = getMap();
104082498Sroberto
104154359Sroberto        while (true) {
104254359Sroberto            final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
104382498Sroberto
104454359Sroberto            if (!compareAndSetMap(oldMap, newMap)) {
104554359Sroberto                oldMap = getMap();
104654359Sroberto                final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
104754359Sroberto
104854359Sroberto                if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
104954359Sroberto                    return oldPropertyLookup;
105054359Sroberto                }
105154359Sroberto            } else {
105254359Sroberto                return newProperty;
1053182007Sroberto            }
1054182007Sroberto        }
1055182007Sroberto    }
1056182007Sroberto
1057182007Sroberto    /**
105854359Sroberto     * Update getter and setter in an object literal.
105954359Sroberto     *
1060182007Sroberto     * @param key    Property key.
106154359Sroberto     * @param getter {@link UserAccessorProperty} defined getter, or null if none
106254359Sroberto     * @param setter {@link UserAccessorProperty} defined setter, or null if none
106382498Sroberto     */
1064182007Sroberto    public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
1065182007Sroberto        final Property oldProperty = getMap().findProperty(key);
106654359Sroberto        if (oldProperty instanceof UserAccessorProperty) {
1067182007Sroberto            modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter);
1068182007Sroberto        } else {
1069182007Sroberto            addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter));
1070182007Sroberto        }
1071182007Sroberto    }
1072182007Sroberto
1073182007Sroberto    private static int getIntValue(final FindProperty find, final int programPoint) {
1074182007Sroberto        final MethodHandle getter = find.getGetter(int.class, programPoint, null);
1075182007Sroberto        if (getter != null) {
1076182007Sroberto            try {
1077182007Sroberto                return (int)getter.invokeExact((Object)find.getGetterReceiver());
1078182007Sroberto            } catch (final Error|RuntimeException e) {
1079182007Sroberto                throw e;
1080182007Sroberto            } catch (final Throwable e) {
1081182007Sroberto                throw new RuntimeException(e);
1082182007Sroberto            }
1083182007Sroberto        }
1084182007Sroberto
1085182007Sroberto        return UNDEFINED_INT;
1086182007Sroberto    }
1087182007Sroberto
1088182007Sroberto    private static long getLongValue(final FindProperty find, final int programPoint) {
1089182007Sroberto        final MethodHandle getter = find.getGetter(long.class, programPoint, null);
1090182007Sroberto        if (getter != null) {
1091182007Sroberto            try {
1092182007Sroberto                return (long)getter.invokeExact((Object)find.getGetterReceiver());
1093182007Sroberto            } catch (final Error|RuntimeException e) {
1094182007Sroberto                throw e;
1095182007Sroberto            } catch (final Throwable e) {
1096182007Sroberto                throw new RuntimeException(e);
1097182007Sroberto            }
1098182007Sroberto        }
1099182007Sroberto
1100182007Sroberto        return UNDEFINED_LONG;
1101182007Sroberto    }
1102182007Sroberto
1103182007Sroberto    private static double getDoubleValue(final FindProperty find, final int programPoint) {
1104182007Sroberto        final MethodHandle getter = find.getGetter(double.class, programPoint, null);
1105182007Sroberto        if (getter != null) {
1106182007Sroberto            try {
1107182007Sroberto                return (double)getter.invokeExact((Object)find.getGetterReceiver());
1108182007Sroberto            } catch (final Error|RuntimeException e) {
1109182007Sroberto                throw e;
1110182007Sroberto            } catch (final Throwable e) {
1111182007Sroberto                throw new RuntimeException(e);
1112182007Sroberto            }
1113182007Sroberto        }
1114182007Sroberto
1115182007Sroberto        return UNDEFINED_DOUBLE;
1116182007Sroberto    }
1117182007Sroberto
1118182007Sroberto    /**
1119182007Sroberto     * Return methodHandle of value function for call.
1120182007Sroberto     *
112154359Sroberto     * @param find      data from find property.
112282498Sroberto     * @param type      method type of function.
112354359Sroberto     * @param bindName  null or name to bind to second argument (property not found method.)
112454359Sroberto     *
112554359Sroberto     * @return value of property as a MethodHandle or null.
112654359Sroberto     */
112754359Sroberto    protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
1128182007Sroberto        return getCallMethodHandle(find.getObjectValue(), type, bindName);
1129182007Sroberto    }
113054359Sroberto
113154359Sroberto    /**
113254359Sroberto     * Return methodHandle of value function for call.
113354359Sroberto     *
113454359Sroberto     * @param value     value of receiver, it not a {@link ScriptFunction} this will return null.
113554359Sroberto     * @param type      method type of function.
113654359Sroberto     * @param bindName  null or name to bind to second argument (property not found method.)
113754359Sroberto     *
113854359Sroberto     * @return value of property as a MethodHandle or null.
113954359Sroberto     */
114054359Sroberto    protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
114154359Sroberto        return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
114254359Sroberto    }
114354359Sroberto
1144182007Sroberto    /**
1145182007Sroberto     * Get value using found property.
1146182007Sroberto     *
1147182007Sroberto     * @param property Found property.
1148182007Sroberto     *
114954359Sroberto     * @return Value of property.
115054359Sroberto     */
115154359Sroberto    public final Object getWithProperty(final Property property) {
115282498Sroberto        return new FindProperty(this, this, property).getObjectValue();
115354359Sroberto    }
115454359Sroberto
115554359Sroberto    /**
115682498Sroberto     * Get a property given a key
115754359Sroberto     *
115854359Sroberto     * @param key property key
115954359Sroberto     *
116054359Sroberto     * @return property for key
116154359Sroberto     */
116254359Sroberto    public final Property getProperty(final String key) {
116354359Sroberto        return getMap().findProperty(key);
116454359Sroberto    }
116554359Sroberto
116654359Sroberto    /**
1167132451Sroberto     * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
116854359Sroberto     * Used for argument access in a vararg function using parameter name.
116954359Sroberto     * Returns the argument at a given key (index)
117054359Sroberto     *
117154359Sroberto     * @param key argument index
117254359Sroberto     *
117354359Sroberto     * @return the argument at the given position, or undefined if not present
117454359Sroberto     */
117554359Sroberto    public Object getArgument(final int key) {
117654359Sroberto        return get(key);
117754359Sroberto    }
117854359Sroberto
117954359Sroberto    /**
118054359Sroberto     * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
118154359Sroberto     * Used for argument access in a vararg function using parameter name.
118254359Sroberto     * Returns the argument at a given key (index)
118354359Sroberto     *
118454359Sroberto     * @param key   argument index
118554359Sroberto     * @param value the value to write at the given index
118654359Sroberto     */
118754359Sroberto    public void setArgument(final int key, final Object value) {
118854359Sroberto        set(key, value, 0);
118954359Sroberto    }
119054359Sroberto
119154359Sroberto    /**
119254359Sroberto     * Return the current context from the object's map.
119354359Sroberto     * @return Current context.
119454359Sroberto     */
119554359Sroberto    protected Context getContext() {
119654359Sroberto        return Context.fromClass(getClass());
119754359Sroberto    }
119854359Sroberto
119954359Sroberto    /**
120054359Sroberto     * Return the map of an object.
120154359Sroberto     * @return PropertyMap object.
120254359Sroberto     */
1203132451Sroberto    public final PropertyMap getMap() {
120454359Sroberto        return map;
1205182007Sroberto    }
1206182007Sroberto
120754359Sroberto    /**
120854359Sroberto     * Set the initial map.
120954359Sroberto     * @param map Initial map.
121054359Sroberto     */
121154359Sroberto    public final void setMap(final PropertyMap map) {
121254359Sroberto        this.map = map;
121354359Sroberto    }
121454359Sroberto
121554359Sroberto    /**
121654359Sroberto     * Conditionally set the new map if the old map is the same.
121754359Sroberto     * @param oldMap Map prior to manipulation.
121854359Sroberto     * @param newMap Replacement map.
1219132451Sroberto     * @return true if the operation succeeded.
122054359Sroberto     */
1221    protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
1222        if (oldMap == this.map) {
1223            this.map = newMap;
1224            return true;
1225        }
1226        return false;
1227     }
1228
1229    /**
1230     * Return the __proto__ of an object.
1231     * @return __proto__ object.
1232     */
1233    public final ScriptObject getProto() {
1234        return proto;
1235    }
1236
1237    /**
1238     * Get the proto of a specific depth
1239     * @param n depth
1240     * @return proto at given depth
1241     */
1242    public final ScriptObject getProto(final int n) {
1243        assert n > 0;
1244        ScriptObject p = getProto();
1245        for (int i = n; i-- > 0;) {
1246            p = p.getProto();
1247        }
1248        return p;
1249    }
1250
1251    /**
1252     * Set the __proto__ of an object.
1253     * @param newProto new __proto__ to set.
1254     */
1255    public final void setProto(final ScriptObject newProto) {
1256        final ScriptObject oldProto = proto;
1257
1258        if (oldProto != newProto) {
1259            proto = newProto;
1260
1261            // Let current listeners know that the protototype has changed and set our map
1262            final PropertyListeners listeners = getMap().getListeners();
1263            if (listeners != null) {
1264                listeners.protoChanged();
1265            }
1266            // Replace our current allocator map with one that is associated with the new prototype.
1267            setMap(getMap().changeProto(newProto));
1268        }
1269    }
1270
1271    /**
1272     * Set the initial __proto__ of this object. This should be used instead of
1273     * {@link #setProto} if it is known that the current property map will not be
1274     * used on a new object with any other parent property map, so we can pass over
1275     * property map invalidation/evolution.
1276     *
1277     * @param initialProto the initial __proto__ to set.
1278     */
1279    public void setInitialProto(final ScriptObject initialProto) {
1280        this.proto = initialProto;
1281    }
1282
1283    /**
1284     * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype.
1285     * @param obj the object literal that needs to have its prototype initialized to the global Object prototype.
1286     */
1287    public static void setGlobalObjectProto(final ScriptObject obj) {
1288        obj.setInitialProto(Global.objectPrototype());
1289    }
1290
1291    /**
1292     * Set the __proto__ of an object with checks.
1293     * This is the built-in operation [[SetPrototypeOf]]
1294     * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V)
1295     *
1296     * @param newProto Prototype to set.
1297     */
1298    public final void setPrototypeOf(final Object newProto) {
1299        if (newProto == null || newProto instanceof ScriptObject) {
1300            if (! isExtensible()) {
1301                // okay to set same proto again - even if non-extensible
1302
1303                if (newProto == getProto()) {
1304                    return;
1305                }
1306                throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1307            }
1308
1309            // check for circularity
1310            ScriptObject p = (ScriptObject)newProto;
1311            while (p != null) {
1312                if (p == this) {
1313                    throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1314                }
1315                p = p.getProto();
1316            }
1317            setProto((ScriptObject)newProto);
1318        } else {
1319            throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1320        }
1321    }
1322
1323    /**
1324     * Set the __proto__ of an object from an object literal.
1325     * See ES6 draft spec: B.3.1 __proto__ Property Names in
1326     * Object Initializers. Step 6 handling of "__proto__".
1327     *
1328     * @param newProto Prototype to set.
1329     */
1330    public final void setProtoFromLiteral(final Object newProto) {
1331        if (newProto == null || newProto instanceof ScriptObject) {
1332            setPrototypeOf(newProto);
1333        } else {
1334            // Some non-object, non-null. Then, we need to set
1335            // Object.prototype as the new __proto__
1336            //
1337            // var obj = { __proto__ : 34 };
1338            // print(obj.__proto__ === Object.prototype); // => true
1339            setPrototypeOf(Global.objectPrototype());
1340        }
1341    }
1342
1343    /**
1344     * return an array of own property keys associated with the object.
1345     *
1346     * @param all True if to include non-enumerable keys.
1347     * @return Array of keys.
1348     */
1349    public final String[] getOwnKeys(final boolean all) {
1350        return getOwnKeys(all, null);
1351    }
1352
1353    /**
1354     * return an array of own property keys associated with the object.
1355     *
1356     * @param all True if to include non-enumerable keys.
1357     * @param nonEnumerable set of non-enumerable properties seen already.Used
1358       to filter out shadowed, but enumerable properties from proto children.
1359     * @return Array of keys.
1360     */
1361    protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
1362        final List<Object> keys    = new ArrayList<>();
1363        final PropertyMap  selfMap = this.getMap();
1364
1365        final ArrayData array  = getArray();
1366
1367        for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
1368            keys.add(JSType.toString(iter.next().longValue()));
1369        }
1370
1371        for (final Property property : selfMap.getProperties()) {
1372            final boolean enumerable = property.isEnumerable();
1373            final String key = property.getKey();
1374            if (all) {
1375                keys.add(key);
1376            } else if (enumerable) {
1377                // either we don't have non-enumerable filter set or filter set
1378                // does not contain the current property.
1379                if (nonEnumerable == null || !nonEnumerable.contains(key)) {
1380                    keys.add(key);
1381                }
1382            } else {
1383                // store this non-enumerable property for later proto walk
1384                if (nonEnumerable != null) {
1385                    nonEnumerable.add(key);
1386                }
1387            }
1388        }
1389
1390        return keys.toArray(new String[keys.size()]);
1391    }
1392
1393    /**
1394     * Check if this ScriptObject has array entries. This means that someone has
1395     * set values with numeric keys in the object.
1396     *
1397     * @return true if array entries exists.
1398     */
1399    public boolean hasArrayEntries() {
1400        return getArray().length() > 0 || getMap().containsArrayKeys();
1401    }
1402
1403    /**
1404     * Return the valid JavaScript type name descriptor
1405     *
1406     * @return "Object"
1407     */
1408    public String getClassName() {
1409        return "Object";
1410    }
1411
1412    /**
1413     * {@code length} is a well known property. This is its getter.
1414     * Note that this *may* be optimized by other classes
1415     *
1416     * @return length property value for this ScriptObject
1417     */
1418    public Object getLength() {
1419        return get("length");
1420    }
1421
1422    /**
1423     * Stateless toString for ScriptObjects.
1424     *
1425     * @return string description of this object, e.g. {@code [object Object]}
1426     */
1427    public String safeToString() {
1428        return "[object " + getClassName() + "]";
1429    }
1430
1431    /**
1432     * Return the default value of the object with a given preferred type hint.
1433     * The preferred type hints are String.class for type String, Number.class
1434     * for type Number. <p>
1435     *
1436     * A <code>hint</code> of null means "no hint".
1437     *
1438     * ECMA 8.12.8 [[DefaultValue]](hint)
1439     *
1440     * @param typeHint the preferred type hint
1441     * @return the default value
1442     */
1443    public Object getDefaultValue(final Class<?> typeHint) {
1444        // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
1445        // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1446        // are being executed in a long-running program, we move the code and their associated dynamic call sites
1447        // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1448        return Context.getGlobal().getDefaultValue(this, typeHint);
1449    }
1450
1451    /**
1452     * Checking whether a script object is an instance of another. Used
1453     * in {@link ScriptFunction} for hasInstance implementation, walks
1454     * the proto chain
1455     *
1456     * @param instance instace to check
1457     * @return true if 'instance' is an instance of this object
1458     */
1459    public boolean isInstance(final ScriptObject instance) {
1460        return false;
1461    }
1462
1463    /**
1464     * Flag this ScriptObject as non extensible
1465     *
1466     * @return the object after being made non extensible
1467     */
1468    public ScriptObject preventExtensions() {
1469        PropertyMap oldMap = getMap();
1470        while (!compareAndSetMap(oldMap,  getMap().preventExtensions())) {
1471            oldMap = getMap();
1472        }
1473
1474        //invalidate any fast array setters
1475        final ArrayData array = getArray();
1476        assert array != null;
1477        setArray(ArrayData.preventExtension(array));
1478        return this;
1479    }
1480
1481    /**
1482     * Check whether if an Object (not just a ScriptObject) represents JavaScript array
1483     *
1484     * @param obj object to check
1485     *
1486     * @return true if array
1487     */
1488    public static boolean isArray(final Object obj) {
1489        return obj instanceof ScriptObject && ((ScriptObject)obj).isArray();
1490    }
1491
1492    /**
1493     * Check if this ScriptObject is an array
1494     * @return true if array
1495     */
1496    public final boolean isArray() {
1497        return (flags & IS_ARRAY) != 0;
1498    }
1499
1500    /**
1501     * Flag this ScriptObject as being an array
1502     */
1503    public final void setIsArray() {
1504        flags |= IS_ARRAY;
1505    }
1506
1507    /**
1508     * Check if this ScriptObject is an {@code arguments} vector
1509     * @return true if arguments vector
1510     */
1511    public final boolean isArguments() {
1512        return (flags & IS_ARGUMENTS) != 0;
1513    }
1514
1515    /**
1516     * Flag this ScriptObject as being an {@code arguments} vector
1517     */
1518    public final void setIsArguments() {
1519        flags |= IS_ARGUMENTS;
1520    }
1521
1522    /**
1523     * Check if this object has non-writable length property
1524     *
1525     * @return {@code true} if 'length' property is non-writable
1526     */
1527    public boolean isLengthNotWritable() {
1528        return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
1529    }
1530
1531    /**
1532     * Flag this object as having non-writable length property.
1533     */
1534    public void setIsLengthNotWritable() {
1535        flags |= IS_LENGTH_NOT_WRITABLE;
1536    }
1537
1538    /**
1539     * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type
1540     * that can handle elementType
1541     * @param elementType elementType
1542     * @return array data
1543     */
1544    public final ArrayData getArray(final Class<?> elementType) {
1545        if (elementType == null) {
1546            return arrayData;
1547        }
1548        final ArrayData newArrayData = arrayData.convert(elementType);
1549        if (newArrayData != arrayData) {
1550            arrayData = newArrayData;
1551        }
1552        return newArrayData;
1553    }
1554
1555    /**
1556     * Get the {@link ArrayData} for this ScriptObject if it is an array
1557     * @return array data
1558     */
1559    public final ArrayData getArray() {
1560        return arrayData;
1561    }
1562
1563    /**
1564     * Set the {@link ArrayData} for this ScriptObject if it is to be an array
1565     * @param arrayData the array data
1566     */
1567    public final void setArray(final ArrayData arrayData) {
1568        this.arrayData = arrayData;
1569    }
1570
1571    /**
1572     * Check if this ScriptObject is extensible
1573     * @return true if extensible
1574     */
1575    public boolean isExtensible() {
1576        return getMap().isExtensible();
1577    }
1578
1579    /**
1580     * ECMAScript 15.2.3.8 - seal implementation
1581     * @return the sealed ScriptObject
1582     */
1583    public ScriptObject seal() {
1584        PropertyMap oldMap = getMap();
1585
1586        while (true) {
1587            final PropertyMap newMap = getMap().seal();
1588
1589            if (!compareAndSetMap(oldMap, newMap)) {
1590                oldMap = getMap();
1591            } else {
1592                setArray(ArrayData.seal(getArray()));
1593                return this;
1594            }
1595        }
1596    }
1597
1598    /**
1599     * Check whether this ScriptObject is sealed
1600     * @return true if sealed
1601     */
1602    public boolean isSealed() {
1603        return getMap().isSealed();
1604    }
1605
1606    /**
1607     * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
1608     * @return the frozen ScriptObject
1609     */
1610    public ScriptObject freeze() {
1611        PropertyMap oldMap = getMap();
1612
1613        while (true) {
1614            final PropertyMap newMap = getMap().freeze();
1615
1616            if (!compareAndSetMap(oldMap, newMap)) {
1617                oldMap = getMap();
1618            } else {
1619                setArray(ArrayData.freeze(getArray()));
1620                return this;
1621            }
1622        }
1623    }
1624
1625    /**
1626     * Check whether this ScriptObject is frozen
1627     * @return true if frozen
1628     */
1629    public boolean isFrozen() {
1630        return getMap().isFrozen();
1631    }
1632
1633
1634    /**
1635     * Flag this ScriptObject as scope
1636     */
1637    public final void setIsScope() {
1638        if (Context.DEBUG) {
1639            scopeCount++;
1640        }
1641        flags |= IS_SCOPE;
1642    }
1643
1644    /**
1645     * Check whether this ScriptObject is scope
1646     * @return true if scope
1647     */
1648    public final boolean isScope() {
1649        return (flags & IS_SCOPE) != 0;
1650    }
1651
1652    /**
1653     * Tag this script object as built in
1654     */
1655    public final void setIsBuiltin() {
1656        flags |= IS_BUILTIN;
1657    }
1658
1659    /**
1660     * Check if this script object is built in
1661     * @return true if build in
1662     */
1663    public final boolean isBuiltin() {
1664        return (flags & IS_BUILTIN) != 0;
1665    }
1666
1667    /**
1668     * Clears the properties from a ScriptObject
1669     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1670     *
1671     * @param strict strict mode or not
1672     */
1673    public void clear(final boolean strict) {
1674        final Iterator<String> iter = propertyIterator();
1675        while (iter.hasNext()) {
1676            delete(iter.next(), strict);
1677        }
1678    }
1679
1680    /**
1681     * Checks if a property with a given key is present in a ScriptObject
1682     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1683     *
1684     * @param key the key to check for
1685     * @return true if a property with the given key exists, false otherwise
1686     */
1687    public boolean containsKey(final Object key) {
1688        return has(key);
1689    }
1690
1691    /**
1692     * Checks if a property with a given value is present in a ScriptObject
1693     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1694     *
1695     * @param value value to check for
1696     * @return true if a property with the given value exists, false otherwise
1697     */
1698    public boolean containsValue(final Object value) {
1699        final Iterator<Object> iter = valueIterator();
1700        while (iter.hasNext()) {
1701            if (iter.next().equals(value)) {
1702                return true;
1703            }
1704        }
1705        return false;
1706    }
1707
1708    /**
1709     * Returns the set of {@literal <property, value>} entries that make up this
1710     * ScriptObject's properties
1711     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1712     *
1713     * @return an entry set of all the properties in this object
1714     */
1715    public Set<Map.Entry<Object, Object>> entrySet() {
1716        final Iterator<String> iter = propertyIterator();
1717        final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
1718        while (iter.hasNext()) {
1719            final Object key = iter.next();
1720            entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
1721        }
1722        return Collections.unmodifiableSet(entries);
1723    }
1724
1725    /**
1726     * Check whether a ScriptObject contains no properties
1727     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1728     *
1729     * @return true if object has no properties
1730     */
1731    public boolean isEmpty() {
1732        return !propertyIterator().hasNext();
1733    }
1734
1735    /**
1736     * Return the set of keys (property names) for all properties
1737     * in this ScriptObject
1738     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1739     *
1740     * @return keySet of this ScriptObject
1741     */
1742    public Set<Object> keySet() {
1743        final Iterator<String> iter = propertyIterator();
1744        final Set<Object> keySet = new HashSet<>();
1745        while (iter.hasNext()) {
1746            keySet.add(iter.next());
1747        }
1748        return Collections.unmodifiableSet(keySet);
1749    }
1750
1751    /**
1752     * Put a property in the ScriptObject
1753     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1754     *
1755     * @param key property key
1756     * @param value property value
1757     * @param strict strict mode or not
1758     * @return oldValue if property with same key existed already
1759     */
1760    public Object put(final Object key, final Object value, final boolean strict) {
1761        final Object oldValue = get(key);
1762        final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1763        set(key, value, scriptObjectFlags);
1764        return oldValue;
1765    }
1766
1767    /**
1768     * Put several properties in the ScriptObject given a mapping
1769     * of their keys to their values
1770     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1771     *
1772     * @param otherMap a {@literal <key,value>} map of properties to add
1773     * @param strict strict mode or not
1774     */
1775    public void putAll(final Map<?, ?> otherMap, final boolean strict) {
1776        final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
1777        for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
1778            set(entry.getKey(), entry.getValue(), scriptObjectFlags);
1779        }
1780    }
1781
1782    /**
1783     * Remove a property from the ScriptObject.
1784     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1785     *
1786     * @param key the key of the property
1787     * @param strict strict mode or not
1788     * @return the oldValue of the removed property
1789     */
1790    public Object remove(final Object key, final boolean strict) {
1791        final Object oldValue = get(key);
1792        delete(key, strict);
1793        return oldValue;
1794    }
1795
1796    /**
1797     * Return the size of the ScriptObject - i.e. the number of properties
1798     * it contains
1799     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1800     *
1801     * @return number of properties in ScriptObject
1802     */
1803    public int size() {
1804        int n = 0;
1805        for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
1806            n++;
1807        }
1808        return n;
1809    }
1810
1811    /**
1812     * Return the values of the properties in the ScriptObject
1813     * (java.util.Map-like method to help ScriptObjectMirror implementation)
1814     *
1815     * @return collection of values for the properties in this ScriptObject
1816     */
1817    public Collection<Object> values() {
1818        final List<Object>     values = new ArrayList<>(size());
1819        final Iterator<Object> iter   = valueIterator();
1820        while (iter.hasNext()) {
1821            values.add(iter.next());
1822        }
1823        return Collections.unmodifiableList(values);
1824    }
1825
1826    /**
1827     * Lookup method that, given a CallSiteDescriptor, looks up the target
1828     * MethodHandle and creates a GuardedInvocation
1829     * with the appropriate guard(s).
1830     *
1831     * @param desc call site descriptor
1832     * @param request the link request
1833     *
1834     * @return GuardedInvocation for the callsite
1835     */
1836    public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
1837        final int c = desc.getNameTokenCount();
1838        // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
1839        // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
1840        // care about them, and just link to whatever is the first operation.
1841        final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
1842        // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
1843        // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
1844        // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
1845        // operation has an associated name or not.
1846        switch (operator) {
1847        case "getProp":
1848        case "getElem":
1849        case "getMethod":
1850            return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
1851        case "setProp":
1852        case "setElem":
1853            return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc, request);
1854        case "call":
1855            return findCallMethod(desc, request);
1856        case "new":
1857            return findNewMethod(desc, request);
1858        case "callMethod":
1859            return findCallMethodMethod(desc, request);
1860        default:
1861            return null;
1862        }
1863    }
1864
1865    /**
1866     * Find the appropriate New method for an invoke dynamic call.
1867     *
1868     * @param desc The invoke dynamic call site descriptor.
1869     * @param request The link request
1870     *
1871     * @return GuardedInvocation to be invoked at call site.
1872     */
1873    protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1874        return notAFunction();
1875    }
1876
1877    /**
1878     * Find the appropriate CALL method for an invoke dynamic call.
1879     * This generates "not a function" always
1880     *
1881     * @param desc    the call site descriptor.
1882     * @param request the link request
1883     *
1884     * @return GuardedInvocation to be invoed at call site.
1885     */
1886    protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1887        return notAFunction();
1888    }
1889
1890    private GuardedInvocation notAFunction() {
1891        throw typeError("not.a.function", ScriptRuntime.safeToString(this));
1892    }
1893
1894    /**
1895     * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
1896     * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
1897     * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
1898     * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
1899     *
1900     * @param desc    the call site descriptor.
1901     * @param request the link request
1902     *
1903     * @return GuardedInvocation to be invoked at call site.
1904     */
1905    protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1906        // R(P0, P1, ...)
1907        final MethodType callType = desc.getMethodType();
1908        // use type Object(P0) for the getter
1909        final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1910        final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1911
1912        // Object(P0) => Object(P0, P1, ...)
1913        final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1914        // R(Object, P0, P1, ...)
1915        final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1916        // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1917        return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1918    }
1919
1920    /**
1921     * Test whether this object contains in its prototype chain or is itself a with-object.
1922     * @return true if a with-object was found
1923     */
1924    final boolean hasWithScope() {
1925        if (isScope()) {
1926            for (ScriptObject obj = this; obj != null; obj = obj.getProto()) {
1927                if (obj instanceof WithObject) {
1928                    return true;
1929                }
1930            }
1931        }
1932        return false;
1933    }
1934
1935    /**
1936     * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
1937     * {@code depth} times.
1938     * @param methodHandle a method handle
1939     * @param depth        distance to target prototype
1940     * @return the filtered method handle
1941     */
1942    static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
1943        if (depth == 0) {
1944            return methodHandle;
1945        }
1946        final int listIndex = depth - 1; // We don't need 0-deep walker
1947        MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null;
1948
1949        if (filter == null) {
1950            filter = addProtoFilter(GETPROTO, depth - 1);
1951            PROTO_FILTERS.add(null);
1952            PROTO_FILTERS.set(listIndex, filter);
1953        }
1954
1955        return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
1956    }
1957
1958    /**
1959     * Find the appropriate GET method for an invoke dynamic call.
1960     *
1961     * @param desc     the call site descriptor
1962     * @param request  the link request
1963     * @param operator operator for get: getProp, getMethod, getElem etc
1964     *
1965     * @return GuardedInvocation to be invoked at call site.
1966     */
1967    protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1968        final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
1969
1970        String name;
1971        name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1972        if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
1973            if (Global.isBuiltinFunctionPrototypeApply()) {
1974                name = "call";
1975            }
1976        }
1977
1978        if (request.isCallSiteUnstable() || hasWithScope()) {
1979            return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
1980        }
1981
1982        final FindProperty find = findProperty(name, true);
1983        MethodHandle mh;
1984
1985        if (find == null) {
1986            switch (operator) {
1987            case "getElem": // getElem only gets here if element name is constant, so treat it like a property access
1988            case "getProp":
1989                return noSuchProperty(desc, request);
1990            case "getMethod":
1991                return noSuchMethod(desc, request);
1992            default:
1993                throw new AssertionError(operator); // never invoked with any other operation
1994            }
1995        }
1996
1997        final GlobalConstants globalConstants = getGlobalConstants();
1998        if (globalConstants != null) {
1999            final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
2000            if (cinv != null) {
2001                return cinv;
2002            }
2003        }
2004
2005        final Class<?> returnType = desc.getMethodType().returnType();
2006        final Property property   = find.getProperty();
2007
2008        final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
2009                NashornCallSiteDescriptor.getProgramPoint(desc) :
2010                UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
2011
2012        mh = find.getGetter(returnType, programPoint, request);
2013        // Get the appropriate guard for this callsite and property.
2014        final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck);
2015        final ScriptObject owner = find.getOwner();
2016        final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
2017
2018        final SwitchPoint protoSwitchPoint;
2019
2020        if (mh == null) {
2021            mh = Lookup.emptyGetter(returnType);
2022            protoSwitchPoint = getProtoSwitchPoint(name, owner);
2023        } else if (!find.isSelf()) {
2024            assert mh.type().returnType().equals(returnType) :
2025                    "return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
2026            if (!(property instanceof UserAccessorProperty)) {
2027                // Add a filter that replaces the self object with the prototype owning the property.
2028                mh = addProtoFilter(mh, find.getProtoChainLength());
2029            }
2030            protoSwitchPoint = getProtoSwitchPoint(name, owner);
2031        } else {
2032            protoSwitchPoint = null;
2033        }
2034
2035        final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
2036        return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
2037    }
2038
2039    private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
2040        Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod);
2041        final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
2042        final MethodHandle guard   = getScriptObjectGuard(desc.getMethodType(), true);
2043        return new GuardedInvocation(invoker, guard);
2044    }
2045
2046    @SuppressWarnings("unused")
2047    private Object megamorphicGet(final String key, final boolean isMethod) {
2048        final FindProperty find = findProperty(key, true);
2049        if (find != null) {
2050            return find.getObjectValue();
2051        }
2052
2053        return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
2054    }
2055
2056    // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
2057    @SuppressWarnings("unused")
2058    private void declareAndSet(final String key, final Object value) {
2059        final PropertyMap oldMap = getMap();
2060        final FindProperty find = findProperty(key, false);
2061        assert find != null;
2062
2063        final Property property = find.getProperty();
2064        assert property != null;
2065        assert property.needsDeclaration();
2066
2067        final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
2068        setMap(newMap);
2069        set(key, value, 0);
2070    }
2071
2072    /**
2073     * Find the appropriate GETINDEX method for an invoke dynamic call.
2074     *
2075     * @param desc    the call site descriptor
2076     * @param request the link request
2077     *
2078     * @return GuardedInvocation to be invoked at call site.
2079     */
2080    protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2081        final MethodType callType                = desc.getMethodType();
2082        final Class<?>   returnType              = callType.returnType();
2083        final Class<?>   returnClass             = returnType.isPrimitive() ? returnType : Object.class;
2084        final Class<?>   keyClass                = callType.parameterType(1);
2085        final boolean    explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2086
2087        final String name;
2088        if (returnClass.isPrimitive()) {
2089            //turn e.g. get with a double into getDouble
2090            final String returnTypeName = returnClass.getName();
2091            name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
2092        } else {
2093            name = "get";
2094        }
2095
2096        final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
2097        return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2098    }
2099
2100    private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) {
2101        return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck);
2102    }
2103
2104    /**
2105     * Find a handle for a getIndex method
2106     * @param returnType     return type for getter
2107     * @param name           name
2108     * @param elementType    index type for getter
2109     * @param desc           call site descriptor
2110     * @return method handle for getter
2111     */
2112    protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
2113        if (!returnType.isPrimitive()) {
2114            return findOwnMH_V(getClass(), name, returnType, elementType);
2115        }
2116
2117        return MH.insertArguments(
2118                findOwnMH_V(getClass(), name, returnType, elementType, int.class),
2119                2,
2120                NashornCallSiteDescriptor.isOptimistic(desc) ?
2121                        NashornCallSiteDescriptor.getProgramPoint(desc) :
2122                        INVALID_PROGRAM_POINT);
2123    }
2124
2125    /**
2126     * Get a switch point for a property with the given {@code name} that will be invalidated when
2127     * the property definition is changed in this object's prototype chain. Returns {@code null} if
2128     * the property is defined in this object itself.
2129     *
2130     * @param name the property name
2131     * @param owner the property owner, null if property is not defined
2132     * @return a SwitchPoint or null
2133     */
2134    public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
2135        if (owner == this || getProto() == null) {
2136            return null;
2137        }
2138
2139        for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
2140            final ScriptObject parent = obj.getProto();
2141            parent.getMap().addListener(name, obj.getMap());
2142        }
2143
2144        return getMap().getSwitchPoint(name);
2145    }
2146
2147    /**
2148     * Find the appropriate SET method for an invoke dynamic call.
2149     *
2150     * @param desc    the call site descriptor
2151     * @param request the link request
2152     *
2153     * @return GuardedInvocation to be invoked at call site.
2154     */
2155    protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2156        final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2157
2158        if (request.isCallSiteUnstable() || hasWithScope()) {
2159            return findMegaMorphicSetMethod(desc, name);
2160        }
2161
2162        final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2163
2164        /*
2165         * If doing property set on a scope object, we should stop proto search on the first
2166         * non-scope object. Without this, for example, when assigning "toString" on global scope,
2167         * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
2168         *
2169         * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
2170         */
2171        FindProperty find = findProperty(name, true, this);
2172
2173        // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
2174        if (find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
2175            // We should still check if inherited data property is not writable
2176            if (isExtensible() && !find.getProperty().isWritable()) {
2177                return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2178            }
2179            // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well.
2180            if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) {
2181                find = null;
2182            }
2183        }
2184
2185        if (find != null) {
2186            if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
2187                if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) {
2188                    throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode.
2189                }
2190                // Existing, non-writable property
2191                return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
2192            }
2193        } else {
2194            if (!isExtensible()) {
2195                return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false);
2196            }
2197        }
2198
2199        final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
2200
2201        final GlobalConstants globalConstants = getGlobalConstants();
2202        if (globalConstants != null) {
2203            final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
2204            if (cinv != null) {
2205                return cinv;
2206            }
2207        }
2208
2209        return inv;
2210    }
2211
2212    private GlobalConstants getGlobalConstants() {
2213        // Avoid hitting getContext() which might be costly for a non-Global unless needed.
2214        return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
2215    }
2216
2217    private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
2218        final String  name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2219        if (NashornCallSiteDescriptor.isStrict(desc)) {
2220            throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this));
2221        }
2222        assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
2223        return new GuardedInvocation(
2224                Lookup.EMPTY_SETTER,
2225                NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
2226                getProtoSwitchPoint(name, null),
2227                explicitInstanceOfCheck ? null : ClassCastException.class);
2228    }
2229
2230    @SuppressWarnings("unused")
2231    private boolean extensionCheck(final boolean isStrict, final String name) {
2232        if (isExtensible()) {
2233            return true; //go on and do the set. this is our guard
2234        } else if (isStrict) {
2235            //throw an error for attempting to do the set in strict mode
2236            throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this));
2237        } else {
2238            //not extensible, non strict - this is a nop
2239            return false;
2240        }
2241    }
2242
2243    private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
2244        final MethodType        type = desc.getMethodType().insertParameterTypes(1, Object.class);
2245        //never bother with ClassCastExceptionGuard for megamorphic callsites
2246        final GuardedInvocation inv = findSetIndexMethod(getClass(), desc, false, type);
2247        return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
2248    }
2249
2250    @SuppressWarnings("unused")
2251    private static Object globalFilter(final Object object) {
2252        ScriptObject sobj = (ScriptObject) object;
2253        while (sobj != null && !(sobj instanceof Global)) {
2254            sobj = sobj.getProto();
2255        }
2256        return sobj;
2257    }
2258
2259    /**
2260     * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray}
2261     * provides special quick accessor linkage for continuous arrays that are represented as Java arrays
2262     *
2263     * @param desc    call site descriptor
2264     * @param request link request
2265     *
2266     * @return GuardedInvocation to be invoked at call site.
2267     */
2268    protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
2269        return findSetIndexMethod(getClass(), desc, explicitInstanceOfCheck(desc, request), desc.getMethodType());
2270    }
2271
2272    /**
2273     * Find the appropriate SETINDEX method for an invoke dynamic call.
2274     *
2275     * @param clazz the receiver class
2276     * @param desc  the call site descriptor
2277     * @param explicitInstanceOfCheck add an explicit instanceof check?
2278     * @param callType the method type at the call site
2279     *
2280     * @return GuardedInvocation to be invoked at call site.
2281     */
2282    private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) {
2283        assert callType.parameterCount() == 3;
2284        final Class<?> keyClass   = callType.parameterType(1);
2285        final Class<?> valueClass = callType.parameterType(2);
2286
2287        MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, int.class);
2288        methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc));
2289
2290        return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
2291    }
2292
2293    /**
2294     * Fall back if a function property is not found.
2295     * @param desc The call site descriptor
2296     * @param request the link request
2297     * @return GuardedInvocation to be invoked at call site.
2298     */
2299    public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
2300        final String       name      = desc.getNameToken(2);
2301        final FindProperty find      = findProperty(NO_SUCH_METHOD_NAME, true);
2302        final boolean      scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
2303
2304        if (find == null) {
2305            return noSuchProperty(desc, request);
2306        }
2307
2308        final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
2309
2310        final Object value = find.getObjectValue();
2311        if (!(value instanceof ScriptFunction)) {
2312            return createEmptyGetter(desc, explicitInstanceOfCheck, name);
2313        }
2314
2315        final ScriptFunction func = (ScriptFunction)value;
2316        final Object         thiz = scopeCall && func.isStrict() ? UNDEFINED : this;
2317        // TODO: It'd be awesome if we could bind "name" without binding "this".
2318        // Since we're binding this we must use an identity guard here.
2319        return new GuardedInvocation(
2320                MH.dropArguments(
2321                        MH.constant(
2322                                ScriptFunction.class,
2323                                func.makeBoundFunction(thiz, new Object[] { name })),
2324                        0,
2325                        Object.class),
2326                NashornGuards.combineGuards(
2327                        NashornGuards.getIdentityGuard(this),
2328                        NashornGuards.getMapGuard(getMap(), true)));
2329    }
2330
2331    /**
2332     * Fall back if a property is not found.
2333     * @param desc the call site descriptor.
2334     * @param request the link request
2335     * @return GuardedInvocation to be invoked at call site.
2336     */
2337    public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
2338        final String       name        = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
2339        final FindProperty find        = findProperty(NO_SUCH_PROPERTY_NAME, true);
2340        final boolean      scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
2341
2342        if (find != null) {
2343            final Object   value = find.getObjectValue();
2344            ScriptFunction func  = null;
2345            MethodHandle   mh    = null;
2346
2347            if (value instanceof ScriptFunction) {
2348                func = (ScriptFunction)value;
2349                mh   = getCallMethodHandle(func, desc.getMethodType(), name);
2350            }
2351
2352            if (mh != null) {
2353                assert func != null;
2354                if (scopeAccess && func.isStrict()) {
2355                    mh = bindTo(mh, UNDEFINED);
2356                }
2357
2358                return new GuardedInvocation(
2359                        mh,
2360                        find.isSelf()?
2361                            getKnownFunctionPropertyGuardSelf(
2362                                getMap(),
2363                                find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2364                                func)
2365                            :
2366                            //TODO this always does a scriptobject check
2367                            getKnownFunctionPropertyGuardProto(
2368                                getMap(),
2369                                find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
2370                                find.getProtoChainLength(),
2371                                func),
2372                        getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
2373                        //TODO this doesn't need a ClassCastException as guard always checks script object
2374                        null);
2375            }
2376        }
2377
2378        if (scopeAccess) {
2379            throw referenceError("not.defined", name);
2380        }
2381
2382        return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name);
2383    }
2384
2385    /**
2386     * Invoke fall back if a property is not found.
2387     * @param name Name of property.
2388     * @param programPoint program point
2389     * @return Result from call.
2390     */
2391    protected Object invokeNoSuchProperty(final String name, final int programPoint) {
2392        final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
2393
2394        Object ret = UNDEFINED;
2395
2396        if (find != null) {
2397            final Object func = find.getObjectValue();
2398
2399            if (func instanceof ScriptFunction) {
2400                ret = ScriptRuntime.apply((ScriptFunction)func, this, name);
2401            }
2402        }
2403
2404        if (isValid(programPoint)) {
2405            throw new UnwarrantedOptimismException(ret, programPoint);
2406        }
2407
2408        return ret;
2409    }
2410
2411
2412    /**
2413     * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
2414     * @param name the method name
2415     * @return the bound function, or undefined
2416     */
2417    private Object getNoSuchMethod(final String name, final int programPoint) {
2418        final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
2419
2420        if (find == null) {
2421            return invokeNoSuchProperty(name, programPoint);
2422        }
2423
2424        final Object value = find.getObjectValue();
2425        if (!(value instanceof ScriptFunction)) {
2426            return UNDEFINED;
2427        }
2428
2429        return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
2430    }
2431
2432    private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) {
2433        if (NashornCallSiteDescriptor.isOptimistic(desc)) {
2434            throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
2435        }
2436
2437        return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
2438                NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null),
2439                explicitInstanceOfCheck ? null : ClassCastException.class);
2440    }
2441
2442    private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
2443        protected T[] values;
2444        protected final ScriptObject object;
2445        private int index;
2446
2447        ScriptObjectIterator(final ScriptObject object) {
2448            this.object = object;
2449        }
2450
2451        protected abstract void init();
2452
2453        @Override
2454        public boolean hasNext() {
2455            if (values == null) {
2456                init();
2457            }
2458            return index < values.length;
2459        }
2460
2461        @Override
2462        public T next() {
2463            if (values == null) {
2464                init();
2465            }
2466            return values[index++];
2467        }
2468
2469        @Override
2470        public void remove() {
2471            throw new UnsupportedOperationException("remove");
2472        }
2473    }
2474
2475    private static class KeyIterator extends ScriptObjectIterator<String> {
2476        KeyIterator(final ScriptObject object) {
2477            super(object);
2478        }
2479
2480        @Override
2481        protected void init() {
2482            final Set<String> keys = new LinkedHashSet<>();
2483            final Set<String> nonEnumerable = new HashSet<>();
2484            for (ScriptObject self = object; self != null; self = self.getProto()) {
2485                keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable)));
2486            }
2487            this.values = keys.toArray(new String[keys.size()]);
2488        }
2489    }
2490
2491    private static class ValueIterator extends ScriptObjectIterator<Object> {
2492        ValueIterator(final ScriptObject object) {
2493            super(object);
2494        }
2495
2496        @Override
2497        protected void init() {
2498            final ArrayList<Object> valueList = new ArrayList<>();
2499            final Set<String> nonEnumerable = new HashSet<>();
2500            for (ScriptObject self = object; self != null; self = self.getProto()) {
2501                for (final String key : self.getOwnKeys(false, nonEnumerable)) {
2502                    valueList.add(self.get(key));
2503                }
2504            }
2505            this.values = valueList.toArray(new Object[valueList.size()]);
2506        }
2507    }
2508
2509    /**
2510     * Add a spill property for the given key.
2511     * @param key    Property key.
2512     * @param flags  Property flags.
2513     * @return Added property.
2514     */
2515    private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
2516        final PropertyMap propertyMap = getMap();
2517        final int fieldSlot  = propertyMap.getFreeFieldSlot();
2518        final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
2519
2520        Property property;
2521        if (fieldSlot > -1) {
2522            property = hasInitialValue ?
2523                new AccessorProperty(key, propertyFlags, fieldSlot, this, value) :
2524                new AccessorProperty(key, propertyFlags, getClass(), fieldSlot);
2525            property = addOwnProperty(property);
2526        } else {
2527            final int spillSlot = propertyMap.getFreeSpillSlot();
2528            property = hasInitialValue ?
2529                new SpillProperty(key, propertyFlags, spillSlot, this, value) :
2530                new SpillProperty(key, propertyFlags, spillSlot);
2531            property = addOwnProperty(property);
2532            ensureSpillSize(property.getSlot());
2533        }
2534        return property;
2535    }
2536
2537    /**
2538     * Add a spill entry for the given key.
2539     * @param key Property key.
2540     * @return Setter method handle.
2541     */
2542    MethodHandle addSpill(final Class<?> type, final String key) {
2543        return addSpillProperty(key, 0, null, false).getSetter(type, getMap());
2544    }
2545
2546    /**
2547     * Make sure arguments are paired correctly, with respect to more parameters than declared,
2548     * fewer parameters than declared and other things that JavaScript allows. This might involve
2549     * creating collectors.
2550     *
2551     * @param methodHandle method handle for invoke
2552     * @param callType     type of the call
2553     *
2554     * @return method handle with adjusted arguments
2555     */
2556    protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
2557        return pairArguments(methodHandle, callType, null);
2558    }
2559
2560    /**
2561     * Make sure arguments are paired correctly, with respect to more parameters than declared,
2562     * fewer parameters than declared and other things that JavaScript allows. This might involve
2563     * creating collectors.
2564     *
2565     * Make sure arguments are paired correctly.
2566     * @param methodHandle MethodHandle to adjust.
2567     * @param callType     MethodType of the call site.
2568     * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
2569     * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
2570     * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
2571     * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
2572     *
2573     * @return method handle with adjusted arguments
2574     */
2575    public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
2576        final MethodType methodType = methodHandle.type();
2577        if (methodType.equals(callType.changeReturnType(methodType.returnType()))) {
2578            return methodHandle;
2579        }
2580
2581        final int parameterCount = methodType.parameterCount();
2582        final int callCount      = callType.parameterCount();
2583
2584        final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
2585        final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 &&
2586                callType.parameterType(callCount - 1).isArray();
2587
2588        if (isCalleeVarArg) {
2589            return isCallerVarArg ?
2590                methodHandle :
2591                MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
2592        }
2593
2594        if (isCallerVarArg) {
2595            return adaptHandleToVarArgCallSite(methodHandle, callCount);
2596        }
2597
2598        if (callCount < parameterCount) {
2599            final int      missingArgs = parameterCount - callCount;
2600            final Object[] fillers     = new Object[missingArgs];
2601
2602            Arrays.fill(fillers, UNDEFINED);
2603
2604            if (isCalleeVarArg) {
2605                fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY;
2606            }
2607
2608            return MH.insertArguments(
2609                methodHandle,
2610                parameterCount - missingArgs,
2611                fillers);
2612        }
2613
2614        if (callCount > parameterCount) {
2615            final int discardedArgs = callCount - parameterCount;
2616
2617            final Class<?>[] discards = new Class<?>[discardedArgs];
2618            Arrays.fill(discards, Object.class);
2619
2620            return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
2621        }
2622
2623        return methodHandle;
2624    }
2625
2626    static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) {
2627        final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
2628        return MH.filterArguments(
2629            MH.asSpreader(
2630                mh,
2631                Object[].class,
2632                spreadArgs),
2633            callSiteParamCount - 1,
2634            MH.insertArguments(
2635                TRUNCATINGFILTER,
2636                0,
2637                spreadArgs)
2638            );
2639    }
2640
2641    @SuppressWarnings("unused")
2642    private static Object[] truncatingFilter(final int n, final Object[] array) {
2643        final int length = array == null ? 0 : array.length;
2644        if (n == length) {
2645            return array == null ? ScriptRuntime.EMPTY_ARRAY : array;
2646        }
2647
2648        final Object[] newArray = new Object[n];
2649
2650        if (array != null) {
2651            System.arraycopy(array, 0, newArray, 0, Math.min(n, length));
2652        }
2653
2654        if (length < n) {
2655            final Object fill = UNDEFINED;
2656
2657            for (int i = length; i < n; i++) {
2658                newArray[i] = fill;
2659            }
2660        }
2661
2662        return newArray;
2663    }
2664
2665    /**
2666      * Numeric length setter for length property
2667      *
2668      * @param newLength new length to set
2669      */
2670    public final void setLength(final long newLength) {
2671        ArrayData data = getArray();
2672        final long arrayLength = data.length();
2673        if (newLength == arrayLength) {
2674            return;
2675        }
2676
2677        if (newLength > arrayLength) {
2678            data = data.ensure(newLength - 1);
2679            if (data.canDelete(arrayLength, newLength - 1, false)) {
2680               data = data.delete(arrayLength, newLength - 1);
2681            }
2682            setArray(data);
2683            return;
2684        }
2685
2686        if (newLength < arrayLength) {
2687           long actualLength = newLength;
2688
2689           // Check for numeric keys in property map and delete them or adjust length, depending on whether
2690           // they're defined as configurable. See ES5 #15.4.5.2
2691           if (getMap().containsArrayKeys()) {
2692
2693               for (long l = arrayLength - 1; l >= newLength; l--) {
2694                   final FindProperty find = findProperty(JSType.toString(l), false);
2695
2696                   if (find != null) {
2697
2698                       if (find.getProperty().isConfigurable()) {
2699                           deleteOwnProperty(find.getProperty());
2700                       } else {
2701                           actualLength = l + 1;
2702                           break;
2703                       }
2704                   }
2705               }
2706           }
2707
2708           setArray(data.shrink(actualLength));
2709           data.setLength(actualLength);
2710       }
2711    }
2712
2713    private int getInt(final int index, final String key, final int programPoint) {
2714        if (isValidArrayIndex(index)) {
2715            for (ScriptObject object = this; ; ) {
2716                if (object.getMap().containsArrayKeys()) {
2717                    final FindProperty find = object.findProperty(key, false, this);
2718
2719                    if (find != null) {
2720                        return getIntValue(find, programPoint);
2721                    }
2722                }
2723
2724                if ((object = object.getProto()) == null) {
2725                    break;
2726                }
2727
2728                final ArrayData array = object.getArray();
2729
2730                if (array.has(index)) {
2731                    return isValid(programPoint) ?
2732                        array.getIntOptimistic(index, programPoint) :
2733                        array.getInt(index);
2734                }
2735            }
2736        } else {
2737            final FindProperty find = findProperty(key, true);
2738
2739            if (find != null) {
2740                return getIntValue(find, programPoint);
2741            }
2742        }
2743
2744        return JSType.toInt32(invokeNoSuchProperty(key, programPoint));
2745    }
2746
2747    @Override
2748    public int getInt(final Object key, final int programPoint) {
2749        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2750        final int       index        = getArrayIndex(primitiveKey);
2751        final ArrayData array        = getArray();
2752
2753        if (array.has(index)) {
2754            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2755        }
2756
2757        return getInt(index, JSType.toString(primitiveKey), programPoint);
2758    }
2759
2760    @Override
2761    public int getInt(final double key, final int programPoint) {
2762        final int       index = getArrayIndex(key);
2763        final ArrayData array = getArray();
2764
2765        if (array.has(index)) {
2766            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2767        }
2768
2769        return getInt(index, JSType.toString(key), programPoint);
2770    }
2771
2772    @Override
2773    public int getInt(final long key, final int programPoint) {
2774        final int       index = getArrayIndex(key);
2775        final ArrayData array = getArray();
2776
2777        if (array.has(index)) {
2778            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
2779        }
2780
2781        return getInt(index, JSType.toString(key), programPoint);
2782    }
2783
2784    @Override
2785    public int getInt(final int key, final int programPoint) {
2786        final int       index = getArrayIndex(key);
2787        final ArrayData array = getArray();
2788
2789        if (array.has(index)) {
2790            return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key);
2791        }
2792
2793        return getInt(index, JSType.toString(key), programPoint);
2794    }
2795
2796    private long getLong(final int index, final String key, final int programPoint) {
2797        if (isValidArrayIndex(index)) {
2798            for (ScriptObject object = this; ; ) {
2799                if (object.getMap().containsArrayKeys()) {
2800                    final FindProperty find = object.findProperty(key, false, this);
2801                    if (find != null) {
2802                        return getLongValue(find, programPoint);
2803                    }
2804                }
2805
2806                if ((object = object.getProto()) == null) {
2807                    break;
2808                }
2809
2810                final ArrayData array = object.getArray();
2811
2812                if (array.has(index)) {
2813                    return isValid(programPoint) ?
2814                        array.getLongOptimistic(index, programPoint) :
2815                        array.getLong(index);
2816                }
2817            }
2818        } else {
2819            final FindProperty find = findProperty(key, true);
2820
2821            if (find != null) {
2822                return getLongValue(find, programPoint);
2823            }
2824        }
2825
2826        return JSType.toLong(invokeNoSuchProperty(key, programPoint));
2827    }
2828
2829    @Override
2830    public long getLong(final Object key, final int programPoint) {
2831        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2832        final int       index        = getArrayIndex(primitiveKey);
2833        final ArrayData array        = getArray();
2834
2835        if (array.has(index)) {
2836            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2837        }
2838
2839        return getLong(index, JSType.toString(primitiveKey), programPoint);
2840    }
2841
2842    @Override
2843    public long getLong(final double key, final int programPoint) {
2844        final int       index = getArrayIndex(key);
2845        final ArrayData array = getArray();
2846
2847        if (array.has(index)) {
2848            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2849        }
2850
2851        return getLong(index, JSType.toString(key), programPoint);
2852    }
2853
2854    @Override
2855    public long getLong(final long key, final int programPoint) {
2856        final int       index = getArrayIndex(key);
2857        final ArrayData array = getArray();
2858
2859        if (array.has(index)) {
2860            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
2861        }
2862
2863        return getLong(index, JSType.toString(key), programPoint);
2864    }
2865
2866    @Override
2867    public long getLong(final int key, final int programPoint) {
2868        final int       index = getArrayIndex(key);
2869        final ArrayData array = getArray();
2870
2871        if (array.has(index)) {
2872            return isValid(programPoint) ? array.getLongOptimistic(key, programPoint) : array.getLong(key);
2873        }
2874
2875        return getLong(index, JSType.toString(key), programPoint);
2876    }
2877
2878    private double getDouble(final int index, final String key, final int programPoint) {
2879        if (isValidArrayIndex(index)) {
2880            for (ScriptObject object = this; ; ) {
2881                if (object.getMap().containsArrayKeys()) {
2882                    final FindProperty find = object.findProperty(key, false, this);
2883                    if (find != null) {
2884                        return getDoubleValue(find, programPoint);
2885                    }
2886                }
2887
2888                if ((object = object.getProto()) == null) {
2889                    break;
2890                }
2891
2892                final ArrayData array = object.getArray();
2893
2894                if (array.has(index)) {
2895                    return isValid(programPoint) ?
2896                        array.getDoubleOptimistic(index, programPoint) :
2897                        array.getDouble(index);
2898                }
2899            }
2900        } else {
2901            final FindProperty find = findProperty(key, true);
2902
2903            if (find != null) {
2904                return getDoubleValue(find, programPoint);
2905            }
2906        }
2907
2908        return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT));
2909    }
2910
2911    @Override
2912    public double getDouble(final Object key, final int programPoint) {
2913        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2914        final int       index        = getArrayIndex(primitiveKey);
2915        final ArrayData array        = getArray();
2916
2917        if (array.has(index)) {
2918            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2919        }
2920
2921        return getDouble(index, JSType.toString(primitiveKey), programPoint);
2922    }
2923
2924    @Override
2925    public double getDouble(final double key, final int programPoint) {
2926        final int       index = getArrayIndex(key);
2927        final ArrayData array = getArray();
2928
2929        if (array.has(index)) {
2930            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2931        }
2932
2933        return getDouble(index, JSType.toString(key), programPoint);
2934    }
2935
2936    @Override
2937    public double getDouble(final long key, final int programPoint) {
2938        final int       index = getArrayIndex(key);
2939        final ArrayData array = getArray();
2940
2941        if (array.has(index)) {
2942            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
2943        }
2944
2945        return getDouble(index, JSType.toString(key), programPoint);
2946    }
2947
2948    @Override
2949    public double getDouble(final int key, final int programPoint) {
2950        final int       index = getArrayIndex(key);
2951        final ArrayData array = getArray();
2952
2953        if (array.has(index)) {
2954            return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key);
2955        }
2956
2957        return getDouble(index, JSType.toString(key), programPoint);
2958    }
2959
2960    private Object get(final int index, final String key) {
2961        if (isValidArrayIndex(index)) {
2962            for (ScriptObject object = this; ; ) {
2963                if (object.getMap().containsArrayKeys()) {
2964                    final FindProperty find = object.findProperty(key, false, this);
2965
2966                    if (find != null) {
2967                        return find.getObjectValue();
2968                    }
2969                }
2970
2971                if ((object = object.getProto()) == null) {
2972                    break;
2973                }
2974
2975                final ArrayData array = object.getArray();
2976
2977                if (array.has(index)) {
2978                    return array.getObject(index);
2979                }
2980            }
2981        } else {
2982            final FindProperty find = findProperty(key, true);
2983
2984            if (find != null) {
2985                return find.getObjectValue();
2986            }
2987        }
2988
2989        return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
2990    }
2991
2992    @Override
2993    public Object get(final Object key) {
2994        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
2995        final int       index        = getArrayIndex(primitiveKey);
2996        final ArrayData array        = getArray();
2997
2998        if (array.has(index)) {
2999            return array.getObject(index);
3000        }
3001
3002        return get(index, JSType.toString(primitiveKey));
3003    }
3004
3005    @Override
3006    public Object get(final double key) {
3007        final int index = getArrayIndex(key);
3008        final ArrayData array = getArray();
3009
3010        if (array.has(index)) {
3011            return array.getObject(index);
3012        }
3013
3014        return get(index, JSType.toString(key));
3015    }
3016
3017    @Override
3018    public Object get(final long key) {
3019        final int index = getArrayIndex(key);
3020        final ArrayData array = getArray();
3021
3022        if (array.has(index)) {
3023            return array.getObject(index);
3024        }
3025
3026        return get(index, JSType.toString(key));
3027    }
3028
3029    @Override
3030    public Object get(final int key) {
3031        final int index = getArrayIndex(key);
3032        final ArrayData array = getArray();
3033
3034        if (array.has(index)) {
3035            return array.getObject(index);
3036        }
3037
3038        return get(index, JSType.toString(key));
3039    }
3040
3041    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) {
3042        if (getMap().containsArrayKeys()) {
3043            final String       key  = JSType.toString(longIndex);
3044            final FindProperty find = findProperty(key, true);
3045            if (find != null) {
3046                setObject(find, callSiteFlags, key, value);
3047                return true;
3048            }
3049        }
3050        return false;
3051    }
3052
3053    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final int callSiteFlags) {
3054        if (getMap().containsArrayKeys()) {
3055            final String       key  = JSType.toString(longIndex);
3056            final FindProperty find = findProperty(key, true);
3057            if (find != null) {
3058                setObject(find, callSiteFlags, key, value);
3059                return true;
3060            }
3061        }
3062        return false;
3063    }
3064
3065    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) {
3066         if (getMap().containsArrayKeys()) {
3067            final String       key  = JSType.toString(longIndex);
3068            final FindProperty find = findProperty(key, true);
3069            if (find != null) {
3070                setObject(find, callSiteFlags, key, value);
3071                return true;
3072            }
3073        }
3074        return false;
3075    }
3076
3077    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) {
3078        if (getMap().containsArrayKeys()) {
3079            final String       key  = JSType.toString(longIndex);
3080            final FindProperty find = findProperty(key, true);
3081            if (find != null) {
3082                setObject(find, callSiteFlags, key, value);
3083                return true;
3084            }
3085        }
3086        return false;
3087    }
3088
3089    //value agnostic
3090    private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) {
3091        if (longIndex >= oldLength) {
3092            if (!isExtensible()) {
3093                if (isStrictFlag(callSiteFlags)) {
3094                    throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this));
3095                }
3096                return true;
3097            }
3098            setArray(getArray().ensure(longIndex));
3099        }
3100        return false;
3101    }
3102
3103    private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) {
3104        if (longIndex > oldLength) {
3105            ArrayData array = getArray();
3106            if (array.canDelete(oldLength, longIndex - 1, strict)) {
3107                array = array.delete(oldLength, longIndex - 1);
3108            }
3109            setArray(array);
3110        }
3111    }
3112
3113    private void doesNotHave(final int index, final int value, final int callSiteFlags) {
3114        final long oldLength = getArray().length();
3115        final long longIndex = ArrayIndex.toLongIndex(index);
3116        if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3117            final boolean strict = isStrictFlag(callSiteFlags);
3118            setArray(getArray().set(index, value, strict));
3119            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3120        }
3121    }
3122
3123    private void doesNotHave(final int index, final long value, final int callSiteFlags) {
3124        final long oldLength = getArray().length();
3125        final long longIndex = ArrayIndex.toLongIndex(index);
3126        if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3127            final boolean strict = isStrictFlag(callSiteFlags);
3128            setArray(getArray().set(index, value, strict));
3129            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3130        }
3131    }
3132
3133    private void doesNotHave(final int index, final double value, final int callSiteFlags) {
3134        final long oldLength = getArray().length();
3135        final long longIndex = ArrayIndex.toLongIndex(index);
3136        if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3137            final boolean strict = isStrictFlag(callSiteFlags);
3138            setArray(getArray().set(index, value, strict));
3139            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3140        }
3141    }
3142
3143    private void doesNotHave(final int index, final Object value, final int callSiteFlags) {
3144        final long oldLength = getArray().length();
3145        final long longIndex = ArrayIndex.toLongIndex(index);
3146        if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
3147            final boolean strict = isStrictFlag(callSiteFlags);
3148            setArray(getArray().set(index, value, strict));
3149            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
3150        }
3151    }
3152
3153    /**
3154     * This is the most generic of all Object setters. Most of the others use this in some form.
3155     * TODO: should be further specialized
3156     *
3157     * @param find          found property
3158     * @param callSiteFlags callsite flags
3159     * @param key           property key
3160     * @param value         property value
3161     */
3162    public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) {
3163        FindProperty f = find;
3164
3165        invalidateGlobalConstant(key);
3166
3167        if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
3168            final boolean isScope = isScopeFlag(callSiteFlags);
3169            // If the start object of the find is not this object it means the property was found inside a
3170            // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set'
3171            // to the 'with' object.
3172            // Note that although a 'set' operation involving a with statement follows scope rules outside
3173            // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists),
3174            // it follows non-scope rules inside the 'with' expression (set is performed on the top level object).
3175            // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object.
3176            if (isScope && f.getSelf() != this) {
3177                f.getSelf().setObject(null, 0, key, value);
3178                return;
3179            }
3180            // Setting a property should not modify the property in prototype unless this is a scope callsite
3181            // and the owner is a scope object as well (with the exception of 'with' statement handled above).
3182            if (!isScope || !f.getOwner().isScope()) {
3183                f = null;
3184            }
3185        }
3186
3187        if (f != null) {
3188            if (!f.getProperty().isWritable()) {
3189                if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
3190                    throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode.
3191                }
3192                if (isStrictFlag(callSiteFlags)) {
3193                    throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
3194                }
3195                return;
3196            }
3197
3198            f.setValue(value, isStrictFlag(callSiteFlags));
3199
3200        } else if (!isExtensible()) {
3201            if (isStrictFlag(callSiteFlags)) {
3202                throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
3203            }
3204        } else {
3205            ScriptObject sobj = this;
3206            // undefined scope properties are set in the global object.
3207            if (isScope()) {
3208                while (sobj != null && !(sobj instanceof Global)) {
3209                    sobj = sobj.getProto();
3210                }
3211                assert sobj != null : "no parent global object in scope";
3212            }
3213            //this will unbox any Number object to its primitive type in case the
3214            //property supports primitive types, so it doesn't matter that it comes
3215            //in as an Object.
3216            sobj.addSpillProperty(key, 0, value, true);
3217        }
3218    }
3219
3220    @Override
3221    public void set(final Object key, final int value, final int callSiteFlags) {
3222        final Object primitiveKey = JSType.toPrimitive(key, String.class);
3223        final int    index        = getArrayIndex(primitiveKey);
3224
3225        if (isValidArrayIndex(index)) {
3226            final ArrayData data = getArray();
3227            if (data.has(index)) {
3228                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3229            } else {
3230                doesNotHave(index, value, callSiteFlags);
3231            }
3232
3233            return;
3234        }
3235
3236        final String propName = JSType.toString(primitiveKey);
3237        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3238    }
3239
3240    @Override
3241    public void set(final Object key, final long value, final int callSiteFlags) {
3242        final Object primitiveKey = JSType.toPrimitive(key, String.class);
3243        final int    index        = getArrayIndex(primitiveKey);
3244
3245        if (isValidArrayIndex(index)) {
3246            final ArrayData data = getArray();
3247            if (data.has(index)) {
3248                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3249            } else {
3250                doesNotHave(index, value, callSiteFlags);
3251            }
3252
3253            return;
3254        }
3255
3256        final String propName = JSType.toString(primitiveKey);
3257        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3258    }
3259
3260    @Override
3261    public void set(final Object key, final double value, final int callSiteFlags) {
3262        final Object primitiveKey = JSType.toPrimitive(key, String.class);
3263        final int    index        = getArrayIndex(primitiveKey);
3264
3265        if (isValidArrayIndex(index)) {
3266            final ArrayData data = getArray();
3267            if (data.has(index)) {
3268                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3269            } else {
3270                doesNotHave(index, value, callSiteFlags);
3271            }
3272
3273            return;
3274        }
3275
3276        final String propName = JSType.toString(primitiveKey);
3277        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3278    }
3279
3280    @Override
3281    public void set(final Object key, final Object value, final int callSiteFlags) {
3282        final Object primitiveKey = JSType.toPrimitive(key, String.class);
3283        final int    index        = getArrayIndex(primitiveKey);
3284
3285        if (isValidArrayIndex(index)) {
3286            final ArrayData data = getArray();
3287            if (data.has(index)) {
3288                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3289            } else {
3290                doesNotHave(index, value, callSiteFlags);
3291            }
3292
3293            return;
3294        }
3295
3296        final String propName = JSType.toString(primitiveKey);
3297        setObject(findProperty(propName, true), callSiteFlags, propName, value);
3298    }
3299
3300    @Override
3301    public void set(final double key, final int value, final int callSiteFlags) {
3302        final int index = getArrayIndex(key);
3303
3304        if (isValidArrayIndex(index)) {
3305            final ArrayData data = getArray();
3306            if (data.has(index)) {
3307                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3308            } else {
3309                doesNotHave(index, value, callSiteFlags);
3310            }
3311
3312            return;
3313        }
3314
3315        final String propName = JSType.toString(key);
3316        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3317    }
3318
3319    @Override
3320    public void set(final double key, final long value, final int callSiteFlags) {
3321        final int index = getArrayIndex(key);
3322
3323        if (isValidArrayIndex(index)) {
3324            final ArrayData data = getArray();
3325            if (data.has(index)) {
3326                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3327            } else {
3328                doesNotHave(index, value, callSiteFlags);
3329            }
3330
3331            return;
3332        }
3333
3334        final String propName = JSType.toString(key);
3335        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3336    }
3337
3338    @Override
3339    public void set(final double key, final double value, final int callSiteFlags) {
3340        final int index = getArrayIndex(key);
3341
3342        if (isValidArrayIndex(index)) {
3343            final ArrayData data = getArray();
3344            if (data.has(index)) {
3345                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3346            } else {
3347                doesNotHave(index, value, callSiteFlags);
3348            }
3349
3350            return;
3351        }
3352
3353        final String propName = JSType.toString(key);
3354        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3355    }
3356
3357    @Override
3358    public void set(final double key, final Object value, final int callSiteFlags) {
3359        final int index = getArrayIndex(key);
3360
3361        if (isValidArrayIndex(index)) {
3362            final ArrayData data = getArray();
3363            if (data.has(index)) {
3364                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3365            } else {
3366                doesNotHave(index, value, callSiteFlags);
3367            }
3368
3369            return;
3370        }
3371
3372        final String propName = JSType.toString(key);
3373        setObject(findProperty(propName, true), callSiteFlags, propName, value);
3374    }
3375
3376    @Override
3377    public void set(final long key, final int value, final int callSiteFlags) {
3378        final int index = getArrayIndex(key);
3379
3380        if (isValidArrayIndex(index)) {
3381            final ArrayData data = getArray();
3382            if (data.has(index)) {
3383                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3384            } else {
3385                doesNotHave(index, value, callSiteFlags);
3386            }
3387
3388            return;
3389        }
3390
3391        final String propName = JSType.toString(key);
3392        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3393    }
3394
3395    @Override
3396    public void set(final long key, final long value, final int callSiteFlags) {
3397        final int index = getArrayIndex(key);
3398
3399        if (isValidArrayIndex(index)) {
3400            final ArrayData data = getArray();
3401            if (data.has(index)) {
3402                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3403            } else {
3404                doesNotHave(index, value, callSiteFlags);
3405            }
3406
3407            return;
3408        }
3409
3410        final String propName = JSType.toString(key);
3411        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3412    }
3413
3414    @Override
3415    public void set(final long key, final double value, final int callSiteFlags) {
3416        final int index = getArrayIndex(key);
3417
3418        if (isValidArrayIndex(index)) {
3419            final ArrayData data = getArray();
3420            if (data.has(index)) {
3421                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3422            } else {
3423                doesNotHave(index, value, callSiteFlags);
3424            }
3425
3426            return;
3427        }
3428
3429        final String propName = JSType.toString(key);
3430        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3431    }
3432
3433    @Override
3434    public void set(final long key, final Object value, final int callSiteFlags) {
3435        final int index = getArrayIndex(key);
3436
3437        if (isValidArrayIndex(index)) {
3438            final ArrayData data = getArray();
3439            if (data.has(index)) {
3440                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3441            } else {
3442                doesNotHave(index, value, callSiteFlags);
3443            }
3444
3445            return;
3446        }
3447
3448        final String propName = JSType.toString(key);
3449        setObject(findProperty(propName, true), callSiteFlags, propName, value);
3450    }
3451
3452    @Override
3453    public void set(final int key, final int value, final int callSiteFlags) {
3454        final int index = getArrayIndex(key);
3455        if (isValidArrayIndex(index)) {
3456            if (getArray().has(index)) {
3457                final ArrayData data = getArray();
3458                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3459            } else {
3460                doesNotHave(index, value, callSiteFlags);
3461            }
3462            return;
3463        }
3464
3465        final String propName = JSType.toString(key);
3466        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3467    }
3468
3469    @Override
3470    public void set(final int key, final long value, final int callSiteFlags) {
3471        final int index = getArrayIndex(key);
3472
3473        if (isValidArrayIndex(index)) {
3474            final ArrayData data = getArray();
3475            if (data.has(index)) {
3476                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3477            } else {
3478                doesNotHave(index, value, callSiteFlags);
3479            }
3480
3481            return;
3482        }
3483
3484        final String propName = JSType.toString(key);
3485        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3486    }
3487
3488    @Override
3489    public void set(final int key, final double value, final int callSiteFlags) {
3490        final int index = getArrayIndex(key);
3491
3492        if (isValidArrayIndex(index)) {
3493            final ArrayData data = getArray();
3494            if (data.has(index)) {
3495                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3496            } else {
3497                doesNotHave(index, value, callSiteFlags);
3498            }
3499
3500            return;
3501        }
3502
3503        final String propName = JSType.toString(key);
3504        setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value));
3505    }
3506
3507    @Override
3508    public void set(final int key, final Object value, final int callSiteFlags) {
3509        final int index = getArrayIndex(key);
3510
3511        if (isValidArrayIndex(index)) {
3512            final ArrayData data = getArray();
3513            if (data.has(index)) {
3514                setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
3515            } else {
3516                doesNotHave(index, value, callSiteFlags);
3517            }
3518
3519            return;
3520        }
3521
3522        final String propName = JSType.toString(key);
3523        setObject(findProperty(propName, true), callSiteFlags, propName, value);
3524    }
3525
3526    @Override
3527    public boolean has(final Object key) {
3528        final Object primitiveKey = JSType.toPrimitive(key);
3529        final int    index        = getArrayIndex(primitiveKey);
3530        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
3531    }
3532
3533    @Override
3534    public boolean has(final double key) {
3535        final int index = getArrayIndex(key);
3536        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3537    }
3538
3539    @Override
3540    public boolean has(final long key) {
3541        final int index = getArrayIndex(key);
3542        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3543    }
3544
3545    @Override
3546    public boolean has(final int key) {
3547        final int index = getArrayIndex(key);
3548        return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true);
3549    }
3550
3551    private boolean hasArrayProperty(final int index) {
3552        boolean hasArrayKeys = false;
3553
3554        for (ScriptObject self = this; self != null; self = self.getProto()) {
3555            if (self.getArray().has(index)) {
3556                return true;
3557            }
3558            hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys();
3559        }
3560
3561        return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true);
3562    }
3563
3564    @Override
3565    public boolean hasOwnProperty(final Object key) {
3566        final Object primitiveKey = JSType.toPrimitive(key, String.class);
3567        final int    index        = getArrayIndex(primitiveKey);
3568        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
3569    }
3570
3571    @Override
3572    public boolean hasOwnProperty(final int key) {
3573        final int index = getArrayIndex(key);
3574        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3575    }
3576
3577    @Override
3578    public boolean hasOwnProperty(final long key) {
3579        final int index = getArrayIndex(key);
3580        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3581    }
3582
3583    @Override
3584    public boolean hasOwnProperty(final double key) {
3585        final int index = getArrayIndex(key);
3586        return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false);
3587    }
3588
3589    private boolean hasOwnArrayProperty(final int index) {
3590        return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false);
3591    }
3592
3593    @Override
3594    public boolean delete(final int key, final boolean strict) {
3595        final int index = getArrayIndex(key);
3596        final ArrayData array = getArray();
3597
3598        if (array.has(index)) {
3599            if (array.canDelete(index, strict)) {
3600                setArray(array.delete(index));
3601                return true;
3602            }
3603            return false;
3604        }
3605        return deleteObject(JSType.toObject(key), strict);
3606    }
3607
3608    @Override
3609    public boolean delete(final long key, final boolean strict) {
3610        final int index = getArrayIndex(key);
3611        final ArrayData array = getArray();
3612
3613        if (array.has(index)) {
3614            if (array.canDelete(index, strict)) {
3615                setArray(array.delete(index));
3616                return true;
3617            }
3618            return false;
3619        }
3620
3621        return deleteObject(JSType.toObject(key), strict);
3622    }
3623
3624    @Override
3625    public boolean delete(final double key, final boolean strict) {
3626        final int index = getArrayIndex(key);
3627        final ArrayData array = getArray();
3628
3629        if (array.has(index)) {
3630            if (array.canDelete(index, strict)) {
3631                setArray(array.delete(index));
3632                return true;
3633            }
3634            return false;
3635        }
3636
3637        return deleteObject(JSType.toObject(key), strict);
3638    }
3639
3640    @Override
3641    public boolean delete(final Object key, final boolean strict) {
3642        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
3643        final int       index        = getArrayIndex(primitiveKey);
3644        final ArrayData array        = getArray();
3645
3646        if (array.has(index)) {
3647            if (array.canDelete(index, strict)) {
3648                setArray(array.delete(index));
3649                return true;
3650            }
3651            return false;
3652        }
3653
3654        return deleteObject(primitiveKey, strict);
3655    }
3656
3657    private boolean deleteObject(final Object key, final boolean strict) {
3658        final String propName = JSType.toString(key);
3659        final FindProperty find = findProperty(propName, false);
3660
3661        if (find == null) {
3662            return true;
3663        }
3664
3665        if (!find.getProperty().isConfigurable()) {
3666            if (strict) {
3667                throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
3668            }
3669            return false;
3670        }
3671
3672        final Property prop = find.getProperty();
3673        deleteOwnProperty(prop);
3674
3675        return true;
3676    }
3677
3678    /**
3679     * Return a shallow copy of this ScriptObject.
3680     * @return a shallow copy.
3681     */
3682    public final ScriptObject copy() {
3683        try {
3684            return clone();
3685        } catch (final CloneNotSupportedException e) {
3686            throw new RuntimeException(e);
3687        }
3688    }
3689
3690    @Override
3691    protected ScriptObject clone() throws CloneNotSupportedException {
3692        final ScriptObject clone = (ScriptObject) super.clone();
3693        if (objectSpill != null) {
3694            clone.objectSpill = objectSpill.clone();
3695            if (primitiveSpill != null) {
3696                clone.primitiveSpill = primitiveSpill.clone();
3697            }
3698        }
3699        clone.arrayData = arrayData.copy();
3700        return clone;
3701    }
3702
3703    /**
3704     * Make a new UserAccessorProperty property. getter and setter functions are stored in
3705     * this ScriptObject and slot values are used in property object.
3706     *
3707     * @param key the property name
3708     * @param propertyFlags attribute flags of the property
3709     * @param getter getter function for the property
3710     * @param setter setter function for the property
3711     * @return the newly created UserAccessorProperty
3712     */
3713    protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
3714        final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
3715        //property.getSetter(Object.class, getMap());
3716        uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
3717        return uc;
3718    }
3719
3720    /**
3721     * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise.
3722     * @return {@code true} if dual fields should be used.
3723     */
3724    protected boolean useDualFields() {
3725        return !StructureLoader.isSingleFieldStructure(getClass().getName());
3726    }
3727
3728    Object ensureSpillSize(final int slot) {
3729        final int oldLength = objectSpill == null ? 0 : objectSpill.length;
3730        if (slot < oldLength) {
3731            return this;
3732        }
3733        final int newLength = alignUp(slot + 1, SPILL_RATE);
3734        final Object[] newObjectSpill    = new Object[newLength];
3735        final long[]   newPrimitiveSpill = useDualFields() ? new long[newLength] : null;
3736
3737        if (objectSpill != null) {
3738            System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength);
3739            if (primitiveSpill != null && newPrimitiveSpill != null) {
3740                System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength);
3741            }
3742        }
3743
3744        this.primitiveSpill = newPrimitiveSpill;
3745        this.objectSpill    = newObjectSpill;
3746
3747        return this;
3748    }
3749
3750    private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) {
3751        // TODO: figure out how can it work for NativeArray$Prototype etc.
3752        return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3753    }
3754
3755    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
3756        return findOwnMH_V(ScriptObject.class, name, rtype, types);
3757    }
3758
3759    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
3760        return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
3761    }
3762
3763    private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3764        return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func);
3765    }
3766
3767    @SuppressWarnings("unused")
3768    private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) {
3769        if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3770            try {
3771                return getter.invokeExact(self) == func;
3772            } catch (final RuntimeException | Error e) {
3773                throw e;
3774            } catch (final Throwable t) {
3775                throw new RuntimeException(t);
3776            }
3777        }
3778
3779        return false;
3780    }
3781
3782    private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3783        return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func);
3784    }
3785
3786    private static ScriptObject getProto(final ScriptObject self, final int depth) {
3787        ScriptObject proto = self;
3788        for (int d = 0; d < depth; d++) {
3789            proto = proto.getProto();
3790            if (proto == null) {
3791                return null;
3792            }
3793        }
3794
3795        return proto;
3796    }
3797
3798    @SuppressWarnings("unused")
3799    private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) {
3800        if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
3801            final ScriptObject proto = getProto((ScriptObject)self, depth);
3802            if (proto == null) {
3803                return false;
3804            }
3805            try {
3806                return getter.invokeExact((Object)proto) == func;
3807            } catch (final RuntimeException | Error e) {
3808                throw e;
3809            } catch (final Throwable t) {
3810                throw new RuntimeException(t);
3811            }
3812        }
3813
3814        return false;
3815    }
3816
3817    /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
3818    private static int count;
3819
3820    /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
3821    private static int scopeCount;
3822
3823    /**
3824     * Get number of {@code ScriptObject} instances created. If not running in debug
3825     * mode this is always 0
3826     *
3827     * @return number of ScriptObjects created
3828     */
3829    public static int getCount() {
3830        return count;
3831    }
3832
3833    /**
3834     * Get number of scope {@code ScriptObject} instances created. If not running in debug
3835     * mode this is always 0
3836     *
3837     * @return number of scope ScriptObjects created
3838     */
3839    public static int getScopeCount() {
3840        return scopeCount;
3841    }
3842
3843}
3844