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