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

Thu, 17 Oct 2013 17:33:16 +0200

author
hannesw
date
Thu, 17 Oct 2013 17:33:16 +0200
changeset 633
a2065f67857c
parent 620
8c617a092d68
child 636
d8aa87d292eb
permissions
-rw-r--r--

8026701: Array.prototype.splice is slow on dense arrays
Reviewed-by: lagergren, sundar, jlaskey

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

mercurial