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

Tue, 30 Apr 2013 10:05:42 -0300

author
jlaskey
date
Tue, 30 Apr 2013 10:05:42 -0300
changeset 242
b754fb89367d
parent 228
5c98cc846f92
child 243
80cb02dedc83
permissions
-rw-r--r--

8006220: Simplify PropertyMaps
Reviewed-by: hannesw, lagergren
Contributed-by: james.laskey@oracle.com

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

mercurial