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

Thu, 23 Oct 2014 15:19:00 +0400

author
lagergren
date
Thu, 23 Oct 2014 15:19:00 +0400
changeset 1071
78eb2b415108
parent 1028
d79265f2fa92
child 1074
29a4cd3d1f7a
permissions
-rw-r--r--

8061391: concat as a builtin optimistic form, had to remove NoTypedArrayData and replace it, as we throw away a lot of optimistic link opportunities with NoTypedArrayData not being Continuous
Reviewed-by: attila, hannesw

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

mercurial