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