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