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