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