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