1 /* 2 * Copyright (c) 2010, 2019, 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 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 29 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 30 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 31 import java.io.Serializable; 32 import java.lang.invoke.MethodHandle; 33 import java.lang.invoke.SwitchPoint; 34 import java.util.Objects; 35 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 36 37 /** 38 * This is the abstract superclass representing a JavaScript Property. 39 * The {@link PropertyMap} map links keys to properties, and consequently 40 * instances of this class make up the values in the PropertyMap 41 * 42 * @see PropertyMap 43 * @see AccessorProperty 44 * @see UserAccessorProperty 45 */ 46 public abstract class Property implements Serializable { 47 /* 48 * ECMA 8.6.1 Property Attributes 49 * 50 * We use negative flags because most properties are expected to 51 * be 'writable', 'configurable' and 'enumerable'. With negative flags, 52 * we can use leave flag byte initialized with (the default) zero value. 53 */ 54 55 /** Mask for property being both writable, enumerable and configurable */ 56 public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000; 57 58 /** ECMA 8.6.1 - Is this property not writable? */ 59 public static final int NOT_WRITABLE = 1 << 0; 60 61 /** ECMA 8.6.1 - Is this property not enumerable? */ 62 public static final int NOT_ENUMERABLE = 1 << 1; 63 64 /** ECMA 8.6.1 - Is this property not configurable? */ 65 public static final int NOT_CONFIGURABLE = 1 << 2; 66 67 private static final int MODIFY_MASK = NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE; 68 69 /** Is this a function parameter? */ 70 public static final int IS_PARAMETER = 1 << 3; 71 72 /** Is parameter accessed thru arguments? */ 73 public static final int HAS_ARGUMENTS = 1 << 4; 74 75 /** Is this a function declaration property ? */ 76 public static final int IS_FUNCTION_DECLARATION = 1 << 5; 77 78 /** 79 * Is this is a primitive field given to us by Nasgen, i.e. 80 * something we can be sure remains a constant whose type 81 * is narrower than object, e.g. Math.PI which is declared 82 * as a double 83 */ 84 public static final int IS_NASGEN_PRIMITIVE = 1 << 6; 85 86 /** Is this a builtin property, e.g. Function.prototype.apply */ 87 public static final int IS_BUILTIN = 1 << 7; 88 89 /** Is this property bound to a receiver? This means get/set operations will be delegated to 90 * a statically defined object instead of the object passed as callsite parameter. */ 91 public static final int IS_BOUND = 1 << 8; 92 93 /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */ 94 public static final int NEEDS_DECLARATION = 1 << 9; 95 96 /** Is this property an ES6 lexical binding? */ 97 public static final int IS_LEXICAL_BINDING = 1 << 10; 98 99 /** Does this property support dual field representation? */ 100 public static final int DUAL_FIELDS = 1 << 11; 101 102 /** Is this an accessor property as as defined in ES5 8.6.1? */ 103 public static final int IS_ACCESSOR_PROPERTY = 1 << 12; 104 105 /** Property key. */ 106 @SuppressWarnings("serial") // Not statically typed as Serializable 107 private final Object key; 108 109 /** Property flags. */ 110 private int flags; 111 112 /** Property field number or spill slot. */ 113 private final int slot; 114 115 /** 116 * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode 117 * null means undefined, and primitive types are allowed. The reason a special type is used for 118 * undefined, is that are no bits left to represent it in primitive types 119 */ 120 private Class<?> type; 121 122 /** SwitchPoint that is invalidated when property is changed, optional */ 123 protected transient SwitchPoint builtinSwitchPoint; 124 125 private static final long serialVersionUID = 2099814273074501176L; 126 127 /** 128 * Constructor 129 * 130 * @param key property key 131 * @param flags property flags 132 * @param slot property field number or spill slot 133 */ 134 Property(final Object key, final int flags, final int slot) { 135 assert key != null; 136 this.key = key; 137 this.flags = flags; 138 this.slot = slot; 139 } 140 141 /** 142 * Copy constructor 143 * 144 * @param property source property 145 */ 146 Property(final Property property, final int flags) { 147 this.key = property.key; 148 this.slot = property.slot; 149 this.builtinSwitchPoint = property.builtinSwitchPoint; 150 this.flags = flags; 151 } 152 153 /** 154 * Copy function 155 * 156 * @return cloned property 157 */ 158 public abstract Property copy(); 159 160 /** 161 * Copy function 162 * 163 * @param newType new type 164 * @return cloned property with new type 165 */ 166 public abstract Property copy(final Class<?> newType); 167 168 /** 169 * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors, 170 * return the result of merging their flags. 171 * 172 * @param oldDesc first property descriptor 173 * @param newDesc second property descriptor 174 * @return merged flags. 175 */ 176 static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) { 177 int propFlags = 0; 178 boolean value; 179 180 value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable(); 181 if (!value) { 182 propFlags |= NOT_CONFIGURABLE; 183 } 184 185 value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable(); 186 if (!value) { 187 propFlags |= NOT_ENUMERABLE; 188 } 189 190 value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable(); 191 if (!value) { 192 propFlags |= NOT_WRITABLE; 193 } 194 195 return propFlags; 196 } 197 198 /** 199 * Set the change callback for this property, i.e. a SwitchPoint 200 * that will be invalidated when the value of the property is 201 * changed 202 * @param sp SwitchPoint to use for change callback 203 */ 204 public final void setBuiltinSwitchPoint(final SwitchPoint sp) { 205 this.builtinSwitchPoint = sp; 206 } 207 208 /** 209 * Builtin properties have an invalidation switchpoint that is 210 * invalidated when they are set, this is a getter for it 211 * @return builtin switchpoint, or null if none 212 */ 213 public final SwitchPoint getBuiltinSwitchPoint() { 214 return builtinSwitchPoint; 215 } 216 217 /** 218 * Checks if this is a builtin property, this means that it has 219 * a builtin switchpoint that hasn't been invalidated by a setter 220 * @return true if builtin, untouched (unset) property 221 */ 222 public boolean isBuiltin() { 223 return builtinSwitchPoint != null && !builtinSwitchPoint.hasBeenInvalidated(); 224 } 225 226 /** 227 * Property flag utility method for {@link PropertyDescriptor}. Get the property flags 228 * conforming to any Property using this PropertyDescriptor 229 * 230 * @param desc property descriptor 231 * @return flags for properties that conform to property descriptor 232 */ 233 static int toFlags(final PropertyDescriptor desc) { 234 int propFlags = 0; 235 236 if (!desc.isConfigurable()) { 237 propFlags |= NOT_CONFIGURABLE; 238 } 239 if (!desc.isEnumerable()) { 240 propFlags |= NOT_ENUMERABLE; 241 } 242 if (!desc.isWritable()) { 243 propFlags |= NOT_WRITABLE; 244 } 245 246 return propFlags; 247 } 248 249 /** 250 * Check whether this property has a user defined getter function. See {@link UserAccessorProperty} 251 * @param obj object containing getter 252 * @return true if getter function exists, false is default 253 */ 254 public boolean hasGetterFunction(final ScriptObject obj) { 255 return false; 256 } 257 258 /** 259 * Check whether this property has a user defined setter function. See {@link UserAccessorProperty} 260 * @param obj object containing setter 261 * @return true if getter function exists, false is default 262 */ 263 public boolean hasSetterFunction(final ScriptObject obj) { 264 return false; 265 } 266 267 /** 268 * Check whether this property is writable (see ECMA 8.6.1) 269 * @return true if writable 270 */ 271 public boolean isWritable() { 272 return (flags & NOT_WRITABLE) == 0; 273 } 274 275 /** 276 * Check whether this property is writable (see ECMA 8.6.1) 277 * @return true if configurable 278 */ 279 public boolean isConfigurable() { 280 return (flags & NOT_CONFIGURABLE) == 0; 281 } 282 283 /** 284 * Check whether this property is enumerable (see ECMA 8.6.1) 285 * @return true if enumerable 286 */ 287 public boolean isEnumerable() { 288 return (flags & NOT_ENUMERABLE) == 0; 289 } 290 291 /** 292 * Check whether this property is used as a function parameter 293 * @return true if parameter 294 */ 295 public boolean isParameter() { 296 return (flags & IS_PARAMETER) != 0; 297 } 298 299 /** 300 * Check whether this property is in an object with arguments field 301 * @return true if has arguments 302 */ 303 public boolean hasArguments() { 304 return (flags & HAS_ARGUMENTS) != 0; 305 } 306 307 /** 308 * Check whether this is a spill property, i.e. one that will not 309 * be stored in a specially generated field in the property class. 310 * The spill pool is maintained separately, as a growing Object array 311 * in the {@link ScriptObject}. 312 * 313 * @return true if spill property 314 */ 315 public boolean isSpill() { 316 return false; 317 } 318 319 /** 320 * Is this property bound to a receiver? If this method returns {@code true} get and set operations 321 * will be delegated to a statically bound object instead of the object passed as parameter. 322 * 323 * @return true if this is a bound property 324 */ 325 public boolean isBound() { 326 return (flags & IS_BOUND) != 0; 327 } 328 329 /** 330 * Is this a LET or CONST property that needs to see its declaration before being usable? 331 * 332 * @return true if this is a block-scoped variable 333 */ 334 public boolean needsDeclaration() { 335 return (flags & NEEDS_DECLARATION) != 0; 336 } 337 338 /** 339 * Add more property flags to the property. Properties are immutable here, 340 * so any property change that results in a larger flag set results in the 341 * property being cloned. Use only the return value 342 * 343 * @param propertyFlags flags to be OR:ed to the existing property flags 344 * @return new property if property set was changed, {@code this} otherwise 345 */ 346 public Property addFlags(final int propertyFlags) { 347 if ((this.flags & propertyFlags) != propertyFlags) { 348 final Property cloned = this.copy(); 349 cloned.flags |= propertyFlags; 350 return cloned; 351 } 352 return this; 353 } 354 355 /** 356 * Get the flags for this property 357 * @return property flags 358 */ 359 public int getFlags() { 360 return flags; 361 } 362 363 /** 364 * Remove property flags from the property. Properties are immutable here, 365 * so any property change that results in a smaller flag set results in the 366 * property being cloned. Use only the return value 367 * 368 * @param propertyFlags flags to be subtracted from the existing property flags 369 * @return new property if property set was changed, {@code this} otherwise 370 */ 371 public Property removeFlags(final int propertyFlags) { 372 if ((this.flags & propertyFlags) != 0) { 373 final Property cloned = this.copy(); 374 cloned.flags &= ~propertyFlags; 375 return cloned; 376 } 377 return this; 378 } 379 380 /** 381 * Reset the property for this property. Properties are immutable here, 382 * so any property change that results in a different flag sets results in the 383 * property being cloned. Use only the return value 384 * 385 * @param propertyFlags flags that are replacing from the existing property flags 386 * @return new property if property set was changed, {@code this} otherwise 387 */ 388 public Property setFlags(final int propertyFlags) { 389 if (this.flags != propertyFlags) { 390 final Property cloned = this.copy(); 391 cloned.flags &= ~MODIFY_MASK; 392 cloned.flags |= propertyFlags & MODIFY_MASK; 393 return cloned; 394 } 395 return this; 396 } 397 398 /** 399 * Abstract method for retrieving the getter for the property. We do not know 400 * anything about the internal representation when we request the getter, we only 401 * know that the getter will return the property as the given type. 402 * 403 * @param type getter return value type 404 * @return a getter for this property as {@code type} 405 */ 406 public abstract MethodHandle getGetter(final Class<?> type); 407 408 /** 409 * Get an optimistic getter that throws an exception if type is not the known given one 410 * @param type type 411 * @param programPoint program point 412 * @return getter 413 */ 414 public abstract MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint); 415 416 /** 417 * Hook to initialize method handles after deserialization. 418 * 419 * @param structure the structure class 420 */ 421 abstract void initMethodHandles(final Class<?> structure); 422 423 /** 424 * Get the key for this property. This key is an ordinary string. The "name". 425 * @return key for property 426 */ 427 public Object getKey() { 428 return key; 429 } 430 431 /** 432 * Get the field number or spill slot 433 * @return number/slot, -1 if none exists 434 */ 435 public int getSlot() { 436 return slot; 437 } 438 439 /** 440 * get the Object value of this property from {@code owner}. This allows to bypass creation of the 441 * getter MethodHandle for spill and user accessor properties. 442 * 443 * @param self the this object 444 * @param owner the owner of the property 445 * @return the property value 446 */ 447 public abstract int getIntValue(final ScriptObject self, final ScriptObject owner); 448 449 /** 450 * get the Object value of this property from {@code owner}. This allows to bypass creation of the 451 * getter MethodHandle for spill and user accessor properties. 452 * 453 * @param self the this object 454 * @param owner the owner of the property 455 * @return the property value 456 */ 457 public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner); 458 459 /** 460 * get the Object value of this property from {@code owner}. This allows to bypass creation of the 461 * getter MethodHandle for spill and user accessor properties. 462 * 463 * @param self the this object 464 * @param owner the owner of the property 465 * @return the property value 466 */ 467 public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner); 468 469 /** 470 * Set the value of this property in {@code owner}. This allows to bypass creation of the 471 * setter MethodHandle for spill and user accessor properties. 472 * 473 * @param self the this object 474 * @param owner the owner object 475 * @param value the new property value 476 * @param strict is this a strict setter? 477 */ 478 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict); 479 480 /** 481 * Set the value of this property in {@code owner}. This allows to bypass creation of the 482 * setter MethodHandle for spill and user accessor properties. 483 * 484 * @param self the this object 485 * @param owner the owner object 486 * @param value the new property value 487 * @param strict is this a strict setter? 488 */ 489 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict); 490 491 /** 492 * Set the value of this property in {@code owner}. This allows to bypass creation of the 493 * setter MethodHandle for spill and user accessor properties. 494 * 495 * @param self the this object 496 * @param owner the owner object 497 * @param value the new property value 498 * @param strict is this a strict setter? 499 */ 500 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict); 501 502 /** 503 * Returns true if this property has a low-level setter handle. This can be used to determine whether a 504 * nasgen-generated accessor property should be treated as non-writable. For user-created accessor properties 505 * {@link #hasSetterFunction(ScriptObject)} should be used to find whether a setter function exists in 506 * a given object. 507 * 508 * @return true if a native setter handle exists 509 */ 510 public abstract boolean hasNativeSetter(); 511 512 /** 513 * Abstract method for retrieving the setter for the property. We do not know 514 * anything about the internal representation when we request the setter, we only 515 * know that the setter will take the property as a parameter of the given type. 516 * <p> 517 * Note that we have to pass the current property map from which we retrieved 518 * the property here. This is necessary for map guards if, e.g. the internal 519 * representation of the field, and consequently also the setter, changes. Then 520 * we automatically get a map guard that relinks the call site so that the 521 * older setter will never be used again. 522 * <p> 523 * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} 524 * if you are interested in the internal details of this. Note that if you 525 * are running with {@code -Dnashorn.fields.objects=true}, the setters 526 * will currently never change, as all properties are represented as Object field, 527 * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are 528 * boxed/unboxed upon every access, which is not necessarily optimal 529 * 530 * @param type setter parameter type 531 * @param currentMap current property map for property 532 * @return a getter for this property as {@code type} 533 */ 534 public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap); 535 536 /** 537 * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances 538 * can have user defined getters 539 * @param obj the script object 540 * @return user defined getter function, or {@code null} if none exists 541 */ 542 public ScriptFunction getGetterFunction(final ScriptObject obj) { 543 return null; 544 } 545 546 /** 547 * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances 548 * can have user defined getters 549 * @param obj the script object 550 * @return user defined getter function, or {@code null} if none exists 551 */ 552 public ScriptFunction getSetterFunction(final ScriptObject obj) { 553 return null; 554 } 555 556 @Override 557 public int hashCode() { 558 final Class<?> t = getLocalType(); 559 return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (t == null ? 0 : t.hashCode()); 560 } 561 562 @Override 563 public boolean equals(final Object other) { 564 if (this == other) { 565 return true; 566 } 567 568 if (other == null || this.getClass() != other.getClass()) { 569 return false; 570 } 571 572 final Property otherProperty = (Property)other; 573 574 return equalsWithoutType(otherProperty) && 575 getLocalType() == otherProperty.getLocalType(); 576 } 577 578 boolean equalsWithoutType(final Property otherProperty) { 579 return getFlags() == otherProperty.getFlags() && 580 getSlot() == otherProperty.getSlot() && 581 getKey().equals(otherProperty.getKey()); 582 } 583 584 private static String type(final Class<?> type) { 585 if (type == null) { 586 return "undef"; 587 } else if (type == int.class) { 588 return "i"; 589 } else if (type == double.class) { 590 return "d"; 591 } else { 592 return "o"; 593 } 594 } 595 596 /** 597 * Short toString version 598 * @return short toString 599 */ 600 public final String toStringShort() { 601 final StringBuilder sb = new StringBuilder(); 602 final Class<?> t = getLocalType(); 603 sb.append(getKey()).append(" (").append(type(t)).append(')'); 604 return sb.toString(); 605 } 606 607 private static String indent(final String str, final int indent) { 608 final StringBuilder sb = new StringBuilder(); 609 sb.append(str); 610 for (int i = 0; i < indent - str.length(); i++) { 611 sb.append(' '); 612 } 613 return sb.toString(); 614 } 615 616 @Override 617 public String toString() { 618 final StringBuilder sb = new StringBuilder(); 619 final Class<?> t = getLocalType(); 620 621 sb.append(indent(getKey().toString(), 20)). 622 append(" id="). 623 append(Debug.id(this)). 624 append(" (0x"). 625 append(indent(Integer.toHexString(flags), 4)). 626 append(") "). 627 append(getClass().getSimpleName()). 628 append(" {"). 629 append(indent(type(t), 5)). 630 append('}'); 631 632 if (slot != -1) { 633 sb.append(" ["). 634 append("slot="). 635 append(slot). 636 append(']'); 637 } 638 639 return sb.toString(); 640 } 641 642 /** 643 * Get the current type of this property. If you are running with object fields enabled, 644 * this will always be Object.class. See the value representation explanation in 645 * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} 646 * for more information. 647 * 648 * <p>Note that for user accessor properties, this returns the type of the last observed 649 * value passed to or returned by a user accessor. Use {@link #getLocalType()} to always get 650 * the type of the actual value stored in the property slot.</p> 651 * 652 * @return current type of property, null means undefined 653 */ 654 public final Class<?> getType() { 655 return type; 656 } 657 658 /** 659 * Set the type of this property. 660 * @param type new type 661 */ 662 public final void setType(final Class<?> type) { 663 assert type != boolean.class : "no boolean storage support yet - fix this"; 664 this.type = type == null ? null : type.isPrimitive() ? type : Object.class; 665 } 666 667 /** 668 * Get the type of the value in the local property slot. This returns the same as 669 * {@link #getType()} for normal properties, but always returns {@code Object.class} 670 * for {@link UserAccessorProperty}s as their local type is a pair of accessor references. 671 * 672 * @return the local property type 673 */ 674 protected Class<?> getLocalType() { 675 return getType(); 676 } 677 678 /** 679 * Check whether this Property can ever change its type. The default is false, and if 680 * you are not running with dual fields, the type is always object and can never change 681 * @return true if this property can change types 682 */ 683 public boolean canChangeType() { 684 return false; 685 } 686 687 /** 688 * Check whether this property represents a function declaration. 689 * @return whether this property is a function declaration or not. 690 */ 691 public boolean isFunctionDeclaration() { 692 return (flags & IS_FUNCTION_DECLARATION) != 0; 693 } 694 695 /** 696 * Is this a property defined by ES6 let or const? 697 * @return true if this property represents a lexical binding. 698 */ 699 public boolean isLexicalBinding() { 700 return (flags & IS_LEXICAL_BINDING) != 0; 701 } 702 703 /** 704 * Does this property support dual fields for both primitive and object values? 705 * @return true if supports dual fields 706 */ 707 public boolean hasDualFields() { 708 return (flags & DUAL_FIELDS) != 0; 709 } 710 711 /** 712 * Is this an accessor property as defined in ES5 8.6.1? 713 * @return true if this is an accessor property 714 */ 715 public boolean isAccessorProperty() { 716 return (flags & IS_ACCESSOR_PROPERTY) != 0; 717 } 718 }