1/*
2 * Copyright (C) 2009-2019 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
35namespace JSC {
36unsigned PropertyDescriptor::defaultAttributes = PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
37
38bool PropertyDescriptor::writable() const
39{
40 ASSERT(!isAccessorDescriptor());
41 return !(m_attributes & PropertyAttribute::ReadOnly);
42}
43
44bool PropertyDescriptor::enumerable() const
45{
46 return !(m_attributes & PropertyAttribute::DontEnum);
47}
48
49bool PropertyDescriptor::configurable() const
50{
51 return !(m_attributes & PropertyAttribute::DontDelete);
52}
53
54bool PropertyDescriptor::isDataDescriptor() const
55{
56 return m_value || (m_seenAttributes & WritablePresent);
57}
58
59bool PropertyDescriptor::isGenericDescriptor() const
60{
61 return !isAccessorDescriptor() && !isDataDescriptor();
62}
63
64bool PropertyDescriptor::isAccessorDescriptor() const
65{
66 return m_getter || m_setter;
67}
68
69void PropertyDescriptor::setUndefined()
70{
71 m_value = jsUndefined();
72 m_attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete | PropertyAttribute::DontEnum;
73}
74
75GetterSetter* PropertyDescriptor::slowGetterSetter(JSGlobalObject* globalObject)
76{
77 VM& vm = globalObject->vm();
78 JSValue getter = m_getter && !m_getter.isUndefined() ? jsCast<JSObject*>(m_getter) : jsUndefined();
79 JSValue setter = m_setter && !m_setter.isUndefined() ? jsCast<JSObject*>(m_setter) : jsUndefined();
80 return GetterSetter::create(vm, globalObject, getter, setter);
81}
82
83JSValue PropertyDescriptor::getter() const
84{
85 ASSERT(isAccessorDescriptor());
86 return m_getter;
87}
88
89JSValue PropertyDescriptor::setter() const
90{
91 ASSERT(isAccessorDescriptor());
92 return m_setter;
93}
94
95JSObject* PropertyDescriptor::getterObject() const
96{
97 ASSERT(isAccessorDescriptor() && getterPresent());
98 return m_getter.isObject() ? asObject(m_getter) : 0;
99}
100
101JSObject* PropertyDescriptor::setterObject() const
102{
103 ASSERT(isAccessorDescriptor() && setterPresent());
104 return m_setter.isObject() ? asObject(m_setter) : 0;
105}
106
107void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes)
108{
109 ASSERT(value);
110
111 // We need to mask off the PropertyAttribute::CustomValue bit because
112 // PropertyDescriptor::attributesEqual() does an equivalent test on
113 // m_attributes, and a property that has a CustomValue should be indistinguishable
114 // from a property that has a normal value as far as JS code is concerned.
115 // PropertyAttribute does not need knowledge of the underlying implementation
116 // actually being a CustomValue. So, we'll just mask it off up front here.
117 m_attributes = attributes & ~PropertyAttribute::CustomValue;
118 if (value.isGetterSetter()) {
119 m_attributes &= ~PropertyAttribute::ReadOnly; // FIXME: we should be able to ASSERT this!
120
121 GetterSetter* accessor = jsCast<GetterSetter*>(value);
122 m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined();
123 m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined();
124 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
125 } else {
126 m_value = value;
127 m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent;
128 }
129}
130
131void PropertyDescriptor::setCustomDescriptor(unsigned attributes)
132{
133 ASSERT(!(attributes & PropertyAttribute::CustomValue));
134 m_attributes = attributes | PropertyAttribute::Accessor | PropertyAttribute::CustomAccessor;
135 m_attributes &= ~PropertyAttribute::ReadOnly;
136 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
137 setGetter(jsUndefined());
138 setSetter(jsUndefined());
139 m_value = JSValue();
140}
141
142void PropertyDescriptor::setAccessorDescriptor(GetterSetter* accessor, unsigned attributes)
143{
144 ASSERT(attributes & PropertyAttribute::Accessor);
145 ASSERT(!(attributes & PropertyAttribute::CustomValue));
146 attributes &= ~PropertyAttribute::ReadOnly; // FIXME: we should be able to ASSERT this!
147
148 m_attributes = attributes;
149 m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined();
150 m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined();
151 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
152}
153
154void PropertyDescriptor::setWritable(bool writable)
155{
156 if (writable)
157 m_attributes &= ~PropertyAttribute::ReadOnly;
158 else
159 m_attributes |= PropertyAttribute::ReadOnly;
160 m_seenAttributes |= WritablePresent;
161}
162
163void PropertyDescriptor::setEnumerable(bool enumerable)
164{
165 if (enumerable)
166 m_attributes &= ~PropertyAttribute::DontEnum;
167 else
168 m_attributes |= PropertyAttribute::DontEnum;
169 m_seenAttributes |= EnumerablePresent;
170}
171
172void PropertyDescriptor::setConfigurable(bool configurable)
173{
174 if (configurable)
175 m_attributes &= ~PropertyAttribute::DontDelete;
176 else
177 m_attributes |= PropertyAttribute::DontDelete;
178 m_seenAttributes |= ConfigurablePresent;
179}
180
181void PropertyDescriptor::setSetter(JSValue setter)
182{
183 m_setter = setter;
184 m_attributes |= PropertyAttribute::Accessor;
185 m_attributes &= ~PropertyAttribute::ReadOnly;
186}
187
188void PropertyDescriptor::setGetter(JSValue getter)
189{
190 m_getter = getter;
191 m_attributes |= PropertyAttribute::Accessor;
192 m_attributes &= ~PropertyAttribute::ReadOnly;
193}
194
195bool PropertyDescriptor::equalTo(JSGlobalObject* globalObject, const PropertyDescriptor& other) const
196{
197 VM& vm = globalObject->vm();
198 auto scope = DECLARE_THROW_SCOPE(vm);
199 if (other.m_value.isEmpty() != m_value.isEmpty()
200 || other.m_getter.isEmpty() != m_getter.isEmpty()
201 || other.m_setter.isEmpty() != m_setter.isEmpty())
202 return false;
203 if (m_value) {
204 bool isSame = sameValue(globalObject, other.m_value, m_value);
205 RETURN_IF_EXCEPTION(scope, false);
206 if (!isSame)
207 return false;
208 }
209 return (!m_getter || JSValue::strictEqual(globalObject, other.m_getter, m_getter))
210 && (!m_setter || JSValue::strictEqual(globalObject, other.m_setter, m_setter))
211 && attributesEqual(other);
212}
213
214bool PropertyDescriptor::attributesEqual(const PropertyDescriptor& other) const
215{
216 unsigned mismatch = other.m_attributes ^ m_attributes;
217 unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes;
218 if (sharedSeen & WritablePresent && mismatch & PropertyAttribute::ReadOnly)
219 return false;
220 if (sharedSeen & ConfigurablePresent && mismatch & PropertyAttribute::DontDelete)
221 return false;
222 if (sharedSeen & EnumerablePresent && mismatch & PropertyAttribute::DontEnum)
223 return false;
224 return true;
225}
226
227unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const
228{
229 unsigned currentAttributes = current.m_attributes;
230 if (isDataDescriptor() && current.isAccessorDescriptor())
231 currentAttributes |= PropertyAttribute::ReadOnly;
232 unsigned overrideMask = 0;
233 if (writablePresent())
234 overrideMask |= PropertyAttribute::ReadOnly;
235 if (enumerablePresent())
236 overrideMask |= PropertyAttribute::DontEnum;
237 if (configurablePresent())
238 overrideMask |= PropertyAttribute::DontDelete;
239 if (isAccessorDescriptor())
240 overrideMask |= PropertyAttribute::Accessor;
241 return (m_attributes & overrideMask) | (currentAttributes & ~overrideMask & ~PropertyAttribute::CustomAccessor);
242}
243
244}
245