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