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