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