Writable attribute not set correctly when redefining an accessor to a data descriptor
[WebKit-https.git] / Source / JavaScriptCore / runtime / PropertyDescriptor.cpp
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 "Operations.h"
34
35 namespace JSC {
36 unsigned PropertyDescriptor::defaultAttributes = (DontDelete << 1) - 1;
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->getter() ? accessor->getter() : jsUndefined();
110         m_setter = accessor->setter() ? 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::setAccessorDescriptor(GetterSetter* accessor, unsigned attributes)
119 {
120     ASSERT(attributes & Accessor);
121     attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this!
122
123     m_attributes = attributes;
124     m_getter = accessor->getter() ? accessor->getter() : jsUndefined();
125     m_setter = accessor->setter() ? accessor->setter() : jsUndefined();
126     m_seenAttributes = EnumerablePresent | ConfigurablePresent;
127 }
128
129 void PropertyDescriptor::setWritable(bool writable)
130 {
131     if (writable)
132         m_attributes &= ~ReadOnly;
133     else
134         m_attributes |= ReadOnly;
135     m_seenAttributes |= WritablePresent;
136 }
137
138 void PropertyDescriptor::setEnumerable(bool enumerable)
139 {
140     if (enumerable)
141         m_attributes &= ~DontEnum;
142     else
143         m_attributes |= DontEnum;
144     m_seenAttributes |= EnumerablePresent;
145 }
146
147 void PropertyDescriptor::setConfigurable(bool configurable)
148 {
149     if (configurable)
150         m_attributes &= ~DontDelete;
151     else
152         m_attributes |= DontDelete;
153     m_seenAttributes |= ConfigurablePresent;
154 }
155
156 void PropertyDescriptor::setSetter(JSValue setter)
157 {
158     m_setter = setter;
159     m_attributes |= Accessor;
160     m_attributes &= ~ReadOnly;
161 }
162
163 void PropertyDescriptor::setGetter(JSValue getter)
164 {
165     m_getter = getter;
166     m_attributes |= Accessor;
167     m_attributes &= ~ReadOnly;
168 }
169
170 // See ES5.1 9.12
171 bool sameValue(ExecState* exec, JSValue a, JSValue b)
172 {
173     if (!a.isNumber())
174         return JSValue::strictEqual(exec, a, b);
175     if (!b.isNumber())
176         return false;
177     double x = a.asNumber();
178     double y = b.asNumber();
179     if (isnan(x))
180         return isnan(y);
181     return bitwise_cast<uint64_t>(x) == bitwise_cast<uint64_t>(y);
182 }
183
184 bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const
185 {
186     if (!other.m_value == m_value ||
187         !other.m_getter == m_getter ||
188         !other.m_setter == m_setter)
189         return false;
190     return (!m_value || sameValue(exec, other.m_value, m_value))
191         && (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter))
192         && (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter))
193         && attributesEqual(other);
194 }
195
196 bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const
197 {
198     unsigned mismatch = other.m_attributes ^ m_attributes;
199     unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes;
200     if (sharedSeen & WritablePresent && mismatch & ReadOnly)
201         return false;
202     if (sharedSeen & ConfigurablePresent && mismatch & DontDelete)
203         return false;
204     if (sharedSeen & EnumerablePresent && mismatch & DontEnum)
205         return false;
206     return true;
207 }
208
209 unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const
210 {
211     unsigned currentAttributes = current.m_attributes;
212     if (isDataDescriptor() && current.isAccessorDescriptor())
213         currentAttributes |= ReadOnly;
214     unsigned overrideMask = 0;
215     if (writablePresent())
216         overrideMask |= ReadOnly;
217     if (enumerablePresent())
218         overrideMask |= DontEnum;
219     if (configurablePresent())
220         overrideMask |= DontDelete;
221     if (isAccessorDescriptor())
222         overrideMask |= Accessor;
223     return (m_attributes & overrideMask) | (currentAttributes & ~overrideMask);
224 }
225
226 }