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