src/jdk/nashorn/internal/runtime/ScriptObject.java

Thu, 11 Sep 2014 18:04:54 +0200

author
hannesw
date
Thu, 11 Sep 2014 18:04:54 +0200
changeset 1006
e94bfa3c6c6c
parent 991
b7a2db4de254
child 1007
39ba6d257e4c
permissions
-rw-r--r--

8057021: UserAccessorProperty guards fail with multiple globals
Reviewed-by: attila, lagergren

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

mercurial