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