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