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