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