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