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