1/*
2 * Copyright (C) 2005-2019 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#pragma once
22
23#include "DOMAnnotation.h"
24#include "GetVM.h"
25#include "JSCJSValue.h"
26#include "PropertyName.h"
27#include "PropertyOffset.h"
28#include "ScopeOffset.h"
29#include <wtf/Assertions.h>
30#include <wtf/ForbidHeapAllocation.h>
31
32namespace JSC {
33class GetterSetter;
34class JSObject;
35class JSModuleEnvironment;
36
37// ECMA 262-3 8.6.1
38// Property attributes
39enum class PropertyAttribute : unsigned {
40 None = 0,
41 ReadOnly = 1 << 1, // property can be only read, not written
42 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..)
43 DontDelete = 1 << 3, // property can't be deleted
44 Accessor = 1 << 4, // property is a getter/setter
45 CustomAccessor = 1 << 5,
46 CustomValue = 1 << 6,
47 CustomAccessorOrValue = CustomAccessor | CustomValue,
48 AccessorOrCustomAccessorOrValue = Accessor | CustomAccessor | CustomValue,
49
50 // Things that are used by static hashtables are not in the attributes byte in PropertyMapEntry.
51 Function = 1 << 8, // property is a function - only used by static hashtables
52 Builtin = 1 << 9, // property is a builtin function - only used by static hashtables
53 ConstantInteger = 1 << 10, // property is a constant integer - only used by static hashtables
54 CellProperty = 1 << 11, // property is a lazy property - only used by static hashtables
55 ClassStructure = 1 << 12, // property is a lazy class structure - only used by static hashtables
56 PropertyCallback = 1 << 13, // property that is a lazy property callback - only used by static hashtables
57 DOMAttribute = 1 << 14, // property is a simple DOM attribute - only used by static hashtables
58 DOMJITAttribute = 1 << 15, // property is a DOM JIT attribute - only used by static hashtables
59 DOMJITFunction = 1 << 16, // property is a DOM JIT function - only used by static hashtables
60 BuiltinOrFunction = Builtin | Function, // helper only used by static hashtables
61 BuiltinOrFunctionOrLazyProperty = Builtin | Function | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables
62 BuiltinOrFunctionOrAccessorOrLazyProperty = Builtin | Function | Accessor | CellProperty | ClassStructure | PropertyCallback, // helper only used by static hashtables
63 BuiltinOrFunctionOrAccessorOrLazyPropertyOrConstant = Builtin | Function | Accessor | CellProperty | ClassStructure | PropertyCallback | ConstantInteger // helper only used by static hashtables
64};
65
66static constexpr unsigned operator| (PropertyAttribute a, PropertyAttribute b) { return static_cast<unsigned>(a) | static_cast<unsigned>(b); }
67static constexpr unsigned operator| (unsigned a, PropertyAttribute b) { return a | static_cast<unsigned>(b); }
68static constexpr unsigned operator| (PropertyAttribute a, unsigned b) { return static_cast<unsigned>(a) | b; }
69static constexpr unsigned operator&(unsigned a, PropertyAttribute b) { return a & static_cast<unsigned>(b); }
70static constexpr bool operator<(PropertyAttribute a, PropertyAttribute b) { return static_cast<unsigned>(a) < static_cast<unsigned>(b); }
71static constexpr unsigned operator~(PropertyAttribute a) { return ~static_cast<unsigned>(a); }
72static constexpr bool operator<(PropertyAttribute a, unsigned b) { return static_cast<unsigned>(a) < b; }
73static inline unsigned& operator|=(unsigned& a, PropertyAttribute b) { return a |= static_cast<unsigned>(b); }
74
75enum CacheabilityType : uint8_t {
76 CachingDisallowed,
77 CachingAllowed
78};
79
80inline unsigned attributesForStructure(unsigned attributes)
81{
82 // The attributes that are used just for the static hashtable are at bit 8 and higher.
83 return static_cast<uint8_t>(attributes);
84}
85
86class PropertySlot {
87
88 // We rely on PropertySlot being stack allocated when used. This is needed
89 // because we rely on some of its fields being a GC root. For example, it
90 // may be the only thing that points to the CustomGetterSetter property it has.
91 WTF_FORBID_HEAP_ALLOCATION;
92
93 enum PropertyType : uint8_t {
94 TypeUnset,
95 TypeValue,
96 TypeGetter,
97 TypeCustom,
98 TypeCustomAccessor,
99 };
100
101public:
102 enum class InternalMethodType : uint8_t {
103 Get, // [[Get]] internal method in the spec.
104 HasProperty, // [[HasProperty]] internal method in the spec.
105 GetOwnProperty, // [[GetOwnProperty]] internal method in the spec.
106 VMInquiry, // Our VM is just poking around. When this is the InternalMethodType, getOwnPropertySlot is not allowed to do user observable actions.
107 };
108
109 enum class AdditionalDataType : uint8_t {
110 None,
111 DOMAttribute, // Annotated with DOMAttribute information.
112 ModuleNamespace, // ModuleNamespaceObject's environment access.
113 };
114
115 explicit PropertySlot(const JSValue thisValue, InternalMethodType internalMethodType)
116 : m_offset(invalidOffset)
117 , m_thisValue(thisValue)
118 , m_slotBase(nullptr)
119 , m_watchpointSet(nullptr)
120 , m_cacheability(CachingAllowed)
121 , m_propertyType(TypeUnset)
122 , m_internalMethodType(internalMethodType)
123 , m_additionalDataType(AdditionalDataType::None)
124 , m_isTaintedByOpaqueObject(false)
125 {
126 }
127
128 // FIXME: Remove this slotBase / receiver behavior difference in custom values and custom accessors.
129 // https://bugs.webkit.org/show_bug.cgi?id=158014
130 typedef EncodedJSValue (*GetValueFunc)(JSGlobalObject*, EncodedJSValue thisValue, PropertyName);
131
132 JSValue getValue(JSGlobalObject*, PropertyName) const;
133 JSValue getValue(JSGlobalObject*, unsigned propertyName) const;
134 JSValue getPureResult() const;
135
136 bool isCacheable() const { return m_cacheability == CachingAllowed && m_offset != invalidOffset; }
137 bool isUnset() const { return m_propertyType == TypeUnset; }
138 bool isValue() const { return m_propertyType == TypeValue; }
139 bool isAccessor() const { return m_propertyType == TypeGetter; }
140 bool isCustom() const { return m_propertyType == TypeCustom; }
141 bool isCustomAccessor() const { return m_propertyType == TypeCustomAccessor; }
142 bool isCacheableValue() const { return isCacheable() && isValue(); }
143 bool isCacheableGetter() const { return isCacheable() && isAccessor(); }
144 bool isCacheableCustom() const { return isCacheable() && isCustom(); }
145 void setIsTaintedByOpaqueObject() { m_isTaintedByOpaqueObject = true; }
146 bool isTaintedByOpaqueObject() const { return m_isTaintedByOpaqueObject; }
147
148 InternalMethodType internalMethodType() const { return m_internalMethodType; }
149
150 void disableCaching()
151 {
152 m_cacheability = CachingDisallowed;
153 }
154
155 unsigned attributes() const { return m_attributes; }
156
157 PropertyOffset cachedOffset() const
158 {
159 ASSERT(isCacheable());
160 return m_offset;
161 }
162
163 GetterSetter* getterSetter() const
164 {
165 ASSERT(isAccessor());
166 return m_data.getter.getterSetter;
167 }
168
169 GetValueFunc customGetter() const
170 {
171 ASSERT(isCacheableCustom());
172 return m_data.custom.getValue;
173 }
174
175 CustomGetterSetter* customGetterSetter() const
176 {
177 ASSERT(isCustomAccessor());
178 return m_data.customAccessor.getterSetter;
179 }
180
181 JSObject* slotBase() const
182 {
183 return m_slotBase;
184 }
185
186 WatchpointSet* watchpointSet() const
187 {
188 return m_watchpointSet;
189 }
190
191 Optional<DOMAttributeAnnotation> domAttribute() const
192 {
193 if (m_additionalDataType == AdditionalDataType::DOMAttribute)
194 return m_additionalData.domAttribute;
195 return WTF::nullopt;
196 }
197
198 struct ModuleNamespaceSlot {
199 JSModuleEnvironment* environment;
200 unsigned scopeOffset;
201 };
202
203 Optional<ModuleNamespaceSlot> moduleNamespaceSlot() const
204 {
205 if (m_additionalDataType == AdditionalDataType::ModuleNamespace)
206 return m_additionalData.moduleNamespaceSlot;
207 return WTF::nullopt;
208 }
209
210 void setValue(JSObject* slotBase, unsigned attributes, JSValue value)
211 {
212 ASSERT(attributes == attributesForStructure(attributes));
213
214 m_data.value = JSValue::encode(value);
215 m_attributes = attributes;
216
217 ASSERT(slotBase);
218 m_slotBase = slotBase;
219 m_propertyType = TypeValue;
220 m_offset = invalidOffset;
221 }
222
223 void setValue(JSObject* slotBase, unsigned attributes, JSValue value, PropertyOffset offset)
224 {
225 ASSERT(attributes == attributesForStructure(attributes));
226
227 ASSERT(value);
228 m_data.value = JSValue::encode(value);
229 m_attributes = attributes;
230
231 ASSERT(slotBase);
232 m_slotBase = slotBase;
233 m_propertyType = TypeValue;
234 m_offset = offset;
235 }
236
237 void setValue(JSString*, unsigned attributes, JSValue value)
238 {
239 ASSERT(attributes == attributesForStructure(attributes));
240
241 ASSERT(value);
242 m_data.value = JSValue::encode(value);
243 m_attributes = attributes;
244
245 m_slotBase = 0;
246 m_propertyType = TypeValue;
247 m_offset = invalidOffset;
248 }
249
250 void setValueModuleNamespace(JSObject* slotBase, unsigned attributes, JSValue value, JSModuleEnvironment* environment, ScopeOffset scopeOffset)
251 {
252 setValue(slotBase, attributes, value);
253 m_additionalDataType = AdditionalDataType::ModuleNamespace;
254 m_additionalData.moduleNamespaceSlot.environment = environment;
255 m_additionalData.moduleNamespaceSlot.scopeOffset = scopeOffset.offset();
256 }
257
258 void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
259 {
260 ASSERT(attributes == attributesForStructure(attributes));
261
262 ASSERT(getValue);
263 assertIsCFunctionPtr(getValue);
264 m_data.custom.getValue = getValue;
265 m_attributes = attributes;
266
267 ASSERT(slotBase);
268 m_slotBase = slotBase;
269 m_propertyType = TypeCustom;
270 m_offset = invalidOffset;
271 }
272
273 void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, DOMAttributeAnnotation domAttribute)
274 {
275 setCustom(slotBase, attributes, getValue);
276 m_additionalDataType = AdditionalDataType::DOMAttribute;
277 m_additionalData.domAttribute = domAttribute;
278 }
279
280 void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
281 {
282 ASSERT(attributes == attributesForStructure(attributes));
283
284 ASSERT(getValue);
285 assertIsCFunctionPtr(getValue);
286 m_data.custom.getValue = getValue;
287 m_attributes = attributes;
288
289 ASSERT(slotBase);
290 m_slotBase = slotBase;
291 m_propertyType = TypeCustom;
292 m_offset = !invalidOffset;
293 }
294
295 void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue, DOMAttributeAnnotation domAttribute)
296 {
297 setCacheableCustom(slotBase, attributes, getValue);
298 m_additionalDataType = AdditionalDataType::DOMAttribute;
299 m_additionalData.domAttribute = domAttribute;
300 }
301
302 void setCustomGetterSetter(JSObject* slotBase, unsigned attributes, CustomGetterSetter* getterSetter)
303 {
304 ASSERT(attributes == attributesForStructure(attributes));
305 ASSERT(attributes & PropertyAttribute::CustomAccessor);
306
307 disableCaching();
308
309 ASSERT(getterSetter);
310 m_data.customAccessor.getterSetter = getterSetter;
311 m_attributes = attributes;
312
313 ASSERT(slotBase);
314 m_slotBase = slotBase;
315 m_propertyType = TypeCustomAccessor;
316 m_offset = invalidOffset;
317 }
318
319 void setGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter)
320 {
321 ASSERT(attributes == attributesForStructure(attributes));
322
323 ASSERT(getterSetter);
324 m_data.getter.getterSetter = getterSetter;
325 m_attributes = attributes;
326
327 ASSERT(slotBase);
328 m_slotBase = slotBase;
329 m_propertyType = TypeGetter;
330 m_offset = invalidOffset;
331 }
332
333 void setCacheableGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter, PropertyOffset offset)
334 {
335 ASSERT(attributes == attributesForStructure(attributes));
336
337 ASSERT(getterSetter);
338 m_data.getter.getterSetter = getterSetter;
339 m_attributes = attributes;
340
341 ASSERT(slotBase);
342 m_slotBase = slotBase;
343 m_propertyType = TypeGetter;
344 m_offset = offset;
345 }
346
347 JSValue thisValue() const
348 {
349 return m_thisValue;
350 }
351
352 void setThisValue(JSValue thisValue)
353 {
354 m_thisValue = thisValue;
355 }
356
357 void setUndefined()
358 {
359 m_data.value = JSValue::encode(jsUndefined());
360 m_attributes = PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete | PropertyAttribute::DontEnum;
361
362 m_slotBase = 0;
363 m_propertyType = TypeValue;
364 m_offset = invalidOffset;
365 }
366
367 void setWatchpointSet(WatchpointSet& set)
368 {
369 m_watchpointSet = &set;
370 }
371
372private:
373 JS_EXPORT_PRIVATE JSValue functionGetter(JSGlobalObject*) const;
374 JS_EXPORT_PRIVATE JSValue customGetter(JSGlobalObject*, PropertyName) const;
375 JS_EXPORT_PRIVATE JSValue customAccessorGetter(JSGlobalObject*, PropertyName) const;
376
377 union {
378 EncodedJSValue value;
379 struct {
380 GetterSetter* getterSetter;
381 } getter;
382 struct {
383 GetValueFunc getValue;
384 } custom;
385 struct {
386 CustomGetterSetter* getterSetter;
387 } customAccessor;
388 } m_data;
389
390 unsigned m_attributes;
391 PropertyOffset m_offset;
392 JSValue m_thisValue;
393 JSObject* m_slotBase;
394 WatchpointSet* m_watchpointSet;
395 CacheabilityType m_cacheability;
396 PropertyType m_propertyType;
397 InternalMethodType m_internalMethodType;
398 AdditionalDataType m_additionalDataType;
399 bool m_isTaintedByOpaqueObject;
400 union {
401 DOMAttributeAnnotation domAttribute;
402 ModuleNamespaceSlot moduleNamespaceSlot;
403 } m_additionalData;
404};
405
406ALWAYS_INLINE JSValue PropertySlot::getValue(JSGlobalObject* globalObject, PropertyName propertyName) const
407{
408 if (m_propertyType == TypeValue)
409 return JSValue::decode(m_data.value);
410 if (m_propertyType == TypeGetter)
411 return functionGetter(globalObject);
412 if (m_propertyType == TypeCustomAccessor)
413 return customAccessorGetter(globalObject, propertyName);
414 return customGetter(globalObject, propertyName);
415}
416
417ALWAYS_INLINE JSValue PropertySlot::getValue(JSGlobalObject* globalObject, unsigned propertyName) const
418{
419 VM& vm = getVM(globalObject);
420 if (m_propertyType == TypeValue)
421 return JSValue::decode(m_data.value);
422 if (m_propertyType == TypeGetter)
423 return functionGetter(globalObject);
424 if (m_propertyType == TypeCustomAccessor)
425 return customAccessorGetter(globalObject, Identifier::from(vm, propertyName));
426 return customGetter(globalObject, Identifier::from(vm, propertyName));
427}
428
429} // namespace JSC
430