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.io.Serializable;
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.SwitchPoint;
  35 import java.util.Objects;
  36 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
  37 
  38 /**
  39  * This is the abstract superclass representing a JavaScript Property.
  40  * The {@link PropertyMap} map links keys to properties, and consequently
  41  * instances of this class make up the values in the PropertyMap
  42  *
  43  * @see PropertyMap
  44  * @see AccessorProperty
  45  * @see UserAccessorProperty
  46  */
  47 public abstract class Property implements Serializable {
  48     /*
  49      * ECMA 8.6.1 Property Attributes
  50      *
  51      * We use negative flags because most properties are expected to
  52      * be 'writable', 'configurable' and 'enumerable'. With negative flags,
  53      * we can use leave flag byte initialized with (the default) zero value.
  54      */
  55 
  56     /** Mask for property being both writable, enumerable and configurable */
  57     public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000;
  58 
  59     /** ECMA 8.6.1 - Is this property not writable? */
  60     public static final int NOT_WRITABLE     = 1 << 0;
  61 
  62     /** ECMA 8.6.1 - Is this property not enumerable? */
  63     public static final int NOT_ENUMERABLE   = 1 << 1;
  64 
  65     /** ECMA 8.6.1 - Is this property not configurable? */
  66     public static final int NOT_CONFIGURABLE = 1 << 2;
  67 
  68     private static final int MODIFY_MASK     = NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE;
  69 
  70     /** Is this a function parameter? */
  71     public static final int IS_PARAMETER     = 1 << 3;
  72 
  73     /** Is parameter accessed thru arguments? */
  74     public static final int HAS_ARGUMENTS    = 1 << 4;
  75 
  76     /** Is this a function declaration property ? */
  77     public static final int IS_FUNCTION_DECLARATION = 1 << 5;
  78 
  79     /**
  80      * Is this is a primitive field given to us by Nasgen, i.e.
  81      * something we can be sure remains a constant whose type
  82      * is narrower than object, e.g. Math.PI which is declared
  83      * as a double
  84      */
  85     public static final int IS_NASGEN_PRIMITIVE     = 1 << 6;
  86 
  87     /** Is this property bound to a receiver? This means get/set operations will be delegated to
  88      *  a statically defined object instead of the object passed as callsite parameter. */
  89     public static final int IS_BOUND                = 1 << 7;
  90 
  91     /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
  92     public static final int NEEDS_DECLARATION       = 1 << 8;
  93 
  94     /** Property key. */
  95     private final String key;
  96 
  97     /** Property flags. */
  98     private int flags;
  99 
 100     /** Property field number or spill slot. */
 101     private final int slot;
 102 
 103     /** SwitchPoint that is invalidated when property is changed, optional */
 104     protected SwitchPoint changeCallback;
 105 
 106     private static final long serialVersionUID = 2099814273074501176L;
 107 
 108     /**
 109      * Constructor
 110      *
 111      * @param key   property key
 112      * @param flags property flags
 113      * @param slot  property field number or spill slot
 114      */
 115     Property(final String key, final int flags, final int slot) {
 116         assert key != null;
 117         this.key   = key;
 118         this.flags = flags;
 119         this.slot  = slot;
 120     }
 121 
 122     /**
 123      * Copy constructor
 124      *
 125      * @param property source property
 126      */
 127     Property(final Property property, final int flags) {
 128         this.key            = property.key;
 129         this.slot           = property.slot;
 130         this.changeCallback = property.changeCallback;
 131         this.flags          = flags;
 132     }
 133 
 134     /**
 135      * Copy function
 136      *
 137      * @return cloned property
 138      */
 139     public abstract Property copy();
 140 
 141     /**
 142      * Copy function
 143      *
 144      * @param  newType new type
 145      * @return cloned property with new type
 146      */
 147     public abstract Property copy(final Class<?> newType);
 148 
 149     /**
 150      * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
 151      * return the result of merging their flags.
 152      *
 153      * @param oldDesc  first property descriptor
 154      * @param newDesc  second property descriptor
 155      * @return merged flags.
 156      */
 157     static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) {
 158         int     propFlags = 0;
 159         boolean value;
 160 
 161         value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable();
 162         if (!value) {
 163             propFlags |= NOT_CONFIGURABLE;
 164         }
 165 
 166         value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable();
 167         if (!value) {
 168             propFlags |= NOT_ENUMERABLE;
 169         }
 170 
 171         value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable();
 172         if (!value) {
 173             propFlags |= NOT_WRITABLE;
 174         }
 175 
 176         return propFlags;
 177     }
 178 
 179     /**
 180      * Set the change callback for this property, i.e. a SwitchPoint
 181      * that will be invalidated when the value of the property is
 182      * changed
 183      * @param sp SwitchPoint to use for change callback
 184      */
 185     public final void setChangeCallback(final SwitchPoint sp) {
 186         this.changeCallback = sp;
 187     }
 188 
 189     /**
 190      * Property flag utility method for {@link PropertyDescriptor}. Get the property flags
 191      * conforming to any Property using this PropertyDescriptor
 192      *
 193      * @param desc property descriptor
 194      * @return flags for properties that conform to property descriptor
 195      */
 196     static int toFlags(final PropertyDescriptor desc) {
 197         int propFlags = 0;
 198 
 199         if (!desc.isConfigurable()) {
 200             propFlags |= NOT_CONFIGURABLE;
 201         }
 202         if (!desc.isEnumerable()) {
 203             propFlags |= NOT_ENUMERABLE;
 204         }
 205         if (!desc.isWritable()) {
 206             propFlags |= NOT_WRITABLE;
 207         }
 208 
 209         return propFlags;
 210     }
 211 
 212     /**
 213      * Check whether this property has a user defined getter function. See {@link UserAccessorProperty}
 214      * @param obj object containing getter
 215      * @return true if getter function exists, false is default
 216      */
 217     public boolean hasGetterFunction(final ScriptObject obj) {
 218         return false;
 219     }
 220 
 221     /**
 222      * Check whether this property has a user defined setter function. See {@link UserAccessorProperty}
 223      * @param obj object containing setter
 224      * @return true if getter function exists, false is default
 225      */
 226     public boolean hasSetterFunction(final ScriptObject obj) {
 227         return false;
 228     }
 229 
 230     /**
 231      * Check whether this property is writable (see ECMA 8.6.1)
 232      * @return true if writable
 233      */
 234     public boolean isWritable() {
 235         return (flags & NOT_WRITABLE) == 0;
 236     }
 237 
 238     /**
 239      * Check whether this property is writable (see ECMA 8.6.1)
 240      * @return true if configurable
 241      */
 242     public boolean isConfigurable() {
 243         return (flags & NOT_CONFIGURABLE) == 0;
 244     }
 245 
 246     /**
 247      * Check whether this property is enumerable (see ECMA 8.6.1)
 248      * @return true if enumerable
 249      */
 250     public boolean isEnumerable() {
 251         return (flags & NOT_ENUMERABLE) == 0;
 252     }
 253 
 254     /**
 255      * Check whether this property is used as a function parameter
 256      * @return true if parameter
 257      */
 258     public boolean isParameter() {
 259         return (flags & IS_PARAMETER) == IS_PARAMETER;
 260     }
 261 
 262     /**
 263      * Check whether this property is in an object with arguments field
 264      * @return true if has arguments
 265      */
 266     public boolean hasArguments() {
 267         return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
 268     }
 269 
 270     /**
 271      * Check whether this is a spill property, i.e. one that will not
 272      * be stored in a specially generated field in the property class.
 273      * The spill pool is maintained separately, as a growing Object array
 274      * in the {@link ScriptObject}.
 275      *
 276      * @return true if spill property
 277      */
 278     public boolean isSpill() {
 279         return false;
 280     }
 281 
 282     /**
 283      * Is this property bound to a receiver? If this method returns {@code true} get and set operations
 284      * will be delegated to a statically bound object instead of the object passed as parameter.
 285      *
 286      * @return true if this is a bound property
 287      */
 288     public boolean isBound() {
 289         return (flags & IS_BOUND) == IS_BOUND;
 290     }
 291 
 292     /**
 293      * Is this a LET or CONST property that needs to see its declaration before being usable?
 294      *
 295      * @return true if this is a block-scoped variable
 296      */
 297     public boolean needsDeclaration() {
 298         return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION;
 299     }
 300 
 301     /**
 302      * Add more property flags to the property. Properties are immutable here,
 303      * so any property change that results in a larger flag set results in the
 304      * property being cloned. Use only the return value
 305      *
 306      * @param propertyFlags flags to be OR:ed to the existing property flags
 307      * @return new property if property set was changed, {@code this} otherwise
 308      */
 309     public Property addFlags(final int propertyFlags) {
 310         if ((this.flags & propertyFlags) != propertyFlags) {
 311             final Property cloned = this.copy();
 312             cloned.flags |= propertyFlags;
 313             return cloned;
 314         }
 315         return this;
 316     }
 317 
 318     /**
 319      * Check if a flag is set for a property
 320      * @param property property
 321      * @param flag     flag to check
 322      * @return true if flag is set
 323      */
 324     public static boolean checkFlag(final Property property, final int flag) {
 325         return (property.getFlags() & flag) == flag;
 326     }
 327 
 328     /**
 329      * Get the flags for this property
 330      * @return property flags
 331      */
 332     public int getFlags() {
 333         return flags;
 334     }
 335 
 336     /**
 337      * Get the modify flags for this property. The modify flags are the ECMA 8.6.1
 338      * flags that decide if the Property is writable, configurable and/or enumerable.
 339      *
 340      * @return modify flags for property
 341      */
 342     public int getModifyFlags() {
 343         return flags & MODIFY_MASK;
 344     }
 345 
 346     /**
 347      * Remove property flags from the property. Properties are immutable here,
 348      * so any property change that results in a smaller flag set results in the
 349      * property being cloned. Use only the return value
 350      *
 351      * @param propertyFlags flags to be subtracted from the existing property flags
 352      * @return new property if property set was changed, {@code this} otherwise
 353      */
 354     public Property removeFlags(final int propertyFlags) {
 355         if ((this.flags & propertyFlags) != 0) {
 356             final Property cloned = this.copy();
 357             cloned.flags &= ~propertyFlags;
 358             return cloned;
 359         }
 360         return this;
 361     }
 362 
 363     /**
 364      * Reset the property for this property. Properties are immutable here,
 365      * so any property change that results in a different flag sets results in the
 366      * property being cloned. Use only the return value
 367      *
 368      * @param propertyFlags flags that are replacing from the existing property flags
 369      * @return new property if property set was changed, {@code this} otherwise
 370      */
 371     public Property setFlags(final int propertyFlags) {
 372         if (this.flags != propertyFlags) {
 373             final Property cloned = this.copy();
 374             cloned.flags &= ~MODIFY_MASK;
 375             cloned.flags |= propertyFlags & MODIFY_MASK;
 376             return cloned;
 377         }
 378         return this;
 379     }
 380 
 381     /**
 382      * Abstract method for retrieving the getter for the property. We do not know
 383      * anything about the internal representation when we request the getter, we only
 384      * know that the getter will return the property as the given type.
 385      *
 386      * @param type getter return value type
 387      * @return a getter for this property as {@code type}
 388      */
 389     public abstract MethodHandle getGetter(final Class<?> type);
 390 
 391     /**
 392      * Get an optimistic getter that throws an exception if type is not the known given one
 393      * @param type          type
 394      * @param programPoint  program point
 395      * @return getter
 396      */
 397     public abstract MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint);
 398 
 399     /**
 400      * Hook to initialize method handles after deserialization.
 401      *
 402      * @param structure the structure class
 403      */
 404     abstract void initMethodHandles(final Class<?> structure);
 405 
 406     /**
 407      * Get the key for this property. This key is an ordinary string. The "name".
 408      * @return key for property
 409      */
 410     public String getKey() {
 411         return key;
 412     }
 413 
 414     /**
 415      * Get the field number or spill slot
 416      * @return number/slot, -1 if none exists
 417      */
 418     public int getSlot() {
 419         return slot;
 420     }
 421 
 422     /**
 423      * get the Object value of this property from {@code owner}. This allows to bypass creation of the
 424      * getter MethodHandle for spill and user accessor properties.
 425      *
 426      * @param self the this object
 427      * @param owner the owner of the property
 428      * @return  the property value
 429      */
 430     public abstract int getIntValue(final ScriptObject self, final ScriptObject owner);
 431 
 432     /**
 433      * get the Object value of this property from {@code owner}. This allows to bypass creation of the
 434      * getter MethodHandle for spill and user accessor properties.
 435      *
 436      * @param self the this object
 437      * @param owner the owner of the property
 438      * @return  the property value
 439      */
 440     public abstract long getLongValue(final ScriptObject self, final ScriptObject owner);
 441 
 442     /**
 443      * get the Object value of this property from {@code owner}. This allows to bypass creation of the
 444      * getter MethodHandle for spill and user accessor properties.
 445      *
 446      * @param self the this object
 447      * @param owner the owner of the property
 448      * @return  the property value
 449      */
 450     public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner);
 451 
 452     /**
 453      * get the Object value of this property from {@code owner}. This allows to bypass creation of the
 454      * getter MethodHandle for spill and user accessor properties.
 455      *
 456      * @param self the this object
 457      * @param owner the owner of the property
 458      * @return  the property value
 459      */
 460     public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner);
 461 
 462     /**
 463      * Set the value of this property in {@code owner}. This allows to bypass creation of the
 464      * setter MethodHandle for spill and user accessor properties.
 465      *
 466      * @param self the this object
 467      * @param owner the owner object
 468      * @param value the new property value
 469      * @param strict is this a strict setter?
 470      */
 471     public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict);
 472 
 473     /**
 474      * Set the value of this property in {@code owner}. This allows to bypass creation of the
 475      * setter MethodHandle for spill and user accessor properties.
 476      *
 477      * @param self the this object
 478      * @param owner the owner object
 479      * @param value the new property value
 480      * @param strict is this a strict setter?
 481      */
 482     public abstract void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict);
 483 
 484     /**
 485      * Set the value of this property in {@code owner}. This allows to bypass creation of the
 486      * setter MethodHandle for spill and user accessor properties.
 487      *
 488      * @param self the this object
 489      * @param owner the owner object
 490      * @param value the new property value
 491      * @param strict is this a strict setter?
 492      */
 493     public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict);
 494 
 495     /**
 496      * Set the value of this property in {@code owner}. This allows to bypass creation of the
 497      * setter MethodHandle for spill and user accessor properties.
 498      *
 499      * @param self the this object
 500      * @param owner the owner object
 501      * @param value the new property value
 502      * @param strict is this a strict setter?
 503      */
 504     public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict);
 505 
 506     /**
 507      * Abstract method for retrieving the setter for the property. We do not know
 508      * anything about the internal representation when we request the setter, we only
 509      * know that the setter will take the property as a parameter of the given type.
 510      * <p>
 511      * Note that we have to pass the current property map from which we retrieved
 512      * the property here. This is necessary for map guards if, e.g. the internal
 513      * representation of the field, and consequently also the setter, changes. Then
 514      * we automatically get a map guard that relinks the call site so that the
 515      * older setter will never be used again.
 516      * <p>
 517      * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
 518      * if you are interested in the internal details of this. Note that if you
 519      * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
 520      * will currently never change, as all properties are represented as Object field,
 521      * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
 522      * boxed/unboxed upon every access, which is not necessarily optimal
 523      *
 524      * @param type setter parameter type
 525      * @param currentMap current property map for property
 526      * @return a getter for this property as {@code type}
 527      */
 528     public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
 529 
 530     /**
 531      * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
 532      * can have user defined getters
 533      * @param obj the script object
 534      * @return user defined getter function, or {@code null} if none exists
 535      */
 536     public ScriptFunction getGetterFunction(final ScriptObject obj) {
 537         return null;
 538     }
 539 
 540     /**
 541      * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
 542      * can have user defined getters
 543      * @param obj the script object
 544      * @return user defined getter function, or {@code null} if none exists
 545      */
 546     public ScriptFunction getSetterFunction(final ScriptObject obj) {
 547         return null;
 548     }
 549 
 550     @Override
 551     public int hashCode() {
 552         final Class<?> type = getCurrentType();
 553         return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
 554     }
 555 
 556     @Override
 557     public boolean equals(final Object other) {
 558         if (this == other) {
 559             return true;
 560         }
 561 
 562         if (other == null || this.getClass() != other.getClass()) {
 563             return false;
 564         }
 565 
 566         final Property otherProperty = (Property)other;
 567 
 568         return equalsWithoutType(otherProperty) &&
 569                getCurrentType() == otherProperty.getCurrentType();
 570     }
 571 
 572     boolean equalsWithoutType(final Property otherProperty) {
 573         return getFlags() == otherProperty.getFlags() &&
 574                 getSlot() == otherProperty.getSlot() &&
 575                 getKey().equals(otherProperty.getKey());
 576     }
 577 
 578     private static final String type(final Class<?> type) {
 579         if (type == null) {
 580             return "undef";
 581         } else if (type == int.class) {
 582             return "i";
 583         } else if (type == long.class) {
 584             return "j";
 585         } else if (type == double.class) {
 586             return "d";
 587         } else {
 588             return "o";
 589         }
 590     }
 591 
 592     /**
 593      * Short toString version
 594      * @return short toString
 595      */
 596     public final String toStringShort() {
 597         final StringBuilder sb   = new StringBuilder();
 598         final Class<?>      type = getCurrentType();
 599         sb.append(getKey()).append(" (").append(type(type)).append(')');
 600         return sb.toString();
 601     }
 602 
 603     private static String indent(final String str, final int indent) {
 604         final StringBuilder sb = new StringBuilder();
 605         sb.append(str);
 606         for (int i = 0; i < indent - str.length(); i++) {
 607             sb.append(' ');
 608         }
 609         return sb.toString();
 610      }
 611 
 612     @Override
 613     public String toString() {
 614         final StringBuilder sb   = new StringBuilder();
 615         final Class<?>      type = getCurrentType();
 616 
 617         sb.append(indent(getKey(), 20)).
 618             append(" id=").
 619             append(Debug.id(this)).
 620             append(" (0x").
 621             append(indent(Integer.toHexString(flags), 4)).
 622             append(") ").
 623             append(getClass().getSimpleName()).
 624             append(" {").
 625             append(indent(type(type), 5)).
 626             append('}');
 627 
 628         if (slot != -1) {
 629             sb.append(" [").
 630                append("slot=").
 631                append(slot).
 632                append(']');
 633         }
 634 
 635         return sb.toString();
 636     }
 637 
 638     /**
 639      * Get the current type of this field. If you are not running with dual fields enabled,
 640      * this will always be Object.class. See the value representation explanation in
 641      * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
 642      * for more information.
 643      *
 644      * @return current type of property, null means undefined
 645      */
 646     public abstract Class<?> getCurrentType();
 647 
 648     /**
 649      * Reset the current type of this property
 650      * @param currentType new current type
 651      */
 652     public abstract void setCurrentType(final Class<?> currentType);
 653 
 654     /**
 655      * Check whether this Property can ever change its type. The default is false, and if
 656      * you are not running with dual fields, the type is always object and can never change
 657      * @return true if this property can change types
 658      */
 659     public boolean canChangeType() {
 660         return false;
 661     }
 662 
 663     /**
 664      * Check whether this property represents a function declaration.
 665      * @return whether this property is a function declaration or not.
 666      */
 667     public boolean isFunctionDeclaration() {
 668         return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
 669     }
 670 }
--- EOF ---