Property.java revision 1036:f0b5e3900a10
198943Sluigi/*
2117328Sluigi * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
398943Sluigi * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
498943Sluigi *
598943Sluigi * This code is free software; you can redistribute it and/or modify it
698943Sluigi * under the terms of the GNU General Public License version 2 only, as
798943Sluigi * published by the Free Software Foundation.  Oracle designates this
898943Sluigi * particular file as subject to the "Classpath" exception as provided
998943Sluigi * by Oracle in the LICENSE file that accompanied this code.
1098943Sluigi *
1198943Sluigi * This code is distributed in the hope that it will be useful, but WITHOUT
1298943Sluigi * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1398943Sluigi * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1498943Sluigi * version 2 for more details (a copy is included in the LICENSE file that
1598943Sluigi * accompanied this code).
1698943Sluigi *
1798943Sluigi * You should have received a copy of the GNU General Public License version
1898943Sluigi * 2 along with this work; if not, write to the Free Software Foundation,
1998943Sluigi * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2098943Sluigi *
2198943Sluigi * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2298943Sluigi * or visit www.oracle.com if you need additional information or have any
23187604Sluigi * questions.
2498943Sluigi */
2598943Sluigi
2698943Sluigipackage jdk.nashorn.internal.runtime;
2798943Sluigi
28187767Sluigiimport static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
29187767Sluigiimport static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
3098943Sluigiimport static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
3198943Sluigiimport java.io.Serializable;
3298943Sluigiimport java.lang.invoke.MethodHandle;
3398943Sluigiimport java.lang.invoke.SwitchPoint;
3498943Sluigiimport java.util.Objects;
3598943Sluigiimport jdk.nashorn.internal.codegen.ObjectClassGenerator;
3698943Sluigi
3798943Sluigi/**
3898943Sluigi * This is the abstract superclass representing a JavaScript Property.
3998943Sluigi * The {@link PropertyMap} map links keys to properties, and consequently
40187604Sluigi * instances of this class make up the values in the PropertyMap
41136071Sgreen *
42136071Sgreen * @see PropertyMap
4398943Sluigi * @see AccessorProperty
44175659Srwatson * @see UserAccessorProperty
45175659Srwatson */
46169424Smaximpublic abstract class Property implements Serializable {
4798943Sluigi    /*
48165648Spiso     * ECMA 8.6.1 Property Attributes
49136071Sgreen     *
50145246Sbrooks     * We use negative flags because most properties are expected to
5198943Sluigi     * be 'writable', 'configurable' and 'enumerable'. With negative flags,
5298943Sluigi     * we can use leave flag byte initialized with (the default) zero value.
5398943Sluigi     */
5498943Sluigi
55145246Sbrooks    /** Mask for property being both writable, enumerable and configurable */
5698943Sluigi    public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000;
5798943Sluigi
5898943Sluigi    /** ECMA 8.6.1 - Is this property not writable? */
5998943Sluigi    public static final int NOT_WRITABLE     = 1 << 0;
60165648Spiso
6198943Sluigi    /** ECMA 8.6.1 - Is this property not enumerable? */
62187767Sluigi    public static final int NOT_ENUMERABLE   = 1 << 1;
6398943Sluigi
64187767Sluigi    /** ECMA 8.6.1 - Is this property not configurable? */
65187764Sluigi    public static final int NOT_CONFIGURABLE = 1 << 2;
66159636Soleg
67159636Soleg    private static final int MODIFY_MASK     = NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE;
68159636Soleg
69159636Soleg    /** Is this a function parameter? */
70159636Soleg    public static final int IS_PARAMETER     = 1 << 3;
71159636Soleg
72159636Soleg    /** Is parameter accessed thru arguments? */
73159636Soleg    public static final int HAS_ARGUMENTS    = 1 << 4;
74159636Soleg
75159636Soleg    /** Is this a function declaration property ? */
76159636Soleg    public static final int IS_FUNCTION_DECLARATION = 1 << 5;
77159636Soleg
78159636Soleg    /**
79159636Soleg     * Is this is a primitive field given to us by Nasgen, i.e.
80159636Soleg     * something we can be sure remains a constant whose type
81159636Soleg     * is narrower than object, e.g. Math.PI which is declared
82159636Soleg     * as a double
83159636Soleg     */
84159636Soleg    public static final int IS_NASGEN_PRIMITIVE     = 1 << 6;
85159636Soleg
86159636Soleg    /** Is this a builtin property, e.g. Function.prototype.apply */
87159636Soleg    public static final int IS_BUILTIN = 1 << 7;
88159636Soleg
89159636Soleg    /** Is this property bound to a receiver? This means get/set operations will be delegated to
90159636Soleg     *  a statically defined object instead of the object passed as callsite parameter. */
91159636Soleg    public static final int IS_BOUND                = 1 << 7;
92159636Soleg
93158879Soleg    /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
94158879Soleg    public static final int NEEDS_DECLARATION       = 1 << 8;
95187762Sluigi
96187762Sluigi    /** Property key. */
97187762Sluigi    private final String key;
98187762Sluigi
99187762Sluigi    /** Property flags. */
100187762Sluigi    private int flags;
101187762Sluigi
102187762Sluigi    /** Property field number or spill slot. */
103187762Sluigi    private final int slot;
104187762Sluigi
105159636Soleg    /** SwitchPoint that is invalidated when property is changed, optional */
10698943Sluigi    protected transient SwitchPoint builtinSwitchPoint;
10798943Sluigi
10898943Sluigi    private static final long serialVersionUID = 2099814273074501176L;
10998943Sluigi
11098943Sluigi    /**
11198943Sluigi     * Constructor
11298943Sluigi     *
11398943Sluigi     * @param key   property key
11498943Sluigi     * @param flags property flags
11598943Sluigi     * @param slot  property field number or spill slot
11698943Sluigi     */
11798943Sluigi    Property(final String key, final int flags, final int slot) {
11898943Sluigi        assert key != null;
11998943Sluigi        this.key   = key;
12098943Sluigi        this.flags = flags;
12198943Sluigi        this.slot  = slot;
12298943Sluigi    }
12398943Sluigi
12498943Sluigi    /**
12598943Sluigi     * Copy constructor
12698943Sluigi     *
12798943Sluigi     * @param property source property
12898943Sluigi     */
12998943Sluigi    Property(final Property property, final int flags) {
13098943Sluigi        this.key                = property.key;
13198943Sluigi        this.slot               = property.slot;
13298943Sluigi        this.builtinSwitchPoint = property.builtinSwitchPoint;
13398943Sluigi        this.flags              = flags;
13498943Sluigi    }
13598943Sluigi
13698943Sluigi    /**
13798943Sluigi     * Copy function
13898943Sluigi     *
13998943Sluigi     * @return cloned property
14098943Sluigi     */
14198943Sluigi    public abstract Property copy();
14298943Sluigi
14398943Sluigi    /**
14498943Sluigi     * Copy function
14598943Sluigi     *
14698943Sluigi     * @param  newType new type
147172801Srpaulo     * @return cloned property with new type
148172801Srpaulo     */
14998943Sluigi    public abstract Property copy(final Class<?> newType);
15098943Sluigi
15198943Sluigi    /**
15298943Sluigi     * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
15398943Sluigi     * return the result of merging their flags.
15498943Sluigi     *
15598943Sluigi     * @param oldDesc  first property descriptor
15698943Sluigi     * @param newDesc  second property descriptor
15798943Sluigi     * @return merged flags.
15898943Sluigi     */
15998943Sluigi    static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) {
16098943Sluigi        int     propFlags = 0;
16198943Sluigi        boolean value;
16298943Sluigi
16398943Sluigi        value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable();
16498943Sluigi        if (!value) {
16598943Sluigi            propFlags |= NOT_CONFIGURABLE;
16698943Sluigi        }
16798943Sluigi
16898943Sluigi        value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable();
16998943Sluigi        if (!value) {
17098943Sluigi            propFlags |= NOT_ENUMERABLE;
17198943Sluigi        }
17298943Sluigi
17398943Sluigi        value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable();
17498943Sluigi        if (!value) {
17598943Sluigi            propFlags |= NOT_WRITABLE;
17698943Sluigi        }
17798943Sluigi
17898943Sluigi        return propFlags;
17998943Sluigi    }
18098943Sluigi
18198943Sluigi    /**
18298943Sluigi     * Set the change callback for this property, i.e. a SwitchPoint
18398943Sluigi     * that will be invalidated when the value of the property is
18498943Sluigi     * changed
18598943Sluigi     * @param sp SwitchPoint to use for change callback
18698943Sluigi     */
18798943Sluigi    public final void setBuiltinSwitchPoint(final SwitchPoint sp) {
18898943Sluigi        this.builtinSwitchPoint = sp;
18998943Sluigi    }
19098943Sluigi
19198943Sluigi    /**
19298943Sluigi     * Builtin properties have an invalidation switchpoint that is
19398943Sluigi     * invalidated when they are set, this is a getter for it
19498943Sluigi     * @return builtin switchpoint, or null if none
19598943Sluigi     */
19698943Sluigi    public final SwitchPoint getBuiltinSwitchPoint() {
19798943Sluigi        return builtinSwitchPoint;
19898943Sluigi    }
19998943Sluigi
200101641Sluigi    /**
201101641Sluigi     * Checks if this is a builtin property, this means that it has
20298943Sluigi     * a builtin switchpoint that hasn't been invalidated by a setter
20398943Sluigi     * @return true if builtin, untouched (unset) property
20498943Sluigi     */
20598943Sluigi    public boolean isBuiltin() {
20698943Sluigi        return builtinSwitchPoint != null && !builtinSwitchPoint.hasBeenInvalidated();
20798943Sluigi    }
20898943Sluigi
209141351Sglebius    /**
210141351Sglebius     * Property flag utility method for {@link PropertyDescriptor}. Get the property flags
21198943Sluigi     * conforming to any Property using this PropertyDescriptor
21298943Sluigi     *
21398943Sluigi     * @param desc property descriptor
21498943Sluigi     * @return flags for properties that conform to property descriptor
21598943Sluigi     */
21698943Sluigi    static int toFlags(final PropertyDescriptor desc) {
21798943Sluigi        int propFlags = 0;
218165648Spiso
21998943Sluigi        if (!desc.isConfigurable()) {
220136071Sgreen            propFlags |= NOT_CONFIGURABLE;
221136071Sgreen        }
222158879Soleg        if (!desc.isEnumerable()) {
223158879Soleg            propFlags |= NOT_ENUMERABLE;
224136071Sgreen        }
225158879Soleg        if (!desc.isWritable()) {
22698943Sluigi            propFlags |= NOT_WRITABLE;
22798943Sluigi        }
228133600Scsjp
22998943Sluigi        return propFlags;
23098943Sluigi    }
23198943Sluigi
23298943Sluigi    /**
23398943Sluigi     * Check whether this property has a user defined getter function. See {@link UserAccessorProperty}
234136073Sgreen     * @param obj object containing getter
235136073Sgreen     * @return true if getter function exists, false is default
236136073Sgreen     */
23798943Sluigi    public boolean hasGetterFunction(final ScriptObject obj) {
23898943Sluigi        return false;
23998943Sluigi    }
24098943Sluigi
24198943Sluigi    /**
24298943Sluigi     * Check whether this property has a user defined setter function. See {@link UserAccessorProperty}
24398943Sluigi     * @param obj object containing setter
24498943Sluigi     * @return true if getter function exists, false is default
24598943Sluigi     */
24698943Sluigi    public boolean hasSetterFunction(final ScriptObject obj) {
24798943Sluigi        return false;
24898943Sluigi    }
24998943Sluigi
250136075Sgreen    /**
25198943Sluigi     * Check whether this property is writable (see ECMA 8.6.1)
25298943Sluigi     * @return true if writable
25398943Sluigi     */
25498943Sluigi    public boolean isWritable() {
25598943Sluigi        return (flags & NOT_WRITABLE) == 0;
25698943Sluigi    }
257102087Sluigi
258102087Sluigi    /**
259112250Scjc     * Check whether this property is writable (see ECMA 8.6.1)
260128575Sandre     * @return true if configurable
261133387Sandre     */
262117241Sluigi    public boolean isConfigurable() {
263117469Sluigi        return (flags & NOT_CONFIGURABLE) == 0;
26498943Sluigi    }
26598943Sluigi
266101978Sluigi    /**
26798943Sluigi     * Check whether this property is enumerable (see ECMA 8.6.1)
26898943Sluigi     * @return true if enumerable
26998943Sluigi     */
27098943Sluigi    public boolean isEnumerable() {
27198943Sluigi        return (flags & NOT_ENUMERABLE) == 0;
27298943Sluigi    }
27398943Sluigi
27498943Sluigi    /**
27598943Sluigi     * Check whether this property is used as a function parameter
27698943Sluigi     * @return true if parameter
27798943Sluigi     */
27898943Sluigi    public boolean isParameter() {
27998943Sluigi        return (flags & IS_PARAMETER) == IS_PARAMETER;
28098943Sluigi    }
281165648Spiso
282165648Spiso    /**
283165648Spiso     * Check whether this property is in an object with arguments field
284165648Spiso     * @return true if has arguments
285165648Spiso     */
286165648Spiso    public boolean hasArguments() {
287165648Spiso        return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
288165648Spiso    }
289165648Spiso
290165648Spiso    /**
291165648Spiso     * Check whether this is a spill property, i.e. one that will not
292165648Spiso     * be stored in a specially generated field in the property class.
293145246Sbrooks     * The spill pool is maintained separately, as a growing Object array
294145246Sbrooks     * in the {@link ScriptObject}.
295145246Sbrooks     *
296145246Sbrooks     * @return true if spill property
297145246Sbrooks     */
298145246Sbrooks    public boolean isSpill() {
299145246Sbrooks        return false;
300146894Smlaier    }
301146894Smlaier
302149020Sbz    /**
303149020Sbz     * Is this property bound to a receiver? If this method returns {@code true} get and set operations
304178888Sjulian     * will be delegated to a statically bound object instead of the object passed as parameter.
305178888Sjulian     *
306178888Sjulian     * @return true if this is a bound property
30798943Sluigi     */
30898943Sluigi    public boolean isBound() {
30998943Sluigi        return (flags & IS_BOUND) == IS_BOUND;
31098943Sluigi    }
311101978Sluigi
31298943Sluigi    /**
31398943Sluigi     * Is this a LET or CONST property that needs to see its declaration before being usable?
31498943Sluigi     *
31598943Sluigi     * @return true if this is a block-scoped variable
31698943Sluigi     */
31798943Sluigi    public boolean needsDeclaration() {
31898943Sluigi        return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION;
31998943Sluigi    }
32098943Sluigi
32198943Sluigi    /**
32298943Sluigi     * Add more property flags to the property. Properties are immutable here,
32398943Sluigi     * so any property change that results in a larger flag set results in the
32498943Sluigi     * property being cloned. Use only the return value
32598943Sluigi     *
32698943Sluigi     * @param propertyFlags flags to be OR:ed to the existing property flags
32799475Sluigi     * @return new property if property set was changed, {@code this} otherwise
32898943Sluigi     */
329145246Sbrooks    public Property addFlags(final int propertyFlags) {
330145246Sbrooks        if ((this.flags & propertyFlags) != propertyFlags) {
331145246Sbrooks            final Property cloned = this.copy();
332145246Sbrooks            cloned.flags |= propertyFlags;
333145246Sbrooks            return cloned;
33498943Sluigi        }
335117328Sluigi        return this;
33698943Sluigi    }
33798943Sluigi
338165648Spiso    /**
339165648Spiso     * Check if a flag is set for a property
340165648Spiso     * @param property property
341165648Spiso     * @param flag     flag to check
342165648Spiso     * @return true if flag is set
343165648Spiso     */
344165648Spiso    public static boolean checkFlag(final Property property, final int flag) {
345165648Spiso        return (property.getFlags() & flag) == flag;
346165648Spiso    }
347165648Spiso
348165648Spiso    /**
349165648Spiso     * Get the flags for this property
350165648Spiso     * @return property flags
351165648Spiso     */
352165648Spiso    public int getFlags() {
353165648Spiso        return flags;
35498943Sluigi    }
35598943Sluigi
35698943Sluigi    /**
35798943Sluigi     * Get the modify flags for this property. The modify flags are the ECMA 8.6.1
35898943Sluigi     * flags that decide if the Property is writable, configurable and/or enumerable.
35998943Sluigi     *
36098943Sluigi     * @return modify flags for property
36198943Sluigi     */
36298943Sluigi    public int getModifyFlags() {
36398943Sluigi        return flags & MODIFY_MASK;
364141351Sglebius    }
365141351Sglebius
36698943Sluigi    /**
36798943Sluigi     * Remove property flags from the property. Properties are immutable here,
36898943Sluigi     * so any property change that results in a smaller flag set results in the
36998943Sluigi     * property being cloned. Use only the return value
37098943Sluigi     *
37198943Sluigi     * @param propertyFlags flags to be subtracted from the existing property flags
372149020Sbz     * @return new property if property set was changed, {@code this} otherwise
37398943Sluigi     */
374149020Sbz    public Property removeFlags(final int propertyFlags) {
37599475Sluigi        if ((this.flags & propertyFlags) != 0) {
37698943Sluigi            final Property cloned = this.copy();
377117469Sluigi            cloned.flags &= ~propertyFlags;
378165648Spiso            return cloned;
379178888Sjulian        }
380117328Sluigi        return this;
38198943Sluigi    }
38298943Sluigi
383136071Sgreen    /**
384136071Sgreen     * Reset the property for this property. Properties are immutable here,
385136071Sgreen     * so any property change that results in a different flag sets results in the
386158879Soleg     * property being cloned. Use only the return value
387158879Soleg     *
388136071Sgreen     * @param propertyFlags flags that are replacing from the existing property flags
389136071Sgreen     * @return new property if property set was changed, {@code this} otherwise
390136071Sgreen     */
39198943Sluigi    public Property setFlags(final int propertyFlags) {
392158879Soleg        if (this.flags != propertyFlags) {
39398943Sluigi            final Property cloned = this.copy();
39498943Sluigi            cloned.flags &= ~MODIFY_MASK;
395133600Scsjp            cloned.flags |= propertyFlags & MODIFY_MASK;
39698943Sluigi            return cloned;
39798943Sluigi        }
39898943Sluigi        return this;
39998943Sluigi    }
40098943Sluigi
40198943Sluigi    /**
402136073Sgreen     * Abstract method for retrieving the getter for the property. We do not know
403136073Sgreen     * anything about the internal representation when we request the getter, we only
404136073Sgreen     * know that the getter will return the property as the given type.
40598943Sluigi     *
40698943Sluigi     * @param type getter return value type
40798943Sluigi     * @return a getter for this property as {@code type}
40898943Sluigi     */
40998943Sluigi    public abstract MethodHandle getGetter(final Class<?> type);
410178888Sjulian
41198943Sluigi    /**
41298943Sluigi     * Get an optimistic getter that throws an exception if type is not the known given one
41398943Sluigi     * @param type          type
41498943Sluigi     * @param programPoint  program point
41598943Sluigi     * @return getter
41698943Sluigi     */
41798943Sluigi    public abstract MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint);
41898943Sluigi
41998943Sluigi    /**
42098943Sluigi     * Hook to initialize method handles after deserialization.
42198943Sluigi     *
42298943Sluigi     * @param structure the structure class
423136075Sgreen     */
42498943Sluigi    abstract void initMethodHandles(final Class<?> structure);
42598943Sluigi
42698943Sluigi    /**
42798943Sluigi     * Get the key for this property. This key is an ordinary string. The "name".
42898943Sluigi     * @return key for property
42998943Sluigi     */
43098943Sluigi    public String getKey() {
43199909Sluigi        return key;
43298943Sluigi    }
433102087Sluigi
434102087Sluigi    /**
435102087Sluigi     * Get the field number or spill slot
436102087Sluigi     * @return number/slot, -1 if none exists
437102087Sluigi     */
438102087Sluigi    public int getSlot() {
439102087Sluigi        return slot;
440102087Sluigi    }
441112250Scjc
442128575Sandre    /**
443133387Sandre     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
444117241Sluigi     * getter MethodHandle for spill and user accessor properties.
445145246Sbrooks     *
446145246Sbrooks     * @param self the this object
447145246Sbrooks     * @param owner the owner of the property
448145246Sbrooks     * @return  the property value
449145246Sbrooks     */
450145246Sbrooks    public abstract int getIntValue(final ScriptObject self, final ScriptObject owner);
451146894Smlaier
452146894Smlaier    /**
453145246Sbrooks     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
454145246Sbrooks     * getter MethodHandle for spill and user accessor properties.
455145246Sbrooks     *
456145246Sbrooks     * @param self the this object
457117469Sluigi     * @param owner the owner of the property
45898943Sluigi     * @return  the property value
45998943Sluigi     */
46098943Sluigi    public abstract long getLongValue(final ScriptObject self, final ScriptObject owner);
46198943Sluigi
46298943Sluigi    /**
463101641Sluigi     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
464101641Sluigi     * getter MethodHandle for spill and user accessor properties.
465101641Sluigi     *
466101641Sluigi     * @param self the this object
467117328Sluigi     * @param owner the owner of the property
46898943Sluigi     * @return  the property value
46998943Sluigi     */
470117328Sluigi    public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner);
471117328Sluigi
472117328Sluigi    /**
473115793Sticso     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
474115793Sticso     * getter MethodHandle for spill and user accessor properties.
475115793Sticso     *
476129389Sstefanf     * @param self the this object
477115793Sticso     * @param owner the owner of the property
478187767Sluigi     * @return  the property value
479187716Sluigi     */
480187716Sluigi    public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner);
481187716Sluigi
482187716Sluigi    /**
483187716Sluigi     * Set the value of this property in {@code owner}. This allows to bypass creation of the
484187716Sluigi     * setter MethodHandle for spill and user accessor properties.
485187716Sluigi     *
486187716Sluigi     * @param self the this object
487187716Sluigi     * @param owner the owner object
488187767Sluigi     * @param value the new property value
489187716Sluigi     * @param strict is this a strict setter?
490187716Sluigi     */
491187716Sluigi    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict);
492187716Sluigi
493187716Sluigi    /**
494187716Sluigi     * Set the value of this property in {@code owner}. This allows to bypass creation of the
495187716Sluigi     * setter MethodHandle for spill and user accessor properties.
496187716Sluigi     *
497187716Sluigi     * @param self the this object
498117328Sluigi     * @param owner the owner object
499117328Sluigi     * @param value the new property value
500117328Sluigi     * @param strict is this a strict setter?
501117469Sluigi     */
502119740Stmm    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict);
503117328Sluigi
504117328Sluigi    /**
505117328Sluigi     * Set the value of this property in {@code owner}. This allows to bypass creation of the
506117577Sluigi     * setter MethodHandle for spill and user accessor properties.
507187764Sluigi     *
508117328Sluigi     * @param self the this object
509117328Sluigi     * @param owner the owner object
510117328Sluigi     * @param value the new property value
511117328Sluigi     * @param strict is this a strict setter?
512117328Sluigi     */
513117328Sluigi    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict);
514117328Sluigi
515117328Sluigi    /**
516130281Sru     * Set the value of this property in {@code owner}. This allows to bypass creation of the
517165648Spiso     * setter MethodHandle for spill and user accessor properties.
518165648Spiso     *
519165648Spiso     * @param self the this object
520117328Sluigi     * @param owner the owner object
521117328Sluigi     * @param value the new property value
522117328Sluigi     * @param strict is this a strict setter?
523117328Sluigi     */
524117328Sluigi    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict);
525117328Sluigi
526117328Sluigi    /**
52798943Sluigi     * Abstract method for retrieving the setter for the property. We do not know
52898943Sluigi     * anything about the internal representation when we request the setter, we only
529117328Sluigi     * know that the setter will take the property as a parameter of the given type.
53098943Sluigi     * <p>
53198943Sluigi     * Note that we have to pass the current property map from which we retrieved
53298943Sluigi     * the property here. This is necessary for map guards if, e.g. the internal
53398943Sluigi     * representation of the field, and consequently also the setter, changes. Then
53498943Sluigi     * we automatically get a map guard that relinks the call site so that the
535117469Sluigi     * older setter will never be used again.
53698943Sluigi     * <p>
53798943Sluigi     * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
53898943Sluigi     * if you are interested in the internal details of this. Note that if you
53998943Sluigi     * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
54098943Sluigi     * will currently never change, as all properties are represented as Object field,
541129389Sstefanf     * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
54298943Sluigi     * boxed/unboxed upon every access, which is not necessarily optimal
543117328Sluigi     *
544117328Sluigi     * @param type setter parameter type
545117328Sluigi     * @param currentMap current property map for property
546117328Sluigi     * @return a getter for this property as {@code type}
547117469Sluigi     */
548117469Sluigi    public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
54998943Sluigi
55098943Sluigi    /**
55198943Sluigi     * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
55298943Sluigi     * can have user defined getters
55398943Sluigi     * @param obj the script object
55498943Sluigi     * @return user defined getter function, or {@code null} if none exists
55598943Sluigi     */
55698943Sluigi    public ScriptFunction getGetterFunction(final ScriptObject obj) {
557140271Sbrooks        return null;
558140271Sbrooks    }
559140271Sbrooks
560140271Sbrooks    /**
561140271Sbrooks     * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
562140271Sbrooks     * can have user defined getters
563140271Sbrooks     * @param obj the script object
564140271Sbrooks     * @return user defined getter function, or {@code null} if none exists
565187767Sluigi     */
566140271Sbrooks    public ScriptFunction getSetterFunction(final ScriptObject obj) {
567140271Sbrooks        return null;
568140271Sbrooks    }
569140271Sbrooks
570140271Sbrooks    @Override
571140271Sbrooks    public int hashCode() {
572140271Sbrooks        final Class<?> type = getCurrentType();
573140271Sbrooks        return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
574140271Sbrooks    }
575140271Sbrooks
576140271Sbrooks    @Override
577140271Sbrooks    public boolean equals(final Object other) {
578140271Sbrooks        if (this == other) {
579140271Sbrooks            return true;
580140271Sbrooks        }
581140271Sbrooks
582140271Sbrooks        if (other == null || this.getClass() != other.getClass()) {
583140271Sbrooks            return false;
584140271Sbrooks        }
585140271Sbrooks
586140271Sbrooks        final Property otherProperty = (Property)other;
587140271Sbrooks
588140271Sbrooks        return equalsWithoutType(otherProperty) &&
589140271Sbrooks               getCurrentType() == otherProperty.getCurrentType();
590140271Sbrooks    }
591140271Sbrooks
592140271Sbrooks    boolean equalsWithoutType(final Property otherProperty) {
593140271Sbrooks        return getFlags() == otherProperty.getFlags() &&
594140271Sbrooks                getSlot() == otherProperty.getSlot() &&
595140271Sbrooks                getKey().equals(otherProperty.getKey());
596140271Sbrooks    }
597140271Sbrooks
598140271Sbrooks    private static final String type(final Class<?> type) {
599140271Sbrooks        if (type == null) {
600140271Sbrooks            return "undef";
601140271Sbrooks        } else if (type == int.class) {
602140271Sbrooks            return "i";
603140271Sbrooks        } else if (type == long.class) {
604140271Sbrooks            return "j";
605140271Sbrooks        } else if (type == double.class) {
606140271Sbrooks            return "d";
60798943Sluigi        } else {
60898943Sluigi            return "o";
60998943Sluigi        }
610117328Sluigi    }
61198943Sluigi
61298943Sluigi    /**
61398943Sluigi     * Short toString version
614117469Sluigi     * @return short toString
61598943Sluigi     */
616187764Sluigi    public final String toStringShort() {
61798943Sluigi        final StringBuilder sb   = new StringBuilder();
61898943Sluigi        final Class<?>      type = getCurrentType();
61998943Sluigi        sb.append(getKey()).append(" (").append(type(type)).append(')');
62098943Sluigi        return sb.toString();
62198943Sluigi    }
622187764Sluigi
62398943Sluigi    private static String indent(final String str, final int indent) {
62498943Sluigi        final StringBuilder sb = new StringBuilder();
62598943Sluigi        sb.append(str);
62698943Sluigi        for (int i = 0; i < indent - str.length(); i++) {
62798943Sluigi            sb.append(' ');
62898943Sluigi        }
62998943Sluigi        return sb.toString();
63098943Sluigi     }
63198943Sluigi
63298943Sluigi    @Override
63398943Sluigi    public String toString() {
634117328Sluigi        final StringBuilder sb   = new StringBuilder();
635117328Sluigi        final Class<?>      type = getCurrentType();
636117328Sluigi
637117328Sluigi        sb.append(indent(getKey(), 20)).
638117328Sluigi            append(" id=").
639117328Sluigi            append(Debug.id(this)).
640117328Sluigi            append(" (0x").
641136075Sgreen            append(indent(Integer.toHexString(flags), 4)).
642158879Soleg            append(") ").
643117328Sluigi            append(getClass().getSimpleName()).
644117328Sluigi            append(" {").
645117328Sluigi            append(indent(type(type), 5)).
64698943Sluigi            append('}');
647117328Sluigi
64898943Sluigi        if (slot != -1) {
64998943Sluigi            sb.append(" [").
65098943Sluigi               append("slot=").
651102087Sluigi               append(slot).
65298943Sluigi               append(']');
653117328Sluigi        }
65498943Sluigi
655117469Sluigi        return sb.toString();
65698943Sluigi    }
657116690Sluigi
658117328Sluigi    /**
659117328Sluigi     * Get the current type of this field. If you are not running with dual fields enabled,
660116690Sluigi     * this will always be Object.class. See the value representation explanation in
661116690Sluigi     * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
662116690Sluigi     * for more information.
663116690Sluigi     *
66498943Sluigi     * @return current type of property, null means undefined
66598943Sluigi     */
66698943Sluigi    public abstract Class<?> getCurrentType();
66798943Sluigi
66898943Sluigi    /**
66998943Sluigi     * Reset the current type of this property
67098943Sluigi     * @param currentType new current type
67198943Sluigi     */
67298943Sluigi    public abstract void setCurrentType(final Class<?> currentType);
67398943Sluigi
67498943Sluigi    /**
67598943Sluigi     * Check whether this Property can ever change its type. The default is false, and if
67698943Sluigi     * you are not running with dual fields, the type is always object and can never change
67798943Sluigi     * @return true if this property can change types
67898943Sluigi     */
67998943Sluigi    public boolean canChangeType() {
68098943Sluigi        return false;
68198943Sluigi    }
682101628Sluigi
68398943Sluigi    /**
68498943Sluigi     * Check whether this property represents a function declaration.
68598943Sluigi     * @return whether this property is a function declaration or not.
68698943Sluigi     */
687101628Sluigi    public boolean isFunctionDeclaration() {
688101628Sluigi        return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
68998943Sluigi    }
69098943Sluigi}
691101628Sluigi