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