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