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