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     /** ECMA 8.6.1 - Is this property not writable? */
  56     public static final int NOT_WRITABLE     = 0b0000_0000_0001;
  57 
  58     /** ECMA 8.6.1 - Is this property not enumerable? */
  59     public static final int NOT_ENUMERABLE   = 0b0000_0000_0010;
  60 
  61     /** ECMA 8.6.1 - Is this property not configurable? */
  62     public static final int NOT_CONFIGURABLE = 0b0000_0000_0100;
  63 
  64     private static final int MODIFY_MASK     = 0b0000_0000_1111;
  65 
  66     /** Is this a spill property? See {@link AccessorProperty} */
  67     public static final int IS_SPILL         = 0b0000_0001_0000;
  68 
  69     /** Is this a function parameter? */
  70     public static final int IS_PARAMETER     = 0b0000_0010_0000;
  71 
  72     /** Is parameter accessed thru arguments? */
  73     public static final int HAS_ARGUMENTS    = 0b0000_0100_0000;
  74 
  75     /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */
  76     public static final int IS_ALWAYS_OBJECT = 0b0000_1000_0000;
  77 
  78     /** Can this property be primitive? */
  79     public static final int CAN_BE_PRIMITIVE = 0b0001_0000_0000;
  80 
  81     /** Can this property be undefined? */
  82     public static final int CAN_BE_UNDEFINED = 0b0010_0000_0000;
  83 
  84     /** Property key. */
  85     private final String key;
  86 
  87     /** Property flags. */
  88     protected int flags;
  89 
  90     /** Property field number or spill slot. */
  91     private final int slot;
  92 
  93     /**
  94      * Constructor
  95      *
  96      * @param key   property key
  97      * @param flags property flags
  98      * @param slot  property field number or spill slot
  99      */
 100     public Property(final String key, final int flags, final int slot) {
 101         assert key != null;
 102         this.key   = key;
 103         this.flags = flags;
 104         this.slot  = slot;
 105     }
 106 
 107     /**
 108      * Copy constructor
 109      *
 110      * @param property source property
 111      */
 112     protected Property(final Property property) {
 113         this.key   = property.key;
 114         this.flags = property.flags;
 115         this.slot  = property.slot;
 116     }
 117 
 118     /**
 119      * Copy function
 120      *
 121      * @return cloned property
 122      */
 123     protected abstract Property copy();
 124 
 125     /**
 126      * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
 127      * return the result of merging their flags.
 128      *
 129      * @param oldDesc  first property descriptor
 130      * @param newDesc  second property descriptor
 131      * @return merged flags.
 132      */
 133     static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) {
 134         int     propFlags = 0;
 135         boolean value;
 136 
 137         value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable();
 138         if (!value) {
 139             propFlags |= NOT_CONFIGURABLE;
 140         }
 141 
 142         value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable();
 143         if (!value) {
 144             propFlags |= NOT_ENUMERABLE;
 145         }
 146 
 147         value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable();
 148         if (!value) {
 149             propFlags |= NOT_WRITABLE;
 150         }
 151 
 152         return propFlags;
 153     }
 154 
 155     /**
 156      * Property flag utility method for {@link PropertyDescriptor}. Get the property flags
 157      * conforming to any Property using this PropertyDescriptor
 158      *
 159      * @param desc property descriptor
 160      * @return flags for properties that conform to property descriptor
 161      */
 162     static int toFlags(final PropertyDescriptor desc) {
 163         int propFlags = 0;
 164 
 165         if (!desc.isConfigurable()) {
 166             propFlags |= NOT_CONFIGURABLE;
 167         }
 168         if (!desc.isEnumerable()) {
 169             propFlags |= NOT_ENUMERABLE;
 170         }
 171         if (!desc.isWritable()) {
 172             propFlags |= NOT_WRITABLE;
 173         }
 174 
 175         return propFlags;
 176     }
 177 
 178     /**
 179      * Check whether this property has a user defined getter function. See {@link UserAccessorProperty}
 180      * @return true if getter function exists, false is default
 181      */
 182     public boolean hasGetterFunction() {
 183         return false;
 184     }
 185 
 186     /**
 187      * Check whether this property has a user defined setter function. See {@link UserAccessorProperty}
 188      * @return true if getter function exists, false is default
 189      */
 190     public boolean hasSetterFunction() {
 191         return false;
 192     }
 193 
 194     /**
 195      * Check whether this property is writable (see ECMA 8.6.1)
 196      * @return true if writable
 197      */
 198     public boolean isWritable() {
 199         return (flags & NOT_WRITABLE) == 0;
 200     }
 201 
 202     /**
 203      * Check whether this property is writable (see ECMA 8.6.1)
 204      * @return true if configurable
 205      */
 206     public boolean isConfigurable() {
 207         return (flags & NOT_CONFIGURABLE) == 0;
 208     }
 209 
 210     /**
 211      * Check whether this property is enumerable (see ECMA 8.6.1)
 212      * @return true if enumerable
 213      */
 214     public boolean isEnumerable() {
 215         return (flags & NOT_ENUMERABLE) == 0;
 216     }
 217 
 218     /**
 219      * Check whether this property is used as a function parameter
 220      * @return true if parameter
 221      */
 222     public boolean isParameter() {
 223         return (flags & IS_PARAMETER) == IS_PARAMETER;
 224     }
 225 
 226     /**
 227      * Check whether this property is in an object with arguments field
 228      * @return true if has arguments
 229      */
 230     public boolean hasArguments() {
 231         return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
 232     }
 233 
 234     /**
 235      * Check whether this is a spill property, i.e. one that will not
 236      * be stored in a specially generated field in the property class.
 237      * The spill pool is maintained separately, as a growing Object array
 238      * in the {@link ScriptObject}.
 239      *
 240      * @return true if spill property
 241      */
 242     public boolean isSpill() {
 243         return (flags & IS_SPILL) == IS_SPILL;
 244     }
 245 
 246     /**
 247      * Does this property use any slots in the spill array described in
 248      * {@link Property#isSpill}? In that case how many. Currently a property
 249      * only uses max one spill slot, but this may change in future representations
 250      * Only {@link AccessorProperty} instances use spill slots
 251      *
 252      * @return number of spill slots a property is using
 253      */
 254     public int getSpillCount() {
 255         return isSpill() ? 1 : 0;
 256     }
 257 
 258     /**
 259      * Add more property flags to the property. Properties are immutable here,
 260      * so any property change that results in a larger flag set results in the
 261      * property being cloned. Use only the return value
 262      *
 263      * @param propertyFlags flags to be OR:ed to the existing property flags
 264      * @return new property if property set was changed, {@code this} otherwise
 265      */
 266     public Property addFlags(final int propertyFlags) {
 267         if ((this.flags & propertyFlags) != propertyFlags) {
 268             final Property cloned = this.copy();
 269             cloned.flags |= propertyFlags;
 270             return cloned;
 271         }
 272         return this;
 273     }
 274 
 275     /**
 276      * Get the flags for this property
 277      * @return property flags
 278      */
 279     public int getFlags() {
 280         return flags;
 281     }
 282 
 283     /**
 284      * Get the modify flags for this property. The modify flags are the ECMA 8.6.1
 285      * flags that decide if the Property is writable, configurable and/or enumerable.
 286      *
 287      * @return modify flags for property
 288      */
 289     public int getModifyFlags() {
 290         return flags & MODIFY_MASK;
 291     }
 292 
 293     /**
 294      * Remove property flags from the property. Properties are immutable here,
 295      * so any property change that results in a smaller flag set results in the
 296      * property being cloned. Use only the return value
 297      *
 298      * @param propertyFlags flags to be subtracted from the existing property flags
 299      * @return new property if property set was changed, {@code this} otherwise
 300      */
 301     public Property removeFlags(final int propertyFlags) {
 302         if ((this.flags & propertyFlags) != 0) {
 303             final Property cloned = this.copy();
 304             cloned.flags &= ~propertyFlags;
 305             return cloned;
 306         }
 307         return this;
 308     }
 309 
 310     /**
 311      * Reset the property for this property. Properties are immutable here,
 312      * so any property change that results in a different flag sets results in the
 313      * property being cloned. Use only the return value
 314      *
 315      * @param propertyFlags flags that are replacing from the existing property flags
 316      * @return new property if property set was changed, {@code this} otherwise
 317      */
 318     public Property setFlags(final int propertyFlags) {
 319         if (this.flags != propertyFlags) {
 320             final Property cloned = this.copy();
 321             cloned.flags &= ~MODIFY_MASK;
 322             cloned.flags |= propertyFlags & MODIFY_MASK;
 323             return cloned;
 324         }
 325         return this;
 326     }
 327 
 328     /**
 329      * Abstract method for retrieving the getter for the property. We do not know
 330      * anything about the internal representation when we request the getter, we only
 331      * know that the getter will return the property as the given type.
 332      *
 333      * @param type getter return value type
 334      * @return a getter for this property as {@code type}
 335      */
 336     public abstract MethodHandle getGetter(final Class<?> type);
 337 
 338     /**
 339      * Get the key for this property. This key is an ordinary string. The "name".
 340      * @return key for property
 341      */
 342     public String getKey() {
 343         return key;
 344     }
 345 
 346     /**
 347      * Get the field number or spill slot
 348      * @return number/slot, -1 if none exists
 349      */
 350     public int getSlot() {
 351         return slot;
 352     }
 353 
 354     /**
 355      * Abstract method for retrieving the setter for the property. We do not know
 356      * anything about the internal representation when we request the setter, we only
 357      * know that the setter will take the property as a parameter of the given type.
 358      * <p>
 359      * Note that we have to pass the current property map from which we retrieved
 360      * the property here. This is necessary for map guards if, e.g. the internal
 361      * representation of the field, and consequently also the setter, changes. Then
 362      * we automatically get a map guard that relinks the call site so that the
 363      * older setter will never be used again.
 364      * <p>
 365      * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
 366      * if you are interested in the internal details of this. Note that if you
 367      * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
 368      * will currently never change, as all properties are represented as Object field,
 369      * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
 370      * boxed/unboxed upon every access, which is not necessarily optimal
 371      *
 372      * @param type setter parameter type
 373      * @param currentMap current property map for property
 374      * @return a getter for this property as {@code type}
 375      */
 376     public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
 377 
 378     /**
 379      * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
 380      * can have user defined getters
 381      * @param obj the script object
 382      * @return user defined getter function, or {@code null} if none exists
 383      */
 384     public ScriptFunction getGetterFunction(final ScriptObject obj) {
 385         return null;
 386     }
 387 
 388     /**
 389      * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
 390      * can have user defined getters
 391      * @param obj the script object
 392      * @return user defined getter function, or {@code null} if none exists
 393      */
 394     public ScriptFunction getSetterFunction(final ScriptObject obj) {
 395         return null;
 396     }
 397 
 398     @Override
 399     public int hashCode() {
 400         final Class<?> type = getCurrentType();
 401         return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
 402     }
 403 
 404     @Override
 405     public boolean equals(final Object other) {
 406         if (this == other) {
 407             return true;
 408         }
 409 
 410         if (other == null || this.getClass() != other.getClass()) {
 411             return false;
 412         }
 413 
 414         final Property otherProperty = (Property)other;
 415 
 416         return getFlags()       == otherProperty.getFlags() &&
 417                getSlot()        == otherProperty.getSlot() &&
 418                getCurrentType() == otherProperty.getCurrentType() &&
 419                getKey().equals(otherProperty.getKey());
 420     }
 421 
 422     @Override
 423     public String toString() {
 424         final StringBuilder sb   = new StringBuilder();
 425         final Class<?>      type = getCurrentType();
 426 
 427         sb.append(getKey()).
 428             append("(0x").
 429             append(Integer.toHexString(flags)).
 430             append(") ").
 431             append(getClass().getSimpleName()).
 432             append(" {").
 433             append(type == null ? "UNDEFINED" : Type.typeFor(type).getDescriptor()).
 434             append('}');
 435 
 436         if (slot != -1) {
 437             sb.append('[');
 438             sb.append(slot);
 439             sb.append(']');
 440         }
 441 
 442         return sb.toString();
 443     }
 444 
 445     /**
 446      * Get the current type of this field. If you are not running with dual fields enabled,
 447      * this will always be Object.class. See the value representation explanation in
 448      * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
 449      * for more information.
 450      *
 451      * @return current type of property, null means undefined
 452      */
 453     public Class<?> getCurrentType() {
 454         return Object.class;
 455     }
 456 
 457     /**
 458      * Check whether this Property can ever change its type. The default is false, and if
 459      * you are not running with dual fields, the type is always object and can never change
 460      * @return true if this property can change types
 461      */
 462     public boolean canChangeType() {
 463         return false;
 464     }
 465 
 466     /**
 467      * Check whether this Property is ever used as anything but an Object. If this is used only
 468      * as an object, dual fields mode need not even try to represent it as a primitive at any
 469      * callsite, saving map rewrites for performance.
 470      *
 471      * @return true if representation should always be an object field
 472      */
 473     public boolean isAlwaysObject() {
 474         return (flags & IS_ALWAYS_OBJECT) == IS_ALWAYS_OBJECT;
 475     }
 476 
 477     /**
 478      * Check whether this property can be primitive. This is a conservative
 479      * analysis result, so {@code false} might mean that it can still be
 480      * primitive
 481      *
 482      * @return can be primitive status
 483      */
 484     public boolean canBePrimitive() {
 485         return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
 486     }
 487 
 488     /**
 489      * Check whether this property can be primitive. This is a conservative
 490      * analysis result, so {@code true} might mean that it can still be
 491      * defined, but it will never say that a property can not be undefined
 492      * if it can
 493      *
 494      * @return can be undefined status
 495      */
 496     public boolean canBeUndefined() {
 497         return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED;
 498     }
 499 }
--- EOF ---