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