We should support the ability to do a non-effectful getById
[WebKit-https.git] / Source / JavaScriptCore / runtime / PropertySlot.h
1 /*
2  *  Copyright (C) 2005, 2007, 2008, 2015 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 #ifndef PropertySlot_h
22 #define PropertySlot_h
23
24 #include "JSCJSValue.h"
25 #include "PropertyName.h"
26 #include "PropertyOffset.h"
27 #include <wtf/Assertions.h>
28
29 namespace JSC {
30
31 class ExecState;
32 class GetterSetter;
33 class JSObject;
34
35 // ECMA 262-3 8.6.1
36 // Property attributes
37 enum Attribute {
38     None              = 0,
39     ReadOnly          = 1 << 1,  // property can be only read, not written
40     DontEnum          = 1 << 2,  // property doesn't appear in (for .. in ..)
41     DontDelete        = 1 << 3,  // property can't be deleted
42     Accessor          = 1 << 4,  // property is a getter/setter
43     CustomAccessor    = 1 << 5,
44
45     // Things that are used by static hashtables are not in the attributes byte in PropertyMapEntry.
46     Function          = 1 << 8,  // property is a function - only used by static hashtables
47     Builtin           = 1 << 9,  // property is a builtin function - only used by static hashtables
48     ConstantInteger   = 1 << 10, // property is a constant integer - only used by static hashtables
49     BuiltinOrFunction = Builtin | Function, // helper only used by static hashtables
50     BuiltinOrFunctionOrAccessor = Builtin | Function | Accessor, // helper only used by static hashtables
51     BuiltinOrFunctionOrAccessorOrConstant = Builtin | Function | Accessor | ConstantInteger, // helper only used by static hashtables
52 };
53
54 enum CacheabilityType : uint8_t {
55     CachingDisallowed,
56     CachingAllowed
57 };
58
59 inline unsigned attributesForStructure(unsigned attributes)
60 {
61     // The attributes that are used just for the static hashtable are at bit 8 and higher.
62     return static_cast<uint8_t>(attributes);
63 }
64
65 class PropertySlot {
66     enum PropertyType : uint8_t {
67         TypeUnset,
68         TypeValue,
69         TypeGetter,
70         TypeCustom
71     };
72
73 public:
74     enum class InternalMethodType : uint8_t {
75         Get, // [[Get]] internal method in the spec.
76         HasProperty, // [[HasProperty]] internal method in the spec.
77         GetOwnProperty, // [[GetOwnProperty]] internal method in the spec.
78         VMInquiry, // Our VM is just poking around. When this is the InternalMethodType, getOwnPropertySlot is not allowed to do user observable actions.
79     };
80
81     explicit PropertySlot(const JSValue thisValue, InternalMethodType internalMethodType)
82         : m_offset(invalidOffset)
83         , m_thisValue(thisValue)
84         , m_slotBase(nullptr)
85         , m_watchpointSet(nullptr)
86         , m_cacheability(CachingAllowed)
87         , m_propertyType(TypeUnset)
88         , m_internalMethodType(internalMethodType)
89         , m_isTaintedByProxy(false)
90     {
91     }
92
93     typedef EncodedJSValue (*GetValueFunc)(ExecState*, EncodedJSValue thisValue, PropertyName);
94
95     JSValue getValue(ExecState*, PropertyName) const;
96     JSValue getValue(ExecState*, unsigned propertyName) const;
97     JSValue getPureResult() const;
98
99     bool isCacheable() const { return m_cacheability == CachingAllowed && m_offset != invalidOffset; }
100     bool isUnset() const { return m_propertyType == TypeUnset; }
101     bool isValue() const { return m_propertyType == TypeValue; }
102     bool isAccessor() const { return m_propertyType == TypeGetter; }
103     bool isCustom() const { return m_propertyType == TypeCustom; }
104     bool isCacheableValue() const { return isCacheable() && isValue(); }
105     bool isCacheableGetter() const { return isCacheable() && isAccessor(); }
106     bool isCacheableCustom() const { return isCacheable() && isCustom(); }
107     void setIsTaintedByProxy() { m_isTaintedByProxy = true; }
108     bool isTaintedByProxy() const { return m_isTaintedByProxy; }
109
110     InternalMethodType internalMethodType() const { return m_internalMethodType; }
111
112     void disableCaching()
113     {
114         m_cacheability = CachingDisallowed;
115     }
116
117     unsigned attributes() const { return m_attributes; }
118
119     PropertyOffset cachedOffset() const
120     {
121         ASSERT(isCacheable());
122         return m_offset;
123     }
124
125     GetterSetter* getterSetter() const
126     {
127         ASSERT(isAccessor());
128         return m_data.getter.getterSetter;
129     }
130
131     GetValueFunc customGetter() const
132     {
133         ASSERT(isCacheableCustom());
134         return m_data.custom.getValue;
135     }
136
137     JSObject* slotBase() const
138     {
139         return m_slotBase;
140     }
141
142     WatchpointSet* watchpointSet() const
143     {
144         return m_watchpointSet;
145     }
146
147     void setValue(JSObject* slotBase, unsigned attributes, JSValue value)
148     {
149         ASSERT(attributes == attributesForStructure(attributes));
150         
151         m_data.value = JSValue::encode(value);
152         m_attributes = attributes;
153
154         ASSERT(slotBase);
155         m_slotBase = slotBase;
156         m_propertyType = TypeValue;
157         m_offset = invalidOffset;
158     }
159     
160     void setValue(JSObject* slotBase, unsigned attributes, JSValue value, PropertyOffset offset)
161     {
162         ASSERT(attributes == attributesForStructure(attributes));
163         
164         ASSERT(value);
165         m_data.value = JSValue::encode(value);
166         m_attributes = attributes;
167
168         ASSERT(slotBase);
169         m_slotBase = slotBase;
170         m_propertyType = TypeValue;
171         m_offset = offset;
172     }
173
174     void setValue(JSString*, unsigned attributes, JSValue value)
175     {
176         ASSERT(attributes == attributesForStructure(attributes));
177         
178         ASSERT(value);
179         m_data.value = JSValue::encode(value);
180         m_attributes = attributes;
181
182         m_slotBase = 0;
183         m_propertyType = TypeValue;
184         m_offset = invalidOffset;
185     }
186
187     void setCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
188     {
189         ASSERT(attributes == attributesForStructure(attributes));
190         
191         ASSERT(getValue);
192         m_data.custom.getValue = getValue;
193         m_attributes = attributes;
194
195         ASSERT(slotBase);
196         m_slotBase = slotBase;
197         m_propertyType = TypeCustom;
198         m_offset = invalidOffset;
199     }
200     
201     void setCacheableCustom(JSObject* slotBase, unsigned attributes, GetValueFunc getValue)
202     {
203         ASSERT(attributes == attributesForStructure(attributes));
204         
205         ASSERT(getValue);
206         m_data.custom.getValue = getValue;
207         m_attributes = attributes;
208
209         ASSERT(slotBase);
210         m_slotBase = slotBase;
211         m_propertyType = TypeCustom;
212         m_offset = !invalidOffset;
213     }
214
215     void setGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter)
216     {
217         ASSERT(attributes == attributesForStructure(attributes));
218         
219         ASSERT(getterSetter);
220         m_data.getter.getterSetter = getterSetter;
221         m_attributes = attributes;
222
223         ASSERT(slotBase);
224         m_slotBase = slotBase;
225         m_propertyType = TypeGetter;
226         m_offset = invalidOffset;
227     }
228
229     void setCacheableGetterSlot(JSObject* slotBase, unsigned attributes, GetterSetter* getterSetter, PropertyOffset offset)
230     {
231         ASSERT(attributes == attributesForStructure(attributes));
232         
233         ASSERT(getterSetter);
234         m_data.getter.getterSetter = getterSetter;
235         m_attributes = attributes;
236
237         ASSERT(slotBase);
238         m_slotBase = slotBase;
239         m_propertyType = TypeGetter;
240         m_offset = offset;
241     }
242
243     void setThisValue(JSValue thisValue)
244     {
245         m_thisValue = thisValue;
246     }
247
248     void setUndefined()
249     {
250         m_data.value = JSValue::encode(jsUndefined());
251         m_attributes = ReadOnly | DontDelete | DontEnum;
252
253         m_slotBase = 0;
254         m_propertyType = TypeValue;
255         m_offset = invalidOffset;
256     }
257
258     void setWatchpointSet(WatchpointSet& set)
259     {
260         m_watchpointSet = &set;
261     }
262
263 private:
264     JS_EXPORT_PRIVATE JSValue functionGetter(ExecState*) const;
265     JS_EXPORT_PRIVATE JSValue customGetter(ExecState*, PropertyName) const;
266
267     unsigned m_attributes;
268     union {
269         EncodedJSValue value;
270         struct {
271             GetterSetter* getterSetter;
272         } getter;
273         struct {
274             GetValueFunc getValue;
275         } custom;
276     } m_data;
277
278     PropertyOffset m_offset;
279     JSValue m_thisValue;
280     JSObject* m_slotBase;
281     WatchpointSet* m_watchpointSet;
282     CacheabilityType m_cacheability;
283     PropertyType m_propertyType;
284     InternalMethodType m_internalMethodType;
285     bool m_isTaintedByProxy;
286 };
287
288 ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, PropertyName propertyName) const
289 {
290     if (m_propertyType == TypeValue)
291         return JSValue::decode(m_data.value);
292     if (m_propertyType == TypeGetter)
293         return functionGetter(exec);
294     return customGetter(exec, propertyName);
295 }
296
297 ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, unsigned propertyName) const
298 {
299     if (m_propertyType == TypeValue)
300         return JSValue::decode(m_data.value);
301     if (m_propertyType == TypeGetter)
302         return functionGetter(exec);
303     return customGetter(exec, Identifier::from(exec, propertyName));
304 }
305
306 } // namespace JSC
307
308 #endif // PropertySlot_h