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