Property.java revision 1349:1261d91a9e28
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
26package jdk.nashorn.internal.runtime;
27
28import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
29import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
30import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
31import java.io.Serializable;
32import java.lang.invoke.MethodHandle;
33import java.lang.invoke.SwitchPoint;
34import java.util.Objects;
35import 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 */
46public 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     */
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     */
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     */
154    public abstract Property copy();
155
156    /**
157     * Copy function
158     *
159     * @param  newType new type
160     * @return cloned property with new type
161     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
453    public abstract long getLongValue(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     */
463    public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner);
464
465    /**
466     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
467     * getter MethodHandle for spill and user accessor properties.
468     *
469     * @param self the this object
470     * @param owner the owner of the property
471     * @return  the property value
472     */
473    public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner);
474
475    /**
476     * Set the value of this property in {@code owner}. This allows to bypass creation of the
477     * setter MethodHandle for spill and user accessor properties.
478     *
479     * @param self the this object
480     * @param owner the owner object
481     * @param value the new property value
482     * @param strict is this a strict setter?
483     */
484    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict);
485
486    /**
487     * Set the value of this property in {@code owner}. This allows to bypass creation of the
488     * setter MethodHandle for spill and user accessor properties.
489     *
490     * @param self the this object
491     * @param owner the owner object
492     * @param value the new property value
493     * @param strict is this a strict setter?
494     */
495    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict);
496
497    /**
498     * Set the value of this property in {@code owner}. This allows to bypass creation of the
499     * setter MethodHandle for spill and user accessor properties.
500     *
501     * @param self the this object
502     * @param owner the owner object
503     * @param value the new property value
504     * @param strict is this a strict setter?
505     */
506    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict);
507
508    /**
509     * Set the value of this property in {@code owner}. This allows to bypass creation of the
510     * setter MethodHandle for spill and user accessor properties.
511     *
512     * @param self the this object
513     * @param owner the owner object
514     * @param value the new property value
515     * @param strict is this a strict setter?
516     */
517    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict);
518
519    /**
520     * Abstract method for retrieving the setter for the property. We do not know
521     * anything about the internal representation when we request the setter, we only
522     * know that the setter will take the property as a parameter of the given type.
523     * <p>
524     * Note that we have to pass the current property map from which we retrieved
525     * the property here. This is necessary for map guards if, e.g. the internal
526     * representation of the field, and consequently also the setter, changes. Then
527     * we automatically get a map guard that relinks the call site so that the
528     * older setter will never be used again.
529     * <p>
530     * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
531     * if you are interested in the internal details of this. Note that if you
532     * are running with {@code -Dnashorn.fields.objects=true}, the setters
533     * will currently never change, as all properties are represented as Object field,
534     * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
535     * boxed/unboxed upon every access, which is not necessarily optimal
536     *
537     * @param type setter parameter type
538     * @param currentMap current property map for property
539     * @return a getter for this property as {@code type}
540     */
541    public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
542
543    /**
544     * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
545     * can have user defined getters
546     * @param obj the script object
547     * @return user defined getter function, or {@code null} if none exists
548     */
549    public ScriptFunction getGetterFunction(final ScriptObject obj) {
550        return null;
551    }
552
553    /**
554     * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
555     * can have user defined getters
556     * @param obj the script object
557     * @return user defined getter function, or {@code null} if none exists
558     */
559    public ScriptFunction getSetterFunction(final ScriptObject obj) {
560        return null;
561    }
562
563    @Override
564    public int hashCode() {
565        final Class<?> t = getLocalType();
566        return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (t == null ? 0 : t.hashCode());
567    }
568
569    @Override
570    public boolean equals(final Object other) {
571        if (this == other) {
572            return true;
573        }
574
575        if (other == null || this.getClass() != other.getClass()) {
576            return false;
577        }
578
579        final Property otherProperty = (Property)other;
580
581        return equalsWithoutType(otherProperty) &&
582                getLocalType() == otherProperty.getLocalType();
583    }
584
585    boolean equalsWithoutType(final Property otherProperty) {
586        return getFlags() == otherProperty.getFlags() &&
587                getSlot() == otherProperty.getSlot() &&
588                getKey().equals(otherProperty.getKey());
589    }
590
591    private static String type(final Class<?> type) {
592        if (type == null) {
593            return "undef";
594        } else if (type == int.class) {
595            return "i";
596        } else if (type == long.class) {
597            return "j";
598        } else if (type == double.class) {
599            return "d";
600        } else {
601            return "o";
602        }
603    }
604
605    /**
606     * Short toString version
607     * @return short toString
608     */
609    public final String toStringShort() {
610        final StringBuilder sb   = new StringBuilder();
611        final Class<?>      t = getLocalType();
612        sb.append(getKey()).append(" (").append(type(t)).append(')');
613        return sb.toString();
614    }
615
616    private static String indent(final String str, final int indent) {
617        final StringBuilder sb = new StringBuilder();
618        sb.append(str);
619        for (int i = 0; i < indent - str.length(); i++) {
620            sb.append(' ');
621        }
622        return sb.toString();
623     }
624
625    @Override
626    public String toString() {
627        final StringBuilder sb   = new StringBuilder();
628        final Class<?>      t = getLocalType();
629
630        sb.append(indent(getKey(), 20)).
631            append(" id=").
632            append(Debug.id(this)).
633            append(" (0x").
634            append(indent(Integer.toHexString(flags), 4)).
635            append(") ").
636            append(getClass().getSimpleName()).
637            append(" {").
638            append(indent(type(t), 5)).
639            append('}');
640
641        if (slot != -1) {
642            sb.append(" [").
643               append("slot=").
644               append(slot).
645               append(']');
646        }
647
648        return sb.toString();
649    }
650
651    /**
652     * Get the current type of this property. If you are running with object fields enabled,
653     * this will always be Object.class. See the value representation explanation in
654     * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
655     * for more information.
656     *
657     * <p>Note that for user accessor properties, this returns the type of the last observed
658     * value passed to or returned by a user accessor. Use {@link #getLocalType()} to always get
659     * the type of the actual value stored in the property slot.</p>
660     *
661     * @return current type of property, null means undefined
662     */
663    public final Class<?> getType() {
664        return type;
665    }
666
667    /**
668     * Set the type of this property.
669     * @param type new type
670     */
671    public final void setType(final Class<?> type) {
672        assert type != boolean.class : "no boolean storage support yet - fix this";
673        this.type = type == null ? null : type.isPrimitive() ? type : Object.class;
674    }
675
676    /**
677     * Get the type of the value in the local property slot. This returns the same as
678     * {@link #getType()} for normal properties, but always returns {@code Object.class}
679     * for {@link UserAccessorProperty}s as their local type is a pair of accessor references.
680     *
681     * @return the local property type
682     */
683    protected Class<?> getLocalType() {
684        return getType();
685    }
686
687    /**
688     * Check whether this Property can ever change its type. The default is false, and if
689     * you are not running with dual fields, the type is always object and can never change
690     * @return true if this property can change types
691     */
692    public boolean canChangeType() {
693        return false;
694    }
695
696    /**
697     * Check whether this property represents a function declaration.
698     * @return whether this property is a function declaration or not.
699     */
700    public boolean isFunctionDeclaration() {
701        return (flags & IS_FUNCTION_DECLARATION) != 0;
702    }
703
704    /**
705     * Is this a property defined by ES6 let or const?
706     * @return true if this property represents a lexical binding.
707     */
708    public boolean isLexicalBinding() {
709        return (flags & IS_LEXICAL_BINDING) != 0;
710    }
711
712    /**
713     * Does this property support dual fields for both primitive and object values?
714     * @return true if supports dual fields
715     */
716    public boolean hasDualFields() {
717        return (flags & DUAL_FIELDS) != 0;
718    }
719}
720