1 | /* |
2 | This file is part of the KDE libraries |
3 | Copyright (C) 2012 Bernd Buschinski <b.buschinski@googlemail.com> |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; either version 2 of the License, or |
8 | (at your option) any later version. |
9 | |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License along |
16 | with this program; if not, write to the Free Software Foundation, Inc., |
17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
18 | */ |
19 | |
20 | |
21 | #include "propertydescriptor.h" |
22 | #include "object.h" |
23 | #include "operations.h" |
24 | |
25 | #include <stdio.h> |
26 | |
27 | namespace KJS { |
28 | |
29 | PropertyDescriptor::PropertyDescriptor() |
30 | : m_attributes(DontEnum|DontDelete|ReadOnly), |
31 | m_setAttributes(0), |
32 | m_value(0), |
33 | m_getter(0), |
34 | m_setter(0) |
35 | { |
36 | } |
37 | |
38 | //ECMAScript Edition 5.1r6 - 8.10.1 |
39 | bool PropertyDescriptor::isAccessorDescriptor() const |
40 | { |
41 | return (m_getter || m_setter); |
42 | } |
43 | |
44 | //ECMAScript Edition 5.1r6 - 8.10.2 |
45 | bool PropertyDescriptor::isDataDescriptor() const |
46 | { |
47 | if (!m_value && !(writableSet())) |
48 | return false; |
49 | return true; |
50 | } |
51 | |
52 | //ECMAScript Edition 5.1r6 - 8.10.3 |
53 | bool PropertyDescriptor::isGenericDescriptor() const |
54 | { |
55 | return (!isAccessorDescriptor() && !isDataDescriptor()); |
56 | } |
57 | |
58 | //ECMAScript Edition 5.1r6 - 8.10.4 - FromPropertyDescriptor |
59 | JSObject* PropertyDescriptor::fromPropertyDescriptor(ExecState *exec) |
60 | { |
61 | JSObject *ret = new JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()); |
62 | |
63 | if (isDataDescriptor()) { |
64 | ret->put(exec, exec->propertyNames().writable, jsBoolean(writable())); |
65 | ret->put(exec, exec->propertyNames().value, value() ? value() : jsUndefined()); |
66 | } else { |
67 | ret->put(exec, exec->propertyNames().get, getter() ? getter() : jsUndefined()); |
68 | ret->put(exec, exec->propertyNames().set, setter() ? setter() : jsUndefined()); |
69 | } |
70 | |
71 | ret->put(exec, exec->propertyNames().enumerable, jsBoolean(enumerable())); |
72 | ret->put(exec, exec->propertyNames().configurable, jsBoolean(configurable())); |
73 | |
74 | return ret; |
75 | } |
76 | |
77 | //ECMAScript Edition 5.1r6 - 8.10.5 - ToPropertyDescriptor |
78 | bool PropertyDescriptor::setPropertyDescriptorFromObject(ExecState *exec, JSValue* jsValue) |
79 | { |
80 | JSObject *obj = jsValue->getObject(); |
81 | if (!obj) { |
82 | throwError(exec, TypeError, "not an Object" ); |
83 | return false; |
84 | } |
85 | |
86 | if (obj->hasProperty(exec, exec->propertyNames().enumerable)) |
87 | setEnumerable(obj->get(exec, exec->propertyNames().enumerable)->toBoolean(exec)); |
88 | |
89 | if (obj->hasProperty(exec, exec->propertyNames().configurable)) |
90 | setConfigureable(obj->get(exec, exec->propertyNames().configurable)->toBoolean(exec)); |
91 | |
92 | if (obj->hasProperty(exec, exec->propertyNames().value)) |
93 | setValue(obj->get(exec, exec->propertyNames().value)); |
94 | |
95 | if (obj->hasProperty(exec, exec->propertyNames().writable)) |
96 | setWritable(obj->get(exec, exec->propertyNames().writable)->toBoolean(exec)); |
97 | |
98 | if (obj->hasProperty(exec, exec->propertyNames().get)) { |
99 | JSValue* getter = obj->get(exec, exec->propertyNames().get); |
100 | if (!getter->isUndefined()) { |
101 | if (!getter->implementsCall()) { |
102 | throwError(exec, TypeError, "Getter: \'" + getter->toString(exec) + "\' is not Callable" ); |
103 | return false; |
104 | } |
105 | } |
106 | setGetter(getter); |
107 | } |
108 | |
109 | if (obj->hasProperty(exec, exec->propertyNames().set)) { |
110 | JSValue* setter = obj->get(exec, exec->propertyNames().set); |
111 | if (!setter->isUndefined()) { |
112 | if (!setter->implementsCall()) { |
113 | throwError(exec, TypeError, "Setter: \'" + setter->toString(exec) + "\' is not Callable" ); |
114 | return false; |
115 | } |
116 | } |
117 | setSetter(setter); |
118 | } |
119 | |
120 | if (getter() || setter()) { |
121 | if (value() || writableSet()) { |
122 | throwError(exec, TypeError, "can not mix accessor descriptor and data descriptor" ); |
123 | return false; |
124 | } |
125 | } |
126 | return true; |
127 | } |
128 | |
129 | bool PropertyDescriptor::setPropertyDescriptorValues(ExecState*, JSValue* value, unsigned int attributes) |
130 | { |
131 | setEnumerable(!(attributes & DontEnum)); |
132 | setConfigureable(!(attributes & DontDelete)); |
133 | |
134 | if (!value) |
135 | return false; |
136 | if (value->isUndefined() || value->type() != GetterSetterType) { |
137 | setValue(value); |
138 | setWritable(!(attributes & ReadOnly)); |
139 | } else { |
140 | GetterSetterImp *gs = static_cast<GetterSetterImp *>(value); |
141 | setGetter(gs->getGetter() ? gs->getGetter() : jsUndefined()); |
142 | setSetter(gs->getSetter() ? gs->getSetter() : jsUndefined()); |
143 | } |
144 | return true; |
145 | } |
146 | |
147 | bool PropertyDescriptor::configurable() const |
148 | { |
149 | return !(m_attributes & DontDelete); |
150 | } |
151 | |
152 | bool PropertyDescriptor::enumerable() const |
153 | { |
154 | return !(m_attributes & DontEnum); |
155 | } |
156 | |
157 | bool PropertyDescriptor::writable() const |
158 | { |
159 | return !(m_attributes & ReadOnly); |
160 | } |
161 | |
162 | bool PropertyDescriptor::configureSet() const |
163 | { |
164 | return m_setAttributes & ConfigurableSet; |
165 | } |
166 | |
167 | bool PropertyDescriptor::enumerableSet() const |
168 | { |
169 | return m_setAttributes & EnumerableSet; |
170 | } |
171 | |
172 | bool PropertyDescriptor::writableSet() const |
173 | { |
174 | return m_setAttributes & WritableSet; |
175 | } |
176 | |
177 | JSValue* PropertyDescriptor::getter() const |
178 | { |
179 | return m_getter; |
180 | } |
181 | |
182 | JSValue* PropertyDescriptor::setter() const |
183 | { |
184 | return m_setter; |
185 | } |
186 | |
187 | JSValue* PropertyDescriptor::value() const |
188 | { |
189 | return m_value; |
190 | } |
191 | |
192 | void PropertyDescriptor::setEnumerable(bool enumerable) |
193 | { |
194 | if (enumerable) |
195 | m_attributes &= ~DontEnum; |
196 | else |
197 | m_attributes |= DontEnum; |
198 | m_setAttributes |= EnumerableSet; |
199 | } |
200 | |
201 | void PropertyDescriptor::setConfigureable(bool configureable) |
202 | { |
203 | if (configureable) |
204 | m_attributes &= ~DontDelete; |
205 | else |
206 | m_attributes |= DontDelete; |
207 | m_setAttributes |= ConfigurableSet; |
208 | } |
209 | |
210 | void PropertyDescriptor::setValue(JSValue* value) |
211 | { |
212 | m_value = value; |
213 | } |
214 | |
215 | void PropertyDescriptor::setWritable(bool writable) |
216 | { |
217 | if (writable) |
218 | m_attributes &= ~ReadOnly; |
219 | else |
220 | m_attributes |= ReadOnly; |
221 | m_setAttributes |= WritableSet; |
222 | } |
223 | |
224 | void PropertyDescriptor::setGetter(JSValue* getter) |
225 | { |
226 | m_getter = getter; |
227 | m_attributes &= ~ReadOnly; |
228 | } |
229 | |
230 | void PropertyDescriptor::setSetter(JSValue* setter) |
231 | { |
232 | m_setter = setter; |
233 | m_attributes &= ~ReadOnly; |
234 | } |
235 | |
236 | unsigned int PropertyDescriptor::attributes() const |
237 | { |
238 | return m_attributes; |
239 | } |
240 | |
241 | bool PropertyDescriptor::isEmpty() const |
242 | { |
243 | return !m_setAttributes && !m_getter && !m_setter && !m_value; |
244 | } |
245 | |
246 | inline bool compareValue(ExecState* exec, JSValue* a, JSValue* b) |
247 | { |
248 | return (a == b || (a && b && sameValue(exec, a, b))); |
249 | } |
250 | |
251 | // different from compareValue, if "own" getter/setter is missing (is 0) we are still the same |
252 | inline bool compareFunction(ExecState* exec, JSValue* a, JSValue* b) |
253 | { |
254 | return (a == b || (b != 0 && a == 0) || (a && b && sameValue(exec, a, b))); |
255 | } |
256 | |
257 | bool PropertyDescriptor::equalTo(ExecState* exec, PropertyDescriptor& other) const |
258 | { |
259 | return (compareValue(exec, m_value, other.value()) && |
260 | compareFunction(exec, m_getter, other.getter()) && |
261 | compareFunction(exec, m_setter, other.setter()) && |
262 | attributes() == other.attributes()); |
263 | } |
264 | |
265 | unsigned int PropertyDescriptor::attributesWithOverride(PropertyDescriptor& other) const |
266 | { |
267 | unsigned int mismatch = other.m_attributes ^ m_attributes; |
268 | unsigned int sharedSeen = other.m_setAttributes & m_setAttributes; |
269 | unsigned int newAttributes = m_attributes & (DontEnum|DontDelete|ReadOnly); |
270 | |
271 | if ((sharedSeen & WritableSet) && (mismatch & ReadOnly)) |
272 | newAttributes ^= ReadOnly; |
273 | if ((sharedSeen & ConfigurableSet) && (mismatch & DontDelete)) |
274 | newAttributes ^= DontDelete; |
275 | if ((sharedSeen & EnumerableSet) && (mismatch & DontEnum)) |
276 | newAttributes ^= DontEnum; |
277 | |
278 | return newAttributes; |
279 | } |
280 | |
281 | bool PropertyDescriptor::operator==(PropertyDescriptor& other) const |
282 | { |
283 | return (m_value == other.value() && |
284 | m_setter == other.setter() && |
285 | m_getter == other.getter() && |
286 | m_attributes == other.m_attributes && |
287 | writableSet() == other.writableSet() && |
288 | enumerableSet() == other.enumerableSet() && |
289 | configureSet() == other.configureSet()); |
290 | } |
291 | |
292 | } |
293 | |
294 | |