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