ScriptObject.java revision 1746:9c51738fd0e3
1258945Sroberto/* 2280849Scy * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3258945Sroberto * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4258945Sroberto * 5258945Sroberto * This code is free software; you can redistribute it and/or modify it 6258945Sroberto * under the terms of the GNU General Public License version 2 only, as 7258945Sroberto * published by the Free Software Foundation. Oracle designates this 8258945Sroberto * particular file as subject to the "Classpath" exception as provided 9258945Sroberto * by Oracle in the LICENSE file that accompanied this code. 10258945Sroberto * 11258945Sroberto * This code is distributed in the hope that it will be useful, but WITHOUT 12258945Sroberto * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13258945Sroberto * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14258945Sroberto * version 2 for more details (a copy is included in the LICENSE file that 15258945Sroberto * accompanied this code). 16258945Sroberto * 17280849Scy * You should have received a copy of the GNU General Public License version 18258945Sroberto * 2 along with this work; if not, write to the Free Software Foundation, 19280849Scy * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20258945Sroberto * 21258945Sroberto * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22258945Sroberto * or visit www.oracle.com if you need additional information or have any 23258945Sroberto * questions. 24258945Sroberto */ 25258945Sroberto 26258945Srobertopackage jdk.nashorn.internal.runtime; 27258945Sroberto 28258945Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 29258945Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; 30258945Srobertoimport static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 31258945Srobertoimport static jdk.nashorn.internal.lookup.Lookup.MH; 32258945Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 33258945Srobertoimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 34258945Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE; 35258945Srobertoimport static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT; 36258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 37258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 38258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.GET; 39258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.SET; 40258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE; 41258945Srobertoimport static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 42258945Srobertoimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 43258945Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 44258945Srobertoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 45258945Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; 46258945Srobertoimport static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; 47258945Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag; 48258945Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag; 49258945Srobertoimport static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck; 50258945Sroberto 51258945Srobertoimport java.lang.invoke.MethodHandle; 52258945Srobertoimport java.lang.invoke.MethodHandles; 53258945Srobertoimport java.lang.invoke.MethodType; 54258945Srobertoimport java.lang.invoke.SwitchPoint; 55258945Srobertoimport java.lang.reflect.Array; 56258945Srobertoimport java.util.AbstractMap; 57258945Srobertoimport java.util.ArrayList; 58258945Srobertoimport java.util.Arrays; 59258945Srobertoimport java.util.Collection; 60258945Srobertoimport java.util.Collections; 61280849Scyimport java.util.HashSet; 62258945Srobertoimport java.util.Iterator; 63258945Srobertoimport java.util.LinkedHashSet; 64258945Srobertoimport java.util.List; 65258945Srobertoimport java.util.Map; 66280849Scyimport java.util.Set; 67280849Scyimport java.util.concurrent.atomic.LongAdder; 68280849Scyimport jdk.dynalink.CallSiteDescriptor; 69280849Scyimport jdk.dynalink.NamedOperation; 70280849Scyimport jdk.dynalink.StandardOperation; 71280849Scyimport jdk.dynalink.linker.GuardedInvocation; 72280849Scyimport jdk.dynalink.linker.LinkRequest; 73280849Scyimport jdk.nashorn.internal.codegen.CompilerConstants.Call; 74280849Scyimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 75280849Scyimport jdk.nashorn.internal.codegen.types.Type; 76280849Scyimport jdk.nashorn.internal.lookup.Lookup; 77280849Scyimport jdk.nashorn.internal.objects.AccessorPropertyDescriptor; 78280849Scyimport jdk.nashorn.internal.objects.DataPropertyDescriptor; 79280849Scyimport jdk.nashorn.internal.objects.Global; 80280849Scyimport jdk.nashorn.internal.objects.NativeArray; 81280849Scyimport jdk.nashorn.internal.runtime.arrays.ArrayData; 82280849Scyimport jdk.nashorn.internal.runtime.arrays.ArrayIndex; 83280849Scyimport jdk.nashorn.internal.runtime.linker.Bootstrap; 84280849Scyimport jdk.nashorn.internal.runtime.linker.LinkerCallSite; 85280849Scyimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 86280849Scyimport jdk.nashorn.internal.runtime.linker.NashornGuards; 87280849Scy 88280849Scy/** 89280849Scy * Base class for generic JavaScript objects. 90280849Scy * <p> 91280849Scy * Notes: 92280849Scy * <ul> 93280849Scy * <li>The map is used to identify properties in the object.</li> 94280849Scy * <li>If the map is modified then it must be cloned and replaced. This notifies 95280849Scy * any code that made assumptions about the object that things have changed. 96280849Scy * Ex. CallSites that have been validated must check to see if the map has 97280849Scy * changed (or a map from a different object type) and hence relink the method 98280849Scy * to call.</li> 99280849Scy * <li>Modifications of the map include adding/deleting attributes or changing a 100280849Scy * function field value.</li> 101280849Scy * </ul> 102280849Scy */ 103280849Scy 104280849Scypublic abstract class ScriptObject implements PropertyAccess, Cloneable { 105280849Scy /** __proto__ special property name inside object literals. ES6 draft. */ 106280849Scy public static final String PROTO_PROPERTY_NAME = "__proto__"; 107280849Scy 108280849Scy /** Search fall back routine name for "no such method" */ 109280849Scy public static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__"; 110280849Scy 111280849Scy /** Search fall back routine name for "no such property" */ 112280849Scy public static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__"; 113280849Scy 114280849Scy /** Per ScriptObject flag - is this an array object? */ 115280849Scy public static final int IS_ARRAY = 1 << 0; 116280849Scy 117280849Scy /** Per ScriptObject flag - is this an arguments object? */ 118280849Scy public static final int IS_ARGUMENTS = 1 << 1; 119280849Scy 120280849Scy /** Is length property not-writable? */ 121280849Scy public static final int IS_LENGTH_NOT_WRITABLE = 1 << 2; 122280849Scy 123280849Scy /** Is this a builtin object? */ 124280849Scy public static final int IS_BUILTIN = 1 << 3; 125280849Scy 126280849Scy /** 127280849Scy * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and 128280849Scy * {@link ScriptObject#objectSpill} when full 129280849Scy */ 130280849Scy public static final int SPILL_RATE = 8; 131280849Scy 132280849Scy /** Map to property information and accessor functions. Ordered by insertion. */ 133280849Scy private PropertyMap map; 134280849Scy 135280849Scy /** objects proto. */ 136280849Scy private ScriptObject proto; 137280849Scy 138280849Scy /** Object flags. */ 139280849Scy private int flags; 140280849Scy 141280849Scy /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */ 142280849Scy protected long[] primitiveSpill; 143280849Scy 144280849Scy /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */ 145280849Scy protected Object[] objectSpill; 146280849Scy 147280849Scy /** Indexed array data. */ 148280849Scy private ArrayData arrayData; 149280849Scy 150280849Scy /** Method handle to retrieve prototype of this object */ 151280849Scy public static final MethodHandle GETPROTO = findOwnMH_V("getProto", ScriptObject.class); 152280849Scy 153280849Scy static final MethodHandle MEGAMORPHIC_GET = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class, boolean.class); 154280849Scy static final MethodHandle GLOBALFILTER = findOwnMH_S("globalFilter", Object.class, Object.class); 155280849Scy static final MethodHandle DECLARE_AND_SET = findOwnMH_V("declareAndSet", void.class, String.class, Object.class); 156280849Scy 157280849Scy private static final MethodHandle TRUNCATINGFILTER = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class); 158280849Scy private static final MethodHandle KNOWNFUNCPROPGUARDSELF = findOwnMH_S("knownFunctionPropertyGuardSelf", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, ScriptFunction.class); 159280849Scy private static final MethodHandle KNOWNFUNCPROPGUARDPROTO = findOwnMH_S("knownFunctionPropertyGuardProto", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, int.class, ScriptFunction.class); 160280849Scy 161280849Scy private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>(); 162280849Scy 163280849Scy /** Method handle for getting the array data */ 164280849Scy public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class); 165280849Scy 166280849Scy /** Method handle for getting a function argument at a given index. Used from MapCreator */ 167280849Scy public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class); 168280849Scy 169280849Scy /** Method handle for setting a function argument at a given index. Used from MapCreator */ 170280849Scy public static final Call SET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class); 171280849Scy 172280849Scy /** Method handle for getting the proto of a ScriptObject */ 173280849Scy public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class); 174280849Scy 175280849Scy /** Method handle for getting the proto of a ScriptObject */ 176280849Scy public static final Call GET_PROTO_DEPTH = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class); 177280849Scy 178280849Scy /** Method handle for setting the proto of a ScriptObject */ 179280849Scy public static final Call SET_GLOBAL_OBJECT_PROTO = staticCallNoLookup(ScriptObject.class, "setGlobalObjectProto", void.class, ScriptObject.class); 180280849Scy 181280849Scy /** Method handle for setting the proto of a ScriptObject after checking argument */ 182280849Scy public static final Call SET_PROTO_FROM_LITERAL = virtualCallNoLookup(ScriptObject.class, "setProtoFromLiteral", void.class, Object.class); 183280849Scy 184280849Scy /** Method handle for setting the user accessors of a ScriptObject */ 185280849Scy //TODO fastpath this 186280849Scy public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class); 187280849Scy 188280849Scy static final MethodHandle[] SET_SLOW = new MethodHandle[] { 189280849Scy findOwnMH_V("set", void.class, Object.class, int.class, int.class), 190280849Scy findOwnMH_V("set", void.class, Object.class, double.class, int.class), 191280849Scy findOwnMH_V("set", void.class, Object.class, Object.class, int.class) 192280849Scy }; 193280849Scy 194280849Scy /** Method handle to reset the map of this ScriptObject */ 195280849Scy public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class); 196280849Scy 197280849Scy static final MethodHandle CAS_MAP = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class); 198280849Scy static final MethodHandle EXTENSION_CHECK = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class); 199280849Scy static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class); 200280849Scy 201280849Scy /** 202280849Scy * Constructor 203280849Scy */ 204280849Scy public ScriptObject() { 205280849Scy this(null); 206280849Scy } 207280849Scy 208280849Scy /** 209280849Scy * Constructor 210280849Scy * 211280849Scy * @param map {@link PropertyMap} used to create the initial object 212280849Scy */ 213280849Scy public ScriptObject(final PropertyMap map) { 214280849Scy if (Context.DEBUG) { 215280849Scy ScriptObject.count.increment(); 216280849Scy } 217280849Scy this.arrayData = ArrayData.EMPTY_ARRAY; 218280849Scy this.setMap(map == null ? PropertyMap.newMap() : map); 219280849Scy } 220280849Scy 221280849Scy /** 222280849Scy * Constructor that directly sets the prototype to {@code proto} and property map to 223280849Scy * {@code map} without invalidating the map as calling {@link #setProto(ScriptObject)} 224258945Sroberto * would do. This should only be used for objects that are always constructed with the 225258945Sroberto * same combination of prototype and property map. 226258945Sroberto * 227258945Sroberto * @param proto the prototype object 228258945Sroberto * @param map initial {@link PropertyMap} 229258945Sroberto */ 230258945Sroberto protected ScriptObject(final ScriptObject proto, final PropertyMap map) { 231258945Sroberto this(map); 232258945Sroberto this.proto = proto; 233258945Sroberto } 234258945Sroberto 235258945Sroberto /** 236258945Sroberto * Constructor used to instantiate spill properties directly. Used from 237258945Sroberto * SpillObjectCreator. 238258945Sroberto * 239258945Sroberto * @param map property maps 240258945Sroberto * @param primitiveSpill primitive spills 241258945Sroberto * @param objectSpill reference spills 242258945Sroberto */ 243258945Sroberto public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) { 244258945Sroberto this(map); 245258945Sroberto this.primitiveSpill = primitiveSpill; 246258945Sroberto this.objectSpill = objectSpill; 247258945Sroberto assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size"; 248258945Sroberto } 249258945Sroberto 250258945Sroberto /** 251258945Sroberto * Check whether this is a global object 252258945Sroberto * @return true if global 253258945Sroberto */ 254258945Sroberto protected boolean isGlobal() { 255258945Sroberto return false; 256258945Sroberto } 257258945Sroberto 258258945Sroberto private static int alignUp(final int size, final int alignment) { 259258945Sroberto return size + alignment - 1 & ~(alignment - 1); 260258945Sroberto } 261258945Sroberto 262258945Sroberto /** 263258945Sroberto * Given a number of properties, return the aligned to SPILL_RATE 264258945Sroberto * buffer size required for the smallest spill pool needed to 265258945Sroberto * house them 266258945Sroberto * @param nProperties number of properties 267258945Sroberto * @return property buffer length, a multiple of SPILL_RATE 268258945Sroberto */ 269258945Sroberto public static int spillAllocationLength(final int nProperties) { 270258945Sroberto return alignUp(nProperties, SPILL_RATE); 271258945Sroberto } 272258945Sroberto 273258945Sroberto /** 274258945Sroberto * Copy all properties from the source object with their receiver bound to the source. 275258945Sroberto * This function was known as mergeMap 276258945Sroberto * 277258945Sroberto * @param source The source object to copy from. 278258945Sroberto */ 279258945Sroberto public void addBoundProperties(final ScriptObject source) { 280258945Sroberto addBoundProperties(source, source.getMap().getProperties()); 281258945Sroberto } 282258945Sroberto 283258945Sroberto /** 284258945Sroberto * Copy all properties from the array with their receiver bound to the source. 285258945Sroberto * 286258945Sroberto * @param source The source object to copy from. 287258945Sroberto * @param properties The array of properties to copy. 288258945Sroberto */ 289258945Sroberto public void addBoundProperties(final ScriptObject source, final Property[] properties) { 290258945Sroberto PropertyMap newMap = this.getMap(); 291258945Sroberto final boolean extensible = newMap.isExtensible(); 292258945Sroberto 293258945Sroberto for (final Property property : properties) { 294258945Sroberto newMap = addBoundProperty(newMap, source, property, extensible); 295258945Sroberto } 296258945Sroberto 297258945Sroberto this.setMap(newMap); 298258945Sroberto } 299258945Sroberto 300258945Sroberto /** 301258945Sroberto * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the 302258945Sroberto * new interim property map. 303258945Sroberto * 304258945Sroberto * @param propMap the property map 305258945Sroberto * @param source the source object 306258945Sroberto * @param property the property to be added 307258945Sroberto * @param extensible whether the current object is extensible or not 308258945Sroberto * @return the new property map 309258945Sroberto */ 310258945Sroberto protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property, final boolean extensible) { 311258945Sroberto PropertyMap newMap = propMap; 312258945Sroberto final Object key = property.getKey(); 313258945Sroberto final Property oldProp = newMap.findProperty(key); 314258945Sroberto if (oldProp == null) { 315258945Sroberto if (! extensible) { 316258945Sroberto throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 317258945Sroberto } 318258945Sroberto 319258945Sroberto if (property instanceof UserAccessorProperty) { 320258945Sroberto // Note: we copy accessor functions to this object which is semantically different from binding. 321258945Sroberto final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source)); 322258945Sroberto newMap = newMap.addPropertyNoHistory(prop); 323258945Sroberto } else { 324258945Sroberto newMap = newMap.addPropertyBind((AccessorProperty)property, source); 325258945Sroberto } 326258945Sroberto } else { 327258945Sroberto // See ECMA section 10.5 Declaration Binding Instantiation 328258945Sroberto // step 5 processing each function declaration. 329258945Sroberto if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) { 330258945Sroberto if (oldProp instanceof UserAccessorProperty || 331258945Sroberto !(oldProp.isWritable() && oldProp.isEnumerable())) { 332258945Sroberto throw typeError("cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 333258945Sroberto } 334258945Sroberto } 335258945Sroberto } 336258945Sroberto return newMap; 337258945Sroberto } 338258945Sroberto 339258945Sroberto /** 340258945Sroberto * Copy all properties from the array with their receiver bound to the source. 341258945Sroberto * 342258945Sroberto * @param source The source object to copy from. 343258945Sroberto * @param properties The collection of accessor properties to copy. 344258945Sroberto */ 345258945Sroberto public void addBoundProperties(final Object source, final AccessorProperty[] properties) { 346258945Sroberto PropertyMap newMap = this.getMap(); 347258945Sroberto final boolean extensible = newMap.isExtensible(); 348258945Sroberto 349258945Sroberto for (final AccessorProperty property : properties) { 350258945Sroberto final Object key = property.getKey(); 351258945Sroberto 352258945Sroberto if (newMap.findProperty(key) == null) { 353258945Sroberto if (! extensible) { 354258945Sroberto throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 355258945Sroberto } 356258945Sroberto newMap = newMap.addPropertyBind(property, source); 357258945Sroberto } 358258945Sroberto } 359258945Sroberto 360258945Sroberto this.setMap(newMap); 361258945Sroberto } 362258945Sroberto 363258945Sroberto /** 364258945Sroberto * Bind the method handle to the specified receiver, while preserving its original type (it will just ignore the 365258945Sroberto * first argument in lieu of the bound argument). 366258945Sroberto * @param methodHandle Method handle to bind to. 367258945Sroberto * @param receiver Object to bind. 368258945Sroberto * @return Bound method handle. 369258945Sroberto */ 370258945Sroberto static MethodHandle bindTo(final MethodHandle methodHandle, final Object receiver) { 371258945Sroberto return MH.dropArguments(MH.bindTo(methodHandle, receiver), 0, methodHandle.type().parameterType(0)); 372258945Sroberto } 373258945Sroberto 374258945Sroberto /** 375258945Sroberto * Return a property iterator. 376258945Sroberto * @return Property iterator. 377258945Sroberto */ 378258945Sroberto public Iterator<String> propertyIterator() { 379258945Sroberto return new KeyIterator(this); 380258945Sroberto } 381258945Sroberto 382258945Sroberto /** 383258945Sroberto * Return a property value iterator. 384258945Sroberto * @return Property value iterator. 385258945Sroberto */ 386258945Sroberto public Iterator<Object> valueIterator() { 387258945Sroberto return new ValueIterator(this); 388258945Sroberto } 389258945Sroberto 390258945Sroberto /** 391258945Sroberto * ECMA 8.10.1 IsAccessorDescriptor ( Desc ) 392258945Sroberto * @return true if this has a {@link AccessorPropertyDescriptor} with a getter or a setter 393258945Sroberto */ 394258945Sroberto public final boolean isAccessorDescriptor() { 395258945Sroberto return has(GET) || has(SET); 396258945Sroberto } 397258945Sroberto 398258945Sroberto /** 399258945Sroberto * ECMA 8.10.2 IsDataDescriptor ( Desc ) 400258945Sroberto * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable 401258945Sroberto */ 402258945Sroberto public final boolean isDataDescriptor() { 403258945Sroberto return has(VALUE) || has(WRITABLE); 404258945Sroberto } 405258945Sroberto 406258945Sroberto /** 407258945Sroberto * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 408258945Sroberto * 409258945Sroberto * @return property descriptor 410258945Sroberto */ 411258945Sroberto public final PropertyDescriptor toPropertyDescriptor() { 412258945Sroberto final Global global = Context.getGlobal(); 413258945Sroberto 414258945Sroberto final PropertyDescriptor desc; 415258945Sroberto if (isDataDescriptor()) { 416258945Sroberto if (has(SET) || has(GET)) { 417258945Sroberto throw typeError(global, "inconsistent.property.descriptor"); 418258945Sroberto } 419258945Sroberto 420258945Sroberto desc = global.newDataDescriptor(UNDEFINED, false, false, false); 421258945Sroberto } else if (isAccessorDescriptor()) { 422258945Sroberto if (has(VALUE) || has(WRITABLE)) { 423258945Sroberto throw typeError(global, "inconsistent.property.descriptor"); 424258945Sroberto } 425258945Sroberto 426258945Sroberto desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false); 427258945Sroberto } else { 428258945Sroberto desc = global.newGenericDescriptor(false, false); 429258945Sroberto } 430258945Sroberto 431258945Sroberto return desc.fillFrom(this); 432258945Sroberto } 433258945Sroberto 434258945Sroberto /** 435258945Sroberto * ECMA 8.10.5 ToPropertyDescriptor ( Obj ) 436258945Sroberto * 437258945Sroberto * @param global global scope object 438258945Sroberto * @param obj object to create property descriptor from 439258945Sroberto * 440258945Sroberto * @return property descriptor 441258945Sroberto */ 442258945Sroberto public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) { 443258945Sroberto if (obj instanceof ScriptObject) { 444258945Sroberto return ((ScriptObject)obj).toPropertyDescriptor(); 445258945Sroberto } 446258945Sroberto 447258945Sroberto throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); 448258945Sroberto } 449258945Sroberto 450258945Sroberto /** 451258945Sroberto * ECMA 8.12.1 [[GetOwnProperty]] (P) 452258945Sroberto * 453258945Sroberto * @param key property key 454258945Sroberto * 455258945Sroberto * @return Returns the Property Descriptor of the named own property of this 456258945Sroberto * object, or undefined if absent. 457258945Sroberto */ 458258945Sroberto public Object getOwnPropertyDescriptor(final Object key) { 459258945Sroberto final Property property = getMap().findProperty(key); 460258945Sroberto 461258945Sroberto final Global global = Context.getGlobal(); 462258945Sroberto 463258945Sroberto if (property != null) { 464258945Sroberto final ScriptFunction get = property.getGetterFunction(this); 465258945Sroberto final ScriptFunction set = property.getSetterFunction(this); 466258945Sroberto 467258945Sroberto final boolean configurable = property.isConfigurable(); 468258945Sroberto final boolean enumerable = property.isEnumerable(); 469258945Sroberto final boolean writable = property.isWritable(); 470258945Sroberto 471258945Sroberto if (property.isAccessorProperty()) { 472258945Sroberto return global.newAccessorDescriptor( 473258945Sroberto get != null ? 474258945Sroberto get : 475258945Sroberto UNDEFINED, 476258945Sroberto set != null ? 477258945Sroberto set : 478258945Sroberto UNDEFINED, 479258945Sroberto configurable, 480258945Sroberto enumerable); 481258945Sroberto } 482258945Sroberto 483258945Sroberto return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable); 484258945Sroberto } 485258945Sroberto 486258945Sroberto final int index = getArrayIndex(key); 487258945Sroberto final ArrayData array = getArray(); 488258945Sroberto 489258945Sroberto if (array.has(index)) { 490258945Sroberto return array.getDescriptor(global, index); 491258945Sroberto } 492258945Sroberto 493258945Sroberto return UNDEFINED; 494258945Sroberto } 495258945Sroberto 496258945Sroberto /** 497258945Sroberto * ECMA 8.12.2 [[GetProperty]] (P) 498258945Sroberto * 499258945Sroberto * @param key property key 500258945Sroberto * 501258945Sroberto * @return Returns the fully populated Property Descriptor of the named property 502258945Sroberto * of this object, or undefined if absent. 503258945Sroberto */ 504258945Sroberto public Object getPropertyDescriptor(final String key) { 505258945Sroberto final Object res = getOwnPropertyDescriptor(key); 506258945Sroberto 507258945Sroberto if (res != UNDEFINED) { 508258945Sroberto return res; 509258945Sroberto } else if (getProto() != null) { 510258945Sroberto return getProto().getOwnPropertyDescriptor(key); 511258945Sroberto } else { 512258945Sroberto return UNDEFINED; 513258945Sroberto } 514258945Sroberto } 515258945Sroberto 516258945Sroberto /** 517258945Sroberto * Invalidate any existing global constant method handles that may exist for {@code key}. 518258945Sroberto * @param key the property name 519258945Sroberto */ 520258945Sroberto protected void invalidateGlobalConstant(final Object key) { 521258945Sroberto final GlobalConstants globalConstants = getGlobalConstants(); 522258945Sroberto if (globalConstants != null) { 523258945Sroberto globalConstants.delete(key); 524258945Sroberto } 525258945Sroberto } 526258945Sroberto 527258945Sroberto /** 528258945Sroberto * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw) 529258945Sroberto * 530258945Sroberto * @param key the property key 531258945Sroberto * @param propertyDesc the property descriptor 532258945Sroberto * @param reject is the property extensible - true means new definitions are rejected 533258945Sroberto * 534258945Sroberto * @return true if property was successfully defined 535258945Sroberto */ 536258945Sroberto public boolean defineOwnProperty(final Object key, final Object propertyDesc, final boolean reject) { 537258945Sroberto final Global global = Context.getGlobal(); 538258945Sroberto final PropertyDescriptor desc = toPropertyDescriptor(global, propertyDesc); 539258945Sroberto final Object current = getOwnPropertyDescriptor(key); 540258945Sroberto 541258945Sroberto invalidateGlobalConstant(key); 542258945Sroberto 543258945Sroberto if (current == UNDEFINED) { 544258945Sroberto if (isExtensible()) { 545258945Sroberto // add a new own property 546258945Sroberto addOwnProperty(key, desc); 547258945Sroberto return true; 548258945Sroberto } 549258945Sroberto // new property added to non-extensible object 550258945Sroberto if (reject) { 551258945Sroberto throw typeError(global, "object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 552258945Sroberto } 553258945Sroberto return false; 554258945Sroberto } 555258945Sroberto 556258945Sroberto // modifying an existing property 557258945Sroberto final PropertyDescriptor currentDesc = (PropertyDescriptor)current; 558258945Sroberto final PropertyDescriptor newDesc = desc; 559258945Sroberto 560258945Sroberto if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) { 561258945Sroberto // every descriptor field is absent 562258945Sroberto return true; 563258945Sroberto } 564258945Sroberto 565258945Sroberto if (newDesc.hasAndEquals(currentDesc)) { 566258945Sroberto // every descriptor field of the new is same as the current 567258945Sroberto return true; 568258945Sroberto } 569258945Sroberto 570280849Scy if (!currentDesc.isConfigurable()) { 571280849Scy if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) { 572280849Scy // not configurable can not be made configurable 573280849Scy if (reject) { 574280849Scy throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 575258945Sroberto } 576258945Sroberto return false; 577258945Sroberto } 578258945Sroberto 579258945Sroberto if (newDesc.has(ENUMERABLE) && 580258945Sroberto currentDesc.isEnumerable() != newDesc.isEnumerable()) { 581258945Sroberto // cannot make non-enumerable as enumerable or vice-versa 582258945Sroberto if (reject) { 583258945Sroberto throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 584258945Sroberto } 585258945Sroberto return false; 586258945Sroberto } 587258945Sroberto } 588258945Sroberto 589258945Sroberto int propFlags = Property.mergeFlags(currentDesc, newDesc); 590258945Sroberto Property property = getMap().findProperty(key); 591258945Sroberto 592258945Sroberto if (currentDesc.type() == PropertyDescriptor.DATA && 593258945Sroberto (newDesc.type() == PropertyDescriptor.DATA || 594258945Sroberto newDesc.type() == PropertyDescriptor.GENERIC)) { 595258945Sroberto if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) { 596258945Sroberto if (newDesc.has(WRITABLE) && newDesc.isWritable() || 597258945Sroberto newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) { 598258945Sroberto if (reject) { 599280849Scy throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 600280849Scy } 601280849Scy return false; 602280849Scy } 603280849Scy } 604258945Sroberto 605258945Sroberto final boolean newValue = newDesc.has(VALUE); 606258945Sroberto final Object value = newValue ? newDesc.getValue() : currentDesc.getValue(); 607258945Sroberto 608258945Sroberto if (newValue && property != null) { 609258945Sroberto // Temporarily clear flags. 610258945Sroberto property = modifyOwnProperty(property, 0); 611258945Sroberto set(key, value, 0); 612258945Sroberto //this might change the map if we change types of the property 613258945Sroberto //hence we need to read it again. note that we should probably 614258945Sroberto //have the setter return the new property throughout and in 615258945Sroberto //general respect Property return values from modify and add 616258945Sroberto //functions - which we don't seem to do at all here :-( 617258945Sroberto //There is already a bug filed to generify PropertyAccess so we 618258945Sroberto //can have the setter return e.g. a Property 619258945Sroberto property = getMap().findProperty(key); 620258945Sroberto } 621258945Sroberto 622258945Sroberto if (property == null) { 623258945Sroberto // promoting an arrayData value to actual property 624258945Sroberto addOwnProperty(key, propFlags, value); 625258945Sroberto checkIntegerKey(key); 626258945Sroberto } else { 627258945Sroberto // Now set the new flags 628258945Sroberto modifyOwnProperty(property, propFlags); 629258945Sroberto } 630258945Sroberto } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR && 631258945Sroberto (newDesc.type() == PropertyDescriptor.ACCESSOR || 632258945Sroberto newDesc.type() == PropertyDescriptor.GENERIC)) { 633258945Sroberto if (!currentDesc.isConfigurable()) { 634258945Sroberto if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) || 635258945Sroberto newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) { 636258945Sroberto if (reject) { 637258945Sroberto throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 638258945Sroberto } 639258945Sroberto return false; 640258945Sroberto } 641258945Sroberto } 642258945Sroberto // New set the new features. 643258945Sroberto modifyOwnProperty(property, propFlags, 644258945Sroberto newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(), 645258945Sroberto newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter()); 646258945Sroberto } else { 647258945Sroberto // changing descriptor type 648258945Sroberto if (!currentDesc.isConfigurable()) { 649258945Sroberto // not configurable can not be made configurable 650258945Sroberto if (reject) { 651258945Sroberto throw typeError(global, "cant.redefine.property", key.toString(), ScriptRuntime.safeToString(this)); 652258945Sroberto } 653258945Sroberto return false; 654258945Sroberto } 655258945Sroberto 656258945Sroberto propFlags = 0; 657258945Sroberto 658258945Sroberto // Preserve only configurable and enumerable from current desc 659258945Sroberto // if those are not overridden in the new property descriptor. 660258945Sroberto boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable(); 661258945Sroberto if (!value) { 662258945Sroberto propFlags |= Property.NOT_CONFIGURABLE; 663258945Sroberto } 664258945Sroberto value = newDesc.has(ENUMERABLE)? newDesc.isEnumerable() : currentDesc.isEnumerable(); 665258945Sroberto if (!value) { 666258945Sroberto propFlags |= Property.NOT_ENUMERABLE; 667258945Sroberto } 668258945Sroberto 669258945Sroberto final int type = newDesc.type(); 670258945Sroberto if (type == PropertyDescriptor.DATA) { 671258945Sroberto // get writable from the new descriptor 672258945Sroberto value = newDesc.has(WRITABLE) && newDesc.isWritable(); 673258945Sroberto if (!value) { 674258945Sroberto propFlags |= Property.NOT_WRITABLE; 675258945Sroberto } 676258945Sroberto 677258945Sroberto // delete the old property 678258945Sroberto deleteOwnProperty(property); 679258945Sroberto // add new data property 680258945Sroberto addOwnProperty(key, propFlags, newDesc.getValue()); 681258945Sroberto } else if (type == PropertyDescriptor.ACCESSOR) { 682258945Sroberto if (property == null) { 683258945Sroberto addOwnProperty(key, propFlags, 684258945Sroberto newDesc.has(GET) ? newDesc.getGetter() : null, 685258945Sroberto newDesc.has(SET) ? newDesc.getSetter() : null); 686258945Sroberto } else { 687258945Sroberto // Modify old property with the new features. 688258945Sroberto modifyOwnProperty(property, propFlags, 689258945Sroberto newDesc.has(GET) ? newDesc.getGetter() : null, 690258945Sroberto newDesc.has(SET) ? newDesc.getSetter() : null); 691258945Sroberto } 692258945Sroberto } 693258945Sroberto } 694258945Sroberto 695280849Scy checkIntegerKey(key); 696280849Scy 697280849Scy return true; 698258945Sroberto } 699258945Sroberto 700258945Sroberto /** 701258945Sroberto * Almost like defineOwnProperty(int,Object) for arrays this one does 702258945Sroberto * not add 'gap' elements (like the array one does). 703258945Sroberto * 704258945Sroberto * @param index key for property 705258945Sroberto * @param value value to define 706258945Sroberto */ 707258945Sroberto public void defineOwnProperty(final int index, final Object value) { 708258945Sroberto assert isValidArrayIndex(index) : "invalid array index"; 709258945Sroberto final long longIndex = ArrayIndex.toLongIndex(index); 710258945Sroberto final long oldLength = getArray().length(); 711258945Sroberto if (longIndex >= oldLength) { 712258945Sroberto setArray(getArray().ensure(longIndex).safeDelete(oldLength, longIndex - 1, false)); 713258945Sroberto } 714258945Sroberto setArray(getArray().set(index, value, false)); 715258945Sroberto } 716258945Sroberto 717258945Sroberto private void checkIntegerKey(final Object key) { 718258945Sroberto final int index = getArrayIndex(key); 719258945Sroberto 720258945Sroberto if (isValidArrayIndex(index)) { 721258945Sroberto final ArrayData data = getArray(); 722258945Sroberto 723258945Sroberto if (data.has(index)) { 724258945Sroberto setArray(data.delete(index)); 725258945Sroberto } 726258945Sroberto } 727258945Sroberto } 728258945Sroberto 729258945Sroberto /** 730258945Sroberto * Add a new property to the object. 731258945Sroberto * 732258945Sroberto * @param key property key 733258945Sroberto * @param propertyDesc property descriptor for property 734258945Sroberto */ 735258945Sroberto public final void addOwnProperty(final Object key, final PropertyDescriptor propertyDesc) { 736258945Sroberto // Already checked that there is no own property with that key. 737258945Sroberto PropertyDescriptor pdesc = propertyDesc; 738258945Sroberto 739258945Sroberto final int propFlags = Property.toFlags(pdesc); 740258945Sroberto 741258945Sroberto if (pdesc.type() == PropertyDescriptor.GENERIC) { 742258945Sroberto final Global global = Context.getGlobal(); 743258945Sroberto final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false); 744258945Sroberto 745258945Sroberto dDesc.fillFrom((ScriptObject)pdesc); 746258945Sroberto pdesc = dDesc; 747258945Sroberto } 748258945Sroberto 749258945Sroberto final int type = pdesc.type(); 750258945Sroberto if (type == PropertyDescriptor.DATA) { 751258945Sroberto addOwnProperty(key, propFlags, pdesc.getValue()); 752258945Sroberto } else if (type == PropertyDescriptor.ACCESSOR) { 753258945Sroberto addOwnProperty(key, propFlags, 754258945Sroberto pdesc.has(GET) ? pdesc.getGetter() : null, 755258945Sroberto pdesc.has(SET) ? pdesc.getSetter() : null); 756258945Sroberto } 757258945Sroberto 758258945Sroberto checkIntegerKey(key); 759258945Sroberto } 760258945Sroberto 761258945Sroberto /** 762258945Sroberto * Low level property API (not using property descriptors) 763258945Sroberto * <p> 764258945Sroberto * Find a property in the prototype hierarchy. Note: this is final and not 765258945Sroberto * a good idea to override. If you have to, use 766258945Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 767258945Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 768258945Sroberto * overriding way to find array properties 769258945Sroberto * 770258945Sroberto * @see jdk.nashorn.internal.objects.NativeArray 771258945Sroberto * 772258945Sroberto * @param key Property key. 773258945Sroberto * @param deep Whether the search should look up proto chain. 774258945Sroberto * 775258945Sroberto * @return FindPropertyData or null if not found. 776258945Sroberto */ 777258945Sroberto public final FindProperty findProperty(final Object key, final boolean deep) { 778258945Sroberto return findProperty(key, deep, false, this); 779280849Scy } 780280849Scy 781280849Scy /** 782258945Sroberto * Low level property API (not using property descriptors) 783258945Sroberto * <p> 784258945Sroberto * Find a property in the prototype hierarchy. Note: this is not a good idea 785258945Sroberto * to override except as it was done in {@link WithObject}. 786258945Sroberto * If you have to, use 787258945Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getProperty(String)} or 788258945Sroberto * {jdk.nashorn.internal.objects.NativeArray{@link #getPropertyDescriptor(String)} as the 789258945Sroberto * overriding way to find array properties 790258945Sroberto * 791258945Sroberto * @see jdk.nashorn.internal.objects.NativeArray 792258945Sroberto * 793258945Sroberto * @param key Property key. 794258945Sroberto * @param deep true if the search should look up proto chain 795258945Sroberto * @param isScope true if this is a scope access 796258945Sroberto * @param start the object on which the lookup was originally initiated 797258945Sroberto * @return FindPropertyData or null if not found. 798258945Sroberto */ 799258945Sroberto protected FindProperty findProperty(final Object key, final boolean deep, final boolean isScope, final ScriptObject start) { 800258945Sroberto 801258945Sroberto final PropertyMap selfMap = getMap(); 802258945Sroberto final Property property = selfMap.findProperty(key); 803258945Sroberto 804258945Sroberto if (property != null) { 805258945Sroberto return new FindProperty(start, this, property); 806258945Sroberto } 807258945Sroberto 808258945Sroberto if (deep) { 809258945Sroberto final ScriptObject myProto = getProto(); 810258945Sroberto final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, isScope, start); 811258945Sroberto // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate 812258945Sroberto // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null. 813258945Sroberto checkSharedProtoMap(); 814258945Sroberto return find; 815258945Sroberto } 816258945Sroberto 817258945Sroberto return null; 818280849Scy } 819280849Scy 820258945Sroberto /** 821258945Sroberto * Low level property API. This is similar to {@link #findProperty(Object, boolean)} but returns a 822258945Sroberto * {@code boolean} value instead of a {@link FindProperty} object. 823258945Sroberto * @param key Property key. 824258945Sroberto * @param deep Whether the search should look up proto chain. 825258945Sroberto * @return true if the property was found. 826258945Sroberto */ 827258945Sroberto boolean hasProperty(final Object key, final boolean deep) { 828258945Sroberto if (getMap().findProperty(key) != null) { 829258945Sroberto return true; 830258945Sroberto } 831258945Sroberto 832258945Sroberto if (deep) { 833258945Sroberto final ScriptObject myProto = getProto(); 834258945Sroberto if (myProto != null) { 835258945Sroberto return myProto.hasProperty(key, true); 836258945Sroberto } 837258945Sroberto } 838280849Scy 839280849Scy return false; 840258945Sroberto } 841258945Sroberto 842258945Sroberto private SwitchPoint findBuiltinSwitchPoint(final Object key) { 843258945Sroberto for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) { 844258945Sroberto final Property prop = myProto.getMap().findProperty(key); 845258945Sroberto if (prop != null) { 846258945Sroberto final SwitchPoint sp = prop.getBuiltinSwitchPoint(); 847258945Sroberto if (sp != null && !sp.hasBeenInvalidated()) { 848258945Sroberto return sp; 849258945Sroberto } 850258945Sroberto } 851258945Sroberto } 852258945Sroberto return null; 853258945Sroberto } 854258945Sroberto 855258945Sroberto /** 856258945Sroberto * Add a new property to the object. 857258945Sroberto * <p> 858258945Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 859258945Sroberto * 860258945Sroberto * @param key Property key. 861258945Sroberto * @param propertyFlags Property flags. 862258945Sroberto * @param getter Property getter, or null if not defined 863258945Sroberto * @param setter Property setter, or null if not defined 864258945Sroberto * 865258945Sroberto * @return New property. 866258945Sroberto */ 867258945Sroberto public final Property addOwnProperty(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 868258945Sroberto return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter)); 869258945Sroberto } 870258945Sroberto 871258945Sroberto /** 872258945Sroberto * Add a new property to the object. 873258945Sroberto * <p> 874258945Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 875258945Sroberto * 876258945Sroberto * @param key Property key. 877258945Sroberto * @param propertyFlags Property flags. 878258945Sroberto * @param value Value of property 879258945Sroberto * 880258945Sroberto * @return New property. 881258945Sroberto */ 882258945Sroberto public final Property addOwnProperty(final Object key, final int propertyFlags, final Object value) { 883258945Sroberto return addSpillProperty(key, propertyFlags, value, true); 884258945Sroberto } 885258945Sroberto 886258945Sroberto /** 887258945Sroberto * Add a new property to the object. 888258945Sroberto * <p> 889258945Sroberto * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s 890258945Sroberto * 891258945Sroberto * @param newProperty property to add 892258945Sroberto * 893258945Sroberto * @return New property. 894258945Sroberto */ 895258945Sroberto public final Property addOwnProperty(final Property newProperty) { 896258945Sroberto PropertyMap oldMap = getMap(); 897258945Sroberto while (true) { 898258945Sroberto final PropertyMap newMap = oldMap.addProperty(newProperty); 899258945Sroberto if (!compareAndSetMap(oldMap, newMap)) { 900258945Sroberto oldMap = getMap(); 901258945Sroberto final Property oldProperty = oldMap.findProperty(newProperty.getKey()); 902258945Sroberto 903258945Sroberto if (oldProperty != null) { 904258945Sroberto return oldProperty; 905258945Sroberto } 906258945Sroberto } else { 907258945Sroberto return newProperty; 908280849Scy } 909258945Sroberto } 910280849Scy } 911258945Sroberto 912258945Sroberto private void erasePropertyValue(final Property property) { 913258945Sroberto // Erase the property field value with undefined. If the property is an accessor property 914258945Sroberto // we don't want to call the setter!! 915258945Sroberto if (property != null && !property.isAccessorProperty()) { 916258945Sroberto property.setValue(this, this, UNDEFINED, false); 917258945Sroberto } 918258945Sroberto } 919258945Sroberto 920258945Sroberto /** 921258945Sroberto * Delete a property from the object. 922258945Sroberto * 923258945Sroberto * @param property Property to delete. 924258945Sroberto * 925280849Scy * @return true if deleted. 926280849Scy */ 927280849Scy public final boolean deleteOwnProperty(final Property property) { 928280849Scy erasePropertyValue(property); 929280849Scy PropertyMap oldMap = getMap(); 930258945Sroberto 931258945Sroberto while (true) { 932258945Sroberto final PropertyMap newMap = oldMap.deleteProperty(property); 933258945Sroberto 934258945Sroberto if (newMap == null) { 935258945Sroberto return false; 936258945Sroberto } 937258945Sroberto 938258945Sroberto if (!compareAndSetMap(oldMap, newMap)) { 939258945Sroberto oldMap = getMap(); 940258945Sroberto } else { 941258945Sroberto // delete getter and setter function references so that we don't leak 942258945Sroberto if (property instanceof UserAccessorProperty) { 943258945Sroberto ((UserAccessorProperty)property).setAccessors(this, getMap(), null); 944258945Sroberto } 945258945Sroberto 946258945Sroberto invalidateGlobalConstant(property.getKey()); 947258945Sroberto return true; 948258945Sroberto } 949258945Sroberto } 950258945Sroberto 951258945Sroberto } 952258945Sroberto 953258945Sroberto /** 954258945Sroberto * Fast initialization functions for ScriptFunctions that are strict, to avoid 955258945Sroberto * creating setters that probably aren't used. Inject directly into the spill pool 956258945Sroberto * the defaults for "arguments" and "caller" 957258945Sroberto * 958258945Sroberto * @param key property key 959258945Sroberto * @param propertyFlags flags 960258945Sroberto * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 961258945Sroberto * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 962258945Sroberto */ 963258945Sroberto protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 964258945Sroberto final PropertyMap oldMap = getMap(); 965258945Sroberto final int slot = oldMap.getFreeSpillSlot(); 966258945Sroberto ensureSpillSize(slot); 967258945Sroberto objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter); 968258945Sroberto Property newProperty; 969258945Sroberto PropertyMap newMap; 970258945Sroberto do { 971258945Sroberto newProperty = new UserAccessorProperty(key, propertyFlags, slot); 972258945Sroberto newMap = oldMap.addProperty(newProperty); 973258945Sroberto } while (!compareAndSetMap(oldMap, newMap)); 974258945Sroberto } 975258945Sroberto 976258945Sroberto /** 977258945Sroberto * Modify a property in the object 978258945Sroberto * 979258945Sroberto * @param oldProperty property to modify 980258945Sroberto * @param propertyFlags new property flags 981258945Sroberto * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A 982258945Sroberto * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A 983258945Sroberto * 984258945Sroberto * @return new property 985258945Sroberto */ 986258945Sroberto public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 987258945Sroberto Property newProperty; 988258945Sroberto 989258945Sroberto if (oldProperty instanceof UserAccessorProperty) { 990258945Sroberto final UserAccessorProperty uc = (UserAccessorProperty)oldProperty; 991258945Sroberto final int slot = uc.getSlot(); 992258945Sroberto 993258945Sroberto assert uc.getLocalType() == Object.class; 994258945Sroberto final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes 995258945Sroberto assert gs != null; 996258945Sroberto //reuse existing getter setter for speed 997258945Sroberto gs.set(getter, setter); 998258945Sroberto if (uc.getFlags() == (propertyFlags | Property.IS_ACCESSOR_PROPERTY)) { 999258945Sroberto return oldProperty; 1000258945Sroberto } 1001258945Sroberto newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot); 1002258945Sroberto } else { 1003258945Sroberto // erase old property value and create new user accessor property 1004258945Sroberto erasePropertyValue(oldProperty); 1005258945Sroberto newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter); 1006258945Sroberto } 1007258945Sroberto 1008258945Sroberto return modifyOwnProperty(oldProperty, newProperty); 1009258945Sroberto } 1010258945Sroberto 1011258945Sroberto /** 1012258945Sroberto * Modify a property in the object 1013258945Sroberto * 1014258945Sroberto * @param oldProperty property to modify 1015258945Sroberto * @param propertyFlags new property flags 1016258945Sroberto * 1017280849Scy * @return new property 1018280849Scy */ 1019280849Scy public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) { 1020258945Sroberto return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags)); 1021258945Sroberto } 1022258945Sroberto 1023258945Sroberto /** 1024258945Sroberto * Modify a property in the object, replacing a property with a new one 1025258945Sroberto * 1026258945Sroberto * @param oldProperty property to replace 1027258945Sroberto * @param newProperty property to replace it with 1028258945Sroberto * 1029258945Sroberto * @return new property 1030258945Sroberto */ 1031258945Sroberto private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) { 1032258945Sroberto if (oldProperty == newProperty) { 1033258945Sroberto return newProperty; //nop 1034258945Sroberto } 1035258945Sroberto 1036258945Sroberto assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key"; 1037258945Sroberto 1038258945Sroberto PropertyMap oldMap = getMap(); 1039258945Sroberto 1040258945Sroberto while (true) { 1041258945Sroberto final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty); 1042258945Sroberto 1043258945Sroberto if (!compareAndSetMap(oldMap, newMap)) { 1044258945Sroberto oldMap = getMap(); 1045258945Sroberto final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey()); 1046258945Sroberto 1047258945Sroberto if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) { 1048258945Sroberto return oldPropertyLookup; 1049258945Sroberto } 1050258945Sroberto } else { 1051258945Sroberto return newProperty; 1052258945Sroberto } 1053258945Sroberto } 1054258945Sroberto } 1055258945Sroberto 1056258945Sroberto /** 1057258945Sroberto * Update getter and setter in an object literal. 1058258945Sroberto * 1059258945Sroberto * @param key Property key. 1060258945Sroberto * @param getter {@link UserAccessorProperty} defined getter, or null if none 1061258945Sroberto * @param setter {@link UserAccessorProperty} defined setter, or null if none 1062258945Sroberto */ 1063258945Sroberto public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) { 1064258945Sroberto final Property oldProperty = getMap().findProperty(key); 1065258945Sroberto if (oldProperty instanceof UserAccessorProperty) { 1066258945Sroberto modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter); 1067258945Sroberto } else { 1068258945Sroberto addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter)); 1069258945Sroberto } 1070258945Sroberto } 1071258945Sroberto 1072258945Sroberto private static int getIntValue(final FindProperty find, final int programPoint) { 1073258945Sroberto final MethodHandle getter = find.getGetter(int.class, programPoint, null); 1074258945Sroberto if (getter != null) { 1075258945Sroberto try { 1076258945Sroberto return (int)getter.invokeExact((Object)find.getGetterReceiver()); 1077258945Sroberto } catch (final Error|RuntimeException e) { 1078258945Sroberto throw e; 1079258945Sroberto } catch (final Throwable e) { 1080258945Sroberto throw new RuntimeException(e); 1081258945Sroberto } 1082258945Sroberto } 1083258945Sroberto 1084258945Sroberto return UNDEFINED_INT; 1085258945Sroberto } 1086258945Sroberto 1087258945Sroberto private static double getDoubleValue(final FindProperty find, final int programPoint) { 1088258945Sroberto final MethodHandle getter = find.getGetter(double.class, programPoint, null); 1089258945Sroberto if (getter != null) { 1090258945Sroberto try { 1091258945Sroberto return (double)getter.invokeExact((Object)find.getGetterReceiver()); 1092258945Sroberto } catch (final Error|RuntimeException e) { 1093258945Sroberto throw e; 1094258945Sroberto } catch (final Throwable e) { 1095258945Sroberto throw new RuntimeException(e); 1096258945Sroberto } 1097258945Sroberto } 1098258945Sroberto 1099280849Scy return UNDEFINED_DOUBLE; 1100280849Scy } 1101280849Scy 1102258945Sroberto /** 1103258945Sroberto * Return methodHandle of value function for call. 1104258945Sroberto * 1105258945Sroberto * @param find data from find property. 1106258945Sroberto * @param type method type of function. 1107258945Sroberto * @param bindName null or name to bind to second argument (property not found method.) 1108258945Sroberto * 1109258945Sroberto * @return value of property as a MethodHandle or null. 1110258945Sroberto */ 1111258945Sroberto protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) { 1112258945Sroberto return getCallMethodHandle(find.getObjectValue(), type, bindName); 1113258945Sroberto } 1114258945Sroberto 1115258945Sroberto /** 1116258945Sroberto * Return methodHandle of value function for call. 1117258945Sroberto * 1118258945Sroberto * @param value value of receiver, it not a {@link ScriptFunction} this will return null. 1119258945Sroberto * @param type method type of function. 1120258945Sroberto * @param bindName null or name to bind to second argument (property not found method.) 1121258945Sroberto * 1122258945Sroberto * @return value of property as a MethodHandle or null. 1123258945Sroberto */ 1124258945Sroberto protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) { 1125258945Sroberto return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null; 1126258945Sroberto } 1127258945Sroberto 1128258945Sroberto /** 1129258945Sroberto * Get value using found property. 1130258945Sroberto * 1131258945Sroberto * @param property Found property. 1132258945Sroberto * 1133258945Sroberto * @return Value of property. 1134258945Sroberto */ 1135258945Sroberto public final Object getWithProperty(final Property property) { 1136258945Sroberto return new FindProperty(this, this, property).getObjectValue(); 1137280849Scy } 1138280849Scy 1139258945Sroberto /** 1140258945Sroberto * Get a property given a key 1141258945Sroberto * 1142258945Sroberto * @param key property key 1143258945Sroberto * 1144258945Sroberto * @return property for key 1145258945Sroberto */ 1146258945Sroberto public final Property getProperty(final String key) { 1147258945Sroberto return getMap().findProperty(key); 1148258945Sroberto } 1149258945Sroberto 1150258945Sroberto /** 1151258945Sroberto * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 1152258945Sroberto * Used for argument access in a vararg function using parameter name. 1153258945Sroberto * Returns the argument at a given key (index) 1154258945Sroberto * 1155258945Sroberto * @param key argument index 1156258945Sroberto * 1157280849Scy * @return the argument at the given position, or undefined if not present 1158280849Scy */ 1159258945Sroberto public Object getArgument(final int key) { 1160258945Sroberto return get(key); 1161258945Sroberto } 1162258945Sroberto 1163258945Sroberto /** 1164258945Sroberto * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.) 1165258945Sroberto * Used for argument access in a vararg function using parameter name. 1166258945Sroberto * Returns the argument at a given key (index) 1167258945Sroberto * 1168258945Sroberto * @param key argument index 1169258945Sroberto * @param value the value to write at the given index 1170258945Sroberto */ 1171258945Sroberto public void setArgument(final int key, final Object value) { 1172258945Sroberto set(key, value, 0); 1173258945Sroberto } 1174258945Sroberto 1175258945Sroberto /** 1176258945Sroberto * Return the current context from the object's map. 1177258945Sroberto * @return Current context. 1178258945Sroberto */ 1179258945Sroberto protected Context getContext() { 1180258945Sroberto return Context.fromClass(getClass()); 1181258945Sroberto } 1182258945Sroberto 1183258945Sroberto /** 1184258945Sroberto * Return the map of an object. 1185258945Sroberto * @return PropertyMap object. 1186258945Sroberto */ 1187258945Sroberto public final PropertyMap getMap() { 1188258945Sroberto return map; 1189258945Sroberto } 1190258945Sroberto 1191258945Sroberto /** 1192258945Sroberto * Set the initial map. 1193258945Sroberto * @param map Initial map. 1194258945Sroberto */ 1195258945Sroberto public final void setMap(final PropertyMap map) { 1196258945Sroberto this.map = map; 1197258945Sroberto } 1198258945Sroberto 1199258945Sroberto /** 1200258945Sroberto * Conditionally set the new map if the old map is the same. 1201258945Sroberto * @param oldMap Map prior to manipulation. 1202258945Sroberto * @param newMap Replacement map. 1203258945Sroberto * @return true if the operation succeeded. 1204258945Sroberto */ 1205258945Sroberto protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) { 1206258945Sroberto if (oldMap == this.map) { 1207258945Sroberto this.map = newMap; 1208258945Sroberto return true; 1209258945Sroberto } 1210258945Sroberto return false; 1211258945Sroberto } 1212258945Sroberto 1213258945Sroberto /** 1214258945Sroberto * Return the __proto__ of an object. 1215258945Sroberto * @return __proto__ object. 1216258945Sroberto */ 1217258945Sroberto public final ScriptObject getProto() { 1218258945Sroberto return proto; 1219258945Sroberto } 1220258945Sroberto 1221258945Sroberto /** 1222258945Sroberto * Get the proto of a specific depth 1223258945Sroberto * @param n depth 1224258945Sroberto * @return proto at given depth 1225258945Sroberto */ 1226258945Sroberto public final ScriptObject getProto(final int n) { 1227258945Sroberto assert n > 0; 1228258945Sroberto ScriptObject p = getProto(); 1229258945Sroberto for (int i = n; --i > 0;) { 1230258945Sroberto p = p.getProto(); 1231258945Sroberto } 1232280849Scy return p; 1233258945Sroberto } 1234258945Sroberto 1235258945Sroberto /** 1236258945Sroberto * Set the __proto__ of an object. 1237258945Sroberto * @param newProto new __proto__ to set. 1238258945Sroberto */ 1239258945Sroberto public final void setProto(final ScriptObject newProto) { 1240258945Sroberto final ScriptObject oldProto = proto; 1241258945Sroberto 1242258945Sroberto if (oldProto != newProto) { 1243258945Sroberto proto = newProto; 1244258945Sroberto 1245258945Sroberto // Let current listeners know that the prototype has changed 1246258945Sroberto getMap().protoChanged(true); 1247258945Sroberto // Replace our current allocator map with one that is associated with the new prototype. 1248258945Sroberto setMap(getMap().changeProto(newProto)); 1249280849Scy } 1250280849Scy } 1251280849Scy 1252280849Scy /** 1253280849Scy * Set the initial __proto__ of this object. This should be used instead of 1254258945Sroberto * {@link #setProto} if it is known that the current property map will not be 1255258945Sroberto * used on a new object with any other parent property map, so we can pass over 1256258945Sroberto * property map invalidation/evolution. 1257258945Sroberto * 1258258945Sroberto * @param initialProto the initial __proto__ to set. 1259258945Sroberto */ 1260258945Sroberto public void setInitialProto(final ScriptObject initialProto) { 1261258945Sroberto this.proto = initialProto; 1262258945Sroberto } 1263258945Sroberto 1264258945Sroberto /** 1265258945Sroberto * Invoked from generated bytecode to initialize the prototype of object literals to the global Object prototype. 1266258945Sroberto * @param obj the object literal that needs to have its prototype initialized to the global Object prototype. 1267258945Sroberto */ 1268258945Sroberto public static void setGlobalObjectProto(final ScriptObject obj) { 1269258945Sroberto obj.setInitialProto(Global.objectPrototype()); 1270258945Sroberto } 1271258945Sroberto 1272258945Sroberto /** 1273258945Sroberto * Set the __proto__ of an object with checks. 1274258945Sroberto * This is the built-in operation [[SetPrototypeOf]] 1275258945Sroberto * See ES6 draft spec: 9.1.2 [[SetPrototypeOf]] (V) 1276258945Sroberto * 1277258945Sroberto * @param newProto Prototype to set. 1278258945Sroberto */ 1279258945Sroberto public final void setPrototypeOf(final Object newProto) { 1280258945Sroberto if (newProto == null || newProto instanceof ScriptObject) { 1281258945Sroberto if (! isExtensible()) { 1282258945Sroberto // okay to set same proto again - even if non-extensible 1283258945Sroberto 1284258945Sroberto if (newProto == getProto()) { 1285280849Scy return; 1286258945Sroberto } 1287280849Scy throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this)); 1288258945Sroberto } 1289280849Scy 1290280849Scy // check for circularity 1291280849Scy ScriptObject p = (ScriptObject)newProto; 1292280849Scy while (p != null) { 1293280849Scy if (p == this) { 1294280849Scy throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this)); 1295258945Sroberto } 1296280849Scy p = p.getProto(); 1297280849Scy } 1298280849Scy setProto((ScriptObject) newProto); 1299280849Scy } else { 1300280849Scy throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); 1301280849Scy } 1302280849Scy } 1303280849Scy 1304280849Scy /** 1305280849Scy * Set the __proto__ of an object from an object literal. 1306280849Scy * See ES6 draft spec: B.3.1 __proto__ Property Names in 1307280849Scy * Object Initializers. Step 6 handling of "__proto__". 1308280849Scy * 1309280849Scy * @param newProto Prototype to set. 1310280849Scy */ 1311280849Scy public final void setProtoFromLiteral(final Object newProto) { 1312280849Scy if (newProto == null || newProto instanceof ScriptObject) { 1313280849Scy setPrototypeOf(newProto); 1314280849Scy } else { 1315280849Scy // Some non-object, non-null. Then, we need to set 1316280849Scy // Object.prototype as the new __proto__ 1317280849Scy // 1318280849Scy // var obj = { __proto__ : 34 }; 1319280849Scy // print(obj.__proto__ === Object.prototype); // => true 1320280849Scy setPrototypeOf(Global.objectPrototype()); 1321280849Scy } 1322280849Scy } 1323280849Scy 1324280849Scy /** 1325280849Scy * return an array of all property keys - all inherited, non-enumerable included. 1326280849Scy * This is meant for source code completion by interactive shells or editors. 1327280849Scy * 1328280849Scy * @return Array of keys, order of properties is undefined. 1329280849Scy */ 1330280849Scy public String[] getAllKeys() { 1331280849Scy final Set<String> keys = new HashSet<>(); 1332280849Scy final Set<String> nonEnumerable = new HashSet<>(); 1333280849Scy for (ScriptObject self = this; self != null; self = self.getProto()) { 1334280849Scy keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable))); 1335280849Scy } 1336280849Scy return keys.toArray(new String[0]); 1337280849Scy } 1338280849Scy 1339280849Scy /** 1340280849Scy * Return an array of own property keys associated with the object. 1341280849Scy * 1342280849Scy * @param all True if to include non-enumerable keys. 1343280849Scy * @return Array of keys. 1344280849Scy */ 1345280849Scy public final String[] getOwnKeys(final boolean all) { 1346280849Scy return getOwnKeys(String.class, all, null); 1347280849Scy } 1348280849Scy 1349280849Scy /** 1350280849Scy * Return an array of own property keys associated with the object. 1351280849Scy * 1352280849Scy * @param all True if to include non-enumerable keys. 1353280849Scy * @return Array of keys. 1354280849Scy */ 1355280849Scy public final Symbol[] getOwnSymbols(final boolean all) { 1356280849Scy return getOwnKeys(Symbol.class, all, null); 1357280849Scy } 1358280849Scy 1359280849Scy /** 1360280849Scy * return an array of own property keys associated with the object. 1361280849Scy * 1362280849Scy * @param <T> the type returned keys. 1363280849Scy * @param type the type of keys to return, either {@code String.class} or {@code Symbol.class}. 1364280849Scy * @param all True if to include non-enumerable keys. 1365280849Scy * @param nonEnumerable set of non-enumerable properties seen already. Used to 1366280849Scy * filter out shadowed, but enumerable properties from proto children. 1367280849Scy * @return Array of keys. 1368280849Scy */ 1369280849Scy @SuppressWarnings("unchecked") 1370280849Scy protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) { 1371280849Scy final List<Object> keys = new ArrayList<>(); 1372280849Scy final PropertyMap selfMap = this.getMap(); 1373280849Scy 1374280849Scy final ArrayData array = getArray(); 1375280849Scy 1376280849Scy if (type == String.class) { 1377280849Scy for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) { 1378280849Scy keys.add(JSType.toString(iter.next().longValue())); 1379280849Scy } 1380280849Scy } 1381280849Scy 1382280849Scy for (final Property property : selfMap.getProperties()) { 1383280849Scy final boolean enumerable = property.isEnumerable(); 1384280849Scy final Object key = property.getKey(); 1385280849Scy if (!type.isInstance(key)) { 1386280849Scy continue; 1387280849Scy } 1388280849Scy if (all) { 1389280849Scy keys.add(key); 1390280849Scy } else if (enumerable) { 1391280849Scy // either we don't have non-enumerable filter set or filter set 1392280849Scy // does not contain the current property. 1393280849Scy if (nonEnumerable == null || !nonEnumerable.contains(key)) { 1394280849Scy keys.add(key); 1395280849Scy } 1396280849Scy } else { 1397280849Scy // store this non-enumerable property for later proto walk 1398280849Scy if (nonEnumerable != null) { 1399280849Scy nonEnumerable.add((T) key); 1400280849Scy } 1401280849Scy } 1402280849Scy } 1403280849Scy 1404280849Scy return keys.toArray((T[]) Array.newInstance(type, keys.size())); 1405280849Scy } 1406280849Scy 1407280849Scy /** 1408280849Scy * Check if this ScriptObject has array entries. This means that someone has 1409280849Scy * set values with numeric keys in the object. 1410280849Scy * 1411280849Scy * @return true if array entries exists. 1412280849Scy */ 1413258945Sroberto public boolean hasArrayEntries() { 1414258945Sroberto return getArray().length() > 0 || getMap().containsArrayKeys(); 1415258945Sroberto } 1416258945Sroberto 1417258945Sroberto /** 1418258945Sroberto * Return the valid JavaScript type name descriptor 1419258945Sroberto * 1420258945Sroberto * @return "Object" 1421258945Sroberto */ 1422258945Sroberto public String getClassName() { 1423258945Sroberto return "Object"; 1424258945Sroberto } 1425258945Sroberto 1426258945Sroberto /** 1427258945Sroberto * {@code length} is a well known property. This is its getter. 1428258945Sroberto * Note that this *may* be optimized by other classes 1429258945Sroberto * 1430280849Scy * @return length property value for this ScriptObject 1431280849Scy */ 1432280849Scy public Object getLength() { 1433280849Scy return get("length"); 1434280849Scy } 1435258945Sroberto 1436258945Sroberto /** 1437258945Sroberto * Stateless toString for ScriptObjects. 1438258945Sroberto * 1439258945Sroberto * @return string description of this object, e.g. {@code [object Object]} 1440280849Scy */ 1441258945Sroberto public String safeToString() { 1442258945Sroberto return "[object " + getClassName() + "]"; 1443258945Sroberto } 1444258945Sroberto 1445258945Sroberto /** 1446258945Sroberto * Return the default value of the object with a given preferred type hint. 1447258945Sroberto * The preferred type hints are String.class for type String, Number.class 1448258945Sroberto * for type Number. <p> 1449258945Sroberto * 1450 * A <code>hint</code> of null means "no hint". 1451 * 1452 * ECMA 8.12.8 [[DefaultValue]](hint) 1453 * 1454 * @param typeHint the preferred type hint 1455 * @return the default value 1456 */ 1457 public Object getDefaultValue(final Class<?> typeHint) { 1458 // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and 1459 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts 1460 // are being executed in a long-running program, we move the code and their associated dynamic call sites 1461 // (Global.TO_STRING and Global.VALUE_OF) into per-context code. 1462 return Context.getGlobal().getDefaultValue(this, typeHint); 1463 } 1464 1465 /** 1466 * Checking whether a script object is an instance of another. Used 1467 * in {@link ScriptFunction} for hasInstance implementation, walks 1468 * the proto chain 1469 * 1470 * @param instance instance to check 1471 * @return true if 'instance' is an instance of this object 1472 */ 1473 public boolean isInstance(final ScriptObject instance) { 1474 return false; 1475 } 1476 1477 /** 1478 * Flag this ScriptObject as non extensible 1479 * 1480 * @return the object after being made non extensible 1481 */ 1482 public ScriptObject preventExtensions() { 1483 PropertyMap oldMap = getMap(); 1484 while (!compareAndSetMap(oldMap, getMap().preventExtensions())) { 1485 oldMap = getMap(); 1486 } 1487 1488 //invalidate any fast array setters 1489 final ArrayData array = getArray(); 1490 assert array != null; 1491 setArray(ArrayData.preventExtension(array)); 1492 return this; 1493 } 1494 1495 /** 1496 * Check whether if an Object (not just a ScriptObject) represents JavaScript array 1497 * 1498 * @param obj object to check 1499 * 1500 * @return true if array 1501 */ 1502 public static boolean isArray(final Object obj) { 1503 return obj instanceof ScriptObject && ((ScriptObject)obj).isArray(); 1504 } 1505 1506 /** 1507 * Check if this ScriptObject is an array 1508 * @return true if array 1509 */ 1510 public final boolean isArray() { 1511 return (flags & IS_ARRAY) != 0; 1512 } 1513 1514 /** 1515 * Flag this ScriptObject as being an array 1516 */ 1517 public final void setIsArray() { 1518 flags |= IS_ARRAY; 1519 } 1520 1521 /** 1522 * Check if this ScriptObject is an {@code arguments} vector 1523 * @return true if arguments vector 1524 */ 1525 public final boolean isArguments() { 1526 return (flags & IS_ARGUMENTS) != 0; 1527 } 1528 1529 /** 1530 * Flag this ScriptObject as being an {@code arguments} vector 1531 */ 1532 public final void setIsArguments() { 1533 flags |= IS_ARGUMENTS; 1534 } 1535 1536 /** 1537 * Check if this object has non-writable length property 1538 * 1539 * @return {@code true} if 'length' property is non-writable 1540 */ 1541 public boolean isLengthNotWritable() { 1542 return (flags & IS_LENGTH_NOT_WRITABLE) != 0; 1543 } 1544 1545 /** 1546 * Flag this object as having non-writable length property. 1547 */ 1548 public void setIsLengthNotWritable() { 1549 flags |= IS_LENGTH_NOT_WRITABLE; 1550 } 1551 1552 /** 1553 * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type 1554 * that can handle elementType 1555 * @param elementType elementType 1556 * @return array data 1557 */ 1558 public final ArrayData getArray(final Class<?> elementType) { 1559 if (elementType == null) { 1560 return arrayData; 1561 } 1562 final ArrayData newArrayData = arrayData.convert(elementType); 1563 if (newArrayData != arrayData) { 1564 arrayData = newArrayData; 1565 } 1566 return newArrayData; 1567 } 1568 1569 /** 1570 * Get the {@link ArrayData} for this ScriptObject if it is an array 1571 * @return array data 1572 */ 1573 public final ArrayData getArray() { 1574 return arrayData; 1575 } 1576 1577 /** 1578 * Set the {@link ArrayData} for this ScriptObject if it is to be an array 1579 * @param arrayData the array data 1580 */ 1581 public final void setArray(final ArrayData arrayData) { 1582 this.arrayData = arrayData; 1583 } 1584 1585 /** 1586 * Check if this ScriptObject is extensible 1587 * @return true if extensible 1588 */ 1589 public boolean isExtensible() { 1590 return getMap().isExtensible(); 1591 } 1592 1593 /** 1594 * ECMAScript 15.2.3.8 - seal implementation 1595 * @return the sealed ScriptObject 1596 */ 1597 public ScriptObject seal() { 1598 PropertyMap oldMap = getMap(); 1599 1600 while (true) { 1601 final PropertyMap newMap = getMap().seal(); 1602 1603 if (!compareAndSetMap(oldMap, newMap)) { 1604 oldMap = getMap(); 1605 } else { 1606 setArray(ArrayData.seal(getArray())); 1607 return this; 1608 } 1609 } 1610 } 1611 1612 /** 1613 * Check whether this ScriptObject is sealed 1614 * @return true if sealed 1615 */ 1616 public boolean isSealed() { 1617 return getMap().isSealed(); 1618 } 1619 1620 /** 1621 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject 1622 * @return the frozen ScriptObject 1623 */ 1624 public ScriptObject freeze() { 1625 PropertyMap oldMap = getMap(); 1626 1627 while (true) { 1628 final PropertyMap newMap = getMap().freeze(); 1629 1630 if (!compareAndSetMap(oldMap, newMap)) { 1631 oldMap = getMap(); 1632 } else { 1633 setArray(ArrayData.freeze(getArray())); 1634 return this; 1635 } 1636 } 1637 } 1638 1639 /** 1640 * Check whether this ScriptObject is frozen 1641 * @return true if frozen 1642 */ 1643 public boolean isFrozen() { 1644 return getMap().isFrozen(); 1645 } 1646 1647 /** 1648 * Check whether this ScriptObject is scope 1649 * @return true if scope 1650 */ 1651 public boolean isScope() { 1652 return false; 1653 } 1654 1655 /** 1656 * Tag this script object as built in 1657 */ 1658 public final void setIsBuiltin() { 1659 flags |= IS_BUILTIN; 1660 } 1661 1662 /** 1663 * Check if this script object is built in 1664 * @return true if build in 1665 */ 1666 public final boolean isBuiltin() { 1667 return (flags & IS_BUILTIN) != 0; 1668 } 1669 1670 /** 1671 * Clears the properties from a ScriptObject 1672 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1673 * 1674 * @param strict strict mode or not 1675 */ 1676 public void clear(final boolean strict) { 1677 final Iterator<String> iter = propertyIterator(); 1678 while (iter.hasNext()) { 1679 delete(iter.next(), strict); 1680 } 1681 } 1682 1683 /** 1684 * Checks if a property with a given key is present in a ScriptObject 1685 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1686 * 1687 * @param key the key to check for 1688 * @return true if a property with the given key exists, false otherwise 1689 */ 1690 public boolean containsKey(final Object key) { 1691 return has(key); 1692 } 1693 1694 /** 1695 * Checks if a property with a given value is present in a ScriptObject 1696 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1697 * 1698 * @param value value to check for 1699 * @return true if a property with the given value exists, false otherwise 1700 */ 1701 public boolean containsValue(final Object value) { 1702 final Iterator<Object> iter = valueIterator(); 1703 while (iter.hasNext()) { 1704 if (iter.next().equals(value)) { 1705 return true; 1706 } 1707 } 1708 return false; 1709 } 1710 1711 /** 1712 * Returns the set of {@literal <property, value>} entries that make up this 1713 * ScriptObject's properties 1714 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1715 * 1716 * @return an entry set of all the properties in this object 1717 */ 1718 public Set<Map.Entry<Object, Object>> entrySet() { 1719 final Iterator<String> iter = propertyIterator(); 1720 final Set<Map.Entry<Object, Object>> entries = new HashSet<>(); 1721 while (iter.hasNext()) { 1722 final Object key = iter.next(); 1723 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key))); 1724 } 1725 return Collections.unmodifiableSet(entries); 1726 } 1727 1728 /** 1729 * Check whether a ScriptObject contains no properties 1730 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1731 * 1732 * @return true if object has no properties 1733 */ 1734 public boolean isEmpty() { 1735 return !propertyIterator().hasNext(); 1736 } 1737 1738 /** 1739 * Return the set of keys (property names) for all properties 1740 * in this ScriptObject 1741 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1742 * 1743 * @return keySet of this ScriptObject 1744 */ 1745 public Set<Object> keySet() { 1746 final Iterator<String> iter = propertyIterator(); 1747 final Set<Object> keySet = new HashSet<>(); 1748 while (iter.hasNext()) { 1749 keySet.add(iter.next()); 1750 } 1751 return Collections.unmodifiableSet(keySet); 1752 } 1753 1754 /** 1755 * Put a property in the ScriptObject 1756 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1757 * 1758 * @param key property key 1759 * @param value property value 1760 * @param strict strict mode or not 1761 * @return oldValue if property with same key existed already 1762 */ 1763 public Object put(final Object key, final Object value, final boolean strict) { 1764 final Object oldValue = get(key); 1765 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1766 set(key, value, scriptObjectFlags); 1767 return oldValue; 1768 } 1769 1770 /** 1771 * Put several properties in the ScriptObject given a mapping 1772 * of their keys to their values 1773 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1774 * 1775 * @param otherMap a {@literal <key,value>} map of properties to add 1776 * @param strict strict mode or not 1777 */ 1778 public void putAll(final Map<?, ?> otherMap, final boolean strict) { 1779 final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; 1780 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) { 1781 set(entry.getKey(), entry.getValue(), scriptObjectFlags); 1782 } 1783 } 1784 1785 /** 1786 * Remove a property from the ScriptObject. 1787 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1788 * 1789 * @param key the key of the property 1790 * @param strict strict mode or not 1791 * @return the oldValue of the removed property 1792 */ 1793 public Object remove(final Object key, final boolean strict) { 1794 final Object oldValue = get(key); 1795 delete(key, strict); 1796 return oldValue; 1797 } 1798 1799 /** 1800 * Return the size of the ScriptObject - i.e. the number of properties 1801 * it contains 1802 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1803 * 1804 * @return number of properties in ScriptObject 1805 */ 1806 public int size() { 1807 int n = 0; 1808 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) { 1809 n++; 1810 } 1811 return n; 1812 } 1813 1814 /** 1815 * Return the values of the properties in the ScriptObject 1816 * (java.util.Map-like method to help ScriptObjectMirror implementation) 1817 * 1818 * @return collection of values for the properties in this ScriptObject 1819 */ 1820 public Collection<Object> values() { 1821 final List<Object> values = new ArrayList<>(size()); 1822 final Iterator<Object> iter = valueIterator(); 1823 while (iter.hasNext()) { 1824 values.add(iter.next()); 1825 } 1826 return Collections.unmodifiableList(values); 1827 } 1828 1829 /** 1830 * Lookup method that, given a CallSiteDescriptor, looks up the target 1831 * MethodHandle and creates a GuardedInvocation 1832 * with the appropriate guard(s). 1833 * 1834 * @param desc call site descriptor 1835 * @param request the link request 1836 * 1837 * @return GuardedInvocation for the callsite 1838 */ 1839 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) { 1840 // NOTE: we support GET_ELEMENT and SET_ELEMENT as JavaScript doesn't distinguish items from properties. Nashorn itself 1841 // emits "GET_PROPERTY|GET_ELEMENT|GET_METHOD:identifier" for "<expr>.<identifier>" and "GET_ELEMENT|GET_PROPERTY|GET_METHOD" for "<expr>[<expr>]", but we are 1842 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the 1843 // operation has an associated name or not. 1844 final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc); 1845 if (op == null) { 1846 return null; 1847 } 1848 switch (op) { 1849 case GET_PROPERTY: 1850 case GET_ELEMENT: 1851 case GET_METHOD: 1852 return desc.getOperation() instanceof NamedOperation 1853 ? findGetMethod(desc, request, op) 1854 : findGetIndexMethod(desc, request); 1855 case SET_PROPERTY: 1856 case SET_ELEMENT: 1857 return desc.getOperation() instanceof NamedOperation 1858 ? findSetMethod(desc, request) 1859 : findSetIndexMethod(desc, request); 1860 case CALL: 1861 return findCallMethod(desc, request); 1862 case NEW: 1863 return findNewMethod(desc, request); 1864 case CALL_METHOD: 1865 return findCallMethodMethod(desc, request); 1866 default: 1867 } 1868 return null; 1869 } 1870 1871 /** 1872 * Find the appropriate New method for an invoke dynamic call. 1873 * 1874 * @param desc The invoke dynamic call site descriptor. 1875 * @param request The link request 1876 * 1877 * @return GuardedInvocation to be invoked at call site. 1878 */ 1879 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1880 return notAFunction(desc); 1881 } 1882 1883 /** 1884 * Find the appropriate CALL method for an invoke dynamic call. 1885 * This generates "not a function" always 1886 * 1887 * @param desc the call site descriptor. 1888 * @param request the link request 1889 * 1890 * @return GuardedInvocation to be invoked at call site. 1891 */ 1892 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1893 return notAFunction(desc); 1894 } 1895 1896 private GuardedInvocation notAFunction(final CallSiteDescriptor desc) { 1897 throw typeError("not.a.function", NashornCallSiteDescriptor.getFunctionErrorMessage(desc, this)); 1898 } 1899 1900 /** 1901 * Find an implementation for a CALL_METHOD operation. Note that Nashorn internally never uses 1902 * CALL_METHOD, but instead always emits two call sites in bytecode, one for GET_METHOD, and then another 1903 * one for CALL. Explicit support for CALL_METHOD is provided for the benefit of potential external 1904 * callers. The implementation itself actually folds a GET_METHOD method handle into a CALL method handle. 1905 * 1906 * @param desc the call site descriptor. 1907 * @param request the link request 1908 * 1909 * @return GuardedInvocation to be invoked at call site. 1910 */ 1911 protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) { 1912 // R(P0, P1, ...) 1913 final MethodType callType = desc.getMethodType(); 1914 // use type Object(P0) for the getter 1915 final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0))); 1916 final GuardedInvocation getter = findGetMethod(getterType, request, StandardOperation.GET_METHOD); 1917 1918 // Object(P0) => Object(P0, P1, ...) 1919 final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount())); 1920 // R(Object, P0, P1, ...) 1921 final MethodHandle invoker = Bootstrap.createDynamicInvoker("", NashornCallSiteDescriptor.CALL, callType.insertParameterTypes(0, argDroppingGetter.type().returnType())); 1922 // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...) 1923 return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard()); 1924 } 1925 1926 /** 1927 * Test whether this object contains in its prototype chain or is itself a with-object. 1928 * @return true if a with-object was found 1929 */ 1930 boolean hasWithScope() { 1931 return false; 1932 } 1933 1934 /** 1935 * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method 1936 * {@code depth} times. 1937 * @param methodHandle a method handle 1938 * @param depth distance to target prototype 1939 * @return the filtered method handle 1940 */ 1941 static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) { 1942 if (depth == 0) { 1943 return methodHandle; 1944 } 1945 final int listIndex = depth - 1; // We don't need 0-deep walker 1946 MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null; 1947 1948 if (filter == null) { 1949 filter = addProtoFilter(GETPROTO, depth - 1); 1950 PROTO_FILTERS.add(null); 1951 PROTO_FILTERS.set(listIndex, filter); 1952 } 1953 1954 return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0)))); 1955 } 1956 1957 /** 1958 * Find the appropriate GET method for an invoke dynamic call. 1959 * 1960 * @param desc the call site descriptor 1961 * @param request the link request 1962 * @param operation operation for get: getProp, getMethod, getElem etc 1963 * 1964 * @return GuardedInvocation to be invoked at call site. 1965 */ 1966 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final StandardOperation operation) { 1967 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 1968 1969 String name = NashornCallSiteDescriptor.getOperand(desc); 1970 if (NashornCallSiteDescriptor.isApplyToCall(desc)) { 1971 if (Global.isBuiltinFunctionPrototypeApply()) { 1972 name = "call"; 1973 } 1974 } 1975 1976 if (request.isCallSiteUnstable() || hasWithScope()) { 1977 return findMegaMorphicGetMethod(desc, name, operation == StandardOperation.GET_METHOD); 1978 } 1979 1980 final FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this); 1981 MethodHandle mh; 1982 1983 if (find == null) { 1984 switch (operation) { 1985 case GET_ELEMENT: // getElem only gets here if element name is constant, so treat it like a property access 1986 case GET_PROPERTY: 1987 return noSuchProperty(desc, request); 1988 case GET_METHOD: 1989 return noSuchMethod(desc, request); 1990 default: 1991 throw new AssertionError(operation); // never invoked with any other operation 1992 } 1993 } 1994 1995 final GlobalConstants globalConstants = getGlobalConstants(); 1996 if (globalConstants != null) { 1997 final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc); 1998 if (cinv != null) { 1999 return cinv; 2000 } 2001 } 2002 2003 final Class<?> returnType = desc.getMethodType().returnType(); 2004 final Property property = find.getProperty(); 2005 2006 final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? 2007 NashornCallSiteDescriptor.getProgramPoint(desc) : 2008 UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 2009 2010 mh = find.getGetter(returnType, programPoint, request); 2011 // Get the appropriate guard for this callsite and property. 2012 final MethodHandle guard = NashornGuards.getGuard(this, property, desc, explicitInstanceOfCheck); 2013 final ScriptObject owner = find.getOwner(); 2014 final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; 2015 2016 final SwitchPoint[] protoSwitchPoints; 2017 2018 if (mh == null) { 2019 mh = Lookup.emptyGetter(returnType); 2020 protoSwitchPoints = getProtoSwitchPoints(name, owner); 2021 } else if (!find.isSelf()) { 2022 assert mh.type().returnType().equals(returnType) : 2023 "return type mismatch for getter " + mh.type().returnType() + " != " + returnType; 2024 if (!property.isAccessorProperty()) { 2025 // Add a filter that replaces the self object with the prototype owning the property. 2026 mh = addProtoFilter(mh, find.getProtoChainLength()); 2027 } 2028 protoSwitchPoints = getProtoSwitchPoints(name, owner); 2029 } else { 2030 protoSwitchPoints = null; 2031 } 2032 2033 final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception); 2034 return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); 2035 } 2036 2037 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) { 2038 Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic getter: ", desc, " ", name + " ", isMethod); 2039 final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod, NashornCallSiteDescriptor.isScope(desc)); 2040 final MethodHandle guard = getScriptObjectGuard(desc.getMethodType(), true); 2041 return new GuardedInvocation(invoker, guard); 2042 } 2043 2044 @SuppressWarnings("unused") 2045 private Object megamorphicGet(final String key, final boolean isMethod, final boolean isScope) { 2046 final FindProperty find = findProperty(key, true, isScope, this); 2047 if (find != null) { 2048 return find.getObjectValue(); 2049 } 2050 2051 return isMethod ? getNoSuchMethod(key, isScope, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, isScope, INVALID_PROGRAM_POINT); 2052 } 2053 2054 // Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST 2055 @SuppressWarnings("unused") 2056 private void declareAndSet(final String key, final Object value) { 2057 final PropertyMap oldMap = getMap(); 2058 final FindProperty find = findProperty(key, false); 2059 assert find != null; 2060 2061 final Property property = find.getProperty(); 2062 assert property != null; 2063 assert property.needsDeclaration(); 2064 2065 final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION)); 2066 setMap(newMap); 2067 set(key, value, 0); 2068 } 2069 2070 /** 2071 * Find the appropriate GETINDEX method for an invoke dynamic call. 2072 * 2073 * @param desc the call site descriptor 2074 * @param request the link request 2075 * 2076 * @return GuardedInvocation to be invoked at call site. 2077 */ 2078 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2079 final MethodType callType = desc.getMethodType(); 2080 final Class<?> returnType = callType.returnType(); 2081 final Class<?> returnClass = returnType.isPrimitive() ? returnType : Object.class; 2082 final Class<?> keyClass = callType.parameterType(1); 2083 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2084 2085 final String name; 2086 if (returnClass.isPrimitive()) { 2087 //turn e.g. get with a double into getDouble 2088 final String returnTypeName = returnClass.getName(); 2089 name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length()); 2090 } else { 2091 name = "get"; 2092 } 2093 2094 final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc); 2095 return new GuardedInvocation(mh, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2096 } 2097 2098 private static MethodHandle getScriptObjectGuard(final MethodType type, final boolean explicitInstanceOfCheck) { 2099 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck); 2100 } 2101 2102 /** 2103 * Find a handle for a getIndex method 2104 * @param returnType return type for getter 2105 * @param name name 2106 * @param elementType index type for getter 2107 * @param desc call site descriptor 2108 * @return method handle for getter 2109 */ 2110 protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) { 2111 if (!returnType.isPrimitive()) { 2112 return findOwnMH_V(getClass(), name, returnType, elementType); 2113 } 2114 2115 return MH.insertArguments( 2116 findOwnMH_V(getClass(), name, returnType, elementType, int.class), 2117 2, 2118 NashornCallSiteDescriptor.isOptimistic(desc) ? 2119 NashornCallSiteDescriptor.getProgramPoint(desc) : 2120 INVALID_PROGRAM_POINT); 2121 } 2122 2123 /** 2124 * Get a switch point for a property with the given {@code name} that will be invalidated when 2125 * the property definition is changed in this object's prototype chain. Returns {@code null} if 2126 * the property is defined in this object itself. 2127 * 2128 * @param name the property name 2129 * @param owner the property owner, null if property is not defined 2130 * @return a SwitchPoint or null 2131 */ 2132 public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) { 2133 if (owner == this || getProto() == null) { 2134 return null; 2135 } 2136 2137 final List<SwitchPoint> switchPoints = new ArrayList<>(); 2138 for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { 2139 final ScriptObject parent = obj.getProto(); 2140 parent.getMap().addListener(name, obj.getMap()); 2141 final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint(); 2142 if (sp != null && !sp.hasBeenInvalidated()) { 2143 switchPoints.add(sp); 2144 } 2145 } 2146 2147 switchPoints.add(getMap().getSwitchPoint(name)); 2148 return switchPoints.toArray(new SwitchPoint[0]); 2149 } 2150 2151 private void checkSharedProtoMap() { 2152 // Check if our map has an expected shared prototype property map. If it has, make sure that 2153 // the prototype map has not been invalidated, and that it does match the actual map of the prototype. 2154 if (getMap().isInvalidSharedMapFor(getProto())) { 2155 // Change our own map to one that does not assume a shared prototype map. 2156 setMap(getMap().makeUnsharedCopy()); 2157 } 2158 } 2159 2160 /** 2161 * Find the appropriate SET method for an invoke dynamic call. 2162 * 2163 * @param desc the call site descriptor 2164 * @param request the link request 2165 * 2166 * @return GuardedInvocation to be invoked at call site. 2167 */ 2168 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2169 final String name = NashornCallSiteDescriptor.getOperand(desc); 2170 2171 if (request.isCallSiteUnstable() || hasWithScope()) { 2172 return findMegaMorphicSetMethod(desc, name); 2173 } 2174 2175 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2176 2177 /* 2178 * If doing property set on a scope object, we should stop proto search on the first 2179 * non-scope object. Without this, for example, when assigning "toString" on global scope, 2180 * we'll end up assigning it on it's proto - which is Object.prototype.toString !! 2181 * 2182 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString 2183 */ 2184 FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this); 2185 2186 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors. 2187 if (find != null && find.isInheritedOrdinaryProperty()) { 2188 // We should still check if inherited data property is not writable 2189 if (isExtensible() && !find.getProperty().isWritable()) { 2190 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2191 } 2192 // Otherwise, forget the found property unless this is a scope callsite and the owner is a scope object as well. 2193 if (!NashornCallSiteDescriptor.isScope(desc) || !find.getOwner().isScope()) { 2194 find = null; 2195 } 2196 } 2197 2198 if (find != null) { 2199 if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) { 2200 if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) { 2201 throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode. 2202 } 2203 // Existing, non-writable data property 2204 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true); 2205 } 2206 if (!find.getProperty().hasNativeSetter()) { 2207 // Existing accessor property without setter 2208 return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.has.no.setter", true); 2209 } 2210 } else { 2211 if (!isExtensible()) { 2212 return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false); 2213 } 2214 } 2215 2216 final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name)); 2217 2218 final GlobalConstants globalConstants = getGlobalConstants(); 2219 if (globalConstants != null) { 2220 final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request); 2221 if (cinv != null) { 2222 return cinv; 2223 } 2224 } 2225 2226 return inv; 2227 } 2228 2229 private GlobalConstants getGlobalConstants() { 2230 // Avoid hitting getContext() which might be costly for a non-Global unless needed. 2231 return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants(); 2232 } 2233 2234 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) { 2235 final String name = NashornCallSiteDescriptor.getOperand(desc); 2236 if (NashornCallSiteDescriptor.isStrict(desc)) { 2237 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this)); 2238 } 2239 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc); 2240 return new GuardedInvocation( 2241 Lookup.EMPTY_SETTER, 2242 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), 2243 getProtoSwitchPoints(name, null), 2244 explicitInstanceOfCheck ? null : ClassCastException.class); 2245 } 2246 2247 @SuppressWarnings("unused") 2248 private boolean extensionCheck(final boolean isStrict, final String name) { 2249 if (isExtensible()) { 2250 return true; //go on and do the set. this is our guard 2251 } else if (isStrict) { 2252 //throw an error for attempting to do the set in strict mode 2253 throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this)); 2254 } else { 2255 //not extensible, non strict - this is a nop 2256 return false; 2257 } 2258 } 2259 2260 private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { 2261 Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic setter: ", desc, " ", name); 2262 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class); 2263 //never bother with ClassCastExceptionGuard for megamorphic callsites 2264 final GuardedInvocation inv = findSetIndexMethod(getClass(), desc, false, type); 2265 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); 2266 } 2267 2268 @SuppressWarnings("unused") 2269 private static Object globalFilter(final Object object) { 2270 ScriptObject sobj = (ScriptObject) object; 2271 while (sobj != null && !(sobj instanceof Global)) { 2272 sobj = sobj.getProto(); 2273 } 2274 return sobj; 2275 } 2276 2277 /** 2278 * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray} 2279 * provides special quick accessor linkage for continuous arrays that are represented as Java arrays 2280 * 2281 * @param desc call site descriptor 2282 * @param request link request 2283 * 2284 * @return GuardedInvocation to be invoked at call site. 2285 */ 2286 protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value 2287 return findSetIndexMethod(getClass(), desc, explicitInstanceOfCheck(desc, request), desc.getMethodType()); 2288 } 2289 2290 /** 2291 * Find the appropriate SETINDEX method for an invoke dynamic call. 2292 * 2293 * @param clazz the receiver class 2294 * @param desc the call site descriptor 2295 * @param explicitInstanceOfCheck add an explicit instanceof check? 2296 * @param callType the method type at the call site 2297 * 2298 * @return GuardedInvocation to be invoked at call site. 2299 */ 2300 private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) { 2301 assert callType.parameterCount() == 3; 2302 final Class<?> keyClass = callType.parameterType(1); 2303 final Class<?> valueClass = callType.parameterType(2); 2304 2305 MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, int.class); 2306 methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc)); 2307 2308 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 2309 } 2310 2311 /** 2312 * Fall back if a function property is not found. 2313 * @param desc The call site descriptor 2314 * @param request the link request 2315 * @return GuardedInvocation to be invoked at call site. 2316 */ 2317 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 2318 final String name = NashornCallSiteDescriptor.getOperand(desc); 2319 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2320 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc); 2321 2322 if (find == null) { 2323 return noSuchProperty(desc, request); 2324 } 2325 2326 final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request); 2327 2328 final Object value = find.getObjectValue(); 2329 if (!(value instanceof ScriptFunction)) { 2330 return createEmptyGetter(desc, explicitInstanceOfCheck, name); 2331 } 2332 2333 final ScriptFunction func = (ScriptFunction)value; 2334 final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this; 2335 // TODO: It'd be awesome if we could bind "name" without binding "this". 2336 // Since we're binding this we must use an identity guard here. 2337 return new GuardedInvocation( 2338 MH.dropArguments( 2339 MH.constant( 2340 ScriptFunction.class, 2341 func.createBound(thiz, new Object[] { name })), 2342 0, 2343 Object.class), 2344 NashornGuards.combineGuards( 2345 NashornGuards.getIdentityGuard(this), 2346 NashornGuards.getMapGuard(getMap(), true))); 2347 } 2348 2349 /** 2350 * Fall back if a property is not found. 2351 * @param desc the call site descriptor. 2352 * @param request the link request 2353 * @return GuardedInvocation to be invoked at call site. 2354 */ 2355 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 2356 final String name = NashornCallSiteDescriptor.getOperand(desc); 2357 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2358 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); 2359 2360 if (find != null) { 2361 final Object value = find.getObjectValue(); 2362 ScriptFunction func = null; 2363 MethodHandle mh = null; 2364 2365 if (value instanceof ScriptFunction) { 2366 func = (ScriptFunction)value; 2367 mh = getCallMethodHandle(func, desc.getMethodType(), name); 2368 } 2369 2370 if (mh != null) { 2371 assert func != null; 2372 if (scopeAccess && func.isStrict()) { 2373 mh = bindTo(mh, UNDEFINED); 2374 } 2375 2376 return new GuardedInvocation( 2377 mh, 2378 find.isSelf()? 2379 getKnownFunctionPropertyGuardSelf( 2380 getMap(), 2381 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2382 func) 2383 : 2384 //TODO this always does a scriptobject check 2385 getKnownFunctionPropertyGuardProto( 2386 getMap(), 2387 find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), 2388 find.getProtoChainLength(), 2389 func), 2390 getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()), 2391 //TODO this doesn't need a ClassCastException as guard always checks script object 2392 null); 2393 } 2394 } 2395 2396 if (scopeAccess) { 2397 throw referenceError("not.defined", name); 2398 } 2399 2400 return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name); 2401 } 2402 2403 /** 2404 * Invoke fall back if a property is not found. 2405 * @param key Name of property. 2406 * @param isScope is this a scope access? 2407 * @param programPoint program point 2408 * @return Result from call. 2409 */ 2410 protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) { 2411 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); 2412 final Object func = (find != null)? find.getObjectValue() : null; 2413 2414 Object ret = UNDEFINED; 2415 if (func instanceof ScriptFunction) { 2416 final ScriptFunction sfunc = (ScriptFunction)func; 2417 final Object self = isScope && sfunc.isStrict()? UNDEFINED : this; 2418 ret = ScriptRuntime.apply(sfunc, self, key); 2419 } else if (isScope) { 2420 throw referenceError("not.defined", key.toString()); 2421 } 2422 2423 if (isValid(programPoint)) { 2424 throw new UnwarrantedOptimismException(ret, programPoint); 2425 } 2426 2427 return ret; 2428 } 2429 2430 2431 /** 2432 * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined. 2433 * @param name the method name 2434 * @param isScope is this a scope access? 2435 * @return the bound function, or undefined 2436 */ 2437 private Object getNoSuchMethod(final String name, final boolean isScope, final int programPoint) { 2438 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true); 2439 2440 if (find == null) { 2441 return invokeNoSuchProperty(name, isScope, programPoint); 2442 } 2443 2444 final Object value = find.getObjectValue(); 2445 if (!(value instanceof ScriptFunction)) { 2446 if (isScope) { 2447 throw referenceError("not.defined", name); 2448 } 2449 return UNDEFINED; 2450 } 2451 2452 final ScriptFunction func = (ScriptFunction)value; 2453 final Object self = isScope && func.isStrict()? UNDEFINED : this; 2454 return func.createBound(self, new Object[] {name}); 2455 } 2456 2457 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) { 2458 if (NashornCallSiteDescriptor.isOptimistic(desc)) { 2459 throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT); 2460 } 2461 2462 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), 2463 NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null), 2464 explicitInstanceOfCheck ? null : ClassCastException.class); 2465 } 2466 2467 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> { 2468 protected T[] values; 2469 protected final ScriptObject object; 2470 private int index; 2471 2472 ScriptObjectIterator(final ScriptObject object) { 2473 this.object = object; 2474 } 2475 2476 protected abstract void init(); 2477 2478 @Override 2479 public boolean hasNext() { 2480 if (values == null) { 2481 init(); 2482 } 2483 return index < values.length; 2484 } 2485 2486 @Override 2487 public T next() { 2488 if (values == null) { 2489 init(); 2490 } 2491 return values[index++]; 2492 } 2493 2494 @Override 2495 public void remove() { 2496 throw new UnsupportedOperationException("remove"); 2497 } 2498 } 2499 2500 private static class KeyIterator extends ScriptObjectIterator<String> { 2501 KeyIterator(final ScriptObject object) { 2502 super(object); 2503 } 2504 2505 @Override 2506 protected void init() { 2507 final Set<String> keys = new LinkedHashSet<>(); 2508 final Set<String> nonEnumerable = new HashSet<>(); 2509 for (ScriptObject self = object; self != null; self = self.getProto()) { 2510 keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable))); 2511 } 2512 this.values = keys.toArray(new String[0]); 2513 } 2514 } 2515 2516 private static class ValueIterator extends ScriptObjectIterator<Object> { 2517 ValueIterator(final ScriptObject object) { 2518 super(object); 2519 } 2520 2521 @Override 2522 protected void init() { 2523 final ArrayList<Object> valueList = new ArrayList<>(); 2524 final Set<String> nonEnumerable = new HashSet<>(); 2525 for (ScriptObject self = object; self != null; self = self.getProto()) { 2526 for (final String key : self.getOwnKeys(String.class, false, nonEnumerable)) { 2527 valueList.add(self.get(key)); 2528 } 2529 } 2530 this.values = valueList.toArray(new Object[0]); 2531 } 2532 } 2533 2534 /** 2535 * Add a spill property for the given key. 2536 * @param key Property key. 2537 * @param flags Property flags. 2538 * @return Added property. 2539 */ 2540 private Property addSpillProperty(final Object key, final int flags, final Object value, final boolean hasInitialValue) { 2541 final PropertyMap propertyMap = getMap(); 2542 final int fieldSlot = propertyMap.getFreeFieldSlot(); 2543 final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0); 2544 2545 Property property; 2546 if (fieldSlot > -1) { 2547 property = hasInitialValue ? 2548 new AccessorProperty(key, propertyFlags, fieldSlot, this, value) : 2549 new AccessorProperty(key, propertyFlags, getClass(), fieldSlot); 2550 property = addOwnProperty(property); 2551 } else { 2552 final int spillSlot = propertyMap.getFreeSpillSlot(); 2553 property = hasInitialValue ? 2554 new SpillProperty(key, propertyFlags, spillSlot, this, value) : 2555 new SpillProperty(key, propertyFlags, spillSlot); 2556 property = addOwnProperty(property); 2557 ensureSpillSize(property.getSlot()); 2558 } 2559 return property; 2560 } 2561 2562 /** 2563 * Add a spill entry for the given key. 2564 * @param key Property key. 2565 * @return Setter method handle. 2566 */ 2567 MethodHandle addSpill(final Class<?> type, final String key) { 2568 return addSpillProperty(key, 0, null, false).getSetter(type, getMap()); 2569 } 2570 2571 /** 2572 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2573 * fewer parameters than declared and other things that JavaScript allows. This might involve 2574 * creating collectors. 2575 * 2576 * @param methodHandle method handle for invoke 2577 * @param callType type of the call 2578 * 2579 * @return method handle with adjusted arguments 2580 */ 2581 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) { 2582 return pairArguments(methodHandle, callType, null); 2583 } 2584 2585 /** 2586 * Make sure arguments are paired correctly, with respect to more parameters than declared, 2587 * fewer parameters than declared and other things that JavaScript allows. This might involve 2588 * creating collectors. 2589 * 2590 * Make sure arguments are paired correctly. 2591 * @param methodHandle MethodHandle to adjust. 2592 * @param callType MethodType of the call site. 2593 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the 2594 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a 2595 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites 2596 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters. 2597 * 2598 * @return method handle with adjusted arguments 2599 */ 2600 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) { 2601 final MethodType methodType = methodHandle.type(); 2602 if (methodType.equals(callType.changeReturnType(methodType.returnType()))) { 2603 return methodHandle; 2604 } 2605 2606 final int parameterCount = methodType.parameterCount(); 2607 final int callCount = callType.parameterCount(); 2608 2609 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray(); 2610 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg : callCount > 0 && 2611 callType.parameterType(callCount - 1).isArray(); 2612 2613 if (isCalleeVarArg) { 2614 return isCallerVarArg ? 2615 methodHandle : 2616 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1); 2617 } 2618 2619 if (isCallerVarArg) { 2620 return adaptHandleToVarArgCallSite(methodHandle, callCount); 2621 } 2622 2623 if (callCount < parameterCount) { 2624 final int missingArgs = parameterCount - callCount; 2625 final Object[] fillers = new Object[missingArgs]; 2626 2627 Arrays.fill(fillers, UNDEFINED); 2628 2629 if (isCalleeVarArg) { 2630 fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY; 2631 } 2632 2633 return MH.insertArguments( 2634 methodHandle, 2635 parameterCount - missingArgs, 2636 fillers); 2637 } 2638 2639 if (callCount > parameterCount) { 2640 final int discardedArgs = callCount - parameterCount; 2641 2642 final Class<?>[] discards = new Class<?>[discardedArgs]; 2643 Arrays.fill(discards, Object.class); 2644 2645 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards); 2646 } 2647 2648 return methodHandle; 2649 } 2650 2651 static MethodHandle adaptHandleToVarArgCallSite(final MethodHandle mh, final int callSiteParamCount) { 2652 final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1; 2653 return MH.filterArguments( 2654 MH.asSpreader( 2655 mh, 2656 Object[].class, 2657 spreadArgs), 2658 callSiteParamCount - 1, 2659 MH.insertArguments( 2660 TRUNCATINGFILTER, 2661 0, 2662 spreadArgs) 2663 ); 2664 } 2665 2666 @SuppressWarnings("unused") 2667 private static Object[] truncatingFilter(final int n, final Object[] array) { 2668 final int length = array == null ? 0 : array.length; 2669 if (n == length) { 2670 return array == null ? ScriptRuntime.EMPTY_ARRAY : array; 2671 } 2672 2673 final Object[] newArray = new Object[n]; 2674 2675 if (array != null) { 2676 System.arraycopy(array, 0, newArray, 0, Math.min(n, length)); 2677 } 2678 2679 if (length < n) { 2680 final Object fill = UNDEFINED; 2681 2682 for (int i = length; i < n; i++) { 2683 newArray[i] = fill; 2684 } 2685 } 2686 2687 return newArray; 2688 } 2689 2690 /** 2691 * Numeric length setter for length property 2692 * 2693 * @param newLength new length to set 2694 */ 2695 public final void setLength(final long newLength) { 2696 final ArrayData data = getArray(); 2697 final long arrayLength = data.length(); 2698 if (newLength == arrayLength) { 2699 return; 2700 } 2701 2702 if (newLength > arrayLength) { 2703 setArray(data.ensure(newLength - 1).safeDelete(arrayLength, newLength - 1, false)); 2704 return; 2705 } 2706 2707 if (newLength < arrayLength) { 2708 long actualLength = newLength; 2709 2710 // Check for numeric keys in property map and delete them or adjust length, depending on whether 2711 // they're defined as configurable. See ES5 #15.4.5.2 2712 if (getMap().containsArrayKeys()) { 2713 2714 for (long l = arrayLength - 1; l >= newLength; l--) { 2715 final FindProperty find = findProperty(JSType.toString(l), false); 2716 2717 if (find != null) { 2718 2719 if (find.getProperty().isConfigurable()) { 2720 deleteOwnProperty(find.getProperty()); 2721 } else { 2722 actualLength = l + 1; 2723 break; 2724 } 2725 } 2726 } 2727 } 2728 2729 setArray(data.shrink(actualLength)); 2730 data.setLength(actualLength); 2731 } 2732 } 2733 2734 private int getInt(final int index, final Object key, final int programPoint) { 2735 if (isValidArrayIndex(index)) { 2736 for (ScriptObject object = this; ; ) { 2737 if (object.getMap().containsArrayKeys()) { 2738 final FindProperty find = object.findProperty(key, false); 2739 2740 if (find != null) { 2741 return getIntValue(find, programPoint); 2742 } 2743 } 2744 2745 if ((object = object.getProto()) == null) { 2746 break; 2747 } 2748 2749 final ArrayData array = object.getArray(); 2750 2751 if (array.has(index)) { 2752 return isValid(programPoint) ? 2753 array.getIntOptimistic(index, programPoint) : 2754 array.getInt(index); 2755 } 2756 } 2757 } else { 2758 final FindProperty find = findProperty(key, true); 2759 2760 if (find != null) { 2761 return getIntValue(find, programPoint); 2762 } 2763 } 2764 2765 return JSType.toInt32(invokeNoSuchProperty(key, false, programPoint)); 2766 } 2767 2768 @Override 2769 public int getInt(final Object key, final int programPoint) { 2770 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2771 final int index = getArrayIndex(primitiveKey); 2772 final ArrayData array = getArray(); 2773 2774 if (array.has(index)) { 2775 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2776 } 2777 2778 return getInt(index, JSType.toPropertyKey(primitiveKey), programPoint); 2779 } 2780 2781 @Override 2782 public int getInt(final double key, final int programPoint) { 2783 final int index = getArrayIndex(key); 2784 final ArrayData array = getArray(); 2785 2786 if (array.has(index)) { 2787 return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index); 2788 } 2789 2790 return getInt(index, JSType.toString(key), programPoint); 2791 } 2792 2793 @Override 2794 public int getInt(final int key, final int programPoint) { 2795 final int index = getArrayIndex(key); 2796 final ArrayData array = getArray(); 2797 2798 if (array.has(index)) { 2799 return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key); 2800 } 2801 2802 return getInt(index, JSType.toString(key), programPoint); 2803 } 2804 2805 private double getDouble(final int index, final Object key, final int programPoint) { 2806 if (isValidArrayIndex(index)) { 2807 for (ScriptObject object = this; ; ) { 2808 if (object.getMap().containsArrayKeys()) { 2809 final FindProperty find = object.findProperty(key, false); 2810 if (find != null) { 2811 return getDoubleValue(find, programPoint); 2812 } 2813 } 2814 2815 if ((object = object.getProto()) == null) { 2816 break; 2817 } 2818 2819 final ArrayData array = object.getArray(); 2820 2821 if (array.has(index)) { 2822 return isValid(programPoint) ? 2823 array.getDoubleOptimistic(index, programPoint) : 2824 array.getDouble(index); 2825 } 2826 } 2827 } else { 2828 final FindProperty find = findProperty(key, true); 2829 2830 if (find != null) { 2831 return getDoubleValue(find, programPoint); 2832 } 2833 } 2834 2835 return JSType.toNumber(invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT)); 2836 } 2837 2838 @Override 2839 public double getDouble(final Object key, final int programPoint) { 2840 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2841 final int index = getArrayIndex(primitiveKey); 2842 final ArrayData array = getArray(); 2843 2844 if (array.has(index)) { 2845 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2846 } 2847 2848 return getDouble(index, JSType.toPropertyKey(primitiveKey), programPoint); 2849 } 2850 2851 @Override 2852 public double getDouble(final double key, final int programPoint) { 2853 final int index = getArrayIndex(key); 2854 final ArrayData array = getArray(); 2855 2856 if (array.has(index)) { 2857 return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index); 2858 } 2859 2860 return getDouble(index, JSType.toString(key), programPoint); 2861 } 2862 2863 @Override 2864 public double getDouble(final int key, final int programPoint) { 2865 final int index = getArrayIndex(key); 2866 final ArrayData array = getArray(); 2867 2868 if (array.has(index)) { 2869 return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key); 2870 } 2871 2872 return getDouble(index, JSType.toString(key), programPoint); 2873 } 2874 2875 private Object get(final int index, final Object key) { 2876 if (isValidArrayIndex(index)) { 2877 for (ScriptObject object = this; ; ) { 2878 if (object.getMap().containsArrayKeys()) { 2879 final FindProperty find = object.findProperty(key, false); 2880 2881 if (find != null) { 2882 return find.getObjectValue(); 2883 } 2884 } 2885 2886 if ((object = object.getProto()) == null) { 2887 break; 2888 } 2889 2890 final ArrayData array = object.getArray(); 2891 2892 if (array.has(index)) { 2893 return array.getObject(index); 2894 } 2895 } 2896 } else { 2897 final FindProperty find = findProperty(key, true); 2898 2899 if (find != null) { 2900 return find.getObjectValue(); 2901 } 2902 } 2903 2904 return invokeNoSuchProperty(key, false, INVALID_PROGRAM_POINT); 2905 } 2906 2907 @Override 2908 public Object get(final Object key) { 2909 final Object primitiveKey = JSType.toPrimitive(key, String.class); 2910 final int index = getArrayIndex(primitiveKey); 2911 final ArrayData array = getArray(); 2912 2913 if (array.has(index)) { 2914 return array.getObject(index); 2915 } 2916 2917 return get(index, JSType.toPropertyKey(primitiveKey)); 2918 } 2919 2920 @Override 2921 public Object get(final double key) { 2922 final int index = getArrayIndex(key); 2923 final ArrayData array = getArray(); 2924 2925 if (array.has(index)) { 2926 return array.getObject(index); 2927 } 2928 2929 return get(index, JSType.toString(key)); 2930 } 2931 2932 @Override 2933 public Object get(final int key) { 2934 final int index = getArrayIndex(key); 2935 final ArrayData array = getArray(); 2936 2937 if (array.has(index)) { 2938 return array.getObject(index); 2939 } 2940 2941 return get(index, JSType.toString(key)); 2942 } 2943 2944 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final int callSiteFlags) { 2945 if (getMap().containsArrayKeys()) { 2946 final String key = JSType.toString(longIndex); 2947 final FindProperty find = findProperty(key, true); 2948 if (find != null) { 2949 setObject(find, callSiteFlags, key, value); 2950 return true; 2951 } 2952 } 2953 return false; 2954 } 2955 2956 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final int callSiteFlags) { 2957 if (getMap().containsArrayKeys()) { 2958 final String key = JSType.toString(longIndex); 2959 final FindProperty find = findProperty(key, true); 2960 if (find != null) { 2961 setObject(find, callSiteFlags, key, value); 2962 return true; 2963 } 2964 } 2965 return false; 2966 } 2967 2968 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) { 2969 if (getMap().containsArrayKeys()) { 2970 final String key = JSType.toString(longIndex); 2971 final FindProperty find = findProperty(key, true); 2972 if (find != null) { 2973 setObject(find, callSiteFlags, key, value); 2974 return true; 2975 } 2976 } 2977 return false; 2978 } 2979 2980 private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final int callSiteFlags) { 2981 if (getMap().containsArrayKeys()) { 2982 final String key = JSType.toString(longIndex); 2983 final FindProperty find = findProperty(key, true); 2984 if (find != null) { 2985 setObject(find, callSiteFlags, key, value); 2986 return true; 2987 } 2988 } 2989 return false; 2990 } 2991 2992 //value agnostic 2993 private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) { 2994 if (longIndex >= oldLength) { 2995 if (!isExtensible()) { 2996 if (isStrictFlag(callSiteFlags)) { 2997 throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this)); 2998 } 2999 return true; 3000 } 3001 setArray(getArray().ensure(longIndex)); 3002 } 3003 return false; 3004 } 3005 3006 private void doesNotHave(final int index, final int value, final int callSiteFlags) { 3007 final long oldLength = getArray().length(); 3008 final long longIndex = ArrayIndex.toLongIndex(index); 3009 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3010 final boolean strict = isStrictFlag(callSiteFlags); 3011 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3012 } 3013 } 3014 3015 private void doesNotHave(final int index, final double value, final int callSiteFlags) { 3016 final long oldLength = getArray().length(); 3017 final long longIndex = ArrayIndex.toLongIndex(index); 3018 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3019 final boolean strict = isStrictFlag(callSiteFlags); 3020 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3021 } 3022 } 3023 3024 private void doesNotHave(final int index, final Object value, final int callSiteFlags) { 3025 final long oldLength = getArray().length(); 3026 final long longIndex = ArrayIndex.toLongIndex(index); 3027 if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) { 3028 final boolean strict = isStrictFlag(callSiteFlags); 3029 setArray(getArray().set(index, value, strict).safeDelete(oldLength, longIndex - 1, strict)); 3030 } 3031 } 3032 3033 /** 3034 * This is the most generic of all Object setters. Most of the others use this in some form. 3035 * TODO: should be further specialized 3036 * 3037 * @param find found property 3038 * @param callSiteFlags callsite flags 3039 * @param key property key 3040 * @param value property value 3041 */ 3042 public final void setObject(final FindProperty find, final int callSiteFlags, final Object key, final Object value) { 3043 FindProperty f = find; 3044 3045 invalidateGlobalConstant(key); 3046 3047 if (f != null && f.isInheritedOrdinaryProperty()) { 3048 final boolean isScope = isScopeFlag(callSiteFlags); 3049 // If the start object of the find is not this object it means the property was found inside a 3050 // 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set' 3051 // to the 'with' object. 3052 // Note that although a 'set' operation involving a with statement follows scope rules outside 3053 // the 'with' expression (the 'set' operation is performed on the owning prototype if it exists), 3054 // it follows non-scope rules inside the 'with' expression (set is performed on the top level object). 3055 // This is why we clear the callsite flags and FindProperty in the forward call to the 'with' object. 3056 if (isScope && f.getSelf() != this) { 3057 f.getSelf().setObject(null, 0, key, value); 3058 return; 3059 } 3060 // Setting a property should not modify the property in prototype unless this is a scope callsite 3061 // and the owner is a scope object as well (with the exception of 'with' statement handled above). 3062 if (!isScope || !f.getOwner().isScope()) { 3063 f = null; 3064 } 3065 } 3066 3067 if (f != null) { 3068 if (!f.getProperty().isWritable() || !f.getProperty().hasNativeSetter()) { 3069 if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) { 3070 throw typeError("assign.constant", key.toString()); // Overwriting ES6 const should throw also in non-strict mode. 3071 } 3072 if (isStrictFlag(callSiteFlags)) { 3073 throw typeError( 3074 f.getProperty().isAccessorProperty() ? "property.has.no.setter" : "property.not.writable", 3075 key.toString(), ScriptRuntime.safeToString(this)); 3076 } 3077 return; 3078 } 3079 3080 f.setValue(value, isStrictFlag(callSiteFlags)); 3081 3082 } else if (!isExtensible()) { 3083 if (isStrictFlag(callSiteFlags)) { 3084 throw typeError("object.non.extensible", key.toString(), ScriptRuntime.safeToString(this)); 3085 } 3086 } else { 3087 ScriptObject sobj = this; 3088 // undefined scope properties are set in the global object. 3089 if (isScope()) { 3090 while (sobj != null && !(sobj instanceof Global)) { 3091 sobj = sobj.getProto(); 3092 } 3093 assert sobj != null : "no parent global object in scope"; 3094 } 3095 //this will unbox any Number object to its primitive type in case the 3096 //property supports primitive types, so it doesn't matter that it comes 3097 //in as an Object. 3098 sobj.addSpillProperty(key, 0, value, true); 3099 } 3100 } 3101 3102 @Override 3103 public void set(final Object key, final int value, final int callSiteFlags) { 3104 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3105 final int index = getArrayIndex(primitiveKey); 3106 3107 if (isValidArrayIndex(index)) { 3108 final ArrayData data = getArray(); 3109 if (data.has(index)) { 3110 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3111 } else { 3112 doesNotHave(index, value, callSiteFlags); 3113 } 3114 3115 return; 3116 } 3117 3118 final Object propName = JSType.toPropertyKey(primitiveKey); 3119 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3120 } 3121 3122 @Override 3123 public void set(final Object key, final double value, final int callSiteFlags) { 3124 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3125 final int index = getArrayIndex(primitiveKey); 3126 3127 if (isValidArrayIndex(index)) { 3128 final ArrayData data = getArray(); 3129 if (data.has(index)) { 3130 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3131 } else { 3132 doesNotHave(index, value, callSiteFlags); 3133 } 3134 3135 return; 3136 } 3137 3138 final Object propName = JSType.toPropertyKey(primitiveKey); 3139 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3140 } 3141 3142 @Override 3143 public void set(final Object key, final Object value, final int callSiteFlags) { 3144 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3145 final int index = getArrayIndex(primitiveKey); 3146 3147 if (isValidArrayIndex(index)) { 3148 final ArrayData data = getArray(); 3149 if (data.has(index)) { 3150 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3151 } else { 3152 doesNotHave(index, value, callSiteFlags); 3153 } 3154 3155 return; 3156 } 3157 3158 final Object propName = JSType.toPropertyKey(primitiveKey); 3159 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3160 } 3161 3162 @Override 3163 public void set(final double key, final int value, final int callSiteFlags) { 3164 final int index = getArrayIndex(key); 3165 3166 if (isValidArrayIndex(index)) { 3167 final ArrayData data = getArray(); 3168 if (data.has(index)) { 3169 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3170 } else { 3171 doesNotHave(index, value, callSiteFlags); 3172 } 3173 3174 return; 3175 } 3176 3177 final String propName = JSType.toString(key); 3178 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3179 } 3180 3181 @Override 3182 public void set(final double key, final double value, final int callSiteFlags) { 3183 final int index = getArrayIndex(key); 3184 3185 if (isValidArrayIndex(index)) { 3186 final ArrayData data = getArray(); 3187 if (data.has(index)) { 3188 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3189 } else { 3190 doesNotHave(index, value, callSiteFlags); 3191 } 3192 3193 return; 3194 } 3195 3196 final String propName = JSType.toString(key); 3197 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3198 } 3199 3200 @Override 3201 public void set(final double key, final Object value, final int callSiteFlags) { 3202 final int index = getArrayIndex(key); 3203 3204 if (isValidArrayIndex(index)) { 3205 final ArrayData data = getArray(); 3206 if (data.has(index)) { 3207 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3208 } else { 3209 doesNotHave(index, value, callSiteFlags); 3210 } 3211 3212 return; 3213 } 3214 3215 final String propName = JSType.toString(key); 3216 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3217 } 3218 3219 @Override 3220 public void set(final int key, final int value, final int callSiteFlags) { 3221 final int index = getArrayIndex(key); 3222 if (isValidArrayIndex(index)) { 3223 if (getArray().has(index)) { 3224 final ArrayData data = getArray(); 3225 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3226 } else { 3227 doesNotHave(index, value, callSiteFlags); 3228 } 3229 return; 3230 } 3231 3232 final String propName = JSType.toString(key); 3233 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3234 } 3235 3236 @Override 3237 public void set(final int key, final double value, final int callSiteFlags) { 3238 final int index = getArrayIndex(key); 3239 3240 if (isValidArrayIndex(index)) { 3241 final ArrayData data = getArray(); 3242 if (data.has(index)) { 3243 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3244 } else { 3245 doesNotHave(index, value, callSiteFlags); 3246 } 3247 3248 return; 3249 } 3250 3251 final String propName = JSType.toString(key); 3252 setObject(findProperty(propName, true), callSiteFlags, propName, JSType.toObject(value)); 3253 } 3254 3255 @Override 3256 public void set(final int key, final Object value, final int callSiteFlags) { 3257 final int index = getArrayIndex(key); 3258 3259 if (isValidArrayIndex(index)) { 3260 final ArrayData data = getArray(); 3261 if (data.has(index)) { 3262 setArray(data.set(index, value, isStrictFlag(callSiteFlags))); 3263 } else { 3264 doesNotHave(index, value, callSiteFlags); 3265 } 3266 3267 return; 3268 } 3269 3270 final String propName = JSType.toString(key); 3271 setObject(findProperty(propName, true), callSiteFlags, propName, value); 3272 } 3273 3274 @Override 3275 public boolean has(final Object key) { 3276 final Object primitiveKey = JSType.toPrimitive(key); 3277 final int index = getArrayIndex(primitiveKey); 3278 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), true); 3279 } 3280 3281 @Override 3282 public boolean has(final double key) { 3283 final int index = getArrayIndex(key); 3284 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3285 } 3286 3287 @Override 3288 public boolean has(final int key) { 3289 final int index = getArrayIndex(key); 3290 return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(key), true); 3291 } 3292 3293 private boolean hasArrayProperty(final int index) { 3294 boolean hasArrayKeys = false; 3295 3296 for (ScriptObject self = this; self != null; self = self.getProto()) { 3297 if (self.getArray().has(index)) { 3298 return true; 3299 } 3300 hasArrayKeys = hasArrayKeys || self.getMap().containsArrayKeys(); 3301 } 3302 3303 return hasArrayKeys && hasProperty(ArrayIndex.toKey(index), true); 3304 } 3305 3306 @Override 3307 public boolean hasOwnProperty(final Object key) { 3308 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3309 final int index = getArrayIndex(primitiveKey); 3310 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toPropertyKey(primitiveKey), false); 3311 } 3312 3313 @Override 3314 public boolean hasOwnProperty(final int key) { 3315 final int index = getArrayIndex(key); 3316 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3317 } 3318 3319 @Override 3320 public boolean hasOwnProperty(final double key) { 3321 final int index = getArrayIndex(key); 3322 return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(key), false); 3323 } 3324 3325 private boolean hasOwnArrayProperty(final int index) { 3326 return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false); 3327 } 3328 3329 @Override 3330 public boolean delete(final int key, final boolean strict) { 3331 final int index = getArrayIndex(key); 3332 final ArrayData array = getArray(); 3333 3334 if (array.has(index)) { 3335 if (array.canDelete(index, strict)) { 3336 setArray(array.delete(index)); 3337 return true; 3338 } 3339 return false; 3340 } 3341 return deleteObject(JSType.toObject(key), strict); 3342 } 3343 3344 @Override 3345 public boolean delete(final double key, final boolean strict) { 3346 final int index = getArrayIndex(key); 3347 final ArrayData array = getArray(); 3348 3349 if (array.has(index)) { 3350 if (array.canDelete(index, strict)) { 3351 setArray(array.delete(index)); 3352 return true; 3353 } 3354 return false; 3355 } 3356 3357 return deleteObject(JSType.toObject(key), strict); 3358 } 3359 3360 @Override 3361 public boolean delete(final Object key, final boolean strict) { 3362 final Object primitiveKey = JSType.toPrimitive(key, String.class); 3363 final int index = getArrayIndex(primitiveKey); 3364 final ArrayData array = getArray(); 3365 3366 if (array.has(index)) { 3367 if (array.canDelete(index, strict)) { 3368 setArray(array.delete(index)); 3369 return true; 3370 } 3371 return false; 3372 } 3373 3374 return deleteObject(primitiveKey, strict); 3375 } 3376 3377 private boolean deleteObject(final Object key, final boolean strict) { 3378 final Object propName = JSType.toPropertyKey(key); 3379 final FindProperty find = findProperty(propName, false); 3380 3381 if (find == null) { 3382 return true; 3383 } 3384 3385 if (!find.getProperty().isConfigurable()) { 3386 if (strict) { 3387 throw typeError("cant.delete.property", propName.toString(), ScriptRuntime.safeToString(this)); 3388 } 3389 return false; 3390 } 3391 3392 final Property prop = find.getProperty(); 3393 deleteOwnProperty(prop); 3394 3395 return true; 3396 } 3397 3398 /** 3399 * Return a shallow copy of this ScriptObject. 3400 * @return a shallow copy. 3401 */ 3402 public final ScriptObject copy() { 3403 try { 3404 return clone(); 3405 } catch (final CloneNotSupportedException e) { 3406 throw new RuntimeException(e); 3407 } 3408 } 3409 3410 @Override 3411 protected ScriptObject clone() throws CloneNotSupportedException { 3412 final ScriptObject clone = (ScriptObject) super.clone(); 3413 if (objectSpill != null) { 3414 clone.objectSpill = objectSpill.clone(); 3415 if (primitiveSpill != null) { 3416 clone.primitiveSpill = primitiveSpill.clone(); 3417 } 3418 } 3419 clone.arrayData = arrayData.copy(); 3420 return clone; 3421 } 3422 3423 /** 3424 * Make a new UserAccessorProperty property. getter and setter functions are stored in 3425 * this ScriptObject and slot values are used in property object. 3426 * 3427 * @param key the property name 3428 * @param propertyFlags attribute flags of the property 3429 * @param getter getter function for the property 3430 * @param setter setter function for the property 3431 * @return the newly created UserAccessorProperty 3432 */ 3433 protected final UserAccessorProperty newUserAccessors(final Object key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) { 3434 final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags); 3435 //property.getSetter(Object.class, getMap()); 3436 uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter)); 3437 return uc; 3438 } 3439 3440 /** 3441 * Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise. 3442 * @return {@code true} if dual fields should be used. 3443 */ 3444 protected boolean useDualFields() { 3445 return !StructureLoader.isSingleFieldStructure(getClass().getName()); 3446 } 3447 3448 Object ensureSpillSize(final int slot) { 3449 final int oldLength = objectSpill == null ? 0 : objectSpill.length; 3450 if (slot < oldLength) { 3451 return this; 3452 } 3453 final int newLength = alignUp(slot + 1, SPILL_RATE); 3454 final Object[] newObjectSpill = new Object[newLength]; 3455 final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null; 3456 3457 if (objectSpill != null) { 3458 System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength); 3459 if (primitiveSpill != null && newPrimitiveSpill != null) { 3460 System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength); 3461 } 3462 } 3463 3464 this.primitiveSpill = newPrimitiveSpill; 3465 this.objectSpill = newObjectSpill; 3466 3467 return this; 3468 } 3469 3470 private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) { 3471 // TODO: figure out how can it work for NativeArray$Prototype etc. 3472 return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3473 } 3474 3475 private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 3476 return findOwnMH_V(ScriptObject.class, name, rtype, types); 3477 } 3478 3479 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { 3480 return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types)); 3481 } 3482 3483 private static MethodHandle getKnownFunctionPropertyGuardSelf(final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3484 return MH.insertArguments(KNOWNFUNCPROPGUARDSELF, 1, map, getter, func); 3485 } 3486 3487 @SuppressWarnings("unused") 3488 private static boolean knownFunctionPropertyGuardSelf(final Object self, final PropertyMap map, final MethodHandle getter, final ScriptFunction func) { 3489 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3490 try { 3491 return getter.invokeExact(self) == func; 3492 } catch (final RuntimeException | Error e) { 3493 throw e; 3494 } catch (final Throwable t) { 3495 throw new RuntimeException(t); 3496 } 3497 } 3498 3499 return false; 3500 } 3501 3502 private static MethodHandle getKnownFunctionPropertyGuardProto(final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3503 return MH.insertArguments(KNOWNFUNCPROPGUARDPROTO, 1, map, getter, depth, func); 3504 } 3505 3506 private static ScriptObject getProto(final ScriptObject self, final int depth) { 3507 ScriptObject proto = self; 3508 for (int d = 0; d < depth; d++) { 3509 proto = proto.getProto(); 3510 if (proto == null) { 3511 return null; 3512 } 3513 } 3514 3515 return proto; 3516 } 3517 3518 @SuppressWarnings("unused") 3519 private static boolean knownFunctionPropertyGuardProto(final Object self, final PropertyMap map, final MethodHandle getter, final int depth, final ScriptFunction func) { 3520 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) { 3521 final ScriptObject proto = getProto((ScriptObject)self, depth); 3522 if (proto == null) { 3523 return false; 3524 } 3525 try { 3526 return getter.invokeExact((Object)proto) == func; 3527 } catch (final RuntimeException | Error e) { 3528 throw e; 3529 } catch (final Throwable t) { 3530 throw new RuntimeException(t); 3531 } 3532 } 3533 3534 return false; 3535 } 3536 3537 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */ 3538 private static LongAdder count; 3539 3540 static { 3541 if (Context.DEBUG) { 3542 count = new LongAdder(); 3543 } 3544 } 3545 /** 3546 * Get number of {@code ScriptObject} instances created. If not running in debug 3547 * mode this is always 0 3548 * 3549 * @return number of ScriptObjects created 3550 */ 3551 public static long getCount() { 3552 return count.longValue(); 3553 } 3554} 3555