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