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