1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
  29 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
  30 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
  31 
  32 import java.lang.invoke.MethodHandle;
  33 import java.util.Objects;
  34 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  35 import jdk.nashorn.internal.codegen.types.Type;
  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 {
  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 spill property? See {@link AccessorProperty} */
  70     public static final int IS_SPILL         = 1 << 3;
  71 
  72     /** Is this a function parameter? */
  73     public static final int IS_PARAMETER     = 1 << 4;
  74 
  75     /** Is parameter accessed thru arguments? */
  76     public static final int HAS_ARGUMENTS    = 1 << 5;
  77 
  78     /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */
  79     public static final int IS_ALWAYS_OBJECT = 1 << 6;
  80 
  81     /** Can this property be primitive? */
  82     public static final int CAN_BE_PRIMITIVE = 1 << 7;
  83 
  84     /** Can this property be undefined? */
  85     public static final int CAN_BE_UNDEFINED = 1 << 8;
  86 
  87     /* Is this a function declaration property ? */
  88     public static final int IS_FUNCTION_DECLARATION = 1 << 9;
  89 
  90     /* Is this property bound bound to a receiver? */
  91     public static final int IS_BOUND = 1 << 10;
  92 
  93     /** Property key. */
  94     private final String key;
  95 
  96     /** Property flags. */
  97     protected int flags;
  98 
  99     /** Property field number or spill slot. */
 100     private final int slot;
 101 
 102     /**
 103      * Constructor
 104      *
 105      * @param key   property key
 106      * @param flags property flags
 107      * @param slot  property field number or spill slot
 108      */
 109     Property(final String key, final int flags, final int slot) {
 110         assert key != null;
 111         this.key   = key;
 112         this.flags = flags;
 113         this.slot  = slot;
 114     }
 115 
 116     /**
 117      * Copy constructor
 118      *
 119      * @param property source property
 120      */
 121     Property(final Property property) {
 122         this.key   = property.key;
 123         this.flags = property.flags;
 124         this.slot  = property.slot;
 125     }
 126 
 127     /**
 128      * Copy function
 129      *
 130      * @return cloned property
 131      */
 132     abstract Property copy();
 133 
 134     /**
 135      * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
 136      * return the result of merging their flags.
 137      *
 138      * @param oldDesc  first property descriptor
 139      * @param newDesc  second property descriptor
 140      * @return merged flags.
 141      */
 142     static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) {
 143         int     propFlags = 0;
 144         boolean value;
 145 
 146         value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable();
 147         if (!value) {
 148             propFlags |= NOT_CONFIGURABLE;
 149         }
 150 
 151         value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable();
 152         if (!value) {
 153             propFlags |= NOT_ENUMERABLE;
 154         }
 155 
 156         value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable();
 157         if (!value) {
 158             propFlags |= NOT_WRITABLE;
 159         }
 160 
 161         return propFlags;
 162     }
 163 
 164     /**
 165      * Property flag utility method for {@link PropertyDescriptor}. Get the property flags
 166      * conforming to any Property using this PropertyDescriptor
 167      *
 168      * @param desc property descriptor
 169      * @return flags for properties that conform to property descriptor
 170      */
 171     static int toFlags(final PropertyDescriptor desc) {
 172         int propFlags = 0;
 173 
 174         if (!desc.isConfigurable()) {
 175             propFlags |= NOT_CONFIGURABLE;
 176         }
 177         if (!desc.isEnumerable()) {
 178             propFlags |= NOT_ENUMERABLE;
 179         }
 180         if (!desc.isWritable()) {
 181             propFlags |= NOT_WRITABLE;
 182         }
 183 
 184         return propFlags;
 185     }
 186 
 187     /**
 188      * Check whether this property has a user defined getter function. See {@link UserAccessorProperty}
 189      * @param obj object containing getter
 190      * @return true if getter function exists, false is default
 191      */
 192     public boolean hasGetterFunction(final ScriptObject obj) {
 193         return false;
 194     }
 195 
 196     /**
 197      * Check whether this property has a user defined setter function. See {@link UserAccessorProperty}
 198      * @param obj object containing setter
 199      * @return true if getter function exists, false is default
 200      */
 201     public boolean hasSetterFunction(final ScriptObject obj) {
 202         return false;
 203     }
 204 
 205     /**
 206      * Check whether this property is writable (see ECMA 8.6.1)
 207      * @return true if writable
 208      */
 209     public boolean isWritable() {
 210         return (flags & NOT_WRITABLE) == 0;
 211     }
 212 
 213     /**
 214      * Check whether this property is writable (see ECMA 8.6.1)
 215      * @return true if configurable
 216      */
 217     public boolean isConfigurable() {
 218         return (flags & NOT_CONFIGURABLE) == 0;
 219     }
 220 
 221     /**
 222      * Check whether this property is enumerable (see ECMA 8.6.1)
 223      * @return true if enumerable
 224      */
 225     public boolean isEnumerable() {
 226         return (flags & NOT_ENUMERABLE) == 0;
 227     }
 228 
 229     /**
 230      * Check whether this property is used as a function parameter
 231      * @return true if parameter
 232      */
 233     public boolean isParameter() {
 234         return (flags & IS_PARAMETER) == IS_PARAMETER;
 235     }
 236 
 237     /**
 238      * Check whether this property is in an object with arguments field
 239      * @return true if has arguments
 240      */
 241     public boolean hasArguments() {
 242         return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
 243     }
 244 
 245     /**
 246      * Check whether this is a spill property, i.e. one that will not
 247      * be stored in a specially generated field in the property class.
 248      * The spill pool is maintained separately, as a growing Object array
 249      * in the {@link ScriptObject}.
 250      *
 251      * @return true if spill property
 252      */
 253     public boolean isSpill() {
 254         return (flags & IS_SPILL) == IS_SPILL;
 255     }
 256 
 257     /**
 258      * Is this property bound to a receiver?
 259      *
 260      * @return true if this is a bound property
 261      */
 262     public boolean isBound() {
 263         return (flags & IS_BOUND) == IS_BOUND;
 264     }
 265 
 266     /**
 267      * Does this property use any slots in the spill array described in
 268      * {@link Property#isSpill}? In that case how many. Currently a property
 269      * only uses max one spill slot, but this may change in future representations
 270      * Only {@link AccessorProperty} instances use spill slots
 271      *
 272      * @return number of spill slots a property is using
 273      */
 274     public int getSpillCount() {
 275         return isSpill() ? 1 : 0;
 276     }
 277 
 278     /**
 279      * Add more property flags to the property. Properties are immutable here,
 280      * so any property change that results in a larger flag set results in the
 281      * property being cloned. Use only the return value
 282      *
 283      * @param propertyFlags flags to be OR:ed to the existing property flags
 284      * @return new property if property set was changed, {@code this} otherwise
 285      */
 286     public Property addFlags(final int propertyFlags) {
 287         if ((this.flags & propertyFlags) != propertyFlags) {
 288             final Property cloned = this.copy();
 289             cloned.flags |= propertyFlags;
 290             return cloned;
 291         }
 292         return this;
 293     }
 294 
 295     /**
 296      * Get the flags for this property
 297      * @return property flags
 298      */
 299     public int getFlags() {
 300         return flags;
 301     }
 302 
 303     /**
 304      * Get the modify flags for this property. The modify flags are the ECMA 8.6.1
 305      * flags that decide if the Property is writable, configurable and/or enumerable.
 306      *
 307      * @return modify flags for property
 308      */
 309     public int getModifyFlags() {
 310         return flags & MODIFY_MASK;
 311     }
 312 
 313     /**
 314      * Remove property flags from the property. Properties are immutable here,
 315      * so any property change that results in a smaller flag set results in the
 316      * property being cloned. Use only the return value
 317      *
 318      * @param propertyFlags flags to be subtracted from the existing property flags
 319      * @return new property if property set was changed, {@code this} otherwise
 320      */
 321     public Property removeFlags(final int propertyFlags) {
 322         if ((this.flags & propertyFlags) != 0) {
 323             final Property cloned = this.copy();
 324             cloned.flags &= ~propertyFlags;
 325             return cloned;
 326         }
 327         return this;
 328     }
 329 
 330     /**
 331      * Reset the property for this property. Properties are immutable here,
 332      * so any property change that results in a different flag sets results in the
 333      * property being cloned. Use only the return value
 334      *
 335      * @param propertyFlags flags that are replacing from the existing property flags
 336      * @return new property if property set was changed, {@code this} otherwise
 337      */
 338     public Property setFlags(final int propertyFlags) {
 339         if (this.flags != propertyFlags) {
 340             final Property cloned = this.copy();
 341             cloned.flags &= ~MODIFY_MASK;
 342             cloned.flags |= propertyFlags & MODIFY_MASK;
 343             return cloned;
 344         }
 345         return this;
 346     }
 347 
 348     /**
 349      * Abstract method for retrieving the getter for the property. We do not know
 350      * anything about the internal representation when we request the getter, we only
 351      * know that the getter will return the property as the given type.
 352      *
 353      * @param type getter return value type
 354      * @return a getter for this property as {@code type}
 355      */
 356     public abstract MethodHandle getGetter(final Class<?> type);
 357 
 358     /**
 359      * Get the key for this property. This key is an ordinary string. The "name".
 360      * @return key for property
 361      */
 362     public String getKey() {
 363         return key;
 364     }
 365 
 366     /**
 367      * Get the field number or spill slot
 368      * @return number/slot, -1 if none exists
 369      */
 370     public int getSlot() {
 371         return slot;
 372     }
 373 
 374     /**
 375      * Set the value of this property in {@code owner}. This allows to bypass creation of the
 376      * setter MethodHandle for spill and user accessor properties.
 377      *
 378      * @param self the this object
 379      * @param owner the owner object
 380      * @param value the new property value
 381      * @param strict is this a strict setter?
 382      */
 383     public abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict);
 384 
 385     /**
 386      * Set the Object value of this property from {@code owner}. This allows to bypass creation of the
 387      * getter MethodHandle for spill and user accessor properties.
 388      *
 389      * @param self the this object
 390      * @param owner the owner object
 391      * @return  the property value
 392      */
 393     public abstract Object getObjectValue(ScriptObject self, ScriptObject owner);
 394 
 395     /**
 396      * Abstract method for retrieving the setter for the property. We do not know
 397      * anything about the internal representation when we request the setter, we only
 398      * know that the setter will take the property as a parameter of the given type.
 399      * <p>
 400      * Note that we have to pass the current property map from which we retrieved
 401      * the property here. This is necessary for map guards if, e.g. the internal
 402      * representation of the field, and consequently also the setter, changes. Then
 403      * we automatically get a map guard that relinks the call site so that the
 404      * older setter will never be used again.
 405      * <p>
 406      * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
 407      * if you are interested in the internal details of this. Note that if you
 408      * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
 409      * will currently never change, as all properties are represented as Object field,
 410      * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
 411      * boxed/unboxed upon every access, which is not necessarily optimal
 412      *
 413      * @param type setter parameter type
 414      * @param currentMap current property map for property
 415      * @return a getter for this property as {@code type}
 416      */
 417     public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
 418 
 419     /**
 420      * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
 421      * can have user defined getters
 422      * @param obj the script object
 423      * @return user defined getter function, or {@code null} if none exists
 424      */
 425     public ScriptFunction getGetterFunction(final ScriptObject obj) {
 426         return null;
 427     }
 428 
 429     /**
 430      * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
 431      * can have user defined getters
 432      * @param obj the script object
 433      * @return user defined getter function, or {@code null} if none exists
 434      */
 435     public ScriptFunction getSetterFunction(final ScriptObject obj) {
 436         return null;
 437     }
 438 
 439     @Override
 440     public int hashCode() {
 441         final Class<?> type = getCurrentType();
 442         return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
 443     }
 444 
 445     @Override
 446     public boolean equals(final Object other) {
 447         if (this == other) {
 448             return true;
 449         }
 450 
 451         if (other == null || this.getClass() != other.getClass()) {
 452             return false;
 453         }
 454 
 455         final Property otherProperty = (Property)other;
 456 
 457         return getFlags()       == otherProperty.getFlags() &&
 458                getSlot()        == otherProperty.getSlot() &&
 459                getCurrentType() == otherProperty.getCurrentType() &&
 460                getKey().equals(otherProperty.getKey());
 461     }
 462 
 463     @Override
 464     public String toString() {
 465         final StringBuilder sb   = new StringBuilder();
 466         final Class<?>      type = getCurrentType();
 467 
 468         sb.append(getKey()).
 469             append("(0x").
 470             append(Integer.toHexString(flags)).
 471             append(") ").
 472             append(getClass().getSimpleName()).
 473             append(" {").
 474             append(type == null ? "UNDEFINED" : Type.typeFor(type).getDescriptor()).
 475             append('}');
 476 
 477         if (slot != -1) {
 478             sb.append('[');
 479             sb.append(slot);
 480             sb.append(']');
 481         }
 482 
 483         return sb.toString();
 484     }
 485 
 486     /**
 487      * Get the current type of this field. If you are not running with dual fields enabled,
 488      * this will always be Object.class. See the value representation explanation in
 489      * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
 490      * for more information.
 491      *
 492      * @return current type of property, null means undefined
 493      */
 494     public Class<?> getCurrentType() {
 495         return Object.class;
 496     }
 497 
 498     /**
 499      * Check whether this Property can ever change its type. The default is false, and if
 500      * you are not running with dual fields, the type is always object and can never change
 501      * @return true if this property can change types
 502      */
 503     public boolean canChangeType() {
 504         return false;
 505     }
 506 
 507     /**
 508      * Check whether this Property is ever used as anything but an Object. If this is used only
 509      * as an object, dual fields mode need not even try to represent it as a primitive at any
 510      * callsite, saving map rewrites for performance.
 511      *
 512      * @return true if representation should always be an object field
 513      */
 514     public boolean isAlwaysObject() {
 515         return (flags & IS_ALWAYS_OBJECT) == IS_ALWAYS_OBJECT;
 516     }
 517 
 518     /**
 519      * Check whether this property can be primitive. This is a conservative
 520      * analysis result, so {@code false} might mean that it can still be
 521      * primitive
 522      *
 523      * @return can be primitive status
 524      */
 525     public boolean canBePrimitive() {
 526         return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
 527     }
 528 
 529     /**
 530      * Check whether this property can be primitive. This is a conservative
 531      * analysis result, so {@code true} might mean that it can still be
 532      * defined, but it will never say that a property can not be undefined
 533      * if it can
 534      *
 535      * @return can be undefined status
 536      */
 537     public boolean canBeUndefined() {
 538         return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED;
 539     }
 540 
 541     /**
 542      * Check whether this property represents a function declaration.
 543      * @return whether this property is a function declaration or not.
 544      */
 545     public boolean isFunctionDeclaration() {
 546         return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
 547     }
 548 }