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.objects;
27
28import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
29import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
30import static jdk.nashorn.internal.runtime.ScriptRuntime.sameValue;
31
32import java.util.Objects;
33import jdk.nashorn.internal.objects.annotations.Property;
34import jdk.nashorn.internal.objects.annotations.ScriptClass;
35import jdk.nashorn.internal.runtime.JSType;
36import jdk.nashorn.internal.runtime.PropertyDescriptor;
37import jdk.nashorn.internal.runtime.PropertyMap;
38import jdk.nashorn.internal.runtime.ScriptFunction;
39import jdk.nashorn.internal.runtime.ScriptObject;
40import jdk.nashorn.internal.runtime.ScriptRuntime;
41
42/**
43 * Accessor Property descriptor is used to represent attributes an object property
44 * that either has a getter or a setter.
45 *
46 * See ECMA 8.10 The Property Descriptor and Property Identifier Specification Types
47 *
48 */
49@ScriptClass("AccessorPropertyDescriptor")
50public final class AccessorPropertyDescriptor extends ScriptObject implements PropertyDescriptor {
51    /** is this property configurable? */
52    @Property
53    public Object configurable;
54
55    /** is this property enumerable? */
56    @Property
57    public Object enumerable;
58
59    /** getter for property */
60    @Property
61    public Object get;
62
63    /** setter for property */
64    @Property
65    public Object set;
66
67    // initialized by nasgen
68    private static PropertyMap $nasgenmap$;
69
70    AccessorPropertyDescriptor(final boolean configurable, final boolean enumerable, final Object get, final Object set, final Global global) {
71        super(global.getObjectPrototype(), $nasgenmap$);
72        this.configurable = configurable;
73        this.enumerable   = enumerable;
74        this.get          = get;
75        this.set          = set;
76    }
77
78    @Override
79    public boolean isConfigurable() {
80        return JSType.toBoolean(configurable);
81    }
82
83    @Override
84    public boolean isEnumerable() {
85        return JSType.toBoolean(enumerable);
86    }
87
88    @Override
89    public boolean isWritable() {
90        // Not applicable for this. But simplifies flag calculations.
91        return true;
92    }
93
94    @Override
95    public Object getValue() {
96        throw new UnsupportedOperationException("value");
97    }
98
99    @Override
100    public ScriptFunction getGetter() {
101        return (get instanceof ScriptFunction) ? (ScriptFunction)get : null;
102    }
103
104    @Override
105    public ScriptFunction getSetter() {
106        return (set instanceof ScriptFunction) ? (ScriptFunction)set : null;
107    }
108
109    @Override
110    public void setConfigurable(final boolean flag) {
111        this.configurable = flag;
112    }
113
114    @Override
115    public void setEnumerable(final boolean flag) {
116        this.enumerable = flag;
117    }
118
119    @Override
120    public void setWritable(final boolean flag) {
121        throw new UnsupportedOperationException("set writable");
122    }
123
124    @Override
125    public void setValue(final Object value) {
126        throw new UnsupportedOperationException("set value");
127    }
128
129    @Override
130    public void setGetter(final Object getter) {
131        this.get = getter;
132    }
133
134    @Override
135    public void setSetter(final Object setter) {
136        this.set = setter;
137    }
138
139    @Override
140    public PropertyDescriptor fillFrom(final ScriptObject sobj) {
141        if (sobj.has(CONFIGURABLE)) {
142            this.configurable = JSType.toBoolean(sobj.get(CONFIGURABLE));
143        } else {
144            delete(CONFIGURABLE, false);
145        }
146
147        if (sobj.has(ENUMERABLE)) {
148            this.enumerable = JSType.toBoolean(sobj.get(ENUMERABLE));
149        } else {
150            delete(ENUMERABLE, false);
151        }
152
153        if (sobj.has(GET)) {
154            final Object getter = sobj.get(GET);
155            if (getter == UNDEFINED || getter instanceof ScriptFunction) {
156                this.get = getter;
157            } else {
158                throw typeError("not.a.function", ScriptRuntime.safeToString(getter));
159            }
160        } else {
161            delete(GET, false);
162        }
163
164        if (sobj.has(SET)) {
165            final Object setter = sobj.get(SET);
166            if (setter == UNDEFINED || setter instanceof ScriptFunction) {
167                this.set = setter;
168            } else {
169                throw typeError("not.a.function", ScriptRuntime.safeToString(setter));
170            }
171        } else {
172            delete(SET, false);
173        }
174
175        return this;
176    }
177
178    @Override
179    public int type() {
180        return ACCESSOR;
181    }
182
183    @Override
184    public boolean hasAndEquals(final PropertyDescriptor otherDesc) {
185        if (! (otherDesc instanceof AccessorPropertyDescriptor)) {
186            return false;
187        }
188        final AccessorPropertyDescriptor other = (AccessorPropertyDescriptor)otherDesc;
189        return (!has(CONFIGURABLE) || sameValue(configurable, other.configurable)) &&
190               (!has(ENUMERABLE) || sameValue(enumerable, other.enumerable)) &&
191               (!has(GET) || sameValue(get, other.get)) &&
192               (!has(SET) || sameValue(set, other.set));
193    }
194
195    @Override
196    public boolean equals(final Object obj) {
197        if (this == obj) {
198            return true;
199        }
200        if (! (obj instanceof AccessorPropertyDescriptor)) {
201            return false;
202        }
203
204        final AccessorPropertyDescriptor other = (AccessorPropertyDescriptor)obj;
205        return sameValue(configurable, other.configurable) &&
206               sameValue(enumerable, other.enumerable) &&
207               sameValue(get, other.get) &&
208               sameValue(set, other.set);
209    }
210
211    @Override
212    public String toString() {
213        return '[' + getClass().getSimpleName() + " {configurable=" + configurable + " enumerable=" + enumerable + " getter=" + get + " setter=" + set + "}]";
214    }
215
216    @Override
217    public int hashCode() {
218        int hash = 7;
219        hash = 41 * hash + Objects.hashCode(this.configurable);
220        hash = 41 * hash + Objects.hashCode(this.enumerable);
221        hash = 41 * hash + Objects.hashCode(this.get);
222        hash = 41 * hash + Objects.hashCode(this.set);
223        return hash;
224    }
225}
226