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