]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2009 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | ||
27 | #include "config.h" | |
28 | ||
29 | #include "PropertyDescriptor.h" | |
30 | ||
31 | #include "GetterSetter.h" | |
32 | #include "JSObject.h" | |
33 | #include "JSCInlines.h" | |
34 | ||
35 | namespace JSC { | |
36 | unsigned PropertyDescriptor::defaultAttributes = DontDelete | DontEnum | ReadOnly; | |
37 | ||
38 | bool PropertyDescriptor::writable() const | |
39 | { | |
40 | ASSERT(!isAccessorDescriptor()); | |
41 | return !(m_attributes & ReadOnly); | |
42 | } | |
43 | ||
44 | bool PropertyDescriptor::enumerable() const | |
45 | { | |
46 | return !(m_attributes & DontEnum); | |
47 | } | |
48 | ||
49 | bool PropertyDescriptor::configurable() const | |
50 | { | |
51 | return !(m_attributes & DontDelete); | |
52 | } | |
53 | ||
54 | bool PropertyDescriptor::isDataDescriptor() const | |
55 | { | |
56 | return m_value || (m_seenAttributes & WritablePresent); | |
57 | } | |
58 | ||
59 | bool PropertyDescriptor::isGenericDescriptor() const | |
60 | { | |
61 | return !isAccessorDescriptor() && !isDataDescriptor(); | |
62 | } | |
63 | ||
64 | bool PropertyDescriptor::isAccessorDescriptor() const | |
65 | { | |
66 | return m_getter || m_setter; | |
67 | } | |
68 | ||
69 | void PropertyDescriptor::setUndefined() | |
70 | { | |
71 | m_value = jsUndefined(); | |
72 | m_attributes = ReadOnly | DontDelete | DontEnum; | |
73 | } | |
74 | ||
75 | JSValue PropertyDescriptor::getter() const | |
76 | { | |
77 | ASSERT(isAccessorDescriptor()); | |
78 | return m_getter; | |
79 | } | |
80 | ||
81 | JSValue PropertyDescriptor::setter() const | |
82 | { | |
83 | ASSERT(isAccessorDescriptor()); | |
84 | return m_setter; | |
85 | } | |
86 | ||
87 | JSObject* PropertyDescriptor::getterObject() const | |
88 | { | |
89 | ASSERT(isAccessorDescriptor() && getterPresent()); | |
90 | return m_getter.isObject() ? asObject(m_getter) : 0; | |
91 | } | |
92 | ||
93 | JSObject* PropertyDescriptor::setterObject() const | |
94 | { | |
95 | ASSERT(isAccessorDescriptor() && setterPresent()); | |
96 | return m_setter.isObject() ? asObject(m_setter) : 0; | |
97 | } | |
98 | ||
99 | void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) | |
100 | { | |
101 | ASSERT(value); | |
102 | ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); | |
103 | ||
104 | m_attributes = attributes; | |
105 | if (value.isGetterSetter()) { | |
106 | m_attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this! | |
107 | ||
108 | GetterSetter* accessor = asGetterSetter(value); | |
109 | m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined(); | |
110 | m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined(); | |
111 | m_seenAttributes = EnumerablePresent | ConfigurablePresent; | |
112 | } else { | |
113 | m_value = value; | |
114 | m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; | |
115 | } | |
116 | } | |
117 | ||
118 | void PropertyDescriptor::setCustomDescriptor(unsigned attributes) | |
119 | { | |
120 | m_attributes = attributes | Accessor | CustomAccessor; | |
121 | m_attributes &= ~ReadOnly; | |
122 | m_seenAttributes = EnumerablePresent | ConfigurablePresent; | |
123 | setGetter(jsUndefined()); | |
124 | setSetter(jsUndefined()); | |
125 | m_value = JSValue(); | |
126 | } | |
127 | ||
128 | void PropertyDescriptor::setAccessorDescriptor(GetterSetter* accessor, unsigned attributes) | |
129 | { | |
130 | ASSERT(attributes & Accessor); | |
131 | attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this! | |
132 | ||
133 | m_attributes = attributes; | |
134 | m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined(); | |
135 | m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined(); | |
136 | m_seenAttributes = EnumerablePresent | ConfigurablePresent; | |
137 | } | |
138 | ||
139 | void PropertyDescriptor::setWritable(bool writable) | |
140 | { | |
141 | if (writable) | |
142 | m_attributes &= ~ReadOnly; | |
143 | else | |
144 | m_attributes |= ReadOnly; | |
145 | m_seenAttributes |= WritablePresent; | |
146 | } | |
147 | ||
148 | void PropertyDescriptor::setEnumerable(bool enumerable) | |
149 | { | |
150 | if (enumerable) | |
151 | m_attributes &= ~DontEnum; | |
152 | else | |
153 | m_attributes |= DontEnum; | |
154 | m_seenAttributes |= EnumerablePresent; | |
155 | } | |
156 | ||
157 | void PropertyDescriptor::setConfigurable(bool configurable) | |
158 | { | |
159 | if (configurable) | |
160 | m_attributes &= ~DontDelete; | |
161 | else | |
162 | m_attributes |= DontDelete; | |
163 | m_seenAttributes |= ConfigurablePresent; | |
164 | } | |
165 | ||
166 | void PropertyDescriptor::setSetter(JSValue setter) | |
167 | { | |
168 | m_setter = setter; | |
169 | m_attributes |= Accessor; | |
170 | m_attributes &= ~ReadOnly; | |
171 | } | |
172 | ||
173 | void PropertyDescriptor::setGetter(JSValue getter) | |
174 | { | |
175 | m_getter = getter; | |
176 | m_attributes |= Accessor; | |
177 | m_attributes &= ~ReadOnly; | |
178 | } | |
179 | ||
180 | // See ES5.1 9.12 | |
181 | bool sameValue(ExecState* exec, JSValue a, JSValue b) | |
182 | { | |
183 | if (!a.isNumber()) | |
184 | return JSValue::strictEqual(exec, a, b); | |
185 | if (!b.isNumber()) | |
186 | return false; | |
187 | double x = a.asNumber(); | |
188 | double y = b.asNumber(); | |
189 | bool xIsNaN = std::isnan(x); | |
190 | bool yIsNaN = std::isnan(y); | |
191 | if (xIsNaN || yIsNaN) | |
192 | return xIsNaN && yIsNaN; | |
193 | return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y); | |
194 | } | |
195 | ||
196 | bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const | |
197 | { | |
198 | if (other.m_value.isEmpty() != m_value.isEmpty() | |
199 | || other.m_getter.isEmpty() != m_getter.isEmpty() | |
200 | || other.m_setter.isEmpty() != m_setter.isEmpty()) | |
201 | return false; | |
202 | return (!m_value || sameValue(exec, other.m_value, m_value)) | |
203 | && (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) | |
204 | && (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter)) | |
205 | && attributesEqual(other); | |
206 | } | |
207 | ||
208 | bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const | |
209 | { | |
210 | unsigned mismatch = other.m_attributes ^ m_attributes; | |
211 | unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; | |
212 | if (sharedSeen & WritablePresent && mismatch & ReadOnly) | |
213 | return false; | |
214 | if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) | |
215 | return false; | |
216 | if (sharedSeen & EnumerablePresent && mismatch & DontEnum) | |
217 | return false; | |
218 | return true; | |
219 | } | |
220 | ||
221 | unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const | |
222 | { | |
223 | unsigned currentAttributes = current.m_attributes; | |
224 | if (isDataDescriptor() && current.isAccessorDescriptor()) | |
225 | currentAttributes |= ReadOnly; | |
226 | unsigned overrideMask = 0; | |
227 | if (writablePresent()) | |
228 | overrideMask |= ReadOnly; | |
229 | if (enumerablePresent()) | |
230 | overrideMask |= DontEnum; | |
231 | if (configurablePresent()) | |
232 | overrideMask |= DontDelete; | |
233 | if (isAccessorDescriptor()) | |
234 | overrideMask |= Accessor; | |
235 | return (m_attributes & overrideMask) | (currentAttributes & ~overrideMask); | |
236 | } | |
237 | ||
238 | } |