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