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