ScriptObject.java revision 1315:2e50107b1738
154359Sroberto/* 254359Sroberto * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 354359Sroberto * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 482498Sroberto * 554359Sroberto * This code is free software; you can redistribute it and/or modify it 654359Sroberto * under the terms of the GNU General Public License version 2 only, as 754359Sroberto * published by the Free Software Foundation. Oracle designates this 854359Sroberto * particular file as subject to the "Classpath" exception as provided 9106163Sroberto * by Oracle in the LICENSE file that accompanied this code. 1082498Sroberto * 1182498Sroberto * This code is distributed in the hope that it will be useful, but WITHOUT 1282498Sroberto * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13182007Sroberto * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1482498Sroberto * version 2 for more details (a copy is included in the LICENSE file that 15132451Sroberto * accompanied this code). 16182007Sroberto * 17182007Sroberto * You should have received a copy of the GNU General Public License version 18182007Sroberto * 2 along with this work; if not, write to the Free Software Foundation, 19182007Sroberto * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20132451Sroberto * 21132451Sroberto * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2254359Sroberto * or visit www.oracle.com if you need additional information or have any 2354359Sroberto * questions. 2454359Sroberto */ 2554359Sroberto 2654359Srobertopackage jdk.nashorn.internal.runtime; 2754359Sroberto 2854359Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 29200576Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 30200576Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 31200576Srobertoimport static jdk.nashorn.internal.lookup.Lookup.MH; 32106163Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 33200576Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 34200576Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE; 35200576Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT; 3654359Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG; 3754359Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 38200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 39200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.GET; 40200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.SET; 41200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 42200576Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 43200576Srobertoimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 44200576Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 4554359Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 4654359Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; 4754359Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 4854359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag; 4954359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag; 5054359Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck; 5154359Sroberto 5254359Srobertoimport java.lang.invoke.MethodHandle; 5354359Srobertoimport java.lang.invoke.MethodHandles; 5454359Srobertoimport java.lang.invoke.MethodType; 5554359Srobertoimport java.lang.invoke.SwitchPoint; 5654359Srobertoimport java.util.AbstractMap; 5754359Srobertoimport java.util.ArrayList; 5854359Srobertoimport java.util.Arrays; 5954359Srobertoimport java.util.Collection; 6054359Srobertoimport java.util.Collections; 6154359Srobertoimport java.util.HashSet; 6254359Srobertoimport java.util.Iterator; 6354359Srobertoimport java.util.LinkedHashSet; 6454359Srobertoimport java.util.List; 6554359Srobertoimport java.util.Map; 6654359Srobertoimport java.util.Set; 6754359Srobertoimport jdk.internal.dynalink.CallSiteDescriptor; 6854359Srobertoimport jdk.internal.dynalink.linker.GuardedInvocation; 6954359Srobertoimport jdk.internal.dynalink.linker.LinkRequest; 7054359Srobertoimport jdk.internal.dynalink.support.CallSiteDescriptorFactory; 7154359Srobertoimport jdk.nashorn.internal.codegen.CompilerConstants.Call; 7254359Srobertoimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 7354359Srobertoimport jdk.nashorn.internal.codegen.types.Type; 7454359Srobertoimport jdk.nashorn.internal.lookup.Lookup; 7554359Srobertoimport jdk.nashorn.internal.objects.AccessorPropertyDescriptor; 7654359Srobertoimport jdk.nashorn.internal.objects.DataPropertyDescriptor; 7754359Srobertoimport jdk.nashorn.internal.objects.Global; 7854359Srobertoimport jdk.nashorn.internal.objects.NativeArray; 7954359Srobertoimport jdk.nashorn.internal.runtime.arrays.ArrayData; 8054359Srobertoimport jdk.nashorn.internal.runtime.arrays.ArrayIndex; 8154359Srobertoimport jdk.nashorn.internal.runtime.linker.Bootstrap; 8282498Srobertoimport jdk.nashorn.internal.runtime.linker.LinkerCallSite; 8354359Srobertoimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 8454359Srobertoimport jdk.nashorn.internal.runtime.linker.NashornGuards; 8554359Sroberto 8654359Sroberto/** 8754359Sroberto * Base class for generic JavaScript objects. 8854359Sroberto * <p> 8954359Sroberto * Notes: 9054359Sroberto * <ul> 9154359Sroberto * <li>The map is used to identify properties in the object.</li> 9254359Sroberto * <li>If the map is modified then it must be cloned and replaced. This notifies 9354359Sroberto * any code that made assumptions about the object that things have changed. 9454359Sroberto * Ex. CallSites that have been validated must check to see if the map has 9582498Sroberto * changed (or a map from a different object type) and hence relink the method 9654359Sroberto * to call.</li> 9754359Sroberto * <li>Modifications of the map include adding/deleting attributes or changing a 9854359Sroberto * function field value.</li> 9982498Sroberto * </ul> 10054359Sroberto */ 10154359Sroberto 102182007Srobertopublic abstract class ScriptObject implements PropertyAccess, Cloneable { 103132451Sroberto /** __proto__ special property name inside object literals. ES6 draft. */ 104132451Sroberto public static final String PROTO_PROPERTY_NAME = "__proto__"; 105132451Sroberto 106182007Sroberto /** Search fall back routine name for "no such method" */ 107182007Sroberto public static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 108182007Sroberto 109132451Sroberto /** Search fall back routine name for "no such property" */ 110182007Sroberto public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 11182498Sroberto 11254359Sroberto /** Per ScriptObject flag - is this a scope object? */ 11354359Sroberto public static final int IS_SCOPE = 1 << 0; 11454359Sroberto 11554359Sroberto /** Per ScriptObject flag - is this an array object? */ 11654359Sroberto public static final int IS_ARRAY = 1 << 1; 11754359Sroberto 11854359Sroberto /** Per ScriptObject flag - is this an arguments object? */ 11954359Sroberto public static final int IS_ARGUMENTS = 1 << 2; 12054359Sroberto 12154359Sroberto /** Is length property not-writable? */ 12282498Sroberto public static final int IS_LENGTH_NOT_WRITABLE = 1 << 3; 12382498Sroberto 12482498Sroberto /** Is this a builtin object? */ 12582498Sroberto public static final int IS_BUILTIN = 1 << 4; 12654359Sroberto 12754359Sroberto /** 128182007Sroberto * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and 129182007Sroberto * {@link ScriptObject#objectSpill} when full 130182007Sroberto */ 131182007Sroberto public static final int SPILL_RATE = 8; 13254359Sroberto 13354359Sroberto /** Map to property information and accessor functions. Ordered by insertion. */ 13454359Sroberto private PropertyMap map; 13554359Sroberto 13682498Sroberto /** objects proto. */ 13754359Sroberto private ScriptObject proto; 13882498Sroberto 13982498Sroberto /** Object flags. */ 14082498Sroberto private int flags; 14182498Sroberto 14282498Sroberto /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */ 143182007Sroberto protected long[] primitiveSpill; 14454359Sroberto 14554359Sroberto /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ 14654359Sroberto protected Object[] objectSpill; 147182007Sroberto 148182007Sroberto /** Indexed array data. */ 14954359Sroberto private ArrayData arrayData; 150182007Sroberto 151182007Sroberto /** Method handle to retrieve prototype of this object */ 152132451Sroberto public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); 153132451Sroberto 15454359Sroberto static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class); 15554359Sroberto static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); 156182007Sroberto static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); 15754359Sroberto 158182007Sroberto private static final MethodHandle TRUNCATINGFILTER = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class); 159182007Sroberto private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class); 160132451Sroberto private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class); 161132451Sroberto 162132451Sroberto private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>(); 163132451Sroberto 164132451Sroberto /** Method handle for getting the array data */ 165132451Sroberto public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class); 166132451Sroberto 167132451Sroberto /** Method handle for getting a function argument at a given index. Used from MapCreator */ 168182007Sroberto public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); 169132451Sroberto 17054359Sroberto /** Method handle for setting a function argument at a given index. Used from MapCreator */ 17154359Sroberto public static final Call SET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class); 17254359Sroberto 17354359Sroberto /** Method handle for getting the proto of a ScriptObject */ 17454359Sroberto public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class); 17554359Sroberto 17654359Sroberto /** Method handle for getting the proto of a ScriptObject */ 17754359Sroberto public static final Call GET_PROTO_DEPTH = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class); 17854359Sroberto 17954359Sroberto /** Method handle for setting the proto of a ScriptObject */ 18054359Sroberto public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class); 181182007Sroberto 182182007Sroberto /** Method handle for setting the proto of a ScriptObject after checking argument */ 18354359Sroberto public static final Call SET_PROTO_FROM_LITERAL = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class); 18454359Sroberto 18554359Sroberto /** Method handle for setting the user accessors of a ScriptObject */ 18654359Sroberto //TODO fastpath this 18754359Sroberto public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class); 18854359Sroberto 18954359Sroberto static final MethodHandle[] SET_SLOW = new MethodHandle[] { 19054359Sroberto findOwnMH_V("set", void.class, Object.class, int.class, int.class), 19154359Sroberto findOwnMH_V("set", void.class, Object.class, long.class, int.class), 19254359Sroberto findOwnMH_V("set", void.class, Object.class, double.class, int.class), 19354359Sroberto findOwnMH_V("set", void.class, Object.class, Object.class, int.class) 19454359Sroberto }; 19554359Sroberto 19654359Sroberto /** Method handle to reset the map of this ScriptObject */ 19754359Sroberto public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class); 198132451Sroberto 19954359Sroberto static final MethodHandle CAS_MAP = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class); 20054359Sroberto static final MethodHandle EXTENSION_CHECK = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class); 201132451Sroberto static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class); 20254359Sroberto 20354359Sroberto /** 20454359Sroberto * Constructor 20554359Sroberto */ 20654359Sroberto public ScriptObject() { 20754359Sroberto this(null); 208182007Sroberto } 209182007Sroberto 21054359Sroberto /** 211182007Sroberto * Constructor 212182007Sroberto * 213182007Sroberto * @param map {@link PropertyMap} used to create the initial object 214182007Sroberto */ 215182007Sroberto public ScriptObject(final PropertyMap map) { 216182007Sroberto if (Context.DEBUG) { 217182007Sroberto ScriptObject.count++; 218182007Sroberto } 219182007Sroberto this.arrayData = ArrayData.EMPTY_ARRAY; 220182007Sroberto this.setMap(map == null ? PropertyMap.newMap() : map); 221182007Sroberto } 222182007Sroberto 223182007Sroberto /** 224182007Sroberto * Constructor that directly sets the prototype to {@code proto} and property map to 225182007Sroberto * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)} 226182007Sroberto * would do. This should only be used for objects that are always constructed with the 227182007Sroberto * same combination of prototype and property map. 228182007Sroberto * 229182007Sroberto * @param proto the prototype object 230182007Sroberto * @param map intial {@link PropertyMap} 231182007Sroberto */ 232182007Sroberto protected ScriptObject(final ScriptObject proto, final PropertyMap map) { 233182007Sroberto this(map); 234182007Sroberto this.proto = proto; 235182007Sroberto } 236182007Sroberto 237182007Sroberto /** 238182007Sroberto * Constructor used to instantiate spill properties directly. Used from 239182007Sroberto * SpillObjectCreator. 240182007Sroberto * 241182007Sroberto * @param map property maps 242182007Sroberto * @param primitiveSpill primitive spills 243182007Sroberto * @param objectSpill reference spills 244182007Sroberto */ 245182007Sroberto public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { 246182007Sroberto this(map); 247182007Sroberto this.primitiveSpill = primitiveSpill; 248182007Sroberto this.objectSpill = objectSpill; 249182007Sroberto assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; 250182007Sroberto } 251182007Sroberto 252182007Sroberto /** 253182007Sroberto * Check whether this is a global object 254182007Sroberto * @return true if global 255182007Sroberto */ 256182007Sroberto protected boolean isGlobal() { 257182007Sroberto return false; 258182007Sroberto } 259182007Sroberto 260182007Sroberto private static int alignUp(final int size, final int alignment) { 261182007Sroberto return size + alignment - 1 & ~(alignment - 1); 262182007Sroberto } 263182007Sroberto 264182007Sroberto /** 265182007Sroberto * Given a number of properties, return the aligned to SPILL_RATE 266182007Sroberto * buffer size required for the smallest spill pool needed to 267182007Sroberto * house them 268182007Sroberto * @param nProperties number of properties 269182007Sroberto * @return property buffer length, a multiple of SPILL_RATE 270182007Sroberto */ 271182007Sroberto public static int spillAllocationLength(final int nProperties) { 272182007Sroberto return alignUp(nProperties, SPILL_RATE); 273182007Sroberto } 274182007Sroberto 275182007Sroberto /** 276182007Sroberto * Copy all properties from the source object with their receiver bound to the source. 277182007Sroberto * This function was known as mergeMap 278182007Sroberto * 279182007Sroberto * @param source The source object to copy from. 280182007Sroberto */ 281182007Sroberto public void addBoundProperties(final ScriptObject source) { 282182007Sroberto addBoundProperties(source, source.getMap().getProperties()); 283182007Sroberto } 284182007Sroberto 285182007Sroberto /** 286182007Sroberto * Copy all properties from the array with their receiver bound to the source. 287182007Sroberto * 288182007Sroberto * @param source The source object to copy from. 289132451Sroberto * @param properties The array of properties to copy. 290132451Sroberto */ 291132451Sroberto public void addBoundProperties(final ScriptObject source, final Property[] properties) { 292132451Sroberto PropertyMap newMap = this.getMap(); 293132451Sroberto 294132451Sroberto for (final Property property : properties) { 295132451Sroberto newMap = addBoundProperty(newMap, source, property); 296132451Sroberto } 297132451Sroberto 298132451Sroberto this.setMap(newMap); 29954359Sroberto } 30054359Sroberto 30154359Sroberto /** 302182007Sroberto * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the 30354359Sroberto * new interim property map. 30454359Sroberto * 30554359Sroberto * @param propMap the property map 30654359Sroberto * @param source the source object 30754359Sroberto * @param property the property to be added 30854359Sroberto * @return the new property map 30954359Sroberto */ 31054359Sroberto protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) { 311182007Sroberto PropertyMap newMap = propMap; 312182007Sroberto final String key = property.getKey(); 313132451Sroberto final Property oldProp = newMap.findProperty(key); 31454359Sroberto if (oldProp == null) { 31554359Sroberto if (property instanceof UserAccessorProperty) { 31654359Sroberto // Note: we copy accessor functions to this object which is semantically different from binding. 31754359Sroberto final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); 31854359Sroberto newMap = newMap.addPropertyNoHistory(prop); 31954359Sroberto } else { 32054359Sroberto newMap = newMap.addPropertyBind((AccessorProperty)property, source); 32154359Sroberto } 32254359Sroberto } else { 32354359Sroberto // See ECMA section 10.5 Declaration Binding Instantiation 32454359Sroberto // step 5 processing each function declaration. 32554359Sroberto if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) { 32654359Sroberto if (oldProp instanceof UserAccessorProperty || 32754359Sroberto !(oldProp.isWritable() && oldProp.isEnumerable())) { 32854359Sroberto throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this)); 32954359Sroberto } 33054359Sroberto } 33154359Sroberto } 33254359Sroberto return newMap; 33354359Sroberto } 33454359Sroberto 33554359Sroberto /** 33654359Sroberto * Copy all properties from the array with their receiver bound to the source. 33754359Sroberto * 33854359Sroberto * @param source The source object to copy from. 33954359Sroberto * @param properties The collection of accessor properties to copy. 34054359Sroberto */ 34154359Sroberto public void addBoundProperties(final Object source, final AccessorProperty[] properties) { 34254359Sroberto PropertyMap newMap = this.getMap(); 34354359Sroberto 34454359Sroberto for (final AccessorProperty property : properties) { 34554359Sroberto final String key = property.getKey(); 34654359Sroberto 34754359Sroberto if (newMap.findProperty(key) == null) { 34854359Sroberto newMap = newMap.addPropertyBind(property, source); 34954359Sroberto } 35054359Sroberto } 35154359Sroberto 35254359Sroberto this.setMap(newMap); 35354359Sroberto } 35454359Sroberto 35554359Sroberto /** 35682498Sroberto * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the 35782498Sroberto * first argument in lieu of the bound argument). 35882498Sroberto * @param methodHandle Method handle to bind to. 35982498Sroberto * @param receiver Object to bind. 36082498Sroberto * @return Bound method handle. 36182498Sroberto */ 36282498Sroberto static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) { 36382498Sroberto return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0)); 36482498Sroberto } 36582498Sroberto 36654359Sroberto /** 36782498Sroberto * Return a property iterator. 36856746Sroberto * @return Property iterator. 36956746Sroberto */ 37082498Sroberto public Iterator<String> propertyIterator() { 37182498Sroberto return new KeyIterator(this); 37254359Sroberto } 37354359Sroberto 37454359Sroberto /** 37554359Sroberto * Return a property value iterator. 37654359Sroberto * @return Property value iterator. 37754359Sroberto */ 37854359Sroberto public Iterator<Object> valueIterator() { 37954359Sroberto return new ValueIterator(this); 38054359Sroberto } 38154359Sroberto 38254359Sroberto /** 38354359Sroberto * ECMA 8.10.1 IsAccessorDescriptor ( Desc ) 38454359Sroberto * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter 38554359Sroberto */ 38654359Sroberto public final boolean isAccessorDescriptor() { 38754359Sroberto return has(GET) || has(SET); 38854359Sroberto } 38954359Sroberto 39082498Sroberto /** 39154359Sroberto * ECMA 8.10.2 IsDataDescriptor ( Desc ) 39282498Sroberto * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable 39382498Sroberto */ 39482498Sroberto public final boolean isDataDescriptor() { 39582498Sroberto return has(VALUE) || has(WRITABLE); 39654359Sroberto } 39754359Sroberto 39854359Sroberto /** 39954359Sroberto * ECMA 8.10.3 IsGenericDescriptor ( Desc ) 40054359Sroberto * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor} 40154359Sroberto */ 40254359Sroberto public final boolean isGenericDescriptor() { 40354359Sroberto return isAccessorDescriptor() || isDataDescriptor(); 40482498Sroberto } 40554359Sroberto 40682498Sroberto /** 40782498Sroberto * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 40854359Sroberto * 40954359Sroberto * @return property descriptor 41054359Sroberto */ 41182498Sroberto public final PropertyDescriptor toPropertyDescriptor() { 41254359Sroberto final Global global = Context.getGlobal(); 41382498Sroberto 41482498Sroberto final PropertyDescriptor desc; 41582498Sroberto if (isDataDescriptor()) { 41682498Sroberto if (has(SET) || has(GET)) { 41782498Sroberto throw typeError(global, "inconsistent.property.descriptor"); 41854359Sroberto } 41954359Sroberto 42054359Sroberto desc = global.newDataDescriptor(UNDEFINED, false, false, false); 42154359Sroberto } else if (isAccessorDescriptor()) { 42282498Sroberto if (has(VALUE) || has(WRITABLE)) { 42354359Sroberto throw typeError(global, "inconsistent.property.descriptor"); 42482498Sroberto } 42582498Sroberto 42682498Sroberto desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false); 42754359Sroberto } else { 42854359Sroberto desc = global.newGenericDescriptor(false, false); 42954359Sroberto } 43082498Sroberto 43154359Sroberto return desc.fillFrom(this); 43282498Sroberto } 43382498Sroberto 43482498Sroberto /** 43554359Sroberto * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 43654359Sroberto * 43754359Sroberto * @param global global scope object 43856746Sroberto * @param obj object to create property descriptor from 43954359Sroberto * 44054359Sroberto * @return property descriptor 44154359Sroberto */ 44254359Sroberto public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) { 44354359Sroberto if (obj instanceof ScriptObject) { 44454359Sroberto return ((ScriptObject)obj).toPropertyDescriptor(); 44554359Sroberto } 44654359Sroberto 44754359Sroberto throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); 44854359Sroberto } 44954359Sroberto 45054359Sroberto /** 45154359Sroberto * ECMA 8.12.1 [[GetOwnProperty]] (P) 45254359Sroberto * 45354359Sroberto * @param key property key 45454359Sroberto * 455182007Sroberto * @return Returns the Property Descriptor of the named own property of this 456182007Sroberto * object, or undefined if absent. 45754359Sroberto */ 45854359Sroberto public Object getOwnPropertyDescriptor(final String key) { 459182007Sroberto final Property property = getMap().findProperty(key); 460182007Sroberto 461182007Sroberto final Global global = Context.getGlobal(); 462182007Sroberto 463182007Sroberto if (property != null) { 464182007Sroberto final ScriptFunction get = property.getGetterFunction(this); 465182007Sroberto final ScriptFunction set = property.getSetterFunction(this); 466182007Sroberto 467182007Sroberto final boolean configurable = property.isConfigurable(); 468182007Sroberto final boolean enumerable = property.isEnumerable(); 469182007Sroberto final boolean writable = property.isWritable(); 470182007Sroberto 471182007Sroberto if (property instanceof UserAccessorProperty) { 472182007Sroberto return global.newAccessorDescriptor( 473182007Sroberto get != null ? 474182007Sroberto get : 47554359Sroberto UNDEFINED, 47654359Sroberto set != null ? 47782498Sroberto set : 47854359Sroberto UNDEFINED, 47954359Sroberto configurable, 48054359Sroberto enumerable); 48154359Sroberto } 48254359Sroberto 48354359Sroberto return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable); 48454359Sroberto } 48554359Sroberto 48654359Sroberto final int index = getArrayIndex(key); 487106163Sroberto final ArrayData array = getArray(); 48854359Sroberto 48954359Sroberto if (array.has(index)) { 49054359Sroberto return array.getDescriptor(global, index); 49154359Sroberto } 49254359Sroberto 49354359Sroberto return UNDEFINED; 49454359Sroberto } 495182007Sroberto 49654359Sroberto /** 49754359Sroberto * ECMA 8.12.2 [[GetProperty]] (P) 49854359Sroberto * 49954359Sroberto * @param key property key 50054359Sroberto * 501182007Sroberto * @return Returns the fully populated Property Descriptor of the named property 502182007Sroberto * of this object, or undefined if absent. 503182007Sroberto */ 504182007Sroberto public Object getPropertyDescriptor(final String key) { 505182007Sroberto final Object res = getOwnPropertyDescriptor(key); 50654359Sroberto 50754359Sroberto if (res != UNDEFINED) { 50854359Sroberto return res; 50954359Sroberto } else if (getProto() != null) { 510182007Sroberto return getProto().getOwnPropertyDescriptor(key); 511182007Sroberto } else { 512182007Sroberto return UNDEFINED; 513182007Sroberto } 514132451Sroberto } 515132451Sroberto 516182007Sroberto /** 517132451Sroberto * Invalidate any existing global constant method handles that may exist for {@code key}. 518182007Sroberto * @param key the property name 519182007Sroberto */ 520182007Sroberto protected void invalidateGlobalConstant(final String key) { 521182007Sroberto final GlobalConstants globalConstants = getGlobalConstants(); 522182007Sroberto if (globalConstants != null) { 523182007Sroberto globalConstants.delete(key); 524182007Sroberto } 525182007Sroberto } 526182007Sroberto 527182007Sroberto /** 528182007Sroberto * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) 529182007Sroberto * 530182007Sroberto * @param key the property key 531182007Sroberto * @param propertyDesc the property descriptor 532182007Sroberto * @param reject is the property extensible - true means new definitions are rejected 533182007Sroberto * 534182007Sroberto * @return true if property was successfully defined 535182007Sroberto */ 536182007Sroberto public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) { 537182007Sroberto final Global global = Context.getGlobal(); 538182007Sroberto final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc); 539182007Sroberto final Object current = getOwnPropertyDescriptor(key); 540182007Sroberto final String name = JSType.toString(key); 541182007Sroberto 542182007Sroberto invalidateGlobalConstant(key); 543182007Sroberto 544182007Sroberto if (current == UNDEFINED) { 545182007Sroberto if (isExtensible()) { 546182007Sroberto // add a new own property 547182007Sroberto addOwnProperty(key, desc); 548182007Sroberto return true; 549182007Sroberto } 550182007Sroberto // new property added to non-extensible object 551182007Sroberto if (reject) { 552182007Sroberto throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this)); 553182007Sroberto } 554182007Sroberto return false; 555182007Sroberto } 556182007Sroberto 557182007Sroberto // modifying an existing property 55882498Sroberto final PropertyDescriptor currentDesc = (PropertyDescriptor)current; 559182007Sroberto final PropertyDescriptor newDesc = desc; 560182007Sroberto 561182007Sroberto if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) { 562182007Sroberto // every descriptor field is absent 563182007Sroberto return true; 564182007Sroberto } 565182007Sroberto 566182007Sroberto if (newDesc.hasAndEquals(currentDesc)) { 56782498Sroberto // every descriptor field of the new is same as the current 56882498Sroberto return true; 56954359Sroberto } 57054359Sroberto 571182007Sroberto if (!currentDesc.isConfigurable()) { 572182007Sroberto if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) { 573182007Sroberto // not configurable can not be made configurable 574182007Sroberto if (reject) { 575182007Sroberto throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 576182007Sroberto } 577182007Sroberto return false; 578182007Sroberto } 579182007Sroberto 580182007Sroberto if (newDesc.has(ENUMERABLE) && 58154359Sroberto currentDesc.isEnumerable() != newDesc.isEnumerable()) { 58254359Sroberto // cannot make non-enumerable as enumerable or vice-versa 58354359Sroberto if (reject) { 58454359Sroberto throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 58554359Sroberto } 586182007Sroberto return false; 58754359Sroberto } 588182007Sroberto } 58954359Sroberto 590182007Sroberto int propFlags = Property.mergeFlags(currentDesc, newDesc); 59154359Sroberto Property property = getMap().findProperty(key); 59254359Sroberto 59382498Sroberto if (currentDesc.type() == PropertyDescriptor.DATA && 59454359Sroberto (newDesc.type() == PropertyDescriptor.DATA || 59582498Sroberto newDesc.type() == PropertyDescriptor.GENERIC)) { 59654359Sroberto if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) { 59754359Sroberto if (newDesc.has(WRITABLE) && newDesc.isWritable() || 59854359Sroberto newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) { 59954359Sroberto if (reject) { 60054359Sroberto throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 60154359Sroberto } 60254359Sroberto return false; 60354359Sroberto } 60454359Sroberto } 605132451Sroberto 60654359Sroberto final boolean newValue = newDesc.has(VALUE); 60754359Sroberto final Object value = newValue ? newDesc.getValue() : currentDesc.getValue(); 60854359Sroberto 60954359Sroberto if (newValue && property != null) { 61054359Sroberto // Temporarily clear flags. 61154359Sroberto property = modifyOwnProperty(property, 0); 61254359Sroberto set(key, value, 0); 61354359Sroberto //this might change the map if we change types of the property 61454359Sroberto //hence we need to read it again. note that we should probably 61582498Sroberto //have the setter return the new property throughout and in 61654359Sroberto //general respect Property return values from modify and add 61782498Sroberto //functions - which we don't seem to do at all here :-( 61854359Sroberto //There is already a bug filed to generify PropertyAccess so we 61982498Sroberto //can have the setter return e.g. a Property 62054359Sroberto property = getMap().findProperty(key); 62154359Sroberto } 62254359Sroberto 62354359Sroberto if (property == null) { 62454359Sroberto // promoting an arrayData value to actual property 62554359Sroberto addOwnProperty(key, propFlags, value); 62654359Sroberto checkIntegerKey(key); 62754359Sroberto } else { 62854359Sroberto // Now set the new flags 62954359Sroberto modifyOwnProperty(property, propFlags); 63054359Sroberto } 63154359Sroberto } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR && 63254359Sroberto (newDesc.type() == PropertyDescriptor.ACCESSOR || 63354359Sroberto newDesc.type() == PropertyDescriptor.GENERIC)) { 63454359Sroberto if (!currentDesc.isConfigurable()) { 63554359Sroberto if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) || 63654359Sroberto newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) { 63754359Sroberto if (reject) { 63854359Sroberto throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 63954359Sroberto } 64054359Sroberto return false; 64154359Sroberto } 64254359Sroberto } 64354359Sroberto // New set the new features. 64454359Sroberto modifyOwnProperty(property, propFlags, 64554359Sroberto newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(), 64654359Sroberto newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter()); 64754359Sroberto } else { 64854359Sroberto // changing descriptor type 64954359Sroberto if (!currentDesc.isConfigurable()) { 65054359Sroberto // not configurable can not be made configurable 65154359Sroberto if (reject) { 65254359Sroberto throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this)); 65354359Sroberto } 65454359Sroberto return false; 65554359Sroberto } 65654359Sroberto 65754359Sroberto propFlags = 0; 65854359Sroberto 65954359Sroberto // Preserve only configurable and enumerable from current desc 66054359Sroberto // if those are not overridden in the new property descriptor. 66154359Sroberto boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable(); 66254359Sroberto if (!value) { 66354359Sroberto propFlags |= Property.NOT_CONFIGURABLE; 66454359Sroberto } 66554359Sroberto value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable(); 66654359Sroberto if (!value) { 66754359Sroberto propFlags |= Property.NOT_ENUMERABLE; 66854359Sroberto } 66954359Sroberto 67054359Sroberto final int type = newDesc.type(); 67182498Sroberto if (type == PropertyDescriptor.DATA) { 67282498Sroberto // get writable from the new descriptor 67354359Sroberto value = newDesc.has(WRITABLE) && newDesc.isWritable(); 67482498Sroberto if (!value) { 67554359Sroberto propFlags |= Property.NOT_WRITABLE; 67654359Sroberto } 677182007Sroberto 67854359Sroberto // delete the old property 67954359Sroberto deleteOwnProperty(property); 68054359Sroberto // add new data property 68154359Sroberto addOwnProperty(key, propFlags, newDesc.getValue()); 68254359Sroberto } else if (type == PropertyDescriptor.ACCESSOR) { 68354359Sroberto if (property == null) { 68454359Sroberto addOwnProperty(key, propFlags, 68554359Sroberto newDesc.has(GET) ? newDesc.getGetter() : null, 68654359Sroberto newDesc.has(SET) ? newDesc.getSetter() : null); 68754359Sroberto } else { 68854359Sroberto // Modify old property with the new features. 68954359Sroberto modifyOwnProperty(property, propFlags, 690182007Sroberto newDesc.has(GET) ? newDesc.getGetter() : null, 69154359Sroberto newDesc.has(SET) ? newDesc.getSetter() : null); 69254359Sroberto } 69354359Sroberto } 69454359Sroberto } 69554359Sroberto 69654359Sroberto checkIntegerKey(key); 69754359Sroberto 69854359Sroberto return true; 69954359Sroberto } 700132451Sroberto 70154359Sroberto /** 702132451Sroberto * Almost like defineOwnProperty(int,Object) for arrays this one does 703132451Sroberto * not add 'gap' elements (like the array one does). 704132451Sroberto * 705132451Sroberto * @param index key for property 706132451Sroberto * @param value value to define 707132451Sroberto */ 708182007Sroberto public void defineOwnProperty(final int index, final Object value) { 709132451Sroberto assert isValidArrayIndex(index) : "invalid array index"; 710182007Sroberto final long longIndex = ArrayIndex.toLongIndex(index); 711132451Sroberto final long oldLength = getArray().length(); 712132451Sroberto if (longIndex >= oldLength) { 713132451Sroberto setArray(getArray().ensure(longIndex)); 714132451Sroberto doesNotHaveEnsureDelete(longIndex, oldLength, false); 715132451Sroberto } 716132451Sroberto setArray(getArray().set(index, value, false)); 717132451Sroberto } 718182007Sroberto 719182007Sroberto private void checkIntegerKey(final String key) { 720182007Sroberto final int index = getArrayIndex(key); 721182007Sroberto 722182007Sroberto if (isValidArrayIndex(index)) { 723182007Sroberto final ArrayData data = getArray(); 724182007Sroberto 725182007Sroberto if (data.has(index)) { 726182007Sroberto setArray(data.delete(index)); 727182007Sroberto } 728182007Sroberto } 729182007Sroberto } 730132451Sroberto 731132451Sroberto /** 732132451Sroberto * Add a new property to the object. 73354359Sroberto * 73454359Sroberto * @param key property key 73554359Sroberto * @param propertyDesc property descriptor for property 73654359Sroberto */ 73754359Sroberto public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) { 73854359Sroberto // Already checked that there is no own property with that key. 73954359Sroberto PropertyDescriptor pdesc = propertyDesc; 74054359Sroberto 74154359Sroberto final int propFlags = Property.toFlags(pdesc); 74254359Sroberto 743182007Sroberto if (pdesc.type() == PropertyDescriptor.GENERIC) { 74454359Sroberto final Global global = Context.getGlobal(); 74554359Sroberto final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false); 74654359Sroberto 74754359Sroberto dDesc.fillFrom((ScriptObject)pdesc); 74854359Sroberto pdesc = dDesc; 74954359Sroberto } 75054359Sroberto 75154359Sroberto final int type = pdesc.type(); 75254359Sroberto if (type == PropertyDescriptor.DATA) { 75354359Sroberto addOwnProperty(key, propFlags, pdesc.getValue()); 75454359Sroberto } else if (type == PropertyDescriptor.ACCESSOR) { 75554359Sroberto addOwnProperty(key, propFlags, 75682498Sroberto pdesc.has(GET) ? pdesc.getGetter() : null, 75754359Sroberto pdesc.has(SET) ? pdesc.getSetter() : null); 75854359Sroberto } 75954359Sroberto 76054359Sroberto checkIntegerKey(key); 76154359Sroberto } 76282498Sroberto 76354359Sroberto /** 76482498Sroberto * Low level property API (not using property descriptors) 76554359Sroberto * <p> 76654359Sroberto * Find a property in the prototype hierarchy. Note: this is final and not 76754359Sroberto * a good idea to override. If you have to, use 76854359Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 76954359Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 77054359Sroberto * overriding way to find array properties 77154359Sroberto * 77254359Sroberto * @see jdk.nashorn.internal.objects.NativeArray 77354359Sroberto * 77454359Sroberto * @param key Property key. 77554359Sroberto * @param deep Whether the search should look up proto chain. 77654359Sroberto * 77754359Sroberto * @return FindPropertyData or null if not found. 77854359Sroberto */ 77954359Sroberto public final FindProperty findProperty(final String key, final boolean deep) { 78054359Sroberto return findProperty(key, deep, this); 78154359Sroberto } 78254359Sroberto 78354359Sroberto /** 78454359Sroberto * Low level property API (not using property descriptors) 78554359Sroberto * <p> 78654359Sroberto * Find a property in the prototype hierarchy. Note: this is not a good idea 78754359Sroberto * to override except as it was done in {@link WithObject}. 78854359Sroberto * If you have to, use 78954359Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 79054359Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 79154359Sroberto * overriding way to find array properties 79254359Sroberto * 79354359Sroberto * @see jdk.nashorn.internal.objects.NativeArray 79454359Sroberto * 79554359Sroberto * @param key Property key. 79654359Sroberto * @param deep Whether the search should look up proto chain. 79754359Sroberto * @param start the object on which the lookup was originally initiated 79854359Sroberto * 79954359Sroberto * @return FindPropertyData or null if not found. 80054359Sroberto */ 80154359Sroberto protected FindProperty findProperty(final String key, final boolean deep, final ScriptObject start) { 80256746Sroberto 80354359Sroberto final PropertyMap selfMap = getMap(); 80454359Sroberto final Property property = selfMap.findProperty(key); 80554359Sroberto 80654359Sroberto if (property != null) { 80754359Sroberto return new FindProperty(start, this, property); 808182007Sroberto } 809182007Sroberto 81054359Sroberto if (deep) { 81154359Sroberto final ScriptObject myProto = getProto(); 81254359Sroberto if (myProto != null) { 81354359Sroberto return myProto.findProperty(key, deep, start); 81454359Sroberto } 81554359Sroberto } 81654359Sroberto 81754359Sroberto return null; 81854359Sroberto } 81954359Sroberto 82054359Sroberto /** 82154359Sroberto * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a 82254359Sroberto * {@code boolean} value instead of a {@link FindProperty} object. 82354359Sroberto * @param key Property key. 82482498Sroberto * @param deep Whether the search should look up proto chain. 82554359Sroberto * @return true if the property was found. 82654359Sroberto */ 82782498Sroberto boolean hasProperty(final String key, final boolean deep) { 82854359Sroberto if (getMap().findProperty(key) != null) { 82954359Sroberto return true; 83054359Sroberto } 831182007Sroberto 832182007Sroberto if (deep) { 83354359Sroberto final ScriptObject myProto = getProto(); 834182007Sroberto if (myProto != null) { 83554359Sroberto return myProto.hasProperty(key, deep); 836182007Sroberto } 837182007Sroberto } 838132451Sroberto 83982498Sroberto return false; 840132451Sroberto } 84154359Sroberto 84254359Sroberto private SwitchPoint findBuiltinSwitchPoint(final String key) { 843182007Sroberto for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) { 844182007Sroberto final Property prop = myProto.getMap().findProperty(key); 845182007Sroberto if (prop != null) { 846182007Sroberto final SwitchPoint sp = prop.getBuiltinSwitchPoint(); 847182007Sroberto if (sp != null && !sp.hasBeenInvalidated()) { 848182007Sroberto return sp; 849182007Sroberto } 850182007Sroberto } 851182007Sroberto } 852182007Sroberto return null; 853182007Sroberto } 854182007Sroberto 855182007Sroberto /** 856182007Sroberto * Add a new property to the object. 857182007Sroberto * <p> 858182007Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 859182007Sroberto * 860182007Sroberto * @param key Property key. 861182007Sroberto * @param propertyFlags Property flags. 862182007Sroberto * @param getter Property getter, or null if not defined 863182007Sroberto * @param setter Property setter, or null if not defined 864182007Sroberto * 865182007Sroberto * @return New property. 866182007Sroberto */ 867182007Sroberto public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 868182007Sroberto return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter)); 869182007Sroberto } 870182007Sroberto 871182007Sroberto /** 872182007Sroberto * Add a new property to the object. 873182007Sroberto * <p> 874182007Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 875132451Sroberto * 87654359Sroberto * @param key Property key. 877182007Sroberto * @param propertyFlags Property flags. 878182007Sroberto * @param value Value of property 879182007Sroberto * 880182007Sroberto * @return New property. 881182007Sroberto */ 882182007Sroberto public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) { 883182007Sroberto return addSpillProperty(key, propertyFlags, value, true); 884182007Sroberto } 885182007Sroberto 886182007Sroberto /** 887182007Sroberto * Add a new property to the object. 888182007Sroberto * <p> 889182007Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 890182007Sroberto * 891182007Sroberto * @param newProperty property to add 892182007Sroberto * 893182007Sroberto * @return New property. 894182007Sroberto */ 895182007Sroberto public final Property addOwnProperty(final Property newProperty) { 896182007Sroberto PropertyMap oldMap = getMap(); 897182007Sroberto while (true) { 898182007Sroberto final PropertyMap newMap = oldMap.addProperty(newProperty); 899182007Sroberto if (!compareAndSetMap(oldMap, newMap)) { 900182007Sroberto oldMap = getMap(); 901182007Sroberto final Property oldProperty = oldMap.findProperty(newProperty.getKey()); 902182007Sroberto 903182007Sroberto if (oldProperty != null) { 904182007Sroberto return oldProperty; 905182007Sroberto } 906182007Sroberto } else { 907182007Sroberto return newProperty; 908182007Sroberto } 909182007Sroberto } 910182007Sroberto } 911182007Sroberto 912182007Sroberto private void erasePropertyValue(final Property property) { 913182007Sroberto // Erase the property field value with undefined. If the property is defined 914182007Sroberto // by user-defined accessors, we don't want to call the setter!! 915182007Sroberto if (!(property instanceof UserAccessorProperty)) { 916182007Sroberto assert property != null; 917182007Sroberto property.setValue(this, this, UNDEFINED, false); 918182007Sroberto } 919182007Sroberto } 920182007Sroberto 921182007Sroberto /** 922182007Sroberto * Delete a property from the object. 923182007Sroberto * 924182007Sroberto * @param property Property to delete. 925182007Sroberto * 926182007Sroberto * @return true if deleted. 927182007Sroberto */ 928182007Sroberto public final boolean deleteOwnProperty(final Property property) { 92954359Sroberto erasePropertyValue(property); 93054359Sroberto PropertyMap oldMap = getMap(); 931182007Sroberto 932182007Sroberto while (true) { 933182007Sroberto final PropertyMap newMap = oldMap.deleteProperty(property); 934182007Sroberto 935182007Sroberto if (newMap == null) { 936182007Sroberto return false; 937182007Sroberto } 938182007Sroberto 939182007Sroberto if (!compareAndSetMap(oldMap, newMap)) { 940182007Sroberto oldMap = getMap(); 941182007Sroberto } else { 942182007Sroberto // delete getter and setter function references so that we don't leak 943182007Sroberto if (property instanceof UserAccessorProperty) { 944182007Sroberto ((UserAccessorProperty)property).setAccessors(this, getMap(), null); 945182007Sroberto } 946182007Sroberto 947182007Sroberto invalidateGlobalConstant(property.getKey()); 948182007Sroberto return true; 949182007Sroberto } 950182007Sroberto } 951182007Sroberto 952182007Sroberto } 953182007Sroberto 954182007Sroberto /** 955182007Sroberto * Fast initialization functions for ScriptFunctions that are strict, to avoid 956182007Sroberto * creating setters that probably aren't used. Inject directly into the spill pool 957182007Sroberto * the defaults for "arguments" and "caller" 958182007Sroberto * 959182007Sroberto * @param key property key 960182007Sroberto * @param propertyFlags flags 961182007Sroberto * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 96254359Sroberto * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 96354359Sroberto */ 96454359Sroberto protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 96554359Sroberto final PropertyMap oldMap = getMap(); 96654359Sroberto final int slot = oldMap.getFreeSpillSlot(); 96754359Sroberto ensureSpillSize(slot); 96854359Sroberto objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter); 96954359Sroberto Property newProperty; 97054359Sroberto PropertyMap newMap; 97154359Sroberto do { 97254359Sroberto newProperty = new UserAccessorProperty(key, propertyFlags, slot); 97354359Sroberto newMap = oldMap.addProperty(newProperty); 97454359Sroberto } while (!compareAndSetMap(oldMap, newMap)); 97554359Sroberto } 97654359Sroberto 97754359Sroberto /** 97854359Sroberto * Modify a property in the object 97954359Sroberto * 98054359Sroberto * @param oldProperty property to modify 98154359Sroberto * @param propertyFlags new property flags 98282498Sroberto * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 98354359Sroberto * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 984182007Sroberto * 985200576Sroberto * @return new property 98682498Sroberto */ 98754359Sroberto public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 988182007Sroberto Property newProperty; 98954359Sroberto 99054359Sroberto if (oldProperty instanceof UserAccessorProperty) { 99154359Sroberto final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; 99282498Sroberto final int slot = uc.getSlot(); 99354359Sroberto 99454359Sroberto assert uc.getLocalType() == Object.class; 99554359Sroberto final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes 99654359Sroberto assert gs != null; 99754359Sroberto //reuse existing getter setter for speed 99882498Sroberto gs.set(getter, setter); 99954359Sroberto if (uc.getFlags() == propertyFlags) { 100054359Sroberto return oldProperty; 100154359Sroberto } 100254359Sroberto newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); 100354359Sroberto } else { 100454359Sroberto // erase old property value and create new user accessor property 100554359Sroberto erasePropertyValue(oldProperty); 1006182007Sroberto newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter); 100754359Sroberto } 100854359Sroberto 100954359Sroberto return modifyOwnProperty(oldProperty, newProperty); 101054359Sroberto } 101182498Sroberto 101254359Sroberto /** 101382498Sroberto * Modify a property in the object 101454359Sroberto * 101554359Sroberto * @param oldProperty property to modify 101654359Sroberto * @param propertyFlags new property flags 101754359Sroberto * 101854359Sroberto * @return new property 101954359Sroberto */ 102054359Sroberto public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) { 102154359Sroberto return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags)); 102282498Sroberto } 102354359Sroberto 102454359Sroberto /** 102582498Sroberto * Modify a property in the object, replacing a property with a new one 102654359Sroberto * 102754359Sroberto * @param oldProperty property to replace 102854359Sroberto * @param newProperty property to replace it with 102954359Sroberto * 103054359Sroberto * @return new property 103154359Sroberto */ 103254359Sroberto private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) { 103354359Sroberto if (oldProperty == newProperty) { 103454359Sroberto return newProperty; //nop 1035182007Sroberto } 1036132451Sroberto 1037182007Sroberto assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key"; 1038182007Sroberto 1039132451Sroberto PropertyMap oldMap = getMap(); 104082498Sroberto 104154359Sroberto while (true) { 104254359Sroberto final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty); 104382498Sroberto 104454359Sroberto if (!compareAndSetMap(oldMap, newMap)) { 104554359Sroberto oldMap = getMap(); 104654359Sroberto final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey()); 104754359Sroberto 104854359Sroberto if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) { 104954359Sroberto return oldPropertyLookup; 105054359Sroberto } 105154359Sroberto } else { 105254359Sroberto return newProperty; 1053182007Sroberto } 1054182007Sroberto } 1055182007Sroberto } 1056182007Sroberto 1057182007Sroberto /** 105854359Sroberto * Update getter and setter in an object literal. 105954359Sroberto * 1060182007Sroberto * @param key Property key. 106154359Sroberto * @param getter {@link UserAccessorProperty} defined getter, or null if none 106254359Sroberto * @param setter {@link UserAccessorProperty} defined setter, or null if none 106382498Sroberto */ 1064182007Sroberto public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) { 1065182007Sroberto final Property oldProperty = getMap().findProperty(key); 106654359Sroberto if (oldProperty instanceof UserAccessorProperty) { 1067182007Sroberto modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter); 1068182007Sroberto } else { 1069182007Sroberto addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter)); 1070182007Sroberto } 1071182007Sroberto } 1072182007Sroberto 1073182007Sroberto private static int getIntValue(final FindProperty find, final int programPoint) { 1074182007Sroberto final MethodHandle getter = find.getGetter(int.class, programPoint, null); 1075182007Sroberto if (getter != null) { 1076182007Sroberto try { 1077182007Sroberto return (int)getter.invokeExact((Object)find.getGetterReceiver()); 1078182007Sroberto } catch (final Error|RuntimeException e) { 1079182007Sroberto throw e; 1080182007Sroberto } catch (final Throwable e) { 1081182007Sroberto throw new RuntimeException(e); 1082182007Sroberto } 1083182007Sroberto } 1084182007Sroberto 1085182007Sroberto return UNDEFINED_INT; 1086182007Sroberto } 1087182007Sroberto 1088182007Sroberto private static long getLongValue(final FindProperty find, final int programPoint) { 1089182007Sroberto final MethodHandle getter = find.getGetter(long.class, programPoint, null); 1090182007Sroberto if (getter != null) { 1091182007Sroberto try { 1092182007Sroberto return (long)getter.invokeExact((Object)find.getGetterReceiver()); 1093182007Sroberto } catch (final Error|RuntimeException e) { 1094182007Sroberto throw e; 1095182007Sroberto } catch (final Throwable e) { 1096182007Sroberto throw new RuntimeException(e); 1097182007Sroberto } 1098182007Sroberto } 1099182007Sroberto 1100182007Sroberto return UNDEFINED_LONG; 1101182007Sroberto } 1102182007Sroberto 1103182007Sroberto private static double getDoubleValue(final FindProperty find, final int programPoint) { 1104182007Sroberto final MethodHandle getter = find.getGetter(double.class, programPoint, null); 1105182007Sroberto if (getter != null) { 1106182007Sroberto try { 1107182007Sroberto return (double)getter.invokeExact((Object)find.getGetterReceiver()); 1108182007Sroberto } catch (final Error|RuntimeException e) { 1109182007Sroberto throw e; 1110182007Sroberto } catch (final Throwable e) { 1111182007Sroberto throw new RuntimeException(e); 1112182007Sroberto } 1113182007Sroberto } 1114182007Sroberto 1115182007Sroberto return UNDEFINED_DOUBLE; 1116182007Sroberto } 1117182007Sroberto 1118182007Sroberto /** 1119182007Sroberto * Return methodHandle of value function for call. 1120182007Sroberto * 112154359Sroberto * @param find data from find property. 112282498Sroberto * @param type method type of function. 112354359Sroberto * @param bindName null or name to bind to second argument (property not found method.) 112454359Sroberto * 112554359Sroberto * @return value of property as a MethodHandle or null. 112654359Sroberto */ 112754359Sroberto protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) { 1128182007Sroberto return getCallMethodHandle(find.getObjectValue(), type, bindName); 1129182007Sroberto } 113054359Sroberto 113154359Sroberto /** 113254359Sroberto * Return methodHandle of value function for call. 113354359Sroberto * 113454359Sroberto * @param value value of receiver, it not a {@link ScriptFunction} this will return null. 113554359Sroberto * @param type method type of function. 113654359Sroberto * @param bindName null or name to bind to second argument (property not found method.) 113754359Sroberto * 113854359Sroberto * @return value of property as a MethodHandle or null. 113954359Sroberto */ 114054359Sroberto protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) { 114154359Sroberto return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null; 114254359Sroberto } 114354359Sroberto 1144182007Sroberto /** 1145182007Sroberto * Get value using found property. 1146182007Sroberto * 1147182007Sroberto * @param property Found property. 1148182007Sroberto * 114954359Sroberto * @return Value of property. 115054359Sroberto */ 115154359Sroberto public final Object getWithProperty(final Property property) { 115282498Sroberto return new FindProperty(this, this, property).getObjectValue(); 115354359Sroberto } 115454359Sroberto 115554359Sroberto /** 115682498Sroberto * Get a property given a key 115754359Sroberto * 115854359Sroberto * @param key property key 115954359Sroberto * 116054359Sroberto * @return property for key 116154359Sroberto */ 116254359Sroberto public final Property getProperty(final String key) { 116354359Sroberto return getMap().findProperty(key); 116454359Sroberto } 116554359Sroberto 116654359Sroberto /** 1167132451Sroberto * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 116854359Sroberto * Used for argument access in a vararg function using parameter name. 116954359Sroberto * Returns the argument at a given key (index) 117054359Sroberto * 117154359Sroberto * @param key argument index 117254359Sroberto * 117354359Sroberto * @return the argument at the given position, or undefined if not present 117454359Sroberto */ 117554359Sroberto public Object getArgument(final int key) { 117654359Sroberto return get(key); 117754359Sroberto } 117854359Sroberto 117954359Sroberto /** 118054359Sroberto * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 118154359Sroberto * Used for argument access in a vararg function using parameter name. 118254359Sroberto * Returns the argument at a given key (index) 118354359Sroberto * 118454359Sroberto * @param key argument index 118554359Sroberto * @param value the value to write at the given index 118654359Sroberto */ 118754359Sroberto public void setArgument(final int key, final Object value) { 118854359Sroberto set(key, value, 0); 118954359Sroberto } 119054359Sroberto 119154359Sroberto /** 119254359Sroberto * Return the current context from the object's map. 119354359Sroberto * @return Current context. 119454359Sroberto */ 119554359Sroberto protected Context getContext() { 119654359Sroberto return Context.fromClass(getClass()); 119754359Sroberto } 119854359Sroberto 119954359Sroberto /** 120054359Sroberto * Return the map of an object. 120154359Sroberto * @return PropertyMap object. 120254359Sroberto */ 1203132451Sroberto public final PropertyMap getMap() { 120454359Sroberto return map; 1205182007Sroberto } 1206182007Sroberto 120754359Sroberto /** 120854359Sroberto * Set the initial map. 120954359Sroberto * @param map Initial map. 121054359Sroberto */ 121154359Sroberto public final void setMap(final PropertyMap map) { 121254359Sroberto this.map = map; 121354359Sroberto } 121454359Sroberto 121554359Sroberto /** 121654359Sroberto * Conditionally set the new map if the old map is the same. 121754359Sroberto * @param oldMap Map prior to manipulation. 121854359Sroberto * @param newMap Replacement map. 1219132451Sroberto * @return true if the operation succeeded. 122054359Sroberto */ 1221 protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) { 1222 if (oldMap == this.map) { 1223 this.map = newMap; 1224 return true; 1225 } 1226 return false; 1227 } 1228 1229 /** 1230 * Return the __proto__ of an object. 1231 * @return __proto__ object. 1232 */ 1233 public final ScriptObject getProto() { 1234 return proto; 1235 } 1236 1237 /** 1238 * Get the proto of a specific depth 1239 * @param n depth 1240 * @return proto at given depth 1241 */ 1242 public final ScriptObject getProto(final int n) { 1243 assert n > 0; 1244 ScriptObject p = getProto(); 1245 for (int i = n; i-- > 0;) { 1246 p = p.getProto(); 1247 } 1248 return p; 1249 } 1250 1251 /** 1252 * Set the __proto__ of an object. 1253 * @param newProto new __proto__ to set. 1254 */ 1255 public final void setProto(final ScriptObject newProto) { 1256 final ScriptObject oldProto = proto; 1257 1258 if (oldProto != newProto) { 1259 proto = newProto; 1260 1261 // Let current listeners know that the protototype has changed and set our map 1262 final PropertyListeners listeners = getMap().getListeners(); 1263 if (listeners != null) { 1264 listeners.protoChanged(); 1265 } 1266 // Replace our current allocator map with one that is associated with the new prototype. 1267 setMap(getMap().changeProto(newProto)); 1268 } 1269 } 1270 1271 /** 1272 * Set the initial __proto__ of this object. This should be used instead of 1273 * {@link #setProto} if it is known that the current property map will not be 1274 * used on a new object with any other parent property map, so we can pass over 1275 * property map invalidation/evolution. 1276 * 1277 * @param initialProto the initial __proto__ to set. 1278 */ 1279 public void setInitialProto(final ScriptObject initialProto) { 1280 this.proto = initialProto; 1281 } 1282 1283 /** 1284 * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype. 1285 * @param obj the object literal that needs to have its prototype initialized to the global Object prototype. 1286 */ 1287 public static void setGlobalObjectProto(final ScriptObject obj) { 1288 obj.setInitialProto(Global.objectPrototype()); 1289 } 1290 1291 /** 1292 * Set the __proto__ of an object with checks. 1293 * This is the built-in operation [[SetPrototypeOf]] 1294 * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V) 1295 * 1296 * @param newProto Prototype to set. 1297 */ 1298 public final void setPrototypeOf(final Object newProto) { 1299 if (newProto == null || newProto instanceof ScriptObject) { 1300 if (! isExtensible()) { 1301 // okay to set same proto again - even if non-extensible 1302 1303 if (newProto == getProto()) { 1304 return; 1305 } 1306 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this)); 1307 } 1308 1309 // check for circularity 1310 ScriptObject p = (ScriptObject)newProto; 1311 while (p != null) { 1312 if (p == this) { 1313 throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this)); 1314 } 1315 p = p.getProto(); 1316 } 1317 setProto((ScriptObject)newProto); 1318 } else { 1319 throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); 1320 } 1321 } 1322 1323 /** 1324 * Set the __proto__ of an object from an object literal. 1325 * See ES6 draft spec: B.3.1 __proto__ Property Names in 1326 * Object Initializers. Step 6 handling of "__proto__". 1327 * 1328 * @param newProto Prototype to set. 1329 */ 1330 public final void setProtoFromLiteral(final Object newProto) { 1331 if (newProto == null || newProto instanceof ScriptObject) { 1332 setPrototypeOf(newProto); 1333 } else { 1334 // Some non-object, non-null. Then, we need to set 1335 // Object.prototype as the new __proto__ 1336 // 1337 // var obj = { __proto__ : 34 }; 1338 // print(obj.__proto__ === Object.prototype); // => true 1339 setPrototypeOf(Global.objectPrototype()); 1340 } 1341 } 1342 1343 /** 1344 * return an array of own property keys associated with the object. 1345 * 1346 * @param all True if to include non-enumerable keys. 1347 * @return Array of keys. 1348 */ 1349 public final String[] getOwnKeys(final boolean all) { 1350 return getOwnKeys(all, null); 1351 } 1352 1353 /** 1354 * return an array of own property keys associated with the object. 1355 * 1356 * @param all True if to include non-enumerable keys. 1357 * @param nonEnumerable set of non-enumerable properties seen already.Used 1358 to filter out shadowed, but enumerable properties from proto children. 1359 * @return Array of keys. 1360 */ 1361 protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) { 1362 final List<Object> keys = new ArrayList<>(); 1363 final PropertyMap selfMap = this.getMap(); 1364 1365 final ArrayData array = getArray(); 1366 1367 for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) { 1368 keys.add(JSType.toString(iter.next().longValue())); 1369 } 1370 1371 for (final Property property : selfMap.getProperties()) { 1372 final boolean enumerable = property.isEnumerable(); 1373 final String key = property.getKey(); 1374 if (all) { 1375 keys.add(key); 1376 } else if (enumerable) { 1377 // either we don't have non-enumerable filter set or filter set 1378 // does not contain the current property. 1379 if (nonEnumerable == null || !nonEnumerable.contains(key)) { 1380 keys.add(key); 1381 } 1382 } else { 1383 // store this non-enumerable property for later proto walk 1384 if (nonEnumerable != null) { 1385 nonEnumerable.add(key); 1386 } 1387 } 1388 } 1389 1390 return keys.toArray(new String[keys.size()]); 1391 } 1392 1393 /** 1394 * Check if this ScriptObject has array entries. This means that someone has 1395 * set values with numeric keys in the object. 1396 * 1397 * @return true if array entries exists. 1398 */ 1399 public boolean hasArrayEntries() { 1400 return getArray().length() > 0 || getMap().containsArrayKeys(); 1401 } 1402 1403 /** 1404 * Return the valid JavaScript type name descriptor 1405 * 1406 * @return "Object" 1407 */ 1408 public String getClassName() { 1409 return "Object"; 1410 } 1411 1412 /** 1413 * {@code length} is a well known property. This is its getter. 1414 * Note that this *may* be optimized by other classes 1415 * 1416 * @return length property value for this ScriptObject 1417 */ 1418 public Object getLength() { 1419 return get("length"); 1420 } 1421 1422 /** 1423 * Stateless toString for ScriptObjects. 1424 * 1425 * @return string description of this object, e.g. {@code [object Object]} 1426 */ 1427 public String safeToString() { 1428 return "[object " + getClassName() + "]"; 1429 } 1430 1431 /** 1432 * Return the default value of the object with a given preferred type hint. 1433 * The preferred type hints are String.class for type String, Number.class 1434 * for type Number. <p> 1435 * 1436 * A <code>hint</code> of null means "no hint". 1437 * 1438 * ECMA 8.12.8 [[DefaultValue]](hint) 1439 * 1440 * @param typeHint the preferred type hint 1441 * @return the default value 1442 */ 1443 public Object getDefaultValue(final Class<?> typeHint) { 1444 // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and 1445 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts 1446 // are being executed in a long-running program, we move the code and their associated dynamic call sites 1447 // (Global.TO_STRING and Global.VALUE_OF) into per-context code. 1448 return Context.getGlobal().getDefaultValue(this, typeHint); 1449 } 1450 1451 /** 1452 * Checking whether a script object is an instance of another. Used 1453 * in {@link ScriptFunction} for hasInstance implementation, walks 1454 * the proto chain 1455 * 1456 * @param instance instace to check 1457 * @return true if 'instance' is an instance of this object 1458 */ 1459 public boolean isInstance(final ScriptObject instance) { 1460 return false; 1461 } 1462 1463 /** 1464 * Flag this ScriptObject as non extensible 1465 * 1466 * @return the object after being made non extensible 1467 */ 1468 public ScriptObject preventExtensions() { 1469 PropertyMap oldMap = getMap(); 1470 while (!compareAndSetMap(oldMap, getMap().preventExtensions())) { 1471 oldMap = getMap(); 1472 } 1473 1474 //invalidate any fast array setters 1475 final ArrayData array = getArray(); 1476 assert array != null; 1477 setArray(ArrayData.preventExtension(array)); 1478 return this; 1479 } 1480 1481 /** 1482 * Check whether if an Object (not just a ScriptObject) represents JavaScript array 1483 * 1484 * @param obj object to check 1485 * 1486 * @return true if array 1487 */ 1488 public static boolean isArray(final Object obj) { 1489 return obj instanceof ScriptObject && ((ScriptObject)obj).isArray(); 1490 } 1491 1492 /** 1493 * Check if this ScriptObject is an array 1494 * @return true if array 1495 */ 1496 public final boolean isArray() { 1497 return (flags & IS_ARRAY) != 0; 1498 } 1499 1500 /** 1501 * Flag this ScriptObject as being an array 1502 */ 1503 public final void setIsArray() { 1504 flags |= IS_ARRAY; 1505 } 1506 1507 /** 1508 * Check if this ScriptObject is an {@code arguments} vector 1509 * @return true if arguments vector 1510 */ 1511 public final boolean isArguments() { 1512 return (flags & IS_ARGUMENTS) != 0; 1513 } 1514 1515 /** 1516 * Flag this ScriptObject as being an {@code arguments} vector 1517 */ 1518 public final void setIsArguments() { 1519 flags |= IS_ARGUMENTS; 1520 } 1521 1522 /** 1523 * Check if this object has non-writable length property 1524 * 1525 * @return {@code true} if 'length' property is non-writable 1526 */ 1527 public boolean isLengthNotWritable() { 1528 return (flags & IS_LENGTH_NOT_WRITABLE) != 0; 1529 } 1530 1531 /** 1532 * Flag this object as having non-writable length property. 1533 */ 1534 public void setIsLengthNotWritable() { 1535 flags |= IS_LENGTH_NOT_WRITABLE; 1536 } 1537 1538 /** 1539 * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type 1540 * that can handle elementType 1541 * @param elementType elementType 1542 * @return array data 1543 */ 1544 public final ArrayData getArray(final Class<?> elementType) { 1545 if (elementType == null) { 1546 return arrayData; 1547 } 1548 final ArrayData newArrayData = arrayData.convert(elementType); 1549 if (newArrayData != arrayData) { 1550 arrayData = newArrayData; 1551 } 1552 return newArrayData; 1553 } 1554 1555 /** 1556 * Get the {@link ArrayData} for this ScriptObject if it is an array 1557 * @return array data 1558 */ 1559 public final ArrayData getArray() { 1560 return arrayData; 1561 } 1562 1563 /** 1564 * Set the {@link ArrayData} for this ScriptObject if it is to be an array 1565 * @param arrayData the array data 1566 */ 1567 public final void setArray(final ArrayData arrayData) { 1568 this.arrayData = arrayData; 1569 } 1570 1571 /** 1572 * Check if this ScriptObject is extensible 1573 * @return true if extensible 1574 */ 1575 public boolean isExtensible() { 1576 return getMap().isExtensible(); 1577 } 1578 1579 /** 1580 * ECMAScript 15.2.3.8 - seal implementation 1581 * @return the sealed ScriptObject 1582 */ 1583 public ScriptObject seal() { 1584 PropertyMap oldMap = getMap(); 1585 1586 while (true) { 1587 final PropertyMap newMap = getMap().seal(); 1588 1589 if (!compareAndSetMap(oldMap, newMap)) { 1590 oldMap = getMap(); 1591 } else { 1592 setArray(ArrayData.seal(getArray())); 1593 return this; 1594 } 1595 } 1596 } 1597 1598 /** 1599 * Check whether this ScriptObject is sealed 1600 * @return true if sealed 1601 */ 1602 public boolean isSealed() { 1603 return getMap().isSealed(); 1604 } 1605 1606 /** 1607 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject 1608 * @return the frozen ScriptObject 1609 */ 1610 public ScriptObject freeze() { 1611 PropertyMap oldMap = getMap(); 1612 1613 while (true) { 1614 final PropertyMap newMap = getMap().freeze(); 1615 1616 if (!compareAndSetMap(oldMap, newMap)) { 1617 oldMap = getMap(); 1618 } else { 1619 setArray(ArrayData.freeze(getArray())); 1620 return this; 1621 } 1622 } 1623 } 1624 1625 /** 1626 * Check whether this ScriptObject is frozen 1627 * @return true if frozen 1628 */ 1629 public boolean isFrozen() { 1630 return getMap().isFrozen(); 1631 } 1632 1633 1634 /** 1635 * Flag this ScriptObject as scope 1636 */ 1637 public final void setIsScope() { 1638 if (Context.DEBUG) { 1639 scopeCount++; 1640 } 1641 flags |= IS_SCOPE; 1642 } 1643 1644 /** 1645 * Check whether this ScriptObject is scope 1646 * @return true if scope 1647 */ 1648 public final boolean isScope() { 1649 return (flags & IS_SCOPE) != 0; 1650 } 1651 1652 /** 1653 * Tag this script object as built in 1654 */ 1655 public final void setIsBuiltin() { 1656 flags |= IS_BUILTIN; 1657 } 1658 1659 /** 1660 * Check if this script object is built in 1661 * @return true if build in 1662 */ 1663 public final boolean isBuiltin() { 1664 return (flags & IS_BUILTIN) != 0; 1665 } 1666 1667 /** 1668 * Clears the properties from a ScriptObject 1669 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1670 * 1671 * @param strict strict mode or not 1672 */ 1673 public void clear(final boolean strict) { 1674 final Iterator<String> iter = propertyIterator(); 1675 while (iter.hasNext()) { 1676 delete(iter.next(), strict); 1677 } 1678 } 1679 1680 /** 1681 * Checks if a property with a given key is present in a ScriptObject 1682 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1683 * 1684 * @param key the key to check for 1685 * @return true if a property with the given key exists, false otherwise 1686 */ 1687 public boolean containsKey(final Object key) { 1688 return has(key); 1689 } 1690 1691 /** 1692 * Checks if a property with a given value is present in a ScriptObject 1693 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1694 * 1695 * @param value value to check for 1696 * @return true if a property with the given value exists, false otherwise 1697 */ 1698 public boolean containsValue(final Object value) { 1699 final Iterator<Object> iter = valueIterator(); 1700 while (iter.hasNext()) { 1701 if (iter.next().equals(value)) { 1702 return true; 1703 } 1704 } 1705 return false; 1706 } 1707 1708 /** 1709 * Returns the set of {@literal <property, value>} entries that make up this 1710 * ScriptObject's properties 1711 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1712 * 1713 * @return an entry set of all the properties in this object 1714 */ 1715 public Set<Map.Entry<Object, Object>> entrySet() { 1716 final Iterator<String> iter = propertyIterator(); 1717 final Set<Map.Entry<Object, Object>> entries = new HashSet<>(); 1718 while (iter.hasNext()) { 1719 final Object key = iter.next(); 1720 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key))); 1721 } 1722 return Collections.unmodifiableSet(entries); 1723 } 1724 1725 /** 1726 * Check whether a ScriptObject contains no properties 1727 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1728 * 1729 * @return true if object has no properties 1730 */ 1731 public boolean isEmpty() { 1732 return !propertyIterator().hasNext(); 1733 } 1734 1735 /** 1736 * Return the set of keys (property names) for all properties 1737 * in this ScriptObject 1738 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1739 * 1740 * @return keySet of this ScriptObject 1741 */ 1742 public Set<Object> keySet() { 1743 final Iterator<String> iter = propertyIterator(); 1744 final Set<Object> keySet = new HashSet<>(); 1745 while (iter.hasNext()) { 1746 keySet.add(iter.next()); 1747 } 1748 return Collections.unmodifiableSet(keySet); 1749 } 1750 1751 /** 1752 * Put a property in the ScriptObject 1753 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1754 * 1755 * @param key property key 1756 * @param value property value 1757 * @param strict strict mode or not 1758 * @return oldValue if property with same key existed already 1759 */ 1760 public Object put(final Object key, final Object value, final boolean strict) { 1761 final Object oldValue = get(key); 1762 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1763 set(key, value, scriptObjectFlags); 1764 return oldValue; 1765 } 1766 1767 /** 1768 * Put several properties in the ScriptObject given a mapping 1769 * of their keys to their values 1770 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1771 * 1772 * @param otherMap a {@literal <key,value>} map of properties to add 1773 * @param strict strict mode or not 1774 */ 1775 public void putAll(final Map<?, ?> otherMap, final boolean strict) { 1776 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1777 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) { 1778 set(entry.getKey(), entry.getValue(), scriptObjectFlags); 1779 } 1780 } 1781 1782 /** 1783 * Remove a property from the ScriptObject. 1784 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1785 * 1786 * @param key the key of the property 1787 * @param strict strict mode or not 1788 * @return the oldValue of the removed property 1789 */ 1790 public Object remove(final Object key, final boolean strict) { 1791 final Object oldValue = get(key); 1792 delete(key, strict); 1793 return oldValue; 1794 } 1795 1796 /** 1797 * Return the size of the ScriptObject - i.e. the number of properties 1798 * it contains 1799 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1800 * 1801 * @return number of properties in ScriptObject 1802 */ 1803 public int size() { 1804 int n = 0; 1805 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) { 1806 n++; 1807 } 1808 return n; 1809 } 1810 1811 /** 1812 * Return the values of the properties in the ScriptObject 1813 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1814 * 1815 * @return collection of values for the properties in this ScriptObject 1816 */ 1817 public Collection<Object> values() { 1818 final List<Object> values = new ArrayList<>(size()); 1819 final Iterator<Object> iter = valueIterator(); 1820 while (iter.hasNext()) { 1821 values.add(iter.next()); 1822 } 1823 return Collections.unmodifiableList(values); 1824 } 1825 1826 /** 1827 * Lookup method that, given a CallSiteDescriptor, looks up the target 1828 * MethodHandle and creates a GuardedInvocation 1829 * with the appropriate guard(s). 1830 * 1831 * @param desc call site descriptor 1832 * @param request the link request 1833 * 1834 * @return GuardedInvocation for the callsite 1835 */ 1836 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) { 1837 final int c = desc.getNameTokenCount(); 1838 // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem 1839 // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't 1840 // care about them, and just link to whatever is the first operation. 1841 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); 1842 // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself 1843 // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are 1844 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the 1845 // operation has an associated name or not. 1846 switch (operator) { 1847 case "getProp": 1848 case "getElem": 1849 case "getMethod": 1850 return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request); 1851 case "setProp": 1852 case "setElem": 1853 return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc, request); 1854 case "call": 1855 return findCallMethod(desc, request); 1856 case "new": 1857 return findNewMethod(desc, request); 1858 case "callMethod": 1859 return findCallMethodMethod(desc, request); 1860 default: 1861 return null; 1862 } 1863 } 1864 1865 /** 1866 * Find the appropriate New method for an invoke dynamic call. 1867 * 1868 * @param desc The invoke dynamic call site descriptor. 1869 * @param request The link request 1870 * 1871 * @return GuardedInvocation to be invoked at call site. 1872 */ 1873 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1874 return notAFunction(); 1875 } 1876 1877 /** 1878 * Find the appropriate CALL method for an invoke dynamic call. 1879 * This generates "not a function" always 1880 * 1881 * @param desc the call site descriptor. 1882 * @param request the link request 1883 * 1884 * @return GuardedInvocation to be invoed at call site. 1885 */ 1886 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1887 return notAFunction(); 1888 } 1889 1890 private GuardedInvocation notAFunction() { 1891 throw typeError("not.a.function", ScriptRuntime.safeToString(this)); 1892 } 1893 1894 /** 1895 * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses 1896 * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another 1897 * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external 1898 * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle. 1899 * 1900 * @param desc the call site descriptor. 1901 * @param request the link request 1902 * 1903 * @return GuardedInvocation to be invoked at call site. 1904 */ 1905 protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1906 // R(P0, P1, ...) 1907 final MethodType callType = desc.getMethodType(); 1908 // use type Object(P0) for the getter 1909 final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0))); 1910 final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod"); 1911 1912 // Object(P0) => Object(P0, P1, ...) 1913 final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount())); 1914 // R(Object, P0, P1, ...) 1915 final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType())); 1916 // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...) 1917 return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard()); 1918 } 1919 1920 /** 1921 * Test whether this object contains in its prototype chain or is itself a with-object. 1922 * @return true if a with-object was found 1923 */ 1924 final boolean hasWithScope() { 1925 if (isScope()) { 1926 for (ScriptObject obj = this; obj != null; obj = obj.getProto()) { 1927 if (obj instanceof WithObject) { 1928 return true; 1929 } 1930 } 1931 } 1932 return false; 1933 } 1934 1935 /** 1936 * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method 1937 * {@code depth} times. 1938 * @param methodHandle a method handle 1939 * @param depth distance to target prototype 1940 * @return the filtered method handle 1941 */ 1942 static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) { 1943 if (depth == 0) { 1944 return methodHandle; 1945 } 1946 final int listIndex = depth - 1; // We don't need 0-deep walker 1947 MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null; 1948 1949 if (filter == null) { 1950 filter = addProtoFilter(GETPROTO, depth - 1); 1951 PROTO_FILTERS.add(null); 1952 PROTO_FILTERS.set(listIndex, filter); 1953 } 1954 1955 return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0)))); 1956 } 1957 1958 /** 1959 * Find the appropriate GET method for an invoke dynamic call. 1960 * 1961 * @param desc the call site descriptor 1962 * @param request the link request 1963 * @param operator operator for get: getProp, getMethod, getElem etc 1964 * 1965 * @return GuardedInvocation to be invoked at call site. 1966 */ 1967 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { 1968 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 1969 1970 String name; 1971 name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 1972 if (NashornCallSiteDescriptor.isApplyToCall(desc)) { 1973 if (Global.isBuiltinFunctionPrototypeApply()) { 1974 name = "call"; 1975 } 1976 } 1977 1978 if (request.isCallSiteUnstable() || hasWithScope()) { 1979 return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator)); 1980 } 1981 1982 final FindProperty find = findProperty(name, true); 1983 MethodHandle mh; 1984 1985 if (find == null) { 1986 switch (operator) { 1987 case "getElem": // getElem only gets here if element name is constant, so treat it like a property access 1988 case "getProp": 1989 return noSuchProperty(desc, request); 1990 case "getMethod": 1991 return noSuchMethod(desc, request); 1992 default: 1993 throw new AssertionError(operator); // never invoked with any other operation 1994 } 1995 } 1996 1997 final GlobalConstants globalConstants = getGlobalConstants(); 1998 if (globalConstants != null) { 1999 final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc); 2000 if (cinv != null) { 2001 return cinv; 2002 } 2003 } 2004 2005 final Class<?> returnType = desc.getMethodType().returnType(); 2006 final Property property = find.getProperty(); 2007 2008 final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? 2009 NashornCallSiteDescriptor.getProgramPoint(desc) : 2010 UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 2011 2012 mh = find.getGetter(returnType, programPoint, request); 2013 // Get the appropriate guard for this callsite and property. 2014 final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); 2015 final ScriptObject owner = find.getOwner(); 2016 final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; 2017 2018 final SwitchPoint protoSwitchPoint; 2019 2020 if (mh == null) { 2021 mh = Lookup.emptyGetter(returnType); 2022 protoSwitchPoint = getProtoSwitchPoint(name, owner); 2023 } else if (!find.isSelf()) { 2024 assert mh.type().returnType().equals(returnType) : 2025 "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; 2026 if (!(property instanceof UserAccessorProperty)) { 2027 // Add a filter that replaces the self object with the prototype owning the property. 2028 mh = addProtoFilter(mh, find.getProtoChainLength()); 2029 } 2030 protoSwitchPoint = getProtoSwitchPoint(name, owner); 2031 } else { 2032 protoSwitchPoint = null; 2033 } 2034 2035 final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); 2036 return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); 2037 } 2038 2039 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { 2040 Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: " + desc + " " + name + " " +isMethod); 2041 final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod); 2042 final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true); 2043 return new GuardedInvocation(invoker, guard); 2044 } 2045 2046 @SuppressWarnings("unused") 2047 private Object megamorphicGet(final String key, final boolean isMethod) { 2048 final FindProperty find = findProperty(key, true); 2049 if (find != null) { 2050 return find.getObjectValue(); 2051 } 2052 2053 return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); 2054 } 2055 2056 // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST 2057 @SuppressWarnings("unused") 2058 private void declareAndSet(final String key, final Object value) { 2059 final PropertyMap oldMap = getMap(); 2060 final FindProperty find = findProperty(key, false); 2061 assert find != null; 2062 2063 final Property property = find.getProperty(); 2064 assert property != null; 2065 assert property.needsDeclaration(); 2066 2067 final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION)); 2068 setMap(newMap); 2069 set(key, value, 0); 2070 } 2071 2072 /** 2073 * Find the appropriate GETINDEX method for an invoke dynamic call. 2074 * 2075 * @param desc the call site descriptor 2076 * @param request the link request 2077 * 2078 * @return GuardedInvocation to be invoked at call site. 2079 */ 2080 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2081 final MethodType callType = desc.getMethodType(); 2082 final Class<?> returnType = callType.returnType(); 2083 final Class<?> returnClass = returnType.isPrimitive() ? returnType : Object.class; 2084 final Class<?> keyClass = callType.parameterType(1); 2085 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2086 2087 final String name; 2088 if (returnClass.isPrimitive()) { 2089 //turn e.g. get with a double into getDouble 2090 final String returnTypeName = returnClass.getName(); 2091 name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length()); 2092 } else { 2093 name = "get"; 2094 } 2095 2096 final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc); 2097 return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2098 } 2099 2100 private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) { 2101 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck); 2102 } 2103 2104 /** 2105 * Find a handle for a getIndex method 2106 * @param returnType return type for getter 2107 * @param name name 2108 * @param elementType index type for getter 2109 * @param desc call site descriptor 2110 * @return method handle for getter 2111 */ 2112 protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) { 2113 if (!returnType.isPrimitive()) { 2114 return findOwnMH_V(getClass(), name, returnType, elementType); 2115 } 2116 2117 return MH.insertArguments( 2118 findOwnMH_V(getClass(), name, returnType, elementType, int.class), 2119 2, 2120 NashornCallSiteDescriptor.isOptimistic(desc) ? 2121 NashornCallSiteDescriptor.getProgramPoint(desc) : 2122 INVALID_PROGRAM_POINT); 2123 } 2124 2125 /** 2126 * Get a switch point for a property with the given {@code name} that will be invalidated when 2127 * the property definition is changed in this object's prototype chain. Returns {@code null} if 2128 * the property is defined in this object itself. 2129 * 2130 * @param name the property name 2131 * @param owner the property owner, null if property is not defined 2132 * @return a SwitchPoint or null 2133 */ 2134 public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) { 2135 if (owner == this || getProto() == null) { 2136 return null; 2137 } 2138 2139 for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { 2140 final ScriptObject parent = obj.getProto(); 2141 parent.getMap().addListener(name, obj.getMap()); 2142 } 2143 2144 return getMap().getSwitchPoint(name); 2145 } 2146 2147 /** 2148 * Find the appropriate SET method for an invoke dynamic call. 2149 * 2150 * @param desc the call site descriptor 2151 * @param request the link request 2152 * 2153 * @return GuardedInvocation to be invoked at call site. 2154 */ 2155 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2156 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 2157 2158 if (request.isCallSiteUnstable() || hasWithScope()) { 2159 return findMegaMorphicSetMethod(desc, name); 2160 } 2161 2162 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2163 2164 /* 2165 * If doing property set on a scope object, we should stop proto search on the first 2166 * non-scope object. Without this, for example, when assigning "toString" on global scope, 2167 * we'll end up assigning it on it's proto - which is Object.prototype.toString !! 2168 * 2169 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString 2170 */ 2171 FindProperty find = findProperty(name, true, this); 2172 2173 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors. 2174 if (find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { 2175 // We should still check if inherited data property is not writable 2176 if (isExtensible() && !find.getProperty().isWritable()) { 2177 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2178 } 2179 // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well. 2180 if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) { 2181 find = null; 2182 } 2183 } 2184 2185 if (find != null) { 2186 if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { 2187 if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) { 2188 throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode. 2189 } 2190 // Existing, non-writable property 2191 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2192 } 2193 } else { 2194 if (!isExtensible()) { 2195 return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false); 2196 } 2197 } 2198 2199 final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name)); 2200 2201 final GlobalConstants globalConstants = getGlobalConstants(); 2202 if (globalConstants != null) { 2203 final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request); 2204 if (cinv != null) { 2205 return cinv; 2206 } 2207 } 2208 2209 return inv; 2210 } 2211 2212 private GlobalConstants getGlobalConstants() { 2213 // Avoid hitting getContext() which might be costly for a non-Global unless needed. 2214 return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants(); 2215 } 2216 2217 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) { 2218 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 2219 if (NashornCallSiteDescriptor.isStrict(desc)) { 2220 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this)); 2221 } 2222 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc); 2223 return new GuardedInvocation( 2224 Lookup.EMPTY_SETTER, 2225 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), 2226 getProtoSwitchPoint(name, null), 2227 explicitInstanceOfCheck ? null : ClassCastException.class); 2228 } 2229 2230 @SuppressWarnings("unused") 2231 private boolean extensionCheck(final boolean isStrict, final String name) { 2232 if (isExtensible()) { 2233 return true; //go on and do the set. this is our guard 2234 } else if (isStrict) { 2235 //throw an error for attempting to do the set in strict mode 2236 throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this)); 2237 } else { 2238 //not extensible, non strict - this is a nop 2239 return false; 2240 } 2241 } 2242 2243 private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { 2244 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class); 2245 //never bother with ClassCastExceptionGuard for megamorphic callsites 2246 final GuardedInvocation inv = findSetIndexMethod(getClass(), desc, false, type); 2247 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 2248 } 2249 2250 @SuppressWarnings("unused") 2251 private static Object globalFilter(final Object object) { 2252 ScriptObject sobj = (ScriptObject) object; 2253 while (sobj != null && !(sobj instanceof Global)) { 2254 sobj = sobj.getProto(); 2255 } 2256 return sobj; 2257 } 2258 2259 /** 2260 * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray} 2261 * provides special quick accessor linkage for continuous arrays that are represented as Java arrays 2262 * 2263 * @param desc call site descriptor 2264 * @param request link request 2265 * 2266 * @return GuardedInvocation to be invoked at call site. 2267 */ 2268 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value 2269 return findSetIndexMethod(getClass(), desc, explicitInstanceOfCheck(desc, request), desc.getMethodType()); 2270 } 2271 2272 /** 2273 * Find the appropriate SETINDEX method for an invoke dynamic call. 2274 * 2275 * @param clazz the receiver class 2276 * @param desc the call site descriptor 2277 * @param explicitInstanceOfCheck add an explicit instanceof check? 2278 * @param callType the method type at the call site 2279 * 2280 * @return GuardedInvocation to be invoked at call site. 2281 */ 2282 private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) { 2283 assert callType.parameterCount() == 3; 2284 final Class<?> keyClass = callType.parameterType(1); 2285 final Class<?> valueClass = callType.parameterType(2); 2286 2287 MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, int.class); 2288 methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc)); 2289 2290 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2291 } 2292 2293 /** 2294 * Fall back if a function property is not found. 2295 * @param desc The call site descriptor 2296 * @param request the link request 2297 * @return GuardedInvocation to be invoked at call site. 2298 */ 2299 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2300 final String name = desc.getNameToken(2); 2301 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2302 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc); 2303 2304 if (find == null) { 2305 return noSuchProperty(desc, request); 2306 } 2307 2308 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2309 2310 final Object value = find.getObjectValue(); 2311 if (!(value instanceof ScriptFunction)) { 2312 return createEmptyGetter(desc, explicitInstanceOfCheck, name); 2313 } 2314 2315 final ScriptFunction func = (ScriptFunction)value; 2316 final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this; 2317 // TODO: It'd be awesome if we could bind "name" without binding "this". 2318 // Since we're binding this we must use an identity guard here. 2319 return new GuardedInvocation( 2320 MH.dropArguments( 2321 MH.constant( 2322 ScriptFunction.class, 2323 func.makeBoundFunction(thiz, new Object[] { name })), 2324 0, 2325 Object.class), 2326 NashornGuards.combineGuards( 2327 NashornGuards.getIdentityGuard(this), 2328 NashornGuards.getMapGuard(getMap(), true))); 2329 } 2330 2331 /** 2332 * Fall back if a property is not found. 2333 * @param desc the call site descriptor. 2334 * @param request the link request 2335 * @return GuardedInvocation to be invoked at call site. 2336 */ 2337 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 2338 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 2339 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2340 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); 2341 2342 if (find != null) { 2343 final Object value = find.getObjectValue(); 2344 ScriptFunction func = null; 2345 MethodHandle mh = null; 2346 2347 if (value instanceof ScriptFunction) { 2348 func = (ScriptFunction)value; 2349 mh = getCallMethodHandle(func, desc.getMethodType(), name); 2350 } 2351 2352 if (mh != null) { 2353 assert func != null; 2354 if (scopeAccess && func.isStrict()) { 2355 mh = bindTo(mh, UNDEFINED); 2356 } 2357 2358 return new GuardedInvocation( 2359 mh, 2360 find.isSelf()? 2361 getKnownFunctionPropertyGuardSelf( 2362 getMap(), 2363 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2364 func) 2365 : 2366 //TODO this always does a scriptobject check 2367 getKnownFunctionPropertyGuardProto( 2368 getMap(), 2369 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2370 find.getProtoChainLength(), 2371 func), 2372 getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), 2373 //TODO this doesn't need a ClassCastException as guard always checks script object 2374 null); 2375 } 2376 } 2377 2378 if (scopeAccess) { 2379 throw referenceError("not.defined", name); 2380 } 2381 2382 return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name); 2383 } 2384 2385 /** 2386 * Invoke fall back if a property is not found. 2387 * @param name Name of property. 2388 * @param programPoint program point 2389 * @return Result from call. 2390 */ 2391 protected Object invokeNoSuchProperty(final String name, final int programPoint) { 2392 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2393 2394 Object ret = UNDEFINED; 2395 2396 if (find != null) { 2397 final Object func = find.getObjectValue(); 2398 2399 if (func instanceof ScriptFunction) { 2400 ret = ScriptRuntime.apply((ScriptFunction)func, this, name); 2401 } 2402 } 2403 2404 if (isValid(programPoint)) { 2405 throw new UnwarrantedOptimismException(ret, programPoint); 2406 } 2407 2408 return ret; 2409 } 2410 2411 2412 /** 2413 * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. 2414 * @param name the method name 2415 * @return the bound function, or undefined 2416 */ 2417 private Object getNoSuchMethod(final String name, final int programPoint) { 2418 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2419 2420 if (find == null) { 2421 return invokeNoSuchProperty(name, programPoint); 2422 } 2423 2424 final Object value = find.getObjectValue(); 2425 if (!(value instanceof ScriptFunction)) { 2426 return UNDEFINED; 2427 } 2428 2429 return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name}); 2430 } 2431 2432 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) { 2433 if (NashornCallSiteDescriptor.isOptimistic(desc)) { 2434 throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT); 2435 } 2436 2437 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), 2438 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null), 2439 explicitInstanceOfCheck ? null : ClassCastException.class); 2440 } 2441 2442 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> { 2443 protected T[] values; 2444 protected final ScriptObject object; 2445 private int index; 2446 2447 ScriptObjectIterator(final ScriptObject object) { 2448 this.object = object; 2449 } 2450 2451 protected abstract void init(); 2452 2453 @Override 2454 public boolean hasNext() { 2455 if (values == null) { 2456 init(); 2457 } 2458 return index < values.length; 2459 } 2460 2461 @Override 2462 public T next() { 2463 if (values == null) { 2464 init(); 2465 } 2466 return values[index++]; 2467 } 2468 2469 @Override 2470 public void remove() { 2471 throw new UnsupportedOperationException("remove"); 2472 } 2473 } 2474 2475 private static class KeyIterator extends ScriptObjectIterator<String> { 2476 KeyIterator(final ScriptObject object) { 2477 super(object); 2478 } 2479 2480 @Override 2481 protected void init() { 2482 final Set<String> keys = new LinkedHashSet<>(); 2483 final Set<String> nonEnumerable = new HashSet<>(); 2484 for (ScriptObject self = object; self != null; self = self.getProto()) { 2485 keys.addAll(Arrays.asList(self.getOwnKeys(false, nonEnumerable))); 2486 } 2487 this.values = keys.toArray(new String[keys.size()]); 2488 } 2489 } 2490 2491 private static class ValueIterator extends ScriptObjectIterator<Object> { 2492 ValueIterator(final ScriptObject object) { 2493 super(object); 2494 } 2495 2496 @Override 2497 protected void init() { 2498 final ArrayList<Object> valueList = new ArrayList<>(); 2499 final Set<String> nonEnumerable = new HashSet<>(); 2500 for (ScriptObject self = object; self != null; self = self.getProto()) { 2501 for (final String key : self.getOwnKeys(false, nonEnumerable)) { 2502 valueList.add(self.get(key)); 2503 } 2504 } 2505 this.values = valueList.toArray(new Object[valueList.size()]); 2506 } 2507 } 2508 2509 /** 2510 * Add a spill property for the given key. 2511 * @param key Property key. 2512 * @param flags Property flags. 2513 * @return Added property. 2514 */ 2515 private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) { 2516 final PropertyMap propertyMap = getMap(); 2517 final int fieldSlot = propertyMap.getFreeFieldSlot(); 2518 final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0); 2519 2520 Property property; 2521 if (fieldSlot > -1) { 2522 property = hasInitialValue ? 2523 new AccessorProperty(key, propertyFlags, fieldSlot, this, value) : 2524 new AccessorProperty(key, propertyFlags, getClass(), fieldSlot); 2525 property = addOwnProperty(property); 2526 } else { 2527 final int spillSlot = propertyMap.getFreeSpillSlot(); 2528 property = hasInitialValue ? 2529 new SpillProperty(key, propertyFlags, spillSlot, this, value) : 2530 new SpillProperty(key, propertyFlags, spillSlot); 2531 property = addOwnProperty(property); 2532 ensureSpillSize(property.getSlot()); 2533 } 2534 return property; 2535 } 2536 2537 /** 2538 * Add a spill entry for the given key. 2539 * @param key Property key. 2540 * @return Setter method handle. 2541 */ 2542 MethodHandle addSpill(final Class<?> type, final String key) { 2543 return addSpillProperty(key, 0, null, false).getSetter(type, getMap()); 2544 } 2545 2546 /** 2547 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2548 * fewer parameters than declared and other things that JavaScript allows. This might involve 2549 * creating collectors. 2550 * 2551 * @param methodHandle method handle for invoke 2552 * @param callType type of the call 2553 * 2554 * @return method handle with adjusted arguments 2555 */ 2556 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) { 2557 return pairArguments(methodHandle, callType, null); 2558 } 2559 2560 /** 2561 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2562 * fewer parameters than declared and other things that JavaScript allows. This might involve 2563 * creating collectors. 2564 * 2565 * Make sure arguments are paired correctly. 2566 * @param methodHandle MethodHandle to adjust. 2567 * @param callType MethodType of the call site. 2568 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the 2569 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a 2570 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites 2571 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters. 2572 * 2573 * @return method handle with adjusted arguments 2574 */ 2575 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) { 2576 final MethodType methodType = methodHandle.type(); 2577 if (methodType.equals(callType.changeReturnType(methodType.returnType()))) { 2578 return methodHandle; 2579 } 2580 2581 final int parameterCount = methodType.parameterCount(); 2582 final int callCount = callType.parameterCount(); 2583 2584 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray(); 2585 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 && 2586 callType.parameterType(callCount - 1).isArray(); 2587 2588 if (isCalleeVarArg) { 2589 return isCallerVarArg ? 2590 methodHandle : 2591 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1); 2592 } 2593 2594 if (isCallerVarArg) { 2595 return adaptHandleToVarArgCallSite(methodHandle, callCount); 2596 } 2597 2598 if (callCount < parameterCount) { 2599 final int missingArgs = parameterCount - callCount; 2600 final Object[] fillers = new Object[missingArgs]; 2601 2602 Arrays.fill(fillers, UNDEFINED); 2603 2604 if (isCalleeVarArg) { 2605 fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY; 2606 } 2607 2608 return MH.insertArguments( 2609 methodHandle, 2610 parameterCount - missingArgs, 2611 fillers); 2612 } 2613 2614 if (callCount > parameterCount) { 2615 final int discardedArgs = callCount - parameterCount; 2616 2617 final Class<?>[] discards = new Class<?>[discardedArgs]; 2618 Arrays.fill(discards, Object.class); 2619 2620 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards); 2621 } 2622 2623 return methodHandle; 2624 } 2625 2626 static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) { 2627 final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1; 2628 return MH.filterArguments( 2629 MH.asSpreader( 2630 mh, 2631 Object[].class, 2632 spreadArgs), 2633 callSiteParamCount - 1, 2634 MH.insertArguments( 2635 TRUNCATINGFILTER, 2636 0, 2637 spreadArgs) 2638 ); 2639 } 2640 2641 @SuppressWarnings("unused") 2642 private static Object[] truncatingFilter(final int n, final Object[] array) { 2643 final int length = array == null ? 0 : array.length; 2644 if (n == length) { 2645 return array == null ? ScriptRuntime.EMPTY_ARRAY : array; 2646 } 2647 2648 final Object[] newArray = new Object[n]; 2649 2650 if (array != null) { 2651 System.arraycopy(array, 0, newArray, 0, Math.min(n, length)); 2652 } 2653 2654 if (length < n) { 2655 final Object fill = UNDEFINED; 2656 2657 for (int i = length; i < n; i++) { 2658 newArray[i] = fill; 2659 } 2660 } 2661 2662 return newArray; 2663 } 2664 2665 /** 2666 * Numeric length setter for length property 2667 * 2668 * @param newLength new length to set 2669 */ 2670 public final void setLength(final long newLength) { 2671 ArrayData data = getArray(); 2672 final long arrayLength = data.length(); 2673 if (newLength == arrayLength) { 2674 return; 2675 } 2676 2677 if (newLength > arrayLength) { 2678 data = data.ensure(newLength - 1); 2679 if (data.canDelete(arrayLength, newLength - 1, false)) { 2680 data = data.delete(arrayLength, newLength - 1); 2681 } 2682 setArray(data); 2683 return; 2684 } 2685 2686 if (newLength < arrayLength) { 2687 long actualLength = newLength; 2688 2689 // Check for numeric keys in property map and delete them or adjust length, depending on whether 2690 // they're defined as configurable. See ES5 #15.4.5.2 2691 if (getMap().containsArrayKeys()) { 2692 2693 for (long l = arrayLength - 1; l >= newLength; l--) { 2694 final FindProperty find = findProperty(JSType.toString(l), false); 2695 2696 if (find != null) { 2697 2698 if (find.getProperty().isConfigurable()) { 2699 deleteOwnProperty(find.getProperty()); 2700 } else { 2701 actualLength = l + 1; 2702 break; 2703 } 2704 } 2705 } 2706 } 2707 2708 setArray(data.shrink(actualLength)); 2709 data.setLength(actualLength); 2710 } 2711 } 2712 2713 private int getInt(final int index, final String key, final int programPoint) { 2714 if (isValidArrayIndex(index)) { 2715 for (ScriptObject object = this; ; ) { 2716 if (object.getMap().containsArrayKeys()) { 2717 final FindProperty find = object.findProperty(key, false, this); 2718 2719 if (find != null) { 2720 return getIntValue(find, programPoint); 2721 } 2722 } 2723 2724 if ((object = object.getProto()) == null) { 2725 break; 2726 } 2727 2728 final ArrayData array = object.getArray(); 2729 2730 if (array.has(index)) { 2731 return isValid(programPoint) ? 2732 array.getIntOptimistic(index, programPoint) : 2733 array.getInt(index); 2734 } 2735 } 2736 } else { 2737 final FindProperty find = findProperty(key, true); 2738 2739 if (find != null) { 2740 return getIntValue(find, programPoint); 2741 } 2742 } 2743 2744 return JSType.toInt32(invokeNoSuchProperty(key, programPoint)); 2745 } 2746 2747 @Override 2748 public int getInt(final Object key, final int programPoint) { 2749 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2750 final int index = getArrayIndex(primitiveKey); 2751 final ArrayData array = getArray(); 2752 2753 if (array.has(index)) { 2754 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2755 } 2756 2757 return getInt(index, JSType.toString(primitiveKey), programPoint); 2758 } 2759 2760 @Override 2761 public int getInt(final double key, final int programPoint) { 2762 final int index = getArrayIndex(key); 2763 final ArrayData array = getArray(); 2764 2765 if (array.has(index)) { 2766 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2767 } 2768 2769 return getInt(index, JSType.toString(key), programPoint); 2770 } 2771 2772 @Override 2773 public int getInt(final long key, final int programPoint) { 2774 final int index = getArrayIndex(key); 2775 final ArrayData array = getArray(); 2776 2777 if (array.has(index)) { 2778 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2779 } 2780 2781 return getInt(index, JSType.toString(key), programPoint); 2782 } 2783 2784 @Override 2785 public int getInt(final int key, final int programPoint) { 2786 final int index = getArrayIndex(key); 2787 final ArrayData array = getArray(); 2788 2789 if (array.has(index)) { 2790 return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key); 2791 } 2792 2793 return getInt(index, JSType.toString(key), programPoint); 2794 } 2795 2796 private long getLong(final int index, final String key, final int programPoint) { 2797 if (isValidArrayIndex(index)) { 2798 for (ScriptObject object = this; ; ) { 2799 if (object.getMap().containsArrayKeys()) { 2800 final FindProperty find = object.findProperty(key, false, this); 2801 if (find != null) { 2802 return getLongValue(find, programPoint); 2803 } 2804 } 2805 2806 if ((object = object.getProto()) == null) { 2807 break; 2808 } 2809 2810 final ArrayData array = object.getArray(); 2811 2812 if (array.has(index)) { 2813 return isValid(programPoint) ? 2814 array.getLongOptimistic(index, programPoint) : 2815 array.getLong(index); 2816 } 2817 } 2818 } else { 2819 final FindProperty find = findProperty(key, true); 2820 2821 if (find != null) { 2822 return getLongValue(find, programPoint); 2823 } 2824 } 2825 2826 return JSType.toLong(invokeNoSuchProperty(key, programPoint)); 2827 } 2828 2829 @Override 2830 public long getLong(final Object key, final int programPoint) { 2831 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2832 final int index = getArrayIndex(primitiveKey); 2833 final ArrayData array = getArray(); 2834 2835 if (array.has(index)) { 2836 return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index); 2837 } 2838 2839 return getLong(index, JSType.toString(primitiveKey), programPoint); 2840 } 2841 2842 @Override 2843 public long getLong(final double key, final int programPoint) { 2844 final int index = getArrayIndex(key); 2845 final ArrayData array = getArray(); 2846 2847 if (array.has(index)) { 2848 return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index); 2849 } 2850 2851 return getLong(index, JSType.toString(key), programPoint); 2852 } 2853 2854 @Override 2855 public long getLong(final long key, final int programPoint) { 2856 final int index = getArrayIndex(key); 2857 final ArrayData array = getArray(); 2858 2859 if (array.has(index)) { 2860 return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index); 2861 } 2862 2863 return getLong(index, JSType.toString(key), programPoint); 2864 } 2865 2866 @Override 2867 public long getLong(final int key, final int programPoint) { 2868 final int index = getArrayIndex(key); 2869 final ArrayData array = getArray(); 2870 2871 if (array.has(index)) { 2872 return isValid(programPoint) ? array.getLongOptimistic(key, programPoint) : array.getLong(key); 2873 } 2874 2875 return getLong(index, JSType.toString(key), programPoint); 2876 } 2877 2878 private double getDouble(final int index, final String key, final int programPoint) { 2879 if (isValidArrayIndex(index)) { 2880 for (ScriptObject object = this; ; ) { 2881 if (object.getMap().containsArrayKeys()) { 2882 final FindProperty find = object.findProperty(key, false, this); 2883 if (find != null) { 2884 return getDoubleValue(find, programPoint); 2885 } 2886 } 2887 2888 if ((object = object.getProto()) == null) { 2889 break; 2890 } 2891 2892 final ArrayData array = object.getArray(); 2893 2894 if (array.has(index)) { 2895 return isValid(programPoint) ? 2896 array.getDoubleOptimistic(index, programPoint) : 2897 array.getDouble(index); 2898 } 2899 } 2900 } else { 2901 final FindProperty find = findProperty(key, true); 2902 2903 if (find != null) { 2904 return getDoubleValue(find, programPoint); 2905 } 2906 } 2907 2908 return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT)); 2909 } 2910 2911 @Override 2912 public double getDouble(final Object key, final int programPoint) { 2913 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2914 final int index = getArrayIndex(primitiveKey); 2915 final ArrayData array = getArray(); 2916 2917 if (array.has(index)) { 2918 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2919 } 2920 2921 return getDouble(index, JSType.toString(primitiveKey), programPoint); 2922 } 2923 2924 @Override 2925 public double getDouble(final double key, final int programPoint) { 2926 final int index = getArrayIndex(key); 2927 final ArrayData array = getArray(); 2928 2929 if (array.has(index)) { 2930 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2931 } 2932 2933 return getDouble(index, JSType.toString(key), programPoint); 2934 } 2935 2936 @Override 2937 public double getDouble(final long key, final int programPoint) { 2938 final int index = getArrayIndex(key); 2939 final ArrayData array = getArray(); 2940 2941 if (array.has(index)) { 2942 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2943 } 2944 2945 return getDouble(index, JSType.toString(key), programPoint); 2946 } 2947 2948 @Override 2949 public double getDouble(final int key, final int programPoint) { 2950 final int index = getArrayIndex(key); 2951 final ArrayData array = getArray(); 2952 2953 if (array.has(index)) { 2954 return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key); 2955 } 2956 2957 return getDouble(index, JSType.toString(key), programPoint); 2958 } 2959 2960 private Object get(final int index, final String key) { 2961 if (isValidArrayIndex(index)) { 2962 for (ScriptObject object = this; ; ) { 2963 if (object.getMap().containsArrayKeys()) { 2964 final FindProperty find = object.findProperty(key, false, this); 2965 2966 if (find != null) { 2967 return find.getObjectValue(); 2968 } 2969 } 2970 2971 if ((object = object.getProto()) == null) { 2972 break; 2973 } 2974 2975 final ArrayData array = object.getArray(); 2976 2977 if (array.has(index)) { 2978 return array.getObject(index); 2979 } 2980 } 2981 } else { 2982 final FindProperty find = findProperty(key, true); 2983 2984 if (find != null) { 2985 return find.getObjectValue(); 2986 } 2987 } 2988 2989 return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT); 2990 } 2991 2992 @Override 2993 public Object get(final Object key) { 2994 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2995 final int index = getArrayIndex(primitiveKey); 2996 final ArrayData array = getArray(); 2997 2998 if (array.has(index)) { 2999 return array.getObject(index); 3000 } 3001 3002 return get(index, JSType.toString(primitiveKey)); 3003 } 3004 3005 @Override 3006 public Object get(final double key) { 3007 final int index = getArrayIndex(key); 3008 final ArrayData array = getArray(); 3009 3010 if (array.has(index)) { 3011 return array.getObject(index); 3012 } 3013 3014 return get(index, JSType.toString(key)); 3015 } 3016 3017 @Override 3018 public Object get(final long key) { 3019 final int index = getArrayIndex(key); 3020 final ArrayData array = getArray(); 3021 3022 if (array.has(index)) { 3023 return array.getObject(index); 3024 } 3025 3026 return get(index, JSType.toString(key)); 3027 } 3028 3029 @Override 3030 public Object get(final int key) { 3031 final int index = getArrayIndex(key); 3032 final ArrayData array = getArray(); 3033 3034 if (array.has(index)) { 3035 return array.getObject(index); 3036 } 3037 3038 return get(index, JSType.toString(key)); 3039 } 3040 3041 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) { 3042 if (getMap().containsArrayKeys()) { 3043 final String key = JSType.toString(longIndex); 3044 final FindProperty find = findProperty(key, true); 3045 if (find != null) { 3046 setObject(find, callSiteFlags, key, value); 3047 return true; 3048 } 3049 } 3050 return false; 3051 } 3052 3053 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final int callSiteFlags) { 3054 if (getMap().containsArrayKeys()) { 3055 final String key = JSType.toString(longIndex); 3056 final FindProperty find = findProperty(key, true); 3057 if (find != null) { 3058 setObject(find, callSiteFlags, key, value); 3059 return true; 3060 } 3061 } 3062 return false; 3063 } 3064 3065 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) { 3066 if (getMap().containsArrayKeys()) { 3067 final String key = JSType.toString(longIndex); 3068 final FindProperty find = findProperty(key, true); 3069 if (find != null) { 3070 setObject(find, callSiteFlags, key, value); 3071 return true; 3072 } 3073 } 3074 return false; 3075 } 3076 3077 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) { 3078 if (getMap().containsArrayKeys()) { 3079 final String key = JSType.toString(longIndex); 3080 final FindProperty find = findProperty(key, true); 3081 if (find != null) { 3082 setObject(find, callSiteFlags, key, value); 3083 return true; 3084 } 3085 } 3086 return false; 3087 } 3088 3089 //value agnostic 3090 private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) { 3091 if (longIndex >= oldLength) { 3092 if (!isExtensible()) { 3093 if (isStrictFlag(callSiteFlags)) { 3094 throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this)); 3095 } 3096 return true; 3097 } 3098 setArray(getArray().ensure(longIndex)); 3099 } 3100 return false; 3101 } 3102 3103 private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) { 3104 if (longIndex > oldLength) { 3105 ArrayData array = getArray(); 3106 if (array.canDelete(oldLength, longIndex - 1, strict)) { 3107 array = array.delete(oldLength, longIndex - 1); 3108 } 3109 setArray(array); 3110 } 3111 } 3112 3113 private void doesNotHave(final int index, final int value, final int callSiteFlags) { 3114 final long oldLength = getArray().length(); 3115 final long longIndex = ArrayIndex.toLongIndex(index); 3116 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3117 final boolean strict = isStrictFlag(callSiteFlags); 3118 setArray(getArray().set(index, value, strict)); 3119 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 3120 } 3121 } 3122 3123 private void doesNotHave(final int index, final long value, final int callSiteFlags) { 3124 final long oldLength = getArray().length(); 3125 final long longIndex = ArrayIndex.toLongIndex(index); 3126 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3127 final boolean strict = isStrictFlag(callSiteFlags); 3128 setArray(getArray().set(index, value, strict)); 3129 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 3130 } 3131 } 3132 3133 private void doesNotHave(final int index, final double value, final int callSiteFlags) { 3134 final long oldLength = getArray().length(); 3135 final long longIndex = ArrayIndex.toLongIndex(index); 3136 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3137 final boolean strict = isStrictFlag(callSiteFlags); 3138 setArray(getArray().set(index, value, strict)); 3139 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 3140 } 3141 } 3142 3143 private void doesNotHave(final int index, final Object value, final int callSiteFlags) { 3144 final long oldLength = getArray().length(); 3145 final long longIndex = ArrayIndex.toLongIndex(index); 3146 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3147 final boolean strict = isStrictFlag(callSiteFlags); 3148 setArray(getArray().set(index, value, strict)); 3149 doesNotHaveEnsureDelete(longIndex, oldLength, strict); 3150 } 3151 } 3152 3153 /** 3154 * This is the most generic of all Object setters. Most of the others use this in some form. 3155 * TODO: should be further specialized 3156 * 3157 * @param find found property 3158 * @param callSiteFlags callsite flags 3159 * @param key property key 3160 * @param value property value 3161 */ 3162 public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) { 3163 FindProperty f = find; 3164 3165 invalidateGlobalConstant(key); 3166 3167 if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { 3168 final boolean isScope = isScopeFlag(callSiteFlags); 3169 // If the start object of the find is not this object it means the property was found inside a 3170 // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set' 3171 // to the 'with' object. 3172 // Note that although a 'set' operation involving a with statement follows scope rules outside 3173 // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists), 3174 // it follows non-scope rules inside the 'with' expression (set is performed on the top level object). 3175 // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object. 3176 if (isScope && f.getSelf() != this) { 3177 f.getSelf().setObject(null, 0, key, value); 3178 return; 3179 } 3180 // Setting a property should not modify the property in prototype unless this is a scope callsite 3181 // and the owner is a scope object as well (with the exception of 'with' statement handled above). 3182 if (!isScope || !f.getOwner().isScope()) { 3183 f = null; 3184 } 3185 } 3186 3187 if (f != null) { 3188 if (!f.getProperty().isWritable()) { 3189 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) { 3190 throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode. 3191 } 3192 if (isStrictFlag(callSiteFlags)) { 3193 throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this)); 3194 } 3195 return; 3196 } 3197 3198 f.setValue(value, isStrictFlag(callSiteFlags)); 3199 3200 } else if (!isExtensible()) { 3201 if (isStrictFlag(callSiteFlags)) { 3202 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); 3203 } 3204 } else { 3205 ScriptObject sobj = this; 3206 // undefined scope properties are set in the global object. 3207 if (isScope()) { 3208 while (sobj != null && !(sobj instanceof Global)) { 3209 sobj = sobj.getProto(); 3210 } 3211 assert sobj != null : "no parent global object in scope"; 3212 } 3213 //this will unbox any Number object to its primitive type in case the 3214 //property supports primitive types, so it doesn't matter that it comes 3215 //in as an Object. 3216 sobj.addSpillProperty(key, 0, value, true); 3217 } 3218 } 3219 3220 @Override 3221 public void set(final Object key, final int value, final int callSiteFlags) { 3222 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3223 final int index = getArrayIndex(primitiveKey); 3224 3225 if (isValidArrayIndex(index)) { 3226 final ArrayData data = getArray(); 3227 if (data.has(index)) { 3228 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3229 } else { 3230 doesNotHave(index, value, callSiteFlags); 3231 } 3232 3233 return; 3234 } 3235 3236 final String propName = JSType.toString(primitiveKey); 3237 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3238 } 3239 3240 @Override 3241 public void set(final Object key, final long value, final int callSiteFlags) { 3242 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3243 final int index = getArrayIndex(primitiveKey); 3244 3245 if (isValidArrayIndex(index)) { 3246 final ArrayData data = getArray(); 3247 if (data.has(index)) { 3248 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3249 } else { 3250 doesNotHave(index, value, callSiteFlags); 3251 } 3252 3253 return; 3254 } 3255 3256 final String propName = JSType.toString(primitiveKey); 3257 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3258 } 3259 3260 @Override 3261 public void set(final Object key, final double value, final int callSiteFlags) { 3262 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3263 final int index = getArrayIndex(primitiveKey); 3264 3265 if (isValidArrayIndex(index)) { 3266 final ArrayData data = getArray(); 3267 if (data.has(index)) { 3268 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3269 } else { 3270 doesNotHave(index, value, callSiteFlags); 3271 } 3272 3273 return; 3274 } 3275 3276 final String propName = JSType.toString(primitiveKey); 3277 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3278 } 3279 3280 @Override 3281 public void set(final Object key, final Object value, final int callSiteFlags) { 3282 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3283 final int index = getArrayIndex(primitiveKey); 3284 3285 if (isValidArrayIndex(index)) { 3286 final ArrayData data = getArray(); 3287 if (data.has(index)) { 3288 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3289 } else { 3290 doesNotHave(index, value, callSiteFlags); 3291 } 3292 3293 return; 3294 } 3295 3296 final String propName = JSType.toString(primitiveKey); 3297 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3298 } 3299 3300 @Override 3301 public void set(final double key, final int value, final int callSiteFlags) { 3302 final int index = getArrayIndex(key); 3303 3304 if (isValidArrayIndex(index)) { 3305 final ArrayData data = getArray(); 3306 if (data.has(index)) { 3307 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3308 } else { 3309 doesNotHave(index, value, callSiteFlags); 3310 } 3311 3312 return; 3313 } 3314 3315 final String propName = JSType.toString(key); 3316 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3317 } 3318 3319 @Override 3320 public void set(final double key, final long value, final int callSiteFlags) { 3321 final int index = getArrayIndex(key); 3322 3323 if (isValidArrayIndex(index)) { 3324 final ArrayData data = getArray(); 3325 if (data.has(index)) { 3326 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3327 } else { 3328 doesNotHave(index, value, callSiteFlags); 3329 } 3330 3331 return; 3332 } 3333 3334 final String propName = JSType.toString(key); 3335 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3336 } 3337 3338 @Override 3339 public void set(final double key, final double value, final int callSiteFlags) { 3340 final int index = getArrayIndex(key); 3341 3342 if (isValidArrayIndex(index)) { 3343 final ArrayData data = getArray(); 3344 if (data.has(index)) { 3345 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3346 } else { 3347 doesNotHave(index, value, callSiteFlags); 3348 } 3349 3350 return; 3351 } 3352 3353 final String propName = JSType.toString(key); 3354 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3355 } 3356 3357 @Override 3358 public void set(final double key, final Object value, final int callSiteFlags) { 3359 final int index = getArrayIndex(key); 3360 3361 if (isValidArrayIndex(index)) { 3362 final ArrayData data = getArray(); 3363 if (data.has(index)) { 3364 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3365 } else { 3366 doesNotHave(index, value, callSiteFlags); 3367 } 3368 3369 return; 3370 } 3371 3372 final String propName = JSType.toString(key); 3373 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3374 } 3375 3376 @Override 3377 public void set(final long key, final int value, final int callSiteFlags) { 3378 final int index = getArrayIndex(key); 3379 3380 if (isValidArrayIndex(index)) { 3381 final ArrayData data = getArray(); 3382 if (data.has(index)) { 3383 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3384 } else { 3385 doesNotHave(index, value, callSiteFlags); 3386 } 3387 3388 return; 3389 } 3390 3391 final String propName = JSType.toString(key); 3392 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3393 } 3394 3395 @Override 3396 public void set(final long key, final long value, final int callSiteFlags) { 3397 final int index = getArrayIndex(key); 3398 3399 if (isValidArrayIndex(index)) { 3400 final ArrayData data = getArray(); 3401 if (data.has(index)) { 3402 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3403 } else { 3404 doesNotHave(index, value, callSiteFlags); 3405 } 3406 3407 return; 3408 } 3409 3410 final String propName = JSType.toString(key); 3411 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3412 } 3413 3414 @Override 3415 public void set(final long key, final double value, final int callSiteFlags) { 3416 final int index = getArrayIndex(key); 3417 3418 if (isValidArrayIndex(index)) { 3419 final ArrayData data = getArray(); 3420 if (data.has(index)) { 3421 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3422 } else { 3423 doesNotHave(index, value, callSiteFlags); 3424 } 3425 3426 return; 3427 } 3428 3429 final String propName = JSType.toString(key); 3430 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3431 } 3432 3433 @Override 3434 public void set(final long key, final Object value, final int callSiteFlags) { 3435 final int index = getArrayIndex(key); 3436 3437 if (isValidArrayIndex(index)) { 3438 final ArrayData data = getArray(); 3439 if (data.has(index)) { 3440 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3441 } else { 3442 doesNotHave(index, value, callSiteFlags); 3443 } 3444 3445 return; 3446 } 3447 3448 final String propName = JSType.toString(key); 3449 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3450 } 3451 3452 @Override 3453 public void set(final int key, final int value, final int callSiteFlags) { 3454 final int index = getArrayIndex(key); 3455 if (isValidArrayIndex(index)) { 3456 if (getArray().has(index)) { 3457 final ArrayData data = getArray(); 3458 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3459 } else { 3460 doesNotHave(index, value, callSiteFlags); 3461 } 3462 return; 3463 } 3464 3465 final String propName = JSType.toString(key); 3466 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3467 } 3468 3469 @Override 3470 public void set(final int key, final long value, final int callSiteFlags) { 3471 final int index = getArrayIndex(key); 3472 3473 if (isValidArrayIndex(index)) { 3474 final ArrayData data = getArray(); 3475 if (data.has(index)) { 3476 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3477 } else { 3478 doesNotHave(index, value, callSiteFlags); 3479 } 3480 3481 return; 3482 } 3483 3484 final String propName = JSType.toString(key); 3485 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3486 } 3487 3488 @Override 3489 public void set(final int key, final double value, final int callSiteFlags) { 3490 final int index = getArrayIndex(key); 3491 3492 if (isValidArrayIndex(index)) { 3493 final ArrayData data = getArray(); 3494 if (data.has(index)) { 3495 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3496 } else { 3497 doesNotHave(index, value, callSiteFlags); 3498 } 3499 3500 return; 3501 } 3502 3503 final String propName = JSType.toString(key); 3504 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3505 } 3506 3507 @Override 3508 public void set(final int key, final Object value, final int callSiteFlags) { 3509 final int index = getArrayIndex(key); 3510 3511 if (isValidArrayIndex(index)) { 3512 final ArrayData data = getArray(); 3513 if (data.has(index)) { 3514 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3515 } else { 3516 doesNotHave(index, value, callSiteFlags); 3517 } 3518 3519 return; 3520 } 3521 3522 final String propName = JSType.toString(key); 3523 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3524 } 3525 3526 @Override 3527 public boolean has(final Object key) { 3528 final Object primitiveKey = JSType.toPrimitive(key); 3529 final int index = getArrayIndex(primitiveKey); 3530 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true); 3531 } 3532 3533 @Override 3534 public boolean has(final double key) { 3535 final int index = getArrayIndex(key); 3536 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3537 } 3538 3539 @Override 3540 public boolean has(final long key) { 3541 final int index = getArrayIndex(key); 3542 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3543 } 3544 3545 @Override 3546 public boolean has(final int key) { 3547 final int index = getArrayIndex(key); 3548 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3549 } 3550 3551 private boolean hasArrayProperty(final int index) { 3552 boolean hasArrayKeys = false; 3553 3554 for (ScriptObject self = this; self != null; self = self.getProto()) { 3555 if (self.getArray().has(index)) { 3556 return true; 3557 } 3558 hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys(); 3559 } 3560 3561 return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true); 3562 } 3563 3564 @Override 3565 public boolean hasOwnProperty(final Object key) { 3566 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3567 final int index = getArrayIndex(primitiveKey); 3568 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false); 3569 } 3570 3571 @Override 3572 public boolean hasOwnProperty(final int key) { 3573 final int index = getArrayIndex(key); 3574 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3575 } 3576 3577 @Override 3578 public boolean hasOwnProperty(final long key) { 3579 final int index = getArrayIndex(key); 3580 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3581 } 3582 3583 @Override 3584 public boolean hasOwnProperty(final double key) { 3585 final int index = getArrayIndex(key); 3586 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3587 } 3588 3589 private boolean hasOwnArrayProperty(final int index) { 3590 return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false); 3591 } 3592 3593 @Override 3594 public boolean delete(final int key, final boolean strict) { 3595 final int index = getArrayIndex(key); 3596 final ArrayData array = getArray(); 3597 3598 if (array.has(index)) { 3599 if (array.canDelete(index, strict)) { 3600 setArray(array.delete(index)); 3601 return true; 3602 } 3603 return false; 3604 } 3605 return deleteObject(JSType.toObject(key), strict); 3606 } 3607 3608 @Override 3609 public boolean delete(final long key, final boolean strict) { 3610 final int index = getArrayIndex(key); 3611 final ArrayData array = getArray(); 3612 3613 if (array.has(index)) { 3614 if (array.canDelete(index, strict)) { 3615 setArray(array.delete(index)); 3616 return true; 3617 } 3618 return false; 3619 } 3620 3621 return deleteObject(JSType.toObject(key), strict); 3622 } 3623 3624 @Override 3625 public boolean delete(final double key, final boolean strict) { 3626 final int index = getArrayIndex(key); 3627 final ArrayData array = getArray(); 3628 3629 if (array.has(index)) { 3630 if (array.canDelete(index, strict)) { 3631 setArray(array.delete(index)); 3632 return true; 3633 } 3634 return false; 3635 } 3636 3637 return deleteObject(JSType.toObject(key), strict); 3638 } 3639 3640 @Override 3641 public boolean delete(final Object key, final boolean strict) { 3642 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3643 final int index = getArrayIndex(primitiveKey); 3644 final ArrayData array = getArray(); 3645 3646 if (array.has(index)) { 3647 if (array.canDelete(index, strict)) { 3648 setArray(array.delete(index)); 3649 return true; 3650 } 3651 return false; 3652 } 3653 3654 return deleteObject(primitiveKey, strict); 3655 } 3656 3657 private boolean deleteObject(final Object key, final boolean strict) { 3658 final String propName = JSType.toString(key); 3659 final FindProperty find = findProperty(propName, false); 3660 3661 if (find == null) { 3662 return true; 3663 } 3664 3665 if (!find.getProperty().isConfigurable()) { 3666 if (strict) { 3667 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this)); 3668 } 3669 return false; 3670 } 3671 3672 final Property prop = find.getProperty(); 3673 deleteOwnProperty(prop); 3674 3675 return true; 3676 } 3677 3678 /** 3679 * Return a shallow copy of this ScriptObject. 3680 * @return a shallow copy. 3681 */ 3682 public final ScriptObject copy() { 3683 try { 3684 return clone(); 3685 } catch (final CloneNotSupportedException e) { 3686 throw new RuntimeException(e); 3687 } 3688 } 3689 3690 @Override 3691 protected ScriptObject clone() throws CloneNotSupportedException { 3692 final ScriptObject clone = (ScriptObject) super.clone(); 3693 if (objectSpill != null) { 3694 clone.objectSpill = objectSpill.clone(); 3695 if (primitiveSpill != null) { 3696 clone.primitiveSpill = primitiveSpill.clone(); 3697 } 3698 } 3699 clone.arrayData = arrayData.copy(); 3700 return clone; 3701 } 3702 3703 /** 3704 * Make a new UserAccessorProperty property. getter and setter functions are stored in 3705 * this ScriptObject and slot values are used in property object. 3706 * 3707 * @param key the property name 3708 * @param propertyFlags attribute flags of the property 3709 * @param getter getter function for the property 3710 * @param setter setter function for the property 3711 * @return the newly created UserAccessorProperty 3712 */ 3713 protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 3714 final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags); 3715 //property.getSetter(Object.class, getMap()); 3716 uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 3717 return uc; 3718 } 3719 3720 /** 3721 * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise. 3722 * @return {@code true} if dual fields should be used. 3723 */ 3724 protected boolean useDualFields() { 3725 return !StructureLoader.isSingleFieldStructure(getClass().getName()); 3726 } 3727 3728 Object ensureSpillSize(final int slot) { 3729 final int oldLength = objectSpill == null ? 0 : objectSpill.length; 3730 if (slot < oldLength) { 3731 return this; 3732 } 3733 final int newLength = alignUp(slot + 1, SPILL_RATE); 3734 final Object[] newObjectSpill = new Object[newLength]; 3735 final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null; 3736 3737 if (objectSpill != null) { 3738 System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength); 3739 if (primitiveSpill != null && newPrimitiveSpill != null) { 3740 System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength); 3741 } 3742 } 3743 3744 this.primitiveSpill = newPrimitiveSpill; 3745 this.objectSpill = newObjectSpill; 3746 3747 return this; 3748 } 3749 3750 private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) { 3751 // TODO: figure out how can it work for NativeArray$Prototype etc. 3752 return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3753 } 3754 3755 private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 3756 return findOwnMH_V(ScriptObject.class, name, rtype, types); 3757 } 3758 3759 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 3760 return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3761 } 3762 3763 private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3764 return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func); 3765 } 3766 3767 @SuppressWarnings("unused") 3768 private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3769 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3770 try { 3771 return getter.invokeExact(self) == func; 3772 } catch (final RuntimeException | Error e) { 3773 throw e; 3774 } catch (final Throwable t) { 3775 throw new RuntimeException(t); 3776 } 3777 } 3778 3779 return false; 3780 } 3781 3782 private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3783 return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func); 3784 } 3785 3786 private static ScriptObject getProto(final ScriptObject self, final int depth) { 3787 ScriptObject proto = self; 3788 for (int d = 0; d < depth; d++) { 3789 proto = proto.getProto(); 3790 if (proto == null) { 3791 return null; 3792 } 3793 } 3794 3795 return proto; 3796 } 3797 3798 @SuppressWarnings("unused") 3799 private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3800 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3801 final ScriptObject proto = getProto((ScriptObject)self, depth); 3802 if (proto == null) { 3803 return false; 3804 } 3805 try { 3806 return getter.invokeExact((Object)proto) == func; 3807 } catch (final RuntimeException | Error e) { 3808 throw e; 3809 } catch (final Throwable t) { 3810 throw new RuntimeException(t); 3811 } 3812 } 3813 3814 return false; 3815 } 3816 3817 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ 3818 private static int count; 3819 3820 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */ 3821 private static int scopeCount; 3822 3823 /** 3824 * Get number of {@code ScriptObject} instances created. If not running in debug 3825 * mode this is always 0 3826 * 3827 * @return number of ScriptObjects created 3828 */ 3829 public static int getCount() { 3830 return count; 3831 } 3832 3833 /** 3834 * Get number of scope {@code ScriptObject} instances created. If not running in debug 3835 * mode this is always 0 3836 * 3837 * @return number of scope ScriptObjects created 3838 */ 3839 public static int getScopeCount() { 3840 return scopeCount; 3841 } 3842 3843} 3844