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