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