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