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