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