Property.java revision 1002:2f0161551858
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;
31
32import java.io.Serializable;
33import java.lang.invoke.MethodHandle;
34import java.lang.invoke.SwitchPoint;
35import java.util.Objects;
36import 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 */
47public 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}
671