1/*
2 * Copyright (C) 2009, 2016 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(ExecState* exec)
76{
77 VM& vm = exec->vm();
78 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
79 JSValue getter = m_getter && !m_getter.isUndefined() ? jsCast<JSObject*>(m_getter) : jsUndefined();
80 JSValue setter = m_setter && !m_setter.isUndefined() ? jsCast<JSObject*>(m_setter) : jsUndefined();
81 return GetterSetter::create(vm, globalObject, getter, setter);
82}
83
84JSValue PropertyDescriptor::getter() const
85{
86 ASSERT(isAccessorDescriptor());
87 return m_getter;
88}
89
90JSValue PropertyDescriptor::setter() const
91{
92 ASSERT(isAccessorDescriptor());
93 return m_setter;
94}
95
96JSObject* PropertyDescriptor::getterObject() const
97{
98 ASSERT(isAccessorDescriptor() && getterPresent());
99 return m_getter.isObject() ? asObject(m_getter) : 0;
100}
101
102JSObject* PropertyDescriptor::setterObject() const
103{
104 ASSERT(isAccessorDescriptor() && setterPresent());
105 return m_setter.isObject() ? asObject(m_setter) : 0;
106}
107
108void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes)
109{
110 ASSERT(value);
111
112 // We need to mask off the PropertyAttribute::CustomValue bit because
113 // PropertyDescriptor::attributesEqual() does an equivalent test on
114 // m_attributes, and a property that has a CustomValue should be indistinguishable
115 // from a property that has a normal value as far as JS code is concerned.
116 // PropertyAttribute does not need knowledge of the underlying implementation
117 // actually being a CustomValue. So, we'll just mask it off up front here.
118 m_attributes = attributes & ~PropertyAttribute::CustomValue;
119 if (value.isGetterSetter()) {
120 m_attributes &= ~PropertyAttribute::ReadOnly; // FIXME: we should be able to ASSERT this!
121
122 GetterSetter* accessor = jsCast<GetterSetter*>(value);
123 m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined();
124 m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined();
125 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
126 } else {
127 m_value = value;
128 m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent;
129 }
130}
131
132void PropertyDescriptor::setCustomDescriptor(unsigned attributes)
133{
134 ASSERT(!(attributes & PropertyAttribute::CustomValue));
135 m_attributes = attributes | PropertyAttribute::Accessor | PropertyAttribute::CustomAccessor;
136 m_attributes &= ~PropertyAttribute::ReadOnly;
137 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
138 setGetter(jsUndefined());
139 setSetter(jsUndefined());
140 m_value = JSValue();
141}
142
143void PropertyDescriptor::setAccessorDescriptor(GetterSetter* accessor, unsigned attributes)
144{
145 ASSERT(attributes & PropertyAttribute::Accessor);
146 ASSERT(!(attributes & PropertyAttribute::CustomValue));
147 attributes &= ~PropertyAttribute::ReadOnly; // FIXME: we should be able to ASSERT this!
148
149 m_attributes = attributes;
150 m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined();
151 m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined();
152 m_seenAttributes = EnumerablePresent | ConfigurablePresent;
153}
154
155void PropertyDescriptor::setWritable(bool writable)
156{
157 if (writable)
158 m_attributes &= ~PropertyAttribute::ReadOnly;
159 else
160 m_attributes |= PropertyAttribute::ReadOnly;
161 m_seenAttributes |= WritablePresent;
162}
163
164void PropertyDescriptor::setEnumerable(bool enumerable)
165{
166 if (enumerable)
167 m_attributes &= ~PropertyAttribute::DontEnum;
168 else
169 m_attributes |= PropertyAttribute::DontEnum;
170 m_seenAttributes |= EnumerablePresent;
171}
172
173void PropertyDescriptor::setConfigurable(bool configurable)
174{
175 if (configurable)
176 m_attributes &= ~PropertyAttribute::DontDelete;
177 else
178 m_attributes |= PropertyAttribute::DontDelete;
179 m_seenAttributes |= ConfigurablePresent;
180}
181
182void PropertyDescriptor::setSetter(JSValue setter)
183{
184 m_setter = setter;
185 m_attributes |= PropertyAttribute::Accessor;
186 m_attributes &= ~PropertyAttribute::ReadOnly;
187}
188
189void PropertyDescriptor::setGetter(JSValue getter)
190{
191 m_getter = getter;
192 m_attributes |= PropertyAttribute::Accessor;
193 m_attributes &= ~PropertyAttribute::ReadOnly;
194}
195
196bool 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
208bool 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 & PropertyAttribute::ReadOnly)
213 return false;
214 if (sharedSeen & ConfigurablePresent && mismatch & PropertyAttribute::DontDelete)
215 return false;
216 if (sharedSeen & EnumerablePresent && mismatch & PropertyAttribute::DontEnum)
217 return false;
218 return true;
219}
220
221unsigned PropertyDescriptor::attributesOverridingCurrent(const PropertyDescriptor& current) const
222{
223 unsigned currentAttributes = current.m_attributes;
224 if (isDataDescriptor() && current.isAccessorDescriptor())
225 currentAttributes |= PropertyAttribute::ReadOnly;
226 unsigned overrideMask = 0;
227 if (writablePresent())
228 overrideMask |= PropertyAttribute::ReadOnly;
229 if (enumerablePresent())
230 overrideMask |= PropertyAttribute::DontEnum;
231 if (configurablePresent())
232 overrideMask |= PropertyAttribute::DontDelete;
233 if (isAccessorDescriptor())
234 overrideMask |= PropertyAttribute::Accessor;
235 return (m_attributes & overrideMask) | (currentAttributes & ~overrideMask & ~PropertyAttribute::CustomAccessor);
236}
237
238}
239