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