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