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

Wed, 04 Sep 2013 19:58:16 +0530

author
sundar
date
Wed, 04 Sep 2013 19:58:16 +0530
changeset 538
e43ab4062636
parent 537
b5ff11e00050
child 541
c3b6ce7b74bf
permissions
-rw-r--r--

8024174: Setting __proto__ property in Object literal should be supported
Reviewed-by: jlaskey, lagergren

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.runtime;
jlaskey@3 27
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;
jlaskey@3 40
jlaskey@3 41 import java.lang.invoke.MethodHandle;
jlaskey@3 42 import java.lang.invoke.MethodHandles;
jlaskey@3 43 import java.lang.invoke.MethodType;
jlaskey@3 44 import java.util.AbstractMap;
jlaskey@3 45 import java.util.ArrayList;
jlaskey@3 46 import java.util.Arrays;
jlaskey@3 47 import java.util.Collection;
jlaskey@3 48 import java.util.Collections;
jlaskey@3 49 import java.util.HashSet;
jlaskey@3 50 import java.util.Iterator;
jlaskey@3 51 import java.util.LinkedHashSet;
jlaskey@3 52 import java.util.List;
jlaskey@3 53 import java.util.Map;
jlaskey@3 54 import java.util.Set;
lagergren@524 55
attila@90 56 import jdk.internal.dynalink.CallSiteDescriptor;
attila@90 57 import jdk.internal.dynalink.linker.GuardedInvocation;
attila@90 58 import jdk.internal.dynalink.linker.LinkRequest;
attila@90 59 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
jlaskey@3 60 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
lagergren@96 61 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
attila@217 62 import jdk.nashorn.internal.lookup.Lookup;
attila@217 63 import jdk.nashorn.internal.lookup.MethodHandleFactory;
jlaskey@3 64 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
jlaskey@3 65 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
jlaskey@3 66 import jdk.nashorn.internal.runtime.arrays.ArrayData;
hannesw@334 67 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
jlaskey@3 68 import jdk.nashorn.internal.runtime.linker.Bootstrap;
attila@217 69 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
jlaskey@3 70 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
jlaskey@3 71 import jdk.nashorn.internal.runtime.linker.NashornGuards;
jlaskey@3 72
jlaskey@3 73 /**
jlaskey@3 74 * Base class for generic JavaScript objects.
jlaskey@3 75 * <p>
jlaskey@3 76 * Notes:
jlaskey@3 77 * <ul>
jlaskey@3 78 * <li>The map is used to identify properties in the object.</li>
jlaskey@3 79 * <li>If the map is modified then it must be cloned and replaced. This notifies
jlaskey@3 80 * any code that made assumptions about the object that things have changed.
jlaskey@3 81 * Ex. CallSites that have been validated must check to see if the map has
jlaskey@3 82 * changed (or a map from a different object type) and hence relink the method
jlaskey@3 83 * to call.</li>
jlaskey@3 84 * <li>Modifications of the map include adding/deleting attributes or changing a
jlaskey@3 85 * function field value.</li>
jlaskey@3 86 * </ul>
jlaskey@3 87 */
jlaskey@3 88
sundar@39 89 public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
sundar@537 90 /** __proto__ special property name */
sundar@538 91 public static final String PROTO_PROPERTY_NAME = "__proto__";
jlaskey@3 92
jlaskey@3 93 /** Search fall back routine name for "no such method" */
hannesw@79 94 static final String NO_SUCH_METHOD_NAME = "__noSuchMethod__";
jlaskey@3 95
jlaskey@3 96 /** Search fall back routine name for "no such property" */
hannesw@79 97 static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
jlaskey@3 98
jlaskey@3 99 /** Per ScriptObject flag - is this a scope object? */
jlaskey@3 100 public static final int IS_SCOPE = 0b0000_0001;
jlaskey@3 101
jlaskey@3 102 /** Per ScriptObject flag - is this an array object? */
jlaskey@3 103 public static final int IS_ARRAY = 0b0000_0010;
jlaskey@3 104
jlaskey@3 105 /** Per ScriptObject flag - is this an arguments object? */
jlaskey@3 106 public static final int IS_ARGUMENTS = 0b0000_0100;
jlaskey@3 107
jlaskey@242 108 /** Is this a prototype PropertyMap? */
jlaskey@242 109 public static final int IS_PROTOTYPE = 0b0000_1000;
jlaskey@242 110
sundar@344 111 /** Is length property not-writable? */
sundar@344 112 public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
sundar@344 113
jlaskey@3 114 /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
jlaskey@3 115 public static final int SPILL_RATE = 8;
jlaskey@3 116
jlaskey@3 117 /** Map to property information and accessor functions. Ordered by insertion. */
jlaskey@3 118 private PropertyMap map;
jlaskey@3 119
jlaskey@242 120 /** objects proto. */
jlaskey@242 121 private ScriptObject proto;
jlaskey@242 122
jlaskey@242 123 /** Context of the object, lazily cached. */
jlaskey@242 124 private Context context;
jlaskey@242 125
jlaskey@3 126 /** Object flags. */
jlaskey@3 127 private int flags;
jlaskey@3 128
jlaskey@242 129 /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
jlaskey@3 130 public Object[] spill;
jlaskey@3 131
jlaskey@3 132 /** Indexed array data. */
jlaskey@3 133 private ArrayData arrayData;
jlaskey@3 134
sundar@537 135 static final MethodHandle GETPROTO = findOwnMH("getProto", ScriptObject.class);
sundar@537 136 static final MethodHandle SETPROTOCHECK = findOwnMH("setProtoCheck", void.class, Object.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@334 394 final int index = ArrayIndex.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 */
jlaskey@3 597 protected final void defineOwnProperty(final int index, final Object value) {
hannesw@334 598 assert ArrayIndex.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@334 608 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 609
hannesw@334 610 if (ArrayIndex.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@334 620 final int index = ArrayIndex.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 /**
jlaskey@3 723 * Add a new property to the object.
jlaskey@3 724 * <p>
jlaskey@3 725 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
jlaskey@3 726 *
jlaskey@3 727 * @param key Property key.
jlaskey@3 728 * @param propertyFlags Property flags.
jlaskey@3 729 * @param getter Property getter, or null if not defined
jlaskey@3 730 * @param setter Property setter, or null if not defined
jlaskey@3 731 *
jlaskey@3 732 * @return New property.
jlaskey@3 733 */
jlaskey@3 734 public final Property addOwnProperty(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
jlaskey@3 735 return addOwnProperty(newUserAccessors(key, propertyFlags, getter, setter));
jlaskey@3 736 }
jlaskey@3 737
jlaskey@3 738 /**
jlaskey@3 739 * Add a new property to the object.
jlaskey@3 740 * <p>
jlaskey@3 741 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
jlaskey@3 742 *
jlaskey@3 743 * @param key Property key.
jlaskey@3 744 * @param propertyFlags Property flags.
jlaskey@3 745 * @param value Value of property
jlaskey@3 746 *
jlaskey@3 747 * @return New property.
jlaskey@3 748 */
jlaskey@3 749 public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
hannesw@291 750 final Property property = addSpillProperty(key, propertyFlags);
hannesw@291 751 property.setObjectValue(this, this, value, false);
hannesw@291 752 return property;
jlaskey@3 753 }
jlaskey@3 754
jlaskey@3 755 /**
jlaskey@3 756 * Add a new property to the object.
jlaskey@3 757 * <p>
jlaskey@3 758 * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
jlaskey@3 759 *
jlaskey@3 760 * @param newProperty property to add
jlaskey@3 761 *
jlaskey@3 762 * @return New property.
jlaskey@3 763 */
jlaskey@3 764 public final Property addOwnProperty(final Property newProperty) {
jlaskey@3 765 PropertyMap oldMap = getMap();
jlaskey@3 766
jlaskey@3 767 while (true) {
jlaskey@3 768 final PropertyMap newMap = oldMap.addProperty(newProperty);
jlaskey@3 769
jlaskey@3 770 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 771 oldMap = getMap();
jlaskey@3 772 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
jlaskey@3 773
jlaskey@3 774 if (oldProperty != null) {
jlaskey@3 775 return oldProperty;
jlaskey@3 776 }
jlaskey@3 777 } else {
jlaskey@3 778 return newProperty;
jlaskey@3 779 }
jlaskey@3 780 }
jlaskey@3 781 }
jlaskey@3 782
jlaskey@3 783 private void erasePropertyValue(final Property property) {
jlaskey@3 784 // Erase the property field value with undefined. If the property is defined
jlaskey@3 785 // by user-defined accessors, we don't want to call the setter!!
jlaskey@3 786 if (!(property instanceof UserAccessorProperty)) {
hannesw@291 787 property.setObjectValue(this, this, UNDEFINED, false);
jlaskey@3 788 }
jlaskey@3 789 }
jlaskey@3 790
jlaskey@3 791 /**
jlaskey@3 792 * Delete a property from the object.
jlaskey@3 793 *
jlaskey@3 794 * @param property Property to delete.
jlaskey@3 795 *
jlaskey@3 796 * @return true if deleted.
jlaskey@3 797 */
jlaskey@3 798 public final boolean deleteOwnProperty(final Property property) {
jlaskey@3 799 erasePropertyValue(property);
jlaskey@3 800 PropertyMap oldMap = getMap();
jlaskey@3 801
jlaskey@3 802 while (true) {
jlaskey@3 803 final PropertyMap newMap = oldMap.deleteProperty(property);
jlaskey@3 804
jlaskey@3 805 if (newMap == null) {
jlaskey@3 806 return false;
jlaskey@3 807 }
jlaskey@3 808
jlaskey@3 809 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 810 oldMap = getMap();
jlaskey@3 811 } else {
jlaskey@3 812 // delete getter and setter function references so that we don't leak
jlaskey@3 813 if (property instanceof UserAccessorProperty) {
jlaskey@3 814 final UserAccessorProperty uc = (UserAccessorProperty) property;
jlaskey@242 815 setSpill(uc.getGetterSlot(), null);
jlaskey@242 816 setSpill(uc.getSetterSlot(), null);
jlaskey@3 817 }
jlaskey@3 818 return true;
jlaskey@3 819 }
jlaskey@3 820 }
jlaskey@3 821 }
jlaskey@3 822
jlaskey@3 823 /**
jlaskey@3 824 * Modify a property in the object
jlaskey@3 825 *
jlaskey@3 826 * @param oldProperty property to modify
jlaskey@3 827 * @param propertyFlags new property flags
jlaskey@3 828 * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A
jlaskey@3 829 * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
jlaskey@3 830 *
jlaskey@3 831 * @return new property
jlaskey@3 832 */
jlaskey@3 833 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
jlaskey@3 834 Property newProperty;
jlaskey@3 835 if (oldProperty instanceof UserAccessorProperty) {
jlaskey@3 836 final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
jlaskey@379 837 final int getterSlot = uc.getGetterSlot();
jlaskey@379 838 final int setterSlot = uc.getSetterSlot();
jlaskey@242 839 setSpill(getterSlot, getter);
jlaskey@242 840 setSpill(setterSlot, setter);
jlaskey@379 841
jlaskey@3 842 // if just flipping getter and setter with new functions, no need to change property or map
jlaskey@379 843 if (uc.flags == propertyFlags) {
jlaskey@3 844 return oldProperty;
jlaskey@3 845 }
jlaskey@379 846
jlaskey@379 847 newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
jlaskey@3 848 } else {
jlaskey@3 849 // erase old property value and create new user accessor property
jlaskey@3 850 erasePropertyValue(oldProperty);
jlaskey@3 851 newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
jlaskey@3 852 }
jlaskey@3 853
jlaskey@3 854 notifyPropertyModified(this, oldProperty, newProperty);
jlaskey@3 855
jlaskey@3 856 return modifyOwnProperty(oldProperty, newProperty);
jlaskey@3 857 }
jlaskey@3 858
jlaskey@3 859 /**
jlaskey@3 860 * Modify a property in the object
jlaskey@3 861 *
jlaskey@3 862 * @param oldProperty property to modify
jlaskey@3 863 * @param propertyFlags new property flags
jlaskey@3 864 *
jlaskey@3 865 * @return new property
jlaskey@3 866 */
jlaskey@3 867 public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags) {
jlaskey@3 868 return modifyOwnProperty(oldProperty, oldProperty.setFlags(propertyFlags));
jlaskey@3 869 }
jlaskey@3 870
jlaskey@3 871 /**
jlaskey@3 872 * Modify a property in the object, replacing a property with a new one
jlaskey@3 873 *
jlaskey@3 874 * @param oldProperty property to replace
jlaskey@3 875 * @param newProperty property to replace it with
jlaskey@3 876 *
jlaskey@3 877 * @return new property
jlaskey@3 878 */
jlaskey@3 879 private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
jlaskey@3 880 assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
jlaskey@3 881
jlaskey@3 882 PropertyMap oldMap = getMap();
jlaskey@3 883
jlaskey@3 884 while (true) {
jlaskey@3 885 final PropertyMap newMap = oldMap.replaceProperty(oldProperty, newProperty);
jlaskey@3 886
jlaskey@3 887 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 888 oldMap = getMap();
jlaskey@3 889 final Property oldPropertyLookup = oldMap.findProperty(oldProperty.getKey());
jlaskey@3 890
jlaskey@3 891 if (oldPropertyLookup != null && oldPropertyLookup.equals(newProperty)) {
jlaskey@3 892 return oldPropertyLookup;
jlaskey@3 893 }
jlaskey@3 894 } else {
jlaskey@3 895 return newProperty;
jlaskey@3 896 }
jlaskey@3 897 }
jlaskey@3 898 }
jlaskey@3 899
jlaskey@3 900 /**
jlaskey@3 901 * Update getter and setter in an object literal.
jlaskey@3 902 *
jlaskey@3 903 * @param key Property key.
jlaskey@3 904 * @param getter {@link UserAccessorProperty} defined getter, or null if none
jlaskey@3 905 * @param setter {@link UserAccessorProperty} defined setter, or null if none
jlaskey@3 906 */
jlaskey@3 907 public final void setUserAccessors(final String key, final ScriptFunction getter, final ScriptFunction setter) {
jlaskey@3 908 final Property oldProperty = getMap().findProperty(key);
jlaskey@379 909 if (oldProperty instanceof UserAccessorProperty) {
jlaskey@382 910 modifyOwnProperty(oldProperty, oldProperty.getFlags(), getter, setter);
jlaskey@3 911 } else {
jlaskey@379 912 addOwnProperty(newUserAccessors(key, oldProperty != null ? oldProperty.getFlags() : 0, getter, setter));
jlaskey@3 913 }
jlaskey@3 914 }
jlaskey@3 915
jlaskey@3 916 private static int getIntValue(final FindProperty find) {
jlaskey@3 917 final MethodHandle getter = find.getGetter(int.class);
jlaskey@3 918 if (getter != null) {
jlaskey@3 919 try {
jlaskey@212 920 return (int)getter.invokeExact((Object)find.getGetterReceiver());
jlaskey@3 921 } catch (final Error|RuntimeException e) {
jlaskey@3 922 throw e;
jlaskey@3 923 } catch (final Throwable e) {
jlaskey@3 924 throw new RuntimeException(e);
jlaskey@3 925 }
jlaskey@3 926 }
jlaskey@3 927
jlaskey@3 928 return ObjectClassGenerator.UNDEFINED_INT;
jlaskey@3 929 }
jlaskey@3 930
jlaskey@3 931 private static long getLongValue(final FindProperty find) {
jlaskey@3 932 final MethodHandle getter = find.getGetter(long.class);
jlaskey@3 933 if (getter != null) {
jlaskey@3 934 try {
jlaskey@212 935 return (long)getter.invokeExact((Object)find.getGetterReceiver());
jlaskey@3 936 } catch (final Error|RuntimeException e) {
jlaskey@3 937 throw e;
jlaskey@3 938 } catch (final Throwable e) {
jlaskey@3 939 throw new RuntimeException(e);
jlaskey@3 940 }
jlaskey@3 941 }
jlaskey@3 942
jlaskey@3 943 return ObjectClassGenerator.UNDEFINED_LONG;
jlaskey@3 944 }
jlaskey@3 945
jlaskey@3 946 private static double getDoubleValue(final FindProperty find) {
jlaskey@3 947 final MethodHandle getter = find.getGetter(double.class);
jlaskey@3 948 if (getter != null) {
jlaskey@3 949 try {
jlaskey@212 950 return (double)getter.invokeExact((Object)find.getGetterReceiver());
jlaskey@3 951 } catch (final Error|RuntimeException e) {
jlaskey@3 952 throw e;
jlaskey@3 953 } catch (final Throwable e) {
jlaskey@3 954 throw new RuntimeException(e);
jlaskey@3 955 }
jlaskey@3 956 }
jlaskey@3 957
jlaskey@3 958 return ObjectClassGenerator.UNDEFINED_DOUBLE;
jlaskey@3 959 }
jlaskey@3 960
jlaskey@3 961 /**
jlaskey@3 962 * Get the object value of a property
jlaskey@3 963 *
jlaskey@3 964 * @param find {@link FindProperty} lookup result
jlaskey@3 965 *
jlaskey@3 966 * @return the value of the property
jlaskey@3 967 */
jlaskey@3 968 protected static Object getObjectValue(final FindProperty find) {
hannesw@291 969 return find.getObjectValue();
jlaskey@3 970 }
jlaskey@3 971
jlaskey@3 972 /**
jlaskey@3 973 * Return methodHandle of value function for call.
jlaskey@3 974 *
jlaskey@3 975 * @param find data from find property.
jlaskey@3 976 * @param type method type of function.
jlaskey@3 977 * @param bindName null or name to bind to second argument (property not found method.)
jlaskey@3 978 *
jlaskey@3 979 * @return value of property as a MethodHandle or null.
jlaskey@3 980 */
jlaskey@3 981 protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
jlaskey@3 982 return getCallMethodHandle(getObjectValue(find), type, bindName);
jlaskey@3 983 }
jlaskey@3 984
jlaskey@3 985 /**
jlaskey@3 986 * Return methodHandle of value function for call.
jlaskey@3 987 *
jlaskey@3 988 * @param value value of receiver, it not a {@link ScriptFunction} this will return null.
jlaskey@3 989 * @param type method type of function.
jlaskey@3 990 * @param bindName null or name to bind to second argument (property not found method.)
jlaskey@3 991 *
jlaskey@3 992 * @return value of property as a MethodHandle or null.
jlaskey@3 993 */
jlaskey@3 994 protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
jlaskey@3 995 return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
jlaskey@3 996 }
jlaskey@3 997
jlaskey@3 998 /**
jlaskey@3 999 * Get value using found property.
jlaskey@3 1000 *
jlaskey@3 1001 * @param property Found property.
jlaskey@3 1002 *
jlaskey@3 1003 * @return Value of property.
jlaskey@3 1004 */
jlaskey@3 1005 public final Object getWithProperty(final Property property) {
jlaskey@80 1006 return getObjectValue(new FindProperty(this, this, property));
jlaskey@3 1007 }
jlaskey@3 1008
jlaskey@3 1009 /**
jlaskey@3 1010 * Get a property given a key
jlaskey@3 1011 *
jlaskey@3 1012 * @param key property key
jlaskey@3 1013 *
jlaskey@3 1014 * @return property for key
jlaskey@3 1015 */
jlaskey@3 1016 public final Property getProperty(final String key) {
jlaskey@3 1017 return getMap().findProperty(key);
jlaskey@3 1018 }
jlaskey@3 1019
jlaskey@3 1020 /**
jlaskey@3 1021 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
jlaskey@3 1022 * Used for argument access in a vararg function using parameter name.
jlaskey@3 1023 * Returns the argument at a given key (index)
jlaskey@3 1024 *
jlaskey@3 1025 * @param key argument index
jlaskey@3 1026 *
jlaskey@3 1027 * @return the argument at the given position, or undefined if not present
jlaskey@3 1028 */
jlaskey@3 1029 public Object getArgument(final int key) {
jlaskey@3 1030 return get(key);
jlaskey@3 1031 }
jlaskey@3 1032
jlaskey@3 1033 /**
jlaskey@3 1034 * Overridden by {@link jdk.nashorn.internal.objects.NativeArguments} class (internal use.)
jlaskey@3 1035 * Used for argument access in a vararg function using parameter name.
jlaskey@3 1036 * Returns the argument at a given key (index)
jlaskey@3 1037 *
jlaskey@3 1038 * @param key argument index
jlaskey@3 1039 * @param value the value to write at the given index
jlaskey@3 1040 */
jlaskey@3 1041 public void setArgument(final int key, final Object value) {
sundar@344 1042 set(key, value, false);
jlaskey@3 1043 }
jlaskey@3 1044
lagergren@57 1045 /**
lagergren@57 1046 * Return true if the script object context is strict
lagergren@57 1047 * @return true if strict context
lagergren@57 1048 */
sundar@41 1049 public final boolean isStrictContext() {
sundar@41 1050 return getContext()._strict;
sundar@41 1051 }
sundar@41 1052
jlaskey@3 1053 /**
sundar@472 1054 * Checks if this object belongs to the given context
sundar@472 1055 * @param ctx context to check against
sundar@472 1056 * @return true if this object belongs to the given context
sundar@472 1057 */
sundar@472 1058 public final boolean isOfContext(final Context ctx) {
sundar@472 1059 return context == ctx;
sundar@472 1060 }
sundar@472 1061
sundar@472 1062 /**
jlaskey@3 1063 * Return the current context from the object's map.
jlaskey@3 1064 * @return Current context.
jlaskey@3 1065 */
jlaskey@242 1066 protected final Context getContext() {
jlaskey@242 1067 if (context == null) {
jlaskey@242 1068 context = Context.fromClass(getClass());
jlaskey@242 1069 }
jlaskey@242 1070 return context;
jlaskey@3 1071 }
jlaskey@3 1072
jlaskey@3 1073 /**
sundar@414 1074 * Set the current context.
sundar@414 1075 * @param ctx context instance to set
sundar@414 1076 */
sundar@414 1077 protected final void setContext(final Context ctx) {
sundar@414 1078 ctx.getClass();
sundar@414 1079 this.context = ctx;
sundar@414 1080 }
sundar@414 1081
sundar@414 1082 /**
jlaskey@3 1083 * Return the map of an object.
jlaskey@3 1084 * @return PropertyMap object.
jlaskey@3 1085 */
jlaskey@3 1086 public final PropertyMap getMap() {
jlaskey@3 1087 return map;
jlaskey@3 1088 }
jlaskey@3 1089
jlaskey@3 1090 /**
jlaskey@3 1091 * Set the initial map.
jlaskey@3 1092 * @param map Initial map.
jlaskey@3 1093 */
jlaskey@3 1094 public final void setMap(final PropertyMap map) {
jlaskey@3 1095 this.map = map;
jlaskey@3 1096 }
jlaskey@3 1097
jlaskey@3 1098 /**
jlaskey@3 1099 * Conditionally set the new map if the old map is the same.
jlaskey@3 1100 * @param oldMap Map prior to manipulation.
jlaskey@3 1101 * @param newMap Replacement map.
jlaskey@3 1102 * @return true if the operation succeeded.
jlaskey@3 1103 */
jlaskey@3 1104 protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
jlaskey@3 1105 final boolean update = oldMap == this.map;
jlaskey@3 1106
jlaskey@3 1107 if (update) {
jlaskey@3 1108 this.map = newMap;
jlaskey@3 1109 }
jlaskey@3 1110
jlaskey@3 1111 return update;
jlaskey@3 1112 }
jlaskey@3 1113
jlaskey@3 1114 /**
jlaskey@3 1115 * Return the __proto__ of an object.
jlaskey@3 1116 * @return __proto__ object.
jlaskey@3 1117 */
jlaskey@3 1118 public final ScriptObject getProto() {
jlaskey@242 1119 return proto;
jlaskey@3 1120 }
jlaskey@3 1121
jlaskey@3 1122 /**
jlaskey@3 1123 * Set the __proto__ of an object.
jlaskey@3 1124 * @param newProto new __proto__ to set.
jlaskey@3 1125 */
jlaskey@242 1126 public synchronized final void setProto(final ScriptObject newProto) {
jlaskey@242 1127 final ScriptObject oldProto = proto;
jlaskey@242 1128 map = map.changeProto(oldProto, newProto);
jlaskey@242 1129
jlaskey@242 1130 if (newProto != null) {
jlaskey@242 1131 newProto.setIsPrototype();
jlaskey@242 1132 }
jlaskey@242 1133
jlaskey@242 1134 proto = newProto;
jlaskey@242 1135
jlaskey@242 1136 if (isPrototype()) {
sundar@513 1137 // tell listeners that my __proto__ has been changed
sundar@513 1138 notifyProtoChanged(this, oldProto, newProto);
sundar@513 1139
jlaskey@242 1140 if (oldProto != null) {
jlaskey@242 1141 oldProto.removePropertyListener(this);
jlaskey@242 1142 }
jlaskey@242 1143
jlaskey@242 1144 if (newProto != null) {
jlaskey@242 1145 newProto.addPropertyListener(this);
jlaskey@3 1146 }
jlaskey@3 1147 }
jlaskey@3 1148 }
jlaskey@3 1149
jlaskey@3 1150 /**
jlaskey@3 1151 * Set the __proto__ of an object with checks.
jlaskey@3 1152 * @param newProto Prototype to set.
jlaskey@3 1153 */
jlaskey@3 1154 public final void setProtoCheck(final Object newProto) {
sundar@513 1155 if (!isExtensible()) {
sundar@513 1156 throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
sundar@513 1157 }
sundar@513 1158
jlaskey@3 1159 if (newProto == null || newProto instanceof ScriptObject) {
sundar@513 1160 // check for circularity
lagergren@524 1161 ScriptObject p = (ScriptObject)newProto;
lagergren@524 1162 while (p != null) {
lagergren@524 1163 if (p == this) {
sundar@513 1164 throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
sundar@513 1165 }
lagergren@524 1166 p = p.getProto();
sundar@513 1167 }
jlaskey@3 1168 setProto((ScriptObject)newProto);
jlaskey@3 1169 } else {
sundar@44 1170 final ScriptObject global = Context.getGlobalTrusted();
sundar@44 1171 final Object newProtoObject = JSType.toScriptObject(global, newProto);
jlaskey@3 1172
jlaskey@3 1173 if (newProtoObject instanceof ScriptObject) {
jlaskey@3 1174 setProto((ScriptObject)newProtoObject);
jlaskey@3 1175 } else {
lagergren@112 1176 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
jlaskey@3 1177 }
jlaskey@3 1178 }
jlaskey@3 1179 }
jlaskey@3 1180
jlaskey@3 1181 /**
sundar@350 1182 * return an array of own property keys associated with the object.
sundar@350 1183 *
jlaskey@3 1184 * @param all True if to include non-enumerable keys.
jlaskey@3 1185 * @return Array of keys.
jlaskey@3 1186 */
jlaskey@3 1187 public String[] getOwnKeys(final boolean all) {
jlaskey@3 1188 final List<Object> keys = new ArrayList<>();
jlaskey@3 1189 final PropertyMap selfMap = this.getMap();
jlaskey@3 1190
jlaskey@3 1191 final ArrayData array = getArray();
jlaskey@3 1192 final long length = array.length();
jlaskey@3 1193
jlaskey@3 1194 for (long i = 0; i < length; i = array.nextIndex(i)) {
jlaskey@3 1195 if (array.has((int)i)) {
jlaskey@3 1196 keys.add(JSType.toString(i));
jlaskey@3 1197 }
jlaskey@3 1198 }
jlaskey@3 1199
jlaskey@3 1200 for (final Property property : selfMap.getProperties()) {
jlaskey@3 1201 if (all || property.isEnumerable()) {
jlaskey@3 1202 keys.add(property.getKey());
jlaskey@3 1203 }
jlaskey@3 1204 }
jlaskey@3 1205
jlaskey@3 1206 return keys.toArray(new String[keys.size()]);
jlaskey@3 1207 }
jlaskey@3 1208
jlaskey@3 1209 /**
jlaskey@3 1210 * Check if this ScriptObject has array entries. This means that someone has
jlaskey@3 1211 * set values with numeric keys in the object.
jlaskey@3 1212 *
jlaskey@3 1213 * Note: this can be O(n) up to the array length
jlaskey@3 1214 *
jlaskey@3 1215 * @return true if array entries exists.
jlaskey@3 1216 */
jlaskey@3 1217 public boolean hasArrayEntries() {
jlaskey@3 1218 final ArrayData array = getArray();
jlaskey@3 1219 final long length = array.length();
jlaskey@3 1220
jlaskey@3 1221 for (long i = 0; i < length; i++) {
jlaskey@3 1222 if (array.has((int)i)) {
jlaskey@3 1223 return true;
jlaskey@3 1224 }
jlaskey@3 1225 }
jlaskey@3 1226
jlaskey@3 1227 return false;
jlaskey@3 1228 }
jlaskey@3 1229
jlaskey@3 1230 /**
jlaskey@3 1231 * Return the valid JavaScript type name descriptor
jlaskey@3 1232 *
jlaskey@3 1233 * @return "Object"
jlaskey@3 1234 */
jlaskey@3 1235 public String getClassName() {
jlaskey@3 1236 return "Object";
jlaskey@3 1237 }
jlaskey@3 1238
jlaskey@3 1239 /**
jlaskey@3 1240 * {@code length} is a well known property. This is its getter.
jlaskey@3 1241 * Note that this *may* be optimized by other classes
jlaskey@3 1242 *
jlaskey@3 1243 * @return length property value for this ScriptObject
jlaskey@3 1244 */
jlaskey@3 1245 public Object getLength() {
jlaskey@3 1246 return get("length");
jlaskey@3 1247 }
jlaskey@3 1248
jlaskey@3 1249 /**
jlaskey@3 1250 * Stateless toString for ScriptObjects.
jlaskey@3 1251 *
jlaskey@3 1252 * @return string description of this object, e.g. {@code [object Object]}
jlaskey@3 1253 */
jlaskey@3 1254 public String safeToString() {
jlaskey@3 1255 return "[object " + getClassName() + "]";
jlaskey@3 1256 }
jlaskey@3 1257
jlaskey@3 1258 /**
jlaskey@3 1259 * Return the default value of the object with a given preferred type hint.
jlaskey@3 1260 * The preferred type hints are String.class for type String, Number.class
jlaskey@3 1261 * for type Number. <p>
jlaskey@3 1262 *
jlaskey@3 1263 * A <code>hint</code> of null means "no hint".
jlaskey@3 1264 *
jlaskey@3 1265 * ECMA 8.12.8 [[DefaultValue]](hint)
jlaskey@3 1266 *
jlaskey@3 1267 * @param typeHint the preferred type hint
jlaskey@3 1268 * @return the default value
jlaskey@3 1269 */
jlaskey@3 1270 public Object getDefaultValue(final Class<?> typeHint) {
jlaskey@3 1271 // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
jlaskey@3 1272 // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
jlaskey@3 1273 // are being executed in a long-running program, we move the code and their associated dynamic call sites
jlaskey@3 1274 // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
sundar@44 1275 return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
jlaskey@3 1276 }
jlaskey@3 1277
jlaskey@3 1278 /**
jlaskey@3 1279 * Checking whether a script object is an instance of another. Used
jlaskey@3 1280 * in {@link ScriptFunction} for hasInstance implementation, walks
jlaskey@3 1281 * the proto chain
jlaskey@3 1282 *
jlaskey@3 1283 * @param instance instace to check
sundar@350 1284 * @return true if 'instance' is an instance of this object
jlaskey@3 1285 */
jlaskey@3 1286 public boolean isInstance(final ScriptObject instance) {
jlaskey@3 1287 return false;
jlaskey@3 1288 }
jlaskey@3 1289
jlaskey@3 1290 /**
jlaskey@3 1291 * Flag this ScriptObject as non extensible
jlaskey@3 1292 *
jlaskey@3 1293 * @return the object after being made non extensible
jlaskey@3 1294 */
jlaskey@3 1295 public ScriptObject preventExtensions() {
jlaskey@3 1296 PropertyMap oldMap = getMap();
jlaskey@3 1297
jlaskey@3 1298 while (true) {
jlaskey@3 1299 final PropertyMap newMap = getMap().preventExtensions();
jlaskey@3 1300
jlaskey@3 1301 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1302 oldMap = getMap();
jlaskey@3 1303 } else {
jlaskey@3 1304 return this;
jlaskey@3 1305 }
jlaskey@3 1306 }
jlaskey@3 1307 }
jlaskey@3 1308
jlaskey@3 1309 /**
jlaskey@3 1310 * Check whether if an Object (not just a ScriptObject) represents JavaScript array
jlaskey@3 1311 *
jlaskey@3 1312 * @param obj object to check
jlaskey@3 1313 *
jlaskey@3 1314 * @return true if array
jlaskey@3 1315 */
jlaskey@3 1316 public static boolean isArray(final Object obj) {
jlaskey@3 1317 return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
jlaskey@3 1318 }
jlaskey@3 1319
jlaskey@3 1320 /**
jlaskey@3 1321 * Check if this ScriptObject is an array
jlaskey@3 1322 * @return true if array
jlaskey@3 1323 */
jlaskey@3 1324 public final boolean isArray() {
jlaskey@3 1325 return (flags & IS_ARRAY) != 0;
jlaskey@3 1326 }
jlaskey@3 1327
jlaskey@3 1328 /**
jlaskey@3 1329 * Flag this ScriptObject as being an array
jlaskey@3 1330 */
jlaskey@3 1331 public final void setIsArray() {
jlaskey@3 1332 flags |= IS_ARRAY;
jlaskey@3 1333 }
jlaskey@3 1334
jlaskey@3 1335 /**
jlaskey@3 1336 * Check if this ScriptObject is an {@code arguments} vector
jlaskey@3 1337 * @return true if arguments vector
jlaskey@3 1338 */
jlaskey@3 1339 public final boolean isArguments() {
jlaskey@3 1340 return (flags & IS_ARGUMENTS) != 0;
jlaskey@3 1341 }
jlaskey@3 1342
jlaskey@3 1343 /**
jlaskey@3 1344 * Flag this ScriptObject as being an {@code arguments} vector
jlaskey@3 1345 */
jlaskey@3 1346 public final void setIsArguments() {
jlaskey@3 1347 flags |= IS_ARGUMENTS;
jlaskey@3 1348 }
jlaskey@3 1349
jlaskey@3 1350 /**
jlaskey@242 1351 * Check if this object is a prototype
jlaskey@242 1352 *
jlaskey@242 1353 * @return {@code true} if is prototype
jlaskey@242 1354 */
sundar@344 1355 public final boolean isPrototype() {
jlaskey@242 1356 return (flags & IS_PROTOTYPE) != 0;
jlaskey@242 1357 }
jlaskey@242 1358
jlaskey@242 1359 /**
jlaskey@242 1360 * Flag this object as having a prototype.
jlaskey@242 1361 */
sundar@344 1362 public final void setIsPrototype() {
hannesw@243 1363 if (proto != null && !isPrototype()) {
hannesw@243 1364 proto.addPropertyListener(this);
hannesw@243 1365 }
jlaskey@242 1366 flags |= IS_PROTOTYPE;
jlaskey@242 1367 }
jlaskey@242 1368
jlaskey@242 1369 /**
sundar@344 1370 * Check if this object has non-writable length property
sundar@344 1371 *
sundar@344 1372 * @return {@code true} if 'length' property is non-writable
sundar@344 1373 */
sundar@344 1374 public final boolean isLengthNotWritable() {
sundar@344 1375 return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
sundar@344 1376 }
sundar@344 1377
sundar@344 1378 /**
sundar@344 1379 * Flag this object as having non-writable length property
sundar@344 1380 */
sundar@344 1381 public void setIsLengthNotWritable() {
sundar@344 1382 flags |= IS_LENGTH_NOT_WRITABLE;
sundar@344 1383 }
sundar@344 1384
sundar@344 1385 /**
jlaskey@3 1386 * Get the {@link ArrayData} for this ScriptObject if it is an array
jlaskey@3 1387 * @return array data
jlaskey@3 1388 */
jlaskey@3 1389 public final ArrayData getArray() {
jlaskey@3 1390 return arrayData;
jlaskey@3 1391 }
jlaskey@3 1392
jlaskey@3 1393 /**
jlaskey@3 1394 * Set the {@link ArrayData} for this ScriptObject if it is to be an array
jlaskey@3 1395 * @param arrayData the array data
jlaskey@3 1396 */
jlaskey@3 1397 public final void setArray(final ArrayData arrayData) {
jlaskey@3 1398 this.arrayData = arrayData;
jlaskey@3 1399 }
jlaskey@3 1400
jlaskey@3 1401 /**
jlaskey@3 1402 * Check if this ScriptObject is extensible
jlaskey@3 1403 * @return true if extensible
jlaskey@3 1404 */
jlaskey@3 1405 public boolean isExtensible() {
jlaskey@3 1406 return getMap().isExtensible();
jlaskey@3 1407 }
jlaskey@3 1408
jlaskey@3 1409 /**
jlaskey@3 1410 * ECMAScript 15.2.3.8 - seal implementation
jlaskey@3 1411 * @return the sealed ScriptObject
jlaskey@3 1412 */
jlaskey@3 1413 public ScriptObject seal() {
jlaskey@3 1414 PropertyMap oldMap = getMap();
jlaskey@3 1415
jlaskey@3 1416 while (true) {
jlaskey@3 1417 final PropertyMap newMap = getMap().seal();
jlaskey@3 1418
jlaskey@3 1419 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1420 oldMap = getMap();
jlaskey@3 1421 } else {
jlaskey@3 1422 setArray(ArrayData.seal(getArray()));
jlaskey@3 1423 return this;
jlaskey@3 1424 }
jlaskey@3 1425 }
jlaskey@3 1426 }
jlaskey@3 1427
jlaskey@3 1428 /**
jlaskey@3 1429 * Check whether this ScriptObject is sealed
jlaskey@3 1430 * @return true if sealed
jlaskey@3 1431 */
jlaskey@3 1432 public boolean isSealed() {
jlaskey@3 1433 return getMap().isSealed();
jlaskey@3 1434 }
jlaskey@3 1435
jlaskey@3 1436 /**
jlaskey@3 1437 * ECMA 15.2.39 - freeze implementation. Freeze this ScriptObject
jlaskey@3 1438 * @return the frozen ScriptObject
jlaskey@3 1439 */
jlaskey@3 1440 public ScriptObject freeze() {
jlaskey@3 1441 PropertyMap oldMap = getMap();
jlaskey@3 1442
jlaskey@3 1443 while (true) {
jlaskey@3 1444 final PropertyMap newMap = getMap().freeze();
jlaskey@3 1445
jlaskey@3 1446 if (!compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1447 oldMap = getMap();
jlaskey@3 1448 } else {
jlaskey@3 1449 setArray(ArrayData.freeze(getArray()));
jlaskey@3 1450 return this;
jlaskey@3 1451 }
jlaskey@3 1452 }
jlaskey@3 1453 }
jlaskey@3 1454
jlaskey@3 1455 /**
jlaskey@3 1456 * Check whether this ScriptObject is frozen
sundar@350 1457 * @return true if frozen
jlaskey@3 1458 */
jlaskey@3 1459 public boolean isFrozen() {
jlaskey@3 1460 return getMap().isFrozen();
jlaskey@3 1461 }
jlaskey@3 1462
jlaskey@3 1463
jlaskey@3 1464 /**
jlaskey@3 1465 * Flag this ScriptObject as scope
jlaskey@3 1466 */
jlaskey@3 1467 public final void setIsScope() {
jlaskey@3 1468 if (Context.DEBUG) {
jlaskey@3 1469 scopeCount++;
jlaskey@3 1470 }
jlaskey@3 1471 flags |= IS_SCOPE;
jlaskey@3 1472 }
jlaskey@3 1473
jlaskey@3 1474 /**
jlaskey@3 1475 * Check whether this ScriptObject is scope
jlaskey@3 1476 * @return true if scope
jlaskey@3 1477 */
jlaskey@3 1478 public final boolean isScope() {
jlaskey@3 1479 return (flags & IS_SCOPE) != 0;
jlaskey@3 1480 }
jlaskey@3 1481
lagergren@57 1482 /**
lagergren@57 1483 * Clears the properties from a ScriptObject
lagergren@57 1484 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1485 */
jlaskey@3 1486 public void clear() {
sundar@344 1487 final boolean strict = isStrictContext();
jlaskey@3 1488 final Iterator<String> iter = propertyIterator();
jlaskey@3 1489 while (iter.hasNext()) {
jlaskey@3 1490 delete(iter.next(), strict);
jlaskey@3 1491 }
jlaskey@3 1492 }
jlaskey@3 1493
lagergren@57 1494 /**
lagergren@57 1495 * Checks if a property with a given key is present in a ScriptObject
lagergren@57 1496 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1497 *
lagergren@57 1498 * @param key the key to check for
lagergren@57 1499 * @return true if a property with the given key exists, false otherwise
lagergren@57 1500 */
jlaskey@3 1501 public boolean containsKey(final Object key) {
jlaskey@3 1502 return has(key);
jlaskey@3 1503 }
jlaskey@3 1504
lagergren@57 1505 /**
lagergren@57 1506 * Checks if a property with a given value is present in a ScriptObject
lagergren@57 1507 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1508 *
lagergren@57 1509 * @param value value to check for
lagergren@57 1510 * @return true if a property with the given value exists, false otherwise
lagergren@57 1511 */
jlaskey@3 1512 public boolean containsValue(final Object value) {
jlaskey@3 1513 final Iterator<Object> iter = valueIterator();
jlaskey@3 1514 while (iter.hasNext()) {
jlaskey@3 1515 if (iter.next().equals(value)) {
jlaskey@3 1516 return true;
jlaskey@3 1517 }
jlaskey@3 1518 }
jlaskey@3 1519 return false;
jlaskey@3 1520 }
jlaskey@3 1521
lagergren@57 1522 /**
sundar@128 1523 * Returns the set of {@literal <property, value>} entries that make up this
lagergren@57 1524 * ScriptObject's properties
lagergren@57 1525 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1526 *
lagergren@57 1527 * @return an entry set of all the properties in this object
lagergren@57 1528 */
jlaskey@3 1529 public Set<Map.Entry<Object, Object>> entrySet() {
jlaskey@3 1530 final Iterator<String> iter = propertyIterator();
jlaskey@3 1531 final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
jlaskey@3 1532 while (iter.hasNext()) {
jlaskey@3 1533 final Object key = iter.next();
jlaskey@3 1534 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, get(key)));
jlaskey@3 1535 }
jlaskey@3 1536 return Collections.unmodifiableSet(entries);
jlaskey@3 1537 }
jlaskey@3 1538
lagergren@57 1539 /**
lagergren@57 1540 * Check whether a ScriptObject contains no properties
lagergren@57 1541 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1542 *
lagergren@57 1543 * @return true if object has no properties
lagergren@57 1544 */
jlaskey@3 1545 public boolean isEmpty() {
jlaskey@3 1546 return !propertyIterator().hasNext();
jlaskey@3 1547 }
jlaskey@3 1548
lagergren@57 1549 /**
lagergren@57 1550 * Return the set of keys (property names) for all properties
lagergren@57 1551 * in this ScriptObject
lagergren@57 1552 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1553 *
lagergren@57 1554 * @return keySet of this ScriptObject
lagergren@57 1555 */
jlaskey@3 1556 public Set<Object> keySet() {
jlaskey@3 1557 final Iterator<String> iter = propertyIterator();
jlaskey@3 1558 final Set<Object> keySet = new HashSet<>();
jlaskey@3 1559 while (iter.hasNext()) {
jlaskey@3 1560 keySet.add(iter.next());
jlaskey@3 1561 }
jlaskey@3 1562 return Collections.unmodifiableSet(keySet);
jlaskey@3 1563 }
jlaskey@3 1564
lagergren@57 1565 /**
lagergren@57 1566 * Put a property in the ScriptObject
lagergren@57 1567 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1568 *
lagergren@57 1569 * @param key property key
lagergren@57 1570 * @param value property value
lagergren@57 1571 * @return oldValue if property with same key existed already
lagergren@57 1572 */
jlaskey@3 1573 public Object put(final Object key, final Object value) {
jlaskey@3 1574 final Object oldValue = get(key);
sundar@344 1575 set(key, value, isStrictContext());
jlaskey@3 1576 return oldValue;
jlaskey@3 1577 }
jlaskey@3 1578
lagergren@57 1579 /**
lagergren@57 1580 * Put several properties in the ScriptObject given a mapping
lagergren@57 1581 * of their keys to their values
lagergren@57 1582 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1583 *
sundar@128 1584 * @param otherMap a {@literal <key,value>} map of properties to add
lagergren@57 1585 */
jlaskey@3 1586 public void putAll(final Map<?, ?> otherMap) {
sundar@344 1587 final boolean strict = isStrictContext();
jlaskey@3 1588 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
jlaskey@3 1589 set(entry.getKey(), entry.getValue(), strict);
jlaskey@3 1590 }
jlaskey@3 1591 }
jlaskey@3 1592
lagergren@57 1593 /**
lagergren@57 1594 * Remove a property from the ScriptObject.
lagergren@57 1595 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1596 *
lagergren@57 1597 * @param key the key of the property
lagergren@57 1598 * @return the oldValue of the removed property
lagergren@57 1599 */
jlaskey@3 1600 public Object remove(final Object key) {
jlaskey@3 1601 final Object oldValue = get(key);
sundar@344 1602 delete(key, isStrictContext());
jlaskey@3 1603 return oldValue;
jlaskey@3 1604 }
jlaskey@3 1605
lagergren@57 1606 /**
sundar@321 1607 * Delete a property from the ScriptObject.
sundar@321 1608 * (to help ScriptObjectMirror implementation)
sundar@321 1609 *
sundar@321 1610 * @param key the key of the property
sundar@321 1611 * @return if the delete was successful or not
sundar@321 1612 */
sundar@321 1613 public boolean delete(final Object key) {
sundar@344 1614 return delete(key, isStrictContext());
sundar@321 1615 }
sundar@321 1616
sundar@321 1617 /**
lagergren@57 1618 * Return the size of the ScriptObject - i.e. the number of properties
lagergren@57 1619 * it contains
lagergren@57 1620 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1621 *
lagergren@57 1622 * @return number of properties in ScriptObject
lagergren@57 1623 */
jlaskey@3 1624 public int size() {
jlaskey@3 1625 int n = 0;
jlaskey@3 1626 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
jlaskey@3 1627 n++;
jlaskey@3 1628 }
jlaskey@3 1629 return n;
jlaskey@3 1630 }
jlaskey@3 1631
lagergren@57 1632 /**
lagergren@57 1633 * Return the values of the properties in the ScriptObject
lagergren@57 1634 * (java.util.Map-like method to help ScriptObjectMirror implementation)
lagergren@57 1635 *
lagergren@57 1636 * @return collection of values for the properties in this ScriptObject
lagergren@57 1637 */
jlaskey@3 1638 public Collection<Object> values() {
jlaskey@3 1639 final List<Object> values = new ArrayList<>(size());
jlaskey@3 1640 final Iterator<Object> iter = valueIterator();
jlaskey@3 1641 while (iter.hasNext()) {
jlaskey@3 1642 values.add(iter.next());
jlaskey@3 1643 }
jlaskey@3 1644 return Collections.unmodifiableList(values);
jlaskey@3 1645 }
jlaskey@3 1646
jlaskey@3 1647 /**
jlaskey@3 1648 * Lookup method that, given a CallSiteDescriptor, looks up the target
jlaskey@3 1649 * MethodHandle and creates a GuardedInvocation
jlaskey@3 1650 * with the appropriate guard(s).
jlaskey@3 1651 *
jlaskey@3 1652 * @param desc call site descriptor
hannesw@51 1653 * @param request the link request
jlaskey@3 1654 *
jlaskey@3 1655 * @return GuardedInvocation for the callsite
jlaskey@3 1656 */
hannesw@51 1657 public GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request) {
jlaskey@3 1658 final int c = desc.getNameTokenCount();
jlaskey@3 1659 // JavaScript is "immune" to all currently defined Dynalink composite operation - getProp is the same as getElem
jlaskey@3 1660 // is the same as getMethod as JavaScript objects have a single namespace for all three. Therefore, we don't
jlaskey@3 1661 // care about them, and just link to whatever is the first operation.
jlaskey@3 1662 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
jlaskey@3 1663 // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
jlaskey@3 1664 // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
jlaskey@3 1665 // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
jlaskey@3 1666 // operation has an associated name or not.
jlaskey@3 1667 switch (operator) {
jlaskey@3 1668 case "getProp":
jlaskey@3 1669 case "getElem":
jlaskey@3 1670 case "getMethod":
hannesw@51 1671 return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
jlaskey@3 1672 case "setProp":
jlaskey@3 1673 case "setElem":
hannesw@51 1674 return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
jlaskey@3 1675 case "call":
hannesw@51 1676 return findCallMethod(desc, request);
jlaskey@3 1677 case "new":
jlaskey@3 1678 return findNewMethod(desc);
jlaskey@3 1679 case "callMethod":
hannesw@51 1680 return findCallMethodMethod(desc, request);
jlaskey@3 1681 default:
jlaskey@3 1682 return null;
jlaskey@3 1683 }
jlaskey@3 1684 }
jlaskey@3 1685
jlaskey@3 1686 /**
jlaskey@3 1687 * Find the appropriate New method for an invoke dynamic call.
jlaskey@3 1688 *
jlaskey@3 1689 * @param desc The invoke dynamic call site descriptor.
jlaskey@3 1690 *
jlaskey@3 1691 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1692 */
jlaskey@3 1693 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
jlaskey@3 1694 return notAFunction();
jlaskey@3 1695 }
jlaskey@3 1696
jlaskey@3 1697 /**
jlaskey@3 1698 * Find the appropriate CALL method for an invoke dynamic call.
jlaskey@3 1699 * This generates "not a function" always
jlaskey@3 1700 *
hannesw@51 1701 * @param desc the call site descriptor.
hannesw@51 1702 * @param request the link request
jlaskey@3 1703 *
jlaskey@3 1704 * @return GuardedInvocation to be invoed at call site.
jlaskey@3 1705 */
hannesw@51 1706 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
jlaskey@3 1707 return notAFunction();
jlaskey@3 1708 }
jlaskey@3 1709
jlaskey@3 1710 private GuardedInvocation notAFunction() {
lagergren@112 1711 throw typeError("not.a.function", ScriptRuntime.safeToString(this));
jlaskey@3 1712 }
jlaskey@3 1713
jlaskey@3 1714 /**
attila@29 1715 * Find an implementation for a "dyn:callMethod" operation. Note that Nashorn internally never uses
attila@29 1716 * "dyn:callMethod", but instead always emits two call sites in bytecode, one for "dyn:getMethod", and then another
attila@29 1717 * one for "dyn:call". Explicit support for "dyn:callMethod" is provided for the benefit of potential external
attila@29 1718 * callers. The implementation itself actually folds a "dyn:getMethod" method handle into a "dyn:call" method handle.
jlaskey@3 1719 *
hannesw@51 1720 * @param desc the call site descriptor.
hannesw@51 1721 * @param request the link request
jlaskey@3 1722 *
jlaskey@3 1723 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1724 */
hannesw@51 1725 protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
attila@29 1726 // R(P0, P1, ...)
attila@29 1727 final MethodType callType = desc.getMethodType();
attila@29 1728 // use type Object(P0) for the getter
attila@29 1729 final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
hannesw@51 1730 final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
attila@29 1731
attila@29 1732 // Object(P0) => Object(P0, P1, ...)
attila@29 1733 final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
attila@29 1734 // R(Object, P0, P1, ...)
attila@29 1735 final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
attila@29 1736 // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
attila@29 1737 return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
jlaskey@3 1738 }
jlaskey@3 1739
jlaskey@3 1740 /**
jlaskey@3 1741 * Find the appropriate GET method for an invoke dynamic call.
jlaskey@3 1742 *
hannesw@51 1743 * @param desc the call site descriptor
hannesw@51 1744 * @param request the link request
hannesw@51 1745 * @param operator operator for get: getProp, getMethod, getElem etc
jlaskey@3 1746 *
jlaskey@3 1747 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1748 */
hannesw@51 1749 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
jlaskey@212 1750 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
jlaskey@3 1751 final FindProperty find = findProperty(name, true);
jlaskey@3 1752
jlaskey@3 1753 MethodHandle methodHandle;
jlaskey@3 1754
jlaskey@3 1755 if (find == null) {
sundar@537 1756 if (PROTO_PROPERTY_NAME.equals(name)) {
sundar@537 1757 return new GuardedInvocation(GETPROTO, NashornGuards.getScriptObjectGuard());
sundar@537 1758 }
sundar@537 1759
jlaskey@3 1760 if ("getProp".equals(operator)) {
hannesw@51 1761 return noSuchProperty(desc, request);
jlaskey@3 1762 } else if ("getMethod".equals(operator)) {
hannesw@51 1763 return noSuchMethod(desc, request);
jlaskey@3 1764 } else if ("getElem".equals(operator)) {
jlaskey@3 1765 return createEmptyGetter(desc, name);
jlaskey@3 1766 }
jlaskey@3 1767 throw new AssertionError(); // never invoked with any other operation
jlaskey@3 1768 }
jlaskey@3 1769
jlaskey@212 1770 if (request.isCallSiteUnstable()) {
jlaskey@212 1771 return findMegaMorphicGetMethod(desc, name);
jlaskey@212 1772 }
jlaskey@212 1773
jlaskey@3 1774 final Class<?> returnType = desc.getMethodType().returnType();
jlaskey@3 1775 final Property property = find.getProperty();
jlaskey@3 1776 methodHandle = find.getGetter(returnType);
jlaskey@3 1777
jlaskey@3 1778 // getMap() is fine as we have the prototype switchpoint depending on where the property was found
jlaskey@3 1779 final MethodHandle guard = NashornGuards.getMapGuard(getMap());
jlaskey@3 1780
jlaskey@3 1781 if (methodHandle != null) {
jlaskey@3 1782 assert methodHandle.type().returnType().equals(returnType);
jlaskey@3 1783 if (find.isSelf()) {
hannesw@63 1784 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
hannesw@63 1785 NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
jlaskey@3 1786 }
jlaskey@3 1787
jlaskey@3 1788 final ScriptObject prototype = find.getOwner();
jlaskey@3 1789
jlaskey@379 1790 if (!property.hasGetterFunction(prototype)) {
jlaskey@3 1791 methodHandle = bindTo(methodHandle, prototype);
jlaskey@3 1792 }
jlaskey@242 1793 return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
jlaskey@3 1794 }
jlaskey@3 1795
jlaskey@3 1796 assert !NashornCallSiteDescriptor.isFastScope(desc);
jlaskey@242 1797 return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
jlaskey@3 1798 }
jlaskey@3 1799
jlaskey@3 1800 private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
jlaskey@212 1801 final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
jlaskey@212 1802 final GuardedInvocation inv = findGetIndexMethod(mhType);
jlaskey@212 1803
jlaskey@3 1804 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
jlaskey@3 1805 }
jlaskey@3 1806
jlaskey@3 1807 /**
jlaskey@3 1808 * Find the appropriate GETINDEX method for an invoke dynamic call.
jlaskey@3 1809 *
hannesw@51 1810 * @param desc the call site descriptor
hannesw@51 1811 * @param request the link request
jlaskey@3 1812 *
jlaskey@3 1813 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1814 */
hannesw@51 1815 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
jlaskey@3 1816 return findGetIndexMethod(desc.getMethodType());
jlaskey@3 1817 }
jlaskey@3 1818
jlaskey@3 1819 /**
jlaskey@3 1820 * Find the appropriate GETINDEX method for an invoke dynamic call.
jlaskey@3 1821 *
jlaskey@3 1822 * @param callType the call site method type
jlaskey@3 1823 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1824 */
jlaskey@3 1825 private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
jlaskey@3 1826 final Class<?> returnClass = callType.returnType();
jlaskey@3 1827 final Class<?> keyClass = callType.parameterType(1);
jlaskey@3 1828
jlaskey@3 1829 String name = "get";
jlaskey@3 1830 if (returnClass.isPrimitive()) {
jlaskey@3 1831 //turn e.g. get with a double into getDouble
jlaskey@3 1832 final String returnTypeName = returnClass.getName();
jlaskey@3 1833 name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
jlaskey@3 1834 }
jlaskey@3 1835
jlaskey@3 1836 return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
jlaskey@3 1837 }
jlaskey@3 1838
jlaskey@3 1839 private static MethodHandle getScriptObjectGuard(final MethodType type) {
jlaskey@3 1840 return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
jlaskey@3 1841 }
jlaskey@3 1842
jlaskey@3 1843 /**
jlaskey@3 1844 * Find the appropriate SET method for an invoke dynamic call.
jlaskey@3 1845 *
hannesw@51 1846 * @param desc the call site descriptor
hannesw@51 1847 * @param request the link request
jlaskey@3 1848 *
jlaskey@3 1849 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1850 */
hannesw@51 1851 protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
attila@15 1852 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
hannesw@51 1853 if(request.isCallSiteUnstable()) {
jlaskey@3 1854 return findMegaMorphicSetMethod(desc, name);
jlaskey@3 1855 }
jlaskey@3 1856
sundar@17 1857 final boolean scope = isScope();
sundar@17 1858 /*
sundar@17 1859 * If doing property set on a scope object, we should stop proto search on the first
attila@120 1860 * non-scope object. Without this, for example, when assigning "toString" on global scope,
sundar@17 1861 * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
sundar@17 1862 *
sundar@17 1863 * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
sundar@17 1864 */
attila@120 1865 FindProperty find = findProperty(name, true, scope, this);
sundar@537 1866
attila@15 1867 // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
sundar@17 1868 if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
attila@15 1869 // We should still check if inherited data property is not writable
jlaskey@80 1870 if (isExtensible() && !find.getProperty().isWritable()) {
attila@15 1871 return createEmptySetMethod(desc, "property.not.writable", false);
jlaskey@3 1872 }
attila@15 1873 // Otherwise, forget the found property
attila@15 1874 find = null;
jlaskey@3 1875 }
jlaskey@3 1876
jlaskey@3 1877 if (find != null) {
jlaskey@80 1878 if(!find.getProperty().isWritable()) {
attila@15 1879 // Existing, non-writable property
attila@15 1880 return createEmptySetMethod(desc, "property.not.writable", true);
jlaskey@3 1881 }
sundar@537 1882 } else {
sundar@537 1883 if (PROTO_PROPERTY_NAME.equals(name)) {
sundar@537 1884 return new GuardedInvocation(SETPROTOCHECK, NashornGuards.getScriptObjectGuard());
sundar@537 1885 } else if (! isExtensible()) {
sundar@537 1886 return createEmptySetMethod(desc, "object.non.extensible", false);
sundar@537 1887 }
jlaskey@3 1888 }
jlaskey@3 1889
attila@15 1890 return new SetMethodCreator(this, find, desc).createGuardedInvocation();
attila@15 1891 }
attila@15 1892
attila@15 1893 private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
attila@15 1894 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
attila@15 1895 if (NashornCallSiteDescriptor.isStrict(desc)) {
lagergren@112 1896 throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
attila@15 1897 }
attila@15 1898 assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
lagergren@24 1899 final PropertyMap myMap = getMap();
jlaskey@242 1900 return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
jlaskey@3 1901 }
jlaskey@3 1902
jlaskey@3 1903 @SuppressWarnings("unused")
jlaskey@242 1904 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 1905 final ScriptObject obj = (ScriptObject)self;
jlaskey@242 1906 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
jlaskey@242 1907 if (!obj.isExtensible()) {
jlaskey@242 1908 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
jlaskey@242 1909 } else if (obj.compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1910 setter.invokeExact(self, value);
jlaskey@242 1911 } else {
jlaskey@242 1912 obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
jlaskey@3 1913 }
jlaskey@3 1914 }
jlaskey@3 1915
jlaskey@3 1916 @SuppressWarnings("unused")
jlaskey@3 1917 private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
jlaskey@3 1918 final ScriptObject obj = (ScriptObject)self;
jlaskey@242 1919 if (obj.trySetSpill(desc, oldMap, newMap, value)) {
jlaskey@3 1920 obj.spill[index] = value;
jlaskey@3 1921 }
jlaskey@3 1922 }
jlaskey@3 1923
jlaskey@242 1924 private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
jlaskey@3 1925 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
jlaskey@3 1926 if (!isExtensible() && isStrict) {
lagergren@112 1927 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
jlaskey@3 1928 } else if (compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1929 return true;
jlaskey@3 1930 } else {
jlaskey@3 1931 set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
jlaskey@3 1932 return false;
jlaskey@3 1933 }
jlaskey@3 1934 }
jlaskey@3 1935
jlaskey@3 1936 @SuppressWarnings("unused")
jlaskey@3 1937 private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
jlaskey@3 1938 final ScriptObject obj = (ScriptObject)self;
jlaskey@3 1939 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
jlaskey@3 1940
jlaskey@3 1941 if (!obj.isExtensible()) {
jlaskey@3 1942 if (isStrict) {
lagergren@112 1943 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
jlaskey@3 1944 }
jlaskey@3 1945 } else if (obj.compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1946 obj.spill = new Object[SPILL_RATE];
jlaskey@3 1947 obj.spill[index] = value;
jlaskey@3 1948 } else {
jlaskey@3 1949 obj.set(desc.getNameToken(2), value, isStrict);
jlaskey@3 1950 }
jlaskey@3 1951 }
jlaskey@3 1952
jlaskey@3 1953 @SuppressWarnings("unused")
jlaskey@3 1954 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 1955 final ScriptObject obj = (ScriptObject)self;
jlaskey@3 1956 final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
jlaskey@3 1957
jlaskey@3 1958 if (!obj.isExtensible()) {
jlaskey@3 1959 if (isStrict) {
lagergren@112 1960 throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
jlaskey@3 1961 }
jlaskey@3 1962 } else if (obj.compareAndSetMap(oldMap, newMap)) {
jlaskey@3 1963 final int oldLength = obj.spill.length;
jlaskey@3 1964 final Object[] newSpill = new Object[newLength];
jlaskey@3 1965 System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
jlaskey@3 1966 obj.spill = newSpill;
jlaskey@3 1967 obj.spill[index] = value;
jlaskey@3 1968 } else {
jlaskey@3 1969 obj.set(desc.getNameToken(2), value, isStrict);
jlaskey@3 1970 }
jlaskey@3 1971 }
jlaskey@3 1972
jlaskey@3 1973 private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
jlaskey@212 1974 final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
jlaskey@214 1975 final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
jlaskey@3 1976 return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
jlaskey@3 1977 }
jlaskey@3 1978
jlaskey@3 1979 private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
jlaskey@3 1980 return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
jlaskey@3 1981 }
jlaskey@3 1982
jlaskey@3 1983 /**
jlaskey@3 1984 * Find the appropriate SETINDEX method for an invoke dynamic call.
jlaskey@3 1985 *
jlaskey@3 1986 * @param callType the method type at the call site
jlaskey@3 1987 * @param isStrict are we in strict mode?
jlaskey@3 1988 *
jlaskey@3 1989 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 1990 */
jlaskey@3 1991 private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
jlaskey@3 1992 assert callType.parameterCount() == 3;
jlaskey@3 1993
jlaskey@3 1994 final Class<?> keyClass = callType.parameterType(1);
jlaskey@3 1995 final Class<?> valueClass = callType.parameterType(2);
jlaskey@3 1996
jlaskey@3 1997 MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
jlaskey@3 1998 methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
jlaskey@3 1999
jlaskey@3 2000 return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
jlaskey@3 2001 }
jlaskey@3 2002
jlaskey@3 2003 /**
jlaskey@3 2004 * Fall back if a function property is not found.
jlaskey@3 2005 * @param desc The call site descriptor
hannesw@51 2006 * @param request the link request
jlaskey@3 2007 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 2008 */
hannesw@51 2009 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) {
jlaskey@3 2010 final String name = desc.getNameToken(2);
jlaskey@3 2011 final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
jlaskey@3 2012 final boolean scopeCall = isScope() && NashornCallSiteDescriptor.isScope(desc);
jlaskey@3 2013
jlaskey@3 2014 if (find == null) {
sundar@61 2015 return noSuchProperty(desc, request);
jlaskey@3 2016 }
jlaskey@3 2017
sundar@434 2018 final Object value = getObjectValue(find);
sundar@434 2019 if (! (value instanceof ScriptFunction)) {
sundar@434 2020 return createEmptyGetter(desc, name);
sundar@434 2021 }
sundar@434 2022
sundar@434 2023 final ScriptFunction func = (ScriptFunction)value;
jlaskey@3 2024 final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
jlaskey@3 2025 // TODO: It'd be awesome if we could bind "name" without binding "this".
jlaskey@3 2026 return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
jlaskey@3 2027 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
jlaskey@3 2028 null, NashornGuards.getMapGuard(getMap()));
jlaskey@3 2029 }
jlaskey@3 2030
jlaskey@3 2031 /**
jlaskey@3 2032 * Fall back if a property is not found.
jlaskey@3 2033 * @param desc the call site descriptor.
hannesw@51 2034 * @param request the link request
jlaskey@3 2035 * @return GuardedInvocation to be invoked at call site.
jlaskey@3 2036 */
lagergren@524 2037 @SuppressWarnings("null")
hannesw@51 2038 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
jlaskey@212 2039 final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
jlaskey@3 2040 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
jlaskey@3 2041 final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
jlaskey@3 2042
jlaskey@3 2043 if (find != null) {
lagergren@505 2044 final Object value = getObjectValue(find);
lagergren@505 2045 ScriptFunction func = null;
lagergren@505 2046 MethodHandle methodHandle = null;
lagergren@505 2047
sundar@434 2048 if (value instanceof ScriptFunction) {
sundar@434 2049 func = (ScriptFunction)value;
sundar@434 2050 methodHandle = getCallMethodHandle(func, desc.getMethodType(), name);
sundar@434 2051 }
jlaskey@3 2052
jlaskey@3 2053 if (methodHandle != null) {
jlaskey@3 2054 if (scopeAccess && func.isStrict()) {
jlaskey@3 2055 methodHandle = bindTo(methodHandle, UNDEFINED);
jlaskey@3 2056 }
jlaskey@3 2057 return new GuardedInvocation(methodHandle,
jlaskey@242 2058 find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
jlaskey@3 2059 getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
jlaskey@3 2060 }
jlaskey@3 2061 }
jlaskey@3 2062
jlaskey@3 2063 if (scopeAccess) {
lagergren@112 2064 throw referenceError("not.defined", name);
jlaskey@3 2065 }
jlaskey@3 2066
jlaskey@3 2067 return createEmptyGetter(desc, name);
jlaskey@3 2068 }
jlaskey@212 2069 /**
jlaskey@212 2070 * Invoke fall back if a property is not found.
jlaskey@212 2071 * @param name Name of property.
jlaskey@212 2072 * @return Result from call.
jlaskey@212 2073 */
jlaskey@212 2074 private Object invokeNoSuchProperty(final String name) {
jlaskey@212 2075 final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
jlaskey@212 2076
jlaskey@212 2077 if (find != null) {
jlaskey@212 2078 final Object func = getObjectValue(find);
jlaskey@212 2079
jlaskey@212 2080 if (func instanceof ScriptFunction) {
jlaskey@212 2081 return ScriptRuntime.apply((ScriptFunction)func, this, name);
jlaskey@212 2082 }
jlaskey@212 2083 }
jlaskey@212 2084
jlaskey@212 2085 return UNDEFINED;
jlaskey@212 2086 }
jlaskey@3 2087
jlaskey@3 2088 private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
jlaskey@242 2089 return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
jlaskey@3 2090 }
jlaskey@3 2091
jlaskey@3 2092 private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
jlaskey@3 2093 protected T[] values;
jlaskey@3 2094 protected final ScriptObject object;
jlaskey@3 2095 private int index;
jlaskey@3 2096
jlaskey@3 2097 ScriptObjectIterator(final ScriptObject object) {
jlaskey@3 2098 this.object = object;
jlaskey@3 2099 }
jlaskey@3 2100
jlaskey@3 2101 protected abstract void init();
jlaskey@3 2102
jlaskey@3 2103 @Override
jlaskey@3 2104 public boolean hasNext() {
jlaskey@3 2105 if (values == null) {
jlaskey@3 2106 init();
jlaskey@3 2107 }
jlaskey@3 2108 return index < values.length;
jlaskey@3 2109 }
jlaskey@3 2110
jlaskey@3 2111 @Override
jlaskey@3 2112 public T next() {
jlaskey@3 2113 if (values == null) {
jlaskey@3 2114 init();
jlaskey@3 2115 }
jlaskey@3 2116 return values[index++];
jlaskey@3 2117 }
jlaskey@3 2118
jlaskey@3 2119 @Override
jlaskey@3 2120 public void remove() {
jlaskey@3 2121 throw new UnsupportedOperationException();
jlaskey@3 2122 }
jlaskey@3 2123 }
jlaskey@3 2124
jlaskey@3 2125 private static class KeyIterator extends ScriptObjectIterator<String> {
jlaskey@3 2126 KeyIterator(final ScriptObject object) {
jlaskey@3 2127 super(object);
jlaskey@3 2128 }
jlaskey@3 2129
jlaskey@3 2130 @Override
jlaskey@3 2131 protected void init() {
jlaskey@3 2132 final Set<String> keys = new LinkedHashSet<>();
jlaskey@3 2133 for (ScriptObject self = object; self != null; self = self.getProto()) {
jlaskey@3 2134 keys.addAll(Arrays.asList(self.getOwnKeys(false)));
jlaskey@3 2135 }
jlaskey@3 2136 this.values = keys.toArray(new String[keys.size()]);
jlaskey@3 2137 }
jlaskey@3 2138 }
jlaskey@3 2139
jlaskey@3 2140 private static class ValueIterator extends ScriptObjectIterator<Object> {
jlaskey@3 2141 ValueIterator(final ScriptObject object) {
jlaskey@3 2142 super(object);
jlaskey@3 2143 }
jlaskey@3 2144
jlaskey@3 2145 @Override
jlaskey@3 2146 protected void init() {
jlaskey@3 2147 final ArrayList<Object> valueList = new ArrayList<>();
jlaskey@3 2148 for (ScriptObject self = object; self != null; self = self.getProto()) {
jlaskey@3 2149 for (final String key : self.getOwnKeys(false)) {
jlaskey@3 2150 valueList.add(self.get(key));
jlaskey@3 2151 }
jlaskey@3 2152 }
jlaskey@3 2153 this.values = valueList.toArray(new Object[valueList.size()]);
jlaskey@3 2154 }
jlaskey@3 2155 }
jlaskey@3 2156
jlaskey@3 2157 /**
jlaskey@3 2158 * Add a spill property for the given key.
jlaskey@3 2159 * @param key Property key.
jlaskey@3 2160 * @param propertyFlags Property flags.
jlaskey@3 2161 * @return Added property.
jlaskey@3 2162 */
jlaskey@3 2163 private Property addSpillProperty(final String key, final int propertyFlags) {
jlaskey@242 2164 int fieldCount = getMap().getFieldCount();
jlaskey@242 2165 int fieldMaximum = getMap().getFieldMaximum();
jlaskey@242 2166 Property property;
jlaskey@242 2167
jlaskey@242 2168 if (fieldCount < fieldMaximum) {
jlaskey@242 2169 property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
jlaskey@242 2170 notifyPropertyAdded(this, property);
jlaskey@242 2171 property = addOwnProperty(property);
jlaskey@242 2172 } else {
jlaskey@242 2173 int i = getMap().getSpillLength();
hannesw@291 2174 property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
jlaskey@242 2175 notifyPropertyAdded(this, property);
jlaskey@242 2176 property = addOwnProperty(property);
jlaskey@242 2177 i = property.getSlot();
jlaskey@3 2178
jlaskey@3 2179 final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
jlaskey@242 2180
jlaskey@242 2181 if (spill == null || newLength > spill.length) {
jlaskey@242 2182 final Object[] newSpill = new Object[newLength];
jlaskey@242 2183
jlaskey@242 2184 if (spill != null) {
jlaskey@242 2185 System.arraycopy(spill, 0, newSpill, 0, spill.length);
jlaskey@242 2186 }
jlaskey@242 2187
jlaskey@242 2188 spill = newSpill;
jlaskey@3 2189 }
jlaskey@3 2190 }
jlaskey@3 2191
jlaskey@242 2192 return property;
jlaskey@3 2193 }
jlaskey@3 2194
jlaskey@3 2195
jlaskey@3 2196 /**
jlaskey@3 2197 * Add a spill entry for the given key.
hannesw@291 2198 * @param key Property key.
jlaskey@3 2199 * @return Setter method handle.
jlaskey@3 2200 */
hannesw@291 2201 MethodHandle addSpill(final String key) {
hannesw@291 2202 final Property spillProperty = addSpillProperty(key, 0);
jlaskey@3 2203 final Class<?> type = Object.class;
jlaskey@3 2204 return spillProperty.getSetter(type, getMap()); //TODO specfields
jlaskey@3 2205 }
jlaskey@3 2206
jlaskey@3 2207 /**
jlaskey@3 2208 * Make sure arguments are paired correctly, with respect to more parameters than declared,
jlaskey@3 2209 * fewer parameters than declared and other things that JavaScript allows. This might involve
jlaskey@3 2210 * creating collectors.
jlaskey@3 2211 *
jlaskey@3 2212 * @param methodHandle method handle for invoke
jlaskey@3 2213 * @param callType type of the call
jlaskey@3 2214 *
jlaskey@3 2215 * @return method handle with adjusted arguments
jlaskey@3 2216 */
jlaskey@3 2217 protected static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType) {
jlaskey@3 2218 return pairArguments(methodHandle, callType, null);
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 * Make sure arguments are paired correctly.
jlaskey@3 2227 * @param methodHandle MethodHandle to adjust.
attila@217 2228 * @param callType MethodType of the call site.
attila@217 2229 * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
attila@217 2230 * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
attila@217 2231 * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
attila@217 2232 * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
jlaskey@3 2233 *
jlaskey@3 2234 * @return method handle with adjusted arguments
jlaskey@3 2235 */
jlaskey@3 2236 public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
jlaskey@3 2237
jlaskey@3 2238 final MethodType methodType = methodHandle.type();
jlaskey@3 2239 if (methodType.equals(callType)) {
jlaskey@3 2240 return methodHandle;
jlaskey@3 2241 }
jlaskey@3 2242
jlaskey@3 2243 final int parameterCount = methodType.parameterCount();
jlaskey@3 2244 final int callCount = callType.parameterCount();
jlaskey@3 2245
sundar@50 2246 final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
attila@217 2247 final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
jlaskey@3 2248 callType.parameterType(callCount - 1).isArray());
jlaskey@3 2249
jlaskey@3 2250 if (callCount < parameterCount) {
jlaskey@3 2251 final int missingArgs = parameterCount - callCount;
jlaskey@3 2252 final Object[] fillers = new Object[missingArgs];
jlaskey@3 2253
jlaskey@3 2254 Arrays.fill(fillers, UNDEFINED);
jlaskey@3 2255
jlaskey@3 2256 if (isCalleeVarArg) {
jlaskey@3 2257 fillers[missingArgs - 1] = new Object[0];
jlaskey@3 2258 }
jlaskey@3 2259
jlaskey@3 2260 return MH.insertArguments(
jlaskey@3 2261 methodHandle,
jlaskey@3 2262 parameterCount - missingArgs,
jlaskey@3 2263 fillers);
jlaskey@3 2264 }
jlaskey@3 2265
jlaskey@3 2266 if (isCalleeVarArg) {
jlaskey@3 2267 return isCallerVarArg ?
jlaskey@3 2268 methodHandle :
jlaskey@3 2269 MH.asCollector(methodHandle, Object[].class, callCount - parameterCount + 1);
jlaskey@3 2270 }
jlaskey@3 2271
jlaskey@3 2272 if (isCallerVarArg) {
jlaskey@3 2273 final int spreadArgs = parameterCount - callCount + 1;
jlaskey@3 2274 return MH.filterArguments(
jlaskey@3 2275 MH.asSpreader(
jlaskey@3 2276 methodHandle,
jlaskey@3 2277 Object[].class,
jlaskey@3 2278 spreadArgs),
jlaskey@3 2279 callCount - 1,
jlaskey@3 2280 MH.insertArguments(
jlaskey@3 2281 TRUNCATINGFILTER,
jlaskey@3 2282 0,
jlaskey@3 2283 spreadArgs)
jlaskey@3 2284 );
jlaskey@3 2285 }
jlaskey@3 2286
jlaskey@3 2287 if (callCount > parameterCount) {
jlaskey@3 2288 final int discardedArgs = callCount - parameterCount;
jlaskey@3 2289
jlaskey@3 2290 final Class<?>[] discards = new Class<?>[discardedArgs];
jlaskey@3 2291 Arrays.fill(discards, Object.class);
jlaskey@3 2292
jlaskey@3 2293 return MH.dropArguments(methodHandle, callCount - discardedArgs, discards);
jlaskey@3 2294 }
jlaskey@3 2295
jlaskey@3 2296 return methodHandle;
jlaskey@3 2297 }
jlaskey@3 2298
jlaskey@3 2299 @SuppressWarnings("unused")
jlaskey@3 2300 private static Object[] truncatingFilter(final int n, final Object[] array) {
jlaskey@3 2301 final int length = array == null ? 0 : array.length;
jlaskey@3 2302 if (n == length) {
jlaskey@3 2303 return array == null ? new Object[0] : array;
jlaskey@3 2304 }
jlaskey@3 2305
jlaskey@3 2306 final Object[] newArray = new Object[n];
jlaskey@3 2307
jlaskey@3 2308 if (array != null) {
jlaskey@3 2309 for (int i = 0; i < n && i < length; i++) {
jlaskey@3 2310 newArray[i] = array[i];
jlaskey@3 2311 }
jlaskey@3 2312 }
jlaskey@3 2313
jlaskey@3 2314 if (length < n) {
jlaskey@3 2315 final Object fill = UNDEFINED;
jlaskey@3 2316
jlaskey@3 2317 for (int i = length; i < n; i++) {
jlaskey@3 2318 newArray[i] = fill;
jlaskey@3 2319 }
jlaskey@3 2320 }
jlaskey@3 2321
jlaskey@3 2322 return newArray;
jlaskey@3 2323 }
jlaskey@3 2324
jlaskey@3 2325 /**
jlaskey@3 2326 * Numeric length setter for length property
jlaskey@3 2327 *
jlaskey@3 2328 * @param newLength new length to set
jlaskey@3 2329 */
jlaskey@3 2330 public final void setLength(final long newLength) {
jlaskey@3 2331 final long arrayLength = getArray().length();
jlaskey@3 2332 if (newLength == arrayLength) {
jlaskey@3 2333 return;
jlaskey@3 2334 }
jlaskey@3 2335
sundar@344 2336 final boolean isStrict = isStrictContext();
jlaskey@3 2337
jlaskey@3 2338 if (newLength > arrayLength) {
jlaskey@3 2339 setArray(getArray().ensure(newLength - 1));
jlaskey@3 2340 if (getArray().canDelete(arrayLength, (newLength - 1), isStrict)) {
jlaskey@3 2341 setArray(getArray().delete(arrayLength, (newLength - 1)));
jlaskey@3 2342 }
jlaskey@3 2343 return;
jlaskey@3 2344 }
jlaskey@3 2345
jlaskey@3 2346 if (newLength < arrayLength) {
jlaskey@3 2347 setArray(getArray().shrink(newLength));
jlaskey@3 2348 getArray().setLength(newLength);
jlaskey@3 2349 }
jlaskey@212 2350 }
jlaskey@212 2351
jlaskey@212 2352 private int getInt(final int index, final String key) {
hannesw@334 2353 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@228 2354 for (ScriptObject object = this; ; ) {
jlaskey@228 2355 final FindProperty find = object.findProperty(key, false, false, this);
jlaskey@228 2356
jlaskey@228 2357 if (find != null) {
jlaskey@228 2358 return getIntValue(find);
jlaskey@228 2359 }
jlaskey@228 2360
jlaskey@228 2361 if ((object = object.getProto()) == null) {
jlaskey@228 2362 break;
jlaskey@228 2363 }
jlaskey@228 2364
jlaskey@228 2365 final ArrayData array = object.getArray();
jlaskey@228 2366
jlaskey@228 2367 if (array.has(index)) {
jlaskey@228 2368 return array.getInt(index);
jlaskey@228 2369 }
jlaskey@228 2370 }
jlaskey@228 2371 } else {
jlaskey@228 2372 final FindProperty find = findProperty(key, true);
jlaskey@212 2373
jlaskey@212 2374 if (find != null) {
jlaskey@228 2375 return getIntValue(find);
jlaskey@212 2376 }
jlaskey@212 2377 }
jlaskey@212 2378
jlaskey@212 2379 return JSType.toInt32(invokeNoSuchProperty(key));
jlaskey@212 2380 }
jlaskey@3 2381
jlaskey@3 2382 @Override
jlaskey@3 2383 public int getInt(final Object key) {
hannesw@334 2384 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@228 2385 final ArrayData array = getArray();
jlaskey@228 2386
jlaskey@228 2387 if (array.has(index)) {
jlaskey@228 2388 return array.getInt(index);
jlaskey@228 2389 }
jlaskey@228 2390
hannesw@515 2391 return getInt(index, JSType.toString(key));
jlaskey@3 2392 }
jlaskey@3 2393
jlaskey@3 2394 @Override
jlaskey@3 2395 public int getInt(final double key) {
hannesw@334 2396 final int index = ArrayIndex.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 long key) {
hannesw@334 2408 final int index = ArrayIndex.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 int key) {
jlaskey@228 2420 final ArrayData array = getArray();
jlaskey@228 2421
jlaskey@228 2422 if (array.has(key)) {
jlaskey@228 2423 return array.getInt(key);
jlaskey@228 2424 }
jlaskey@228 2425
hannesw@515 2426 return getInt(key, JSType.toString(key));
jlaskey@212 2427 }
jlaskey@212 2428
jlaskey@212 2429 private long getLong(final int index, final String key) {
hannesw@334 2430 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@228 2431 for (ScriptObject object = this; ; ) {
jlaskey@228 2432 final FindProperty find = object.findProperty(key, false, false, this);
jlaskey@228 2433
jlaskey@228 2434 if (find != null) {
jlaskey@228 2435 return getLongValue(find);
jlaskey@228 2436 }
jlaskey@228 2437
jlaskey@228 2438 if ((object = object.getProto()) == null) {
jlaskey@228 2439 break;
jlaskey@228 2440 }
jlaskey@228 2441
jlaskey@228 2442 final ArrayData array = object.getArray();
jlaskey@228 2443
jlaskey@228 2444 if (array.has(index)) {
jlaskey@228 2445 return array.getLong(index);
jlaskey@228 2446 }
jlaskey@228 2447 }
jlaskey@228 2448 } else {
jlaskey@228 2449 final FindProperty find = findProperty(key, true);
jlaskey@212 2450
jlaskey@212 2451 if (find != null) {
jlaskey@228 2452 return getLongValue(find);
jlaskey@212 2453 }
jlaskey@3 2454 }
jlaskey@3 2455
jlaskey@212 2456 return JSType.toLong(invokeNoSuchProperty(key));
jlaskey@3 2457 }
jlaskey@3 2458
jlaskey@3 2459 @Override
jlaskey@3 2460 public long getLong(final Object key) {
hannesw@334 2461 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@228 2462 final ArrayData array = getArray();
jlaskey@228 2463
jlaskey@228 2464 if (array.has(index)) {
jlaskey@228 2465 return array.getLong(index);
jlaskey@228 2466 }
jlaskey@228 2467
hannesw@515 2468 return getLong(index, JSType.toString(key));
jlaskey@3 2469 }
jlaskey@3 2470
jlaskey@3 2471 @Override
jlaskey@3 2472 public long getLong(final double key) {
hannesw@334 2473 final int index = ArrayIndex.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 long key) {
hannesw@334 2485 final int index = ArrayIndex.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 int key) {
jlaskey@228 2497 final ArrayData array = getArray();
jlaskey@228 2498
jlaskey@228 2499 if (array.has(key)) {
jlaskey@228 2500 return array.getLong(key);
jlaskey@228 2501 }
jlaskey@228 2502
hannesw@515 2503 return getLong(key, JSType.toString(key));
jlaskey@212 2504 }
jlaskey@212 2505
jlaskey@212 2506 private double getDouble(final int index, final String key) {
hannesw@334 2507 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@228 2508 for (ScriptObject object = this; ; ) {
jlaskey@228 2509 final FindProperty find = object.findProperty(key, false, false, this);
jlaskey@228 2510
jlaskey@228 2511 if (find != null) {
jlaskey@228 2512 return getDoubleValue(find);
jlaskey@228 2513 }
jlaskey@228 2514
jlaskey@228 2515 if ((object = object.getProto()) == null) {
jlaskey@228 2516 break;
jlaskey@228 2517 }
jlaskey@228 2518
jlaskey@228 2519 final ArrayData array = object.getArray();
jlaskey@228 2520
jlaskey@228 2521 if (array.has(index)) {
jlaskey@228 2522 return array.getDouble(index);
jlaskey@228 2523 }
jlaskey@228 2524 }
jlaskey@228 2525 } else {
jlaskey@228 2526 final FindProperty find = findProperty(key, true);
jlaskey@212 2527
jlaskey@212 2528 if (find != null) {
jlaskey@228 2529 return getDoubleValue(find);
jlaskey@212 2530 }
jlaskey@3 2531 }
jlaskey@3 2532
jlaskey@212 2533 return JSType.toNumber(invokeNoSuchProperty(key));
jlaskey@3 2534 }
jlaskey@3 2535
jlaskey@3 2536 @Override
jlaskey@3 2537 public double getDouble(final Object key) {
hannesw@334 2538 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@228 2539 final ArrayData array = getArray();
jlaskey@228 2540
jlaskey@228 2541 if (array.has(index)) {
jlaskey@228 2542 return array.getDouble(index);
jlaskey@228 2543 }
jlaskey@228 2544
hannesw@515 2545 return getDouble(index, JSType.toString(key));
jlaskey@3 2546 }
jlaskey@3 2547
jlaskey@3 2548 @Override
jlaskey@3 2549 public double getDouble(final double key) {
hannesw@334 2550 final int index = ArrayIndex.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 long key) {
hannesw@334 2562 final int index = ArrayIndex.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 int key) {
jlaskey@228 2574 final ArrayData array = getArray();
jlaskey@228 2575
jlaskey@228 2576 if (array.has(key)) {
jlaskey@228 2577 return array.getDouble(key);
jlaskey@228 2578 }
jlaskey@228 2579
hannesw@515 2580 return getDouble(key, JSType.toString(key));
jlaskey@212 2581 }
jlaskey@212 2582
jlaskey@212 2583 private Object get(final int index, final String key) {
hannesw@334 2584 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@228 2585 for (ScriptObject object = this; ; ) {
jlaskey@228 2586 final FindProperty find = object.findProperty(key, false, false, this);
jlaskey@228 2587
jlaskey@228 2588 if (find != null) {
jlaskey@228 2589 return getObjectValue(find);
jlaskey@228 2590 }
jlaskey@228 2591
jlaskey@228 2592 if ((object = object.getProto()) == null) {
jlaskey@228 2593 break;
jlaskey@228 2594 }
jlaskey@228 2595
jlaskey@228 2596 final ArrayData array = object.getArray();
jlaskey@228 2597
jlaskey@228 2598 if (array.has(index)) {
jlaskey@228 2599 return array.getObject(index);
jlaskey@228 2600 }
jlaskey@212 2601 }
jlaskey@228 2602 } else {
jlaskey@228 2603 final FindProperty find = findProperty(key, true);
jlaskey@212 2604
jlaskey@212 2605 if (find != null) {
jlaskey@228 2606 return getObjectValue(find);
jlaskey@212 2607 }
jlaskey@3 2608 }
jlaskey@3 2609
jlaskey@212 2610 return invokeNoSuchProperty(key);
jlaskey@3 2611 }
jlaskey@3 2612
jlaskey@3 2613 @Override
jlaskey@3 2614 public Object get(final Object key) {
hannesw@334 2615 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@228 2616 final ArrayData array = getArray();
jlaskey@228 2617
jlaskey@228 2618 if (array.has(index)) {
jlaskey@228 2619 return array.getObject(index);
jlaskey@228 2620 }
jlaskey@228 2621
hannesw@515 2622 return get(index, JSType.toString(key));
jlaskey@3 2623 }
jlaskey@3 2624
jlaskey@3 2625 @Override
jlaskey@3 2626 public Object get(final double key) {
hannesw@334 2627 final int index = ArrayIndex.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 long key) {
hannesw@334 2639 final int index = ArrayIndex.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 int key) {
jlaskey@228 2651 final ArrayData array = getArray();
jlaskey@228 2652
jlaskey@228 2653 if (array.has(key)) {
jlaskey@228 2654 return array.getObject(key);
jlaskey@228 2655 }
jlaskey@228 2656
hannesw@515 2657 return get(key, JSType.toString(key));
jlaskey@3 2658 }
jlaskey@3 2659
jlaskey@3 2660 /**
jlaskey@3 2661 * Handle when an array doesn't have a slot - possibly grow and/or convert array.
jlaskey@3 2662 *
jlaskey@3 2663 * @param index key as index
jlaskey@3 2664 * @param value element value
jlaskey@3 2665 * @param strict are we in strict mode
jlaskey@3 2666 */
jlaskey@3 2667 private void doesNotHave(final int index, final Object value, final boolean strict) {
jlaskey@3 2668 final long oldLength = getArray().length();
hannesw@219 2669 final long longIndex = index & JSType.MAX_UINT;
jlaskey@3 2670
jlaskey@3 2671 if (!getArray().has(index)) {
hannesw@515 2672 final String key = JSType.toString(longIndex);
jlaskey@3 2673 final FindProperty find = findProperty(key, true);
jlaskey@3 2674
jlaskey@3 2675 if (find != null) {
jlaskey@3 2676 setObject(find, strict, key, value);
jlaskey@3 2677 return;
jlaskey@3 2678 }
jlaskey@3 2679 }
jlaskey@3 2680
jlaskey@3 2681 if (longIndex >= oldLength) {
jlaskey@3 2682 if (!isExtensible()) {
jlaskey@3 2683 if (strict) {
lagergren@112 2684 throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
jlaskey@3 2685 }
jlaskey@3 2686 return;
jlaskey@3 2687 }
jlaskey@3 2688 setArray(getArray().ensure(longIndex));
jlaskey@3 2689 }
jlaskey@3 2690
jlaskey@3 2691 if (value instanceof Integer) {
jlaskey@3 2692 setArray(getArray().set(index, (int)value, strict));
jlaskey@3 2693 } else if (value instanceof Long) {
jlaskey@3 2694 setArray(getArray().set(index, (long)value, strict));
jlaskey@3 2695 } else if (value instanceof Double) {
jlaskey@3 2696 setArray(getArray().set(index, (double)value, strict));
jlaskey@3 2697 } else {
jlaskey@3 2698 setArray(getArray().set(index, value, strict));
jlaskey@3 2699 }
jlaskey@3 2700
jlaskey@3 2701 if (longIndex > oldLength) {
jlaskey@3 2702 ArrayData array = getArray();
jlaskey@3 2703
jlaskey@3 2704 if (array.canDelete(oldLength, (longIndex - 1), strict)) {
jlaskey@3 2705 array = array.delete(oldLength, (longIndex - 1));
jlaskey@3 2706 }
jlaskey@3 2707
jlaskey@3 2708 setArray(array);
jlaskey@3 2709 }
jlaskey@3 2710 }
jlaskey@3 2711
jlaskey@3 2712 /**
jlaskey@3 2713 * This is the most generic of all Object setters. Most of the others use this in some form.
jlaskey@3 2714 * TODO: should be further specialized
jlaskey@3 2715 *
jlaskey@3 2716 * @param find found property
jlaskey@3 2717 * @param strict are we in strict mode
jlaskey@3 2718 * @param key property key
jlaskey@3 2719 * @param value property value
jlaskey@3 2720 */
jlaskey@3 2721 public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
jlaskey@3 2722 FindProperty f = find;
jlaskey@3 2723
jlaskey@3 2724 if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
jlaskey@3 2725 f = null;
jlaskey@3 2726 }
jlaskey@3 2727
jlaskey@3 2728 if (f != null) {
jlaskey@80 2729 if (!f.getProperty().isWritable()) {
jlaskey@3 2730 if (strict) {
lagergren@112 2731 throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
jlaskey@3 2732 }
jlaskey@3 2733
jlaskey@3 2734 return;
jlaskey@3 2735 }
jlaskey@3 2736
hannesw@291 2737 f.setObjectValue(value, strict);
hannesw@291 2738
jlaskey@3 2739 } else if (!isExtensible()) {
jlaskey@3 2740 if (strict) {
lagergren@112 2741 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
jlaskey@3 2742 }
jlaskey@3 2743 } else {
jlaskey@3 2744 spill(key, value);
jlaskey@3 2745 }
jlaskey@3 2746 }
jlaskey@3 2747
jlaskey@3 2748 private void spill(final String key, final Object value) {
hannesw@291 2749 addSpillProperty(key, 0).setObjectValue(this, this, value, false);
jlaskey@3 2750 }
jlaskey@3 2751
jlaskey@3 2752
jlaskey@3 2753 @Override
jlaskey@3 2754 public void set(final Object key, final int value, final boolean strict) {
hannesw@334 2755 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2756
hannesw@334 2757 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2758 if (getArray().has(index)) {
jlaskey@3 2759 setArray(getArray().set(index, value, strict));
jlaskey@3 2760 } else {
jlaskey@3 2761 doesNotHave(index, value, strict);
jlaskey@3 2762 }
jlaskey@3 2763
jlaskey@3 2764 return;
jlaskey@3 2765 }
jlaskey@3 2766
jlaskey@3 2767 set(key, JSType.toObject(value), strict);
jlaskey@3 2768 }
jlaskey@3 2769
jlaskey@3 2770 @Override
jlaskey@3 2771 public void set(final Object key, final long value, final boolean strict) {
hannesw@334 2772 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2773
hannesw@334 2774 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2775 if (getArray().has(index)) {
jlaskey@3 2776 setArray(getArray().set(index, value, strict));
jlaskey@3 2777 } else {
jlaskey@3 2778 doesNotHave(index, value, strict);
jlaskey@3 2779 }
jlaskey@3 2780
jlaskey@3 2781 return;
jlaskey@3 2782 }
jlaskey@3 2783
jlaskey@3 2784 set(key, JSType.toObject(value), strict);
jlaskey@3 2785 }
jlaskey@3 2786
jlaskey@3 2787 @Override
jlaskey@3 2788 public void set(final Object key, final double value, final boolean strict) {
hannesw@334 2789 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2790
hannesw@334 2791 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2792 if (getArray().has(index)) {
jlaskey@3 2793 setArray(getArray().set(index, value, strict));
jlaskey@3 2794 } else {
jlaskey@3 2795 doesNotHave(index, value, strict);
jlaskey@3 2796 }
jlaskey@3 2797
jlaskey@3 2798 return;
jlaskey@3 2799 }
jlaskey@3 2800
jlaskey@3 2801 set(key, JSType.toObject(value), strict);
jlaskey@3 2802 }
jlaskey@3 2803
jlaskey@3 2804 @Override
jlaskey@3 2805 public void set(final Object key, final Object value, final boolean strict) {
hannesw@334 2806 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2807
hannesw@334 2808 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2809 if (getArray().has(index)) {
jlaskey@3 2810 setArray(getArray().set(index, value, strict));
jlaskey@3 2811 } else {
jlaskey@3 2812 doesNotHave(index, value, strict);
jlaskey@3 2813 }
jlaskey@3 2814
jlaskey@3 2815 return;
jlaskey@3 2816 }
jlaskey@3 2817
hannesw@515 2818 final String propName = JSType.toString(key);
jlaskey@3 2819 final FindProperty find = findProperty(propName, true);
jlaskey@3 2820
jlaskey@3 2821 setObject(find, strict, propName, value);
jlaskey@3 2822 }
jlaskey@3 2823
jlaskey@3 2824 @Override
jlaskey@3 2825 public void set(final double key, final int value, final boolean strict) {
hannesw@334 2826 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2827
hannesw@334 2828 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2829 if (getArray().has(index)) {
jlaskey@3 2830 setArray(getArray().set(index, value, strict));
jlaskey@3 2831 } else {
jlaskey@3 2832 doesNotHave(index, value, strict);
jlaskey@3 2833 }
jlaskey@3 2834
jlaskey@3 2835 return;
jlaskey@3 2836 }
jlaskey@3 2837
jlaskey@3 2838 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2839 }
jlaskey@3 2840
jlaskey@3 2841 @Override
jlaskey@3 2842 public void set(final double key, final long value, final boolean strict) {
hannesw@334 2843 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2844
hannesw@334 2845 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2846 if (getArray().has(index)) {
jlaskey@3 2847 setArray(getArray().set(index, value, strict));
jlaskey@3 2848 } else {
jlaskey@3 2849 doesNotHave(index, value, strict);
jlaskey@3 2850 }
jlaskey@3 2851
jlaskey@3 2852 return;
jlaskey@3 2853 }
jlaskey@3 2854
jlaskey@3 2855 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2856 }
jlaskey@3 2857
jlaskey@3 2858 @Override
jlaskey@3 2859 public void set(final double key, final double value, final boolean strict) {
hannesw@334 2860 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2861
hannesw@334 2862 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2863 if (getArray().has(index)) {
jlaskey@3 2864 setArray(getArray().set(index, value, strict));
jlaskey@3 2865 } else {
jlaskey@3 2866 doesNotHave(index, value, strict);
jlaskey@3 2867 }
jlaskey@3 2868
jlaskey@3 2869 return;
jlaskey@3 2870 }
jlaskey@3 2871
jlaskey@3 2872 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2873 }
jlaskey@3 2874
jlaskey@3 2875 @Override
jlaskey@3 2876 public void set(final double key, final Object value, final boolean strict) {
hannesw@334 2877 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2878
hannesw@334 2879 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2880 if (getArray().has(index)) {
jlaskey@3 2881 setArray(getArray().set(index, value, strict));
jlaskey@3 2882 } else {
jlaskey@3 2883 doesNotHave(index, value, strict);
jlaskey@3 2884 }
jlaskey@3 2885
jlaskey@3 2886 return;
jlaskey@3 2887 }
jlaskey@3 2888
jlaskey@3 2889 set(JSType.toObject(key), value, strict);
jlaskey@3 2890 }
jlaskey@3 2891
jlaskey@3 2892 @Override
jlaskey@3 2893 public void set(final long key, final int value, final boolean strict) {
hannesw@334 2894 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2895
hannesw@334 2896 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2897 if (getArray().has(index)) {
jlaskey@3 2898 setArray(getArray().set(index, value, strict));
jlaskey@3 2899 } else {
jlaskey@3 2900 doesNotHave(index, value, strict);
jlaskey@3 2901 }
jlaskey@3 2902
jlaskey@3 2903 return;
jlaskey@3 2904 }
jlaskey@3 2905
jlaskey@3 2906 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2907 }
jlaskey@3 2908
jlaskey@3 2909 @Override
jlaskey@3 2910 public void set(final long key, final long value, final boolean strict) {
hannesw@334 2911 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2912
hannesw@334 2913 if (ArrayIndex.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
jlaskey@3 2923 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2924 }
jlaskey@3 2925
jlaskey@3 2926 @Override
jlaskey@3 2927 public void set(final long key, final double value, final boolean strict) {
hannesw@334 2928 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2929
hannesw@334 2930 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2931 if (getArray().has(index)) {
jlaskey@3 2932 setArray(getArray().set(index, value, strict));
jlaskey@3 2933 } else {
jlaskey@3 2934 doesNotHave(index, value, strict);
jlaskey@3 2935 }
jlaskey@3 2936
jlaskey@3 2937 return;
jlaskey@3 2938 }
jlaskey@3 2939
jlaskey@3 2940 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2941 }
jlaskey@3 2942
jlaskey@3 2943 @Override
jlaskey@3 2944 public void set(final long key, final Object value, final boolean strict) {
hannesw@334 2945 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2946
hannesw@334 2947 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2948 if (getArray().has(index)) {
jlaskey@3 2949 setArray(getArray().set(index, value, strict));
jlaskey@3 2950 } else {
jlaskey@3 2951 doesNotHave(index, value, strict);
jlaskey@3 2952 }
jlaskey@3 2953
jlaskey@3 2954 return;
jlaskey@3 2955 }
jlaskey@3 2956
jlaskey@3 2957 set(JSType.toObject(key), value, strict);
jlaskey@3 2958 }
jlaskey@3 2959
jlaskey@3 2960 @Override
jlaskey@3 2961 public void set(final int key, final int value, final boolean strict) {
hannesw@334 2962 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2963
hannesw@334 2964 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2965 if (getArray().has(index)) {
jlaskey@3 2966 setArray(getArray().set(index, value, strict));
jlaskey@3 2967 } else {
jlaskey@3 2968 doesNotHave(index, value, strict);
jlaskey@3 2969 }
jlaskey@3 2970
jlaskey@3 2971 return;
jlaskey@3 2972 }
jlaskey@3 2973
jlaskey@3 2974 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2975 }
jlaskey@3 2976
jlaskey@3 2977 @Override
jlaskey@3 2978 public void set(final int key, final long value, final boolean strict) {
hannesw@334 2979 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2980
hannesw@334 2981 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2982 if (getArray().has(index)) {
jlaskey@3 2983 setArray(getArray().set(index, value, strict));
jlaskey@3 2984 } else {
jlaskey@3 2985 doesNotHave(index, value, strict);
jlaskey@3 2986 }
jlaskey@3 2987
jlaskey@3 2988 return;
jlaskey@3 2989 }
jlaskey@3 2990
jlaskey@3 2991 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 2992 }
jlaskey@3 2993
jlaskey@3 2994 @Override
jlaskey@3 2995 public void set(final int key, final double value, final boolean strict) {
hannesw@334 2996 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 2997
hannesw@334 2998 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 2999 if (getArray().has(index)) {
jlaskey@3 3000 setArray(getArray().set(index, value, strict));
jlaskey@3 3001 } else {
jlaskey@3 3002 doesNotHave(index, value, strict);
jlaskey@3 3003 }
jlaskey@3 3004
jlaskey@3 3005 return;
jlaskey@3 3006 }
jlaskey@3 3007
jlaskey@3 3008 set(JSType.toObject(key), JSType.toObject(value), strict);
jlaskey@3 3009 }
jlaskey@3 3010
jlaskey@3 3011 @Override
jlaskey@3 3012 public void set(final int key, final Object value, final boolean strict) {
hannesw@334 3013 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 3014
hannesw@334 3015 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 3016 if (getArray().has(index)) {
jlaskey@3 3017 setArray(getArray().set(index, value, strict));
jlaskey@3 3018 } else {
jlaskey@3 3019 doesNotHave(index, value, strict);
jlaskey@3 3020 }
jlaskey@3 3021
jlaskey@3 3022 return;
jlaskey@3 3023 }
jlaskey@3 3024
jlaskey@3 3025 set(JSType.toObject(key), value, strict);
jlaskey@3 3026 }
jlaskey@3 3027
jlaskey@3 3028 @Override
jlaskey@3 3029 public boolean has(final Object key) {
hannesw@334 3030 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 3031
hannesw@334 3032 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 3033 for (ScriptObject self = this; self != null; self = self.getProto()) {
jlaskey@3 3034 if (self.getArray().has(index)) {
jlaskey@3 3035 return true;
jlaskey@3 3036 }
jlaskey@3 3037 }
jlaskey@3 3038 }
jlaskey@3 3039
hannesw@515 3040 final FindProperty find = findProperty(JSType.toString(key), true);
jlaskey@3 3041
jlaskey@3 3042 return find != null;
jlaskey@3 3043 }
jlaskey@3 3044
jlaskey@3 3045 @Override
jlaskey@3 3046 public boolean has(final double key) {
hannesw@334 3047 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 3048
hannesw@334 3049 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 3050 for (ScriptObject self = this; self != null; self = self.getProto()) {
jlaskey@3 3051 if (self.getArray().has(index)) {
jlaskey@3 3052 return true;
jlaskey@3 3053 }
jlaskey@3 3054 }
jlaskey@3 3055 }
jlaskey@3 3056
hannesw@515 3057 final FindProperty find = findProperty(JSType.toString(key), true);
jlaskey@3 3058
jlaskey@3 3059 return find != null;
jlaskey@3 3060 }
jlaskey@3 3061
jlaskey@3 3062 @Override
jlaskey@3 3063 public boolean has(final long key) {
hannesw@334 3064 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 3065
hannesw@334 3066 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 3067 for (ScriptObject self = this; self != null; self = self.getProto()) {
jlaskey@3 3068 if (self.getArray().has(index)) {
jlaskey@3 3069 return true;
jlaskey@3 3070 }
jlaskey@3 3071 }
jlaskey@3 3072 }
jlaskey@3 3073
hannesw@515 3074 final FindProperty find = findProperty(JSType.toString(key), true);
jlaskey@3 3075
jlaskey@3 3076 return find != null;
jlaskey@3 3077 }
jlaskey@3 3078
jlaskey@3 3079 @Override
jlaskey@3 3080 public boolean has(final int key) {
hannesw@334 3081 final int index = ArrayIndex.getArrayIndex(key);
hannesw@334 3082
hannesw@334 3083 if (ArrayIndex.isValidArrayIndex(index)) {
jlaskey@3 3084 for (ScriptObject self = this; self != null; self = self.getProto()) {
jlaskey@3 3085 if (self.getArray().has(index)) {
jlaskey@3 3086 return true;
jlaskey@3 3087 }
jlaskey@3 3088 }
jlaskey@3 3089 }
jlaskey@3 3090
hannesw@515 3091 final FindProperty find = findProperty(JSType.toString(key), true);
jlaskey@3 3092
jlaskey@3 3093 return find != null;
jlaskey@3 3094 }
jlaskey@3 3095
jlaskey@3 3096 @Override
jlaskey@3 3097 public boolean hasOwnProperty(final Object key) {
hannesw@334 3098 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3099
jlaskey@3 3100 if (getArray().has(index)) {
jlaskey@3 3101 return true;
jlaskey@3 3102 }
jlaskey@3 3103
hannesw@515 3104 final FindProperty find = findProperty(JSType.toString(key), false);
jlaskey@3 3105
jlaskey@3 3106 return find != null;
jlaskey@3 3107 }
jlaskey@3 3108
jlaskey@3 3109 @Override
jlaskey@3 3110 public boolean hasOwnProperty(final int key) {
hannesw@334 3111 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3112
jlaskey@3 3113 if (getArray().has(index)) {
jlaskey@3 3114 return true;
jlaskey@3 3115 }
jlaskey@3 3116
hannesw@515 3117 final FindProperty find = findProperty(JSType.toString(key), false);
jlaskey@3 3118
jlaskey@3 3119 return find != null;
jlaskey@3 3120 }
jlaskey@3 3121
jlaskey@3 3122 @Override
jlaskey@3 3123 public boolean hasOwnProperty(final long key) {
hannesw@334 3124 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3125
jlaskey@3 3126 if (getArray().has(index)) {
jlaskey@3 3127 return true;
jlaskey@3 3128 }
jlaskey@3 3129
hannesw@515 3130 final FindProperty find = findProperty(JSType.toString(key), false);
jlaskey@3 3131
jlaskey@3 3132 return find != null;
jlaskey@3 3133 }
jlaskey@3 3134
jlaskey@3 3135 @Override
jlaskey@3 3136 public boolean hasOwnProperty(final double key) {
hannesw@334 3137 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3138
jlaskey@3 3139 if (getArray().has(index)) {
jlaskey@3 3140 return true;
jlaskey@3 3141 }
jlaskey@3 3142
hannesw@515 3143 final FindProperty find = findProperty(JSType.toString(key), false);
jlaskey@3 3144
jlaskey@3 3145 return find != null;
jlaskey@3 3146 }
jlaskey@3 3147
jlaskey@3 3148 @Override
jlaskey@3 3149 public boolean delete(final int key, final boolean strict) {
hannesw@334 3150 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3151 final ArrayData array = getArray();
jlaskey@3 3152
jlaskey@3 3153 if (array.has(index)) {
jlaskey@3 3154 if (array.canDelete(index, strict)) {
jlaskey@3 3155 setArray(array.delete(index));
jlaskey@3 3156 return true;
jlaskey@3 3157 }
jlaskey@3 3158 return false;
jlaskey@3 3159 }
jlaskey@3 3160
jlaskey@3 3161 return deleteObject(JSType.toObject(key), strict);
jlaskey@3 3162 }
jlaskey@3 3163
jlaskey@3 3164 @Override
jlaskey@3 3165 public boolean delete(final long key, final boolean strict) {
hannesw@334 3166 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3167 final ArrayData array = getArray();
jlaskey@3 3168
jlaskey@3 3169 if (array.has(index)) {
jlaskey@3 3170 if (array.canDelete(index, strict)) {
jlaskey@3 3171 setArray(array.delete(index));
jlaskey@3 3172 return true;
jlaskey@3 3173 }
jlaskey@3 3174 return false;
jlaskey@3 3175 }
jlaskey@3 3176
jlaskey@3 3177 return deleteObject(JSType.toObject(key), strict);
jlaskey@3 3178 }
jlaskey@3 3179
jlaskey@3 3180 @Override
jlaskey@3 3181 public boolean delete(final double key, final boolean strict) {
hannesw@334 3182 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3183 final ArrayData array = getArray();
jlaskey@3 3184
jlaskey@3 3185 if (array.has(index)) {
jlaskey@3 3186 if (array.canDelete(index, strict)) {
jlaskey@3 3187 setArray(array.delete(index));
jlaskey@3 3188 return true;
jlaskey@3 3189 }
jlaskey@3 3190 return false;
jlaskey@3 3191 }
jlaskey@3 3192
jlaskey@3 3193 return deleteObject(JSType.toObject(key), strict);
jlaskey@3 3194 }
jlaskey@3 3195
jlaskey@3 3196 @Override
jlaskey@3 3197 public boolean delete(final Object key, final boolean strict) {
hannesw@334 3198 final int index = ArrayIndex.getArrayIndex(key);
jlaskey@3 3199 final ArrayData array = getArray();
jlaskey@3 3200
jlaskey@3 3201 if (array.has(index)) {
jlaskey@3 3202 if (array.canDelete(index, strict)) {
jlaskey@3 3203 setArray(array.delete(index));
jlaskey@3 3204 return true;
jlaskey@3 3205 }
jlaskey@3 3206 return false;
jlaskey@3 3207 }
jlaskey@3 3208
jlaskey@3 3209 return deleteObject(key, strict);
jlaskey@3 3210 }
jlaskey@3 3211
jlaskey@3 3212 private boolean deleteObject(final Object key, final boolean strict) {
hannesw@515 3213 final String propName = JSType.toString(key);
jlaskey@3 3214 final FindProperty find = findProperty(propName, false);
jlaskey@3 3215
jlaskey@3 3216 if (find == null) {
jlaskey@3 3217 return true;
jlaskey@3 3218 }
jlaskey@3 3219
jlaskey@80 3220 if (!find.getProperty().isConfigurable()) {
jlaskey@3 3221 if (strict) {
lagergren@112 3222 throw typeError("cant.delete.property", propName, ScriptRuntime.safeToString(this));
jlaskey@3 3223 }
jlaskey@3 3224 return false;
jlaskey@3 3225 }
jlaskey@3 3226
jlaskey@3 3227 final Property prop = find.getProperty();
jlaskey@3 3228 notifyPropertyDeleted(this, prop);
jlaskey@3 3229 deleteOwnProperty(prop);
jlaskey@3 3230
jlaskey@3 3231 return true;
jlaskey@3 3232 }
jlaskey@3 3233
sundar@468 3234 /**
jlaskey@3 3235 * Make a new UserAccessorProperty property. getter and setter functions are stored in
jlaskey@3 3236 * this ScriptObject and slot values are used in property object.
sundar@468 3237 *
sundar@468 3238 * @param key the property name
sundar@468 3239 * @param propertyFlags attribute flags of the property
sundar@468 3240 * @param getter getter function for the property
sundar@468 3241 * @param setter setter function for the property
sundar@468 3242 * @return the newly created UserAccessorProperty
jlaskey@3 3243 */
jlaskey@379 3244 protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
jlaskey@379 3245 final UserAccessorProperty property = getMap().newUserAccessors(key, propertyFlags);
jlaskey@379 3246 setSpill(property.getGetterSlot(), getter);
jlaskey@379 3247 setSpill(property.getSetterSlot(), setter);
jlaskey@379 3248
jlaskey@379 3249 return property;
jlaskey@379 3250 }
jlaskey@379 3251
lagergren@505 3252 /**
lagergren@505 3253 * Write a value to a spill slot
lagergren@505 3254 * @param slot the slot index
lagergren@505 3255 * @param value the value
lagergren@505 3256 */
jlaskey@379 3257 protected final void setSpill(final int slot, final Object value) {
jlaskey@379 3258 if (spill == null) {
jlaskey@379 3259 // create new spill.
jlaskey@379 3260 spill = new Object[Math.max(slot + 1, SPILL_RATE)];
jlaskey@379 3261 } else if (slot >= spill.length) {
jlaskey@379 3262 // grow spill as needed
jlaskey@379 3263 final Object[] newSpill = new Object[slot + 1];
jlaskey@379 3264 System.arraycopy(spill, 0, newSpill, 0, spill.length);
jlaskey@379 3265 spill = newSpill;
jlaskey@3 3266 }
jlaskey@3 3267
jlaskey@379 3268 spill[slot] = value;
jlaskey@3 3269 }
jlaskey@3 3270
lagergren@505 3271 /**
lagergren@505 3272 * Get a value from a spill slot
lagergren@505 3273 * @param slot the slot index
lagergren@505 3274 * @return the value in the spill slot with the given index
lagergren@505 3275 */
jlaskey@379 3276 protected Object getSpill(final int slot) {
jlaskey@379 3277 return spill != null && slot < spill.length ? spill[slot] : null;
jlaskey@3 3278 }
jlaskey@3 3279
jlaskey@3 3280 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
jlaskey@3 3281 final Class<?> own = ScriptObject.class;
jlaskey@3 3282 final MethodType mt = MH.type(rtype, types);
jlaskey@3 3283 try {
jlaskey@3 3284 return MH.findStatic(MethodHandles.lookup(), own, name, mt);
jlaskey@3 3285 } catch (final MethodHandleFactory.LookupException e) {
jlaskey@3 3286 return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
jlaskey@3 3287 }
jlaskey@3 3288 }
jlaskey@3 3289
jlaskey@3 3290 private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
jlaskey@3 3291 return MH.insertArguments(KNOWNFUNCPROPGUARD, 1, map, getter, where, func);
jlaskey@3 3292 }
jlaskey@3 3293
jlaskey@3 3294 @SuppressWarnings("unused")
jlaskey@3 3295 private static boolean knownFunctionPropertyGuard(final Object self, final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
jlaskey@3 3296 if (self instanceof ScriptObject && ((ScriptObject)self).getMap() == map) {
jlaskey@3 3297 try {
jlaskey@3 3298 return getter.invokeExact(where) == func;
jlaskey@3 3299 } catch (final RuntimeException | Error e) {
jlaskey@3 3300 throw e;
jlaskey@3 3301 } catch (final Throwable t) {
jlaskey@3 3302 throw new RuntimeException(t);
jlaskey@3 3303 }
jlaskey@3 3304 }
jlaskey@3 3305
jlaskey@3 3306 return false;
jlaskey@3 3307 }
jlaskey@3 3308
jlaskey@3 3309 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created */
sundar@61 3310 private static int count;
jlaskey@3 3311
jlaskey@3 3312 /** This is updated only in debug mode - counts number of {@code ScriptObject} instances created that are scope */
sundar@61 3313 private static int scopeCount;
jlaskey@3 3314
jlaskey@3 3315 /**
jlaskey@3 3316 * Get number of {@code ScriptObject} instances created. If not running in debug
jlaskey@3 3317 * mode this is always 0
jlaskey@3 3318 *
jlaskey@3 3319 * @return number of ScriptObjects created
jlaskey@3 3320 */
jlaskey@3 3321 public static int getCount() {
jlaskey@3 3322 return count;
jlaskey@3 3323 }
jlaskey@3 3324
jlaskey@3 3325 /**
jlaskey@3 3326 * Get number of scope {@code ScriptObject} instances created. If not running in debug
jlaskey@3 3327 * mode this is always 0
jlaskey@3 3328 *
jlaskey@3 3329 * @return number of scope ScriptObjects created
jlaskey@3 3330 */
jlaskey@3 3331 public static int getScopeCount() {
jlaskey@3 3332 return scopeCount;
jlaskey@3 3333 }
jlaskey@3 3334
jlaskey@3 3335 }

mercurial