1/*
2 * Copyright (C) 2012, 2014, 2015 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 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef JSSymbolTableObject_h
30#define JSSymbolTableObject_h
31
32#include "JSScope.h"
33#include "PropertyDescriptor.h"
34#include "SymbolTable.h"
35#include "VariableWriteFireDetail.h"
36
37namespace JSC {
38
39class JSSymbolTableObject;
40
41class JSSymbolTableObject : public JSScope {
42public:
43 typedef JSScope Base;
44 static const unsigned StructureFlags = Base::StructureFlags | IsEnvironmentRecord | OverridesGetPropertyNames;
45
46 SymbolTable* symbolTable() const { return m_symbolTable.get(); }
47
48 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
49 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
50
51 static ptrdiff_t offsetOfSymbolTable() { return OBJECT_OFFSETOF(JSSymbolTableObject, m_symbolTable); }
52
53protected:
54 JSSymbolTableObject(VM& vm, Structure* structure, JSScope* scope)
55 : Base(vm, structure, scope)
56 {
57 }
58
59 JSSymbolTableObject(VM& vm, Structure* structure, JSScope* scope, SymbolTable* symbolTable)
60 : Base(vm, structure, scope)
61 {
62 ASSERT(symbolTable);
63 setSymbolTable(vm, symbolTable);
64 }
65
66 void setSymbolTable(VM& vm, SymbolTable* symbolTable)
67 {
68 ASSERT(!m_symbolTable);
69 symbolTable->singletonScope()->notifyWrite(vm, this, "Allocated a scope");
70 m_symbolTable.set(vm, this, symbolTable);
71 }
72
73 static void visitChildren(JSCell*, SlotVisitor&);
74
75private:
76 WriteBarrier<SymbolTable> m_symbolTable;
77};
78
79template<typename SymbolTableObjectType>
80inline bool symbolTableGet(
81 SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot)
82{
83 SymbolTable& symbolTable = *object->symbolTable();
84 ConcurrentJITLocker locker(symbolTable.m_lock);
85 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
86 if (iter == symbolTable.end(locker))
87 return false;
88 SymbolTableEntry::Fast entry = iter->value;
89 ASSERT(!entry.isNull());
90
91 ScopeOffset offset = entry.scopeOffset();
92 // Defend against the inspector asking for a var after it has been optimized out.
93 if (!object->isValidScopeOffset(offset))
94 return false;
95
96 slot.setValue(object, entry.getAttributes() | DontDelete, object->variableAt(offset).get());
97 return true;
98}
99
100template<typename SymbolTableObjectType>
101inline bool symbolTableGet(
102 SymbolTableObjectType* object, PropertyName propertyName, PropertyDescriptor& descriptor)
103{
104 SymbolTable& symbolTable = *object->symbolTable();
105 ConcurrentJITLocker locker(symbolTable.m_lock);
106 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
107 if (iter == symbolTable.end(locker))
108 return false;
109 SymbolTableEntry::Fast entry = iter->value;
110 ASSERT(!entry.isNull());
111
112 ScopeOffset offset = entry.scopeOffset();
113 // Defend against the inspector asking for a var after it has been optimized out.
114 if (!object->isValidScopeOffset(offset))
115 return false;
116
117 descriptor.setDescriptor(object->variableAt(offset).get(), entry.getAttributes() | DontDelete);
118 return true;
119}
120
121template<typename SymbolTableObjectType>
122inline bool symbolTableGet(
123 SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot,
124 bool& slotIsWriteable)
125{
126 SymbolTable& symbolTable = *object->symbolTable();
127 ConcurrentJITLocker locker(symbolTable.m_lock);
128 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
129 if (iter == symbolTable.end(locker))
130 return false;
131 SymbolTableEntry::Fast entry = iter->value;
132 ASSERT(!entry.isNull());
133
134 ScopeOffset offset = entry.scopeOffset();
135 // Defend against the inspector asking for a var after it has been optimized out.
136 if (!object->isValidScopeOffset(offset))
137 return false;
138
139 slot.setValue(object, entry.getAttributes() | DontDelete, object->variableAt(offset).get());
140 slotIsWriteable = !entry.isReadOnly();
141 return true;
142}
143
144enum class SymbolTablePutMode {
145 WithAttributes,
146 WithoutAttributes
147};
148
149template<SymbolTablePutMode symbolTablePutMode, typename SymbolTableObjectType>
150inline bool symbolTablePut(
151 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes,
152 bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors, WatchpointSet*& set)
153{
154 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
155
156 VM& vm = exec->vm();
157
158 WriteBarrierBase<Unknown>* reg;
159 {
160 SymbolTable& symbolTable = *object->symbolTable();
161 // FIXME: This is very suspicious. We shouldn't need a GC-safe lock here.
162 // https://bugs.webkit.org/show_bug.cgi?id=134601
163 GCSafeConcurrentJITLocker locker(symbolTable.m_lock, vm.heap);
164 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
165 if (iter == symbolTable.end(locker))
166 return false;
167 bool wasFat;
168 SymbolTableEntry::Fast fastEntry = iter->value.getFast(wasFat);
169 ASSERT(!fastEntry.isNull());
170 if (fastEntry.isReadOnly() && !ignoreReadOnlyErrors) {
171 if (shouldThrowReadOnlyError)
172 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
173 return true;
174 }
175
176 ScopeOffset offset = fastEntry.scopeOffset();
177
178 // Defend against the inspector asking for a var after it has been optimized out.
179 if (!object->isValidScopeOffset(offset))
180 return false;
181
182 set = iter->value.watchpointSet();
183 if (symbolTablePutMode == SymbolTablePutMode::WithAttributes)
184 iter->value.setAttributes(attributes);
185 reg = &object->variableAt(offset);
186 }
187 // I'd prefer we not hold lock while executing barriers, since I prefer to reserve
188 // the right for barriers to be able to trigger GC. And I don't want to hold VM
189 // locks while GC'ing.
190 reg->set(vm, object, value);
191 return true;
192}
193
194template<typename SymbolTableObjectType>
195inline bool symbolTablePutTouchWatchpointSet(
196 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value,
197 bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors)
198{
199 WatchpointSet* set = nullptr;
200 unsigned attributes = 0;
201 bool result = symbolTablePut<SymbolTablePutMode::WithoutAttributes>(object, exec, propertyName, value, attributes, shouldThrowReadOnlyError, ignoreReadOnlyErrors, set);
202 if (set)
203 VariableWriteFireDetail::touch(set, object, propertyName);
204 return result;
205}
206
207template<typename SymbolTableObjectType>
208inline bool symbolTablePutInvalidateWatchpointSet(
209 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value,
210 bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors)
211{
212 WatchpointSet* set = nullptr;
213 unsigned attributes = 0;
214 bool result = symbolTablePut<SymbolTablePutMode::WithoutAttributes>(object, exec, propertyName, value, attributes, shouldThrowReadOnlyError, ignoreReadOnlyErrors, set);
215 if (set)
216 set->invalidate(VariableWriteFireDetail(object, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it.
217 return result;
218}
219
220template<typename SymbolTableObjectType>
221inline bool symbolTablePutWithAttributesTouchWatchpointSet(
222 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName,
223 JSValue value, unsigned attributes)
224{
225 WatchpointSet* set = nullptr;
226 bool shouldThrowReadOnlyError = false;
227 bool ignoreReadOnlyErrors = true;
228 bool result = symbolTablePut<SymbolTablePutMode::WithAttributes>(object, exec, propertyName, value, attributes, shouldThrowReadOnlyError, ignoreReadOnlyErrors, set);
229 if (set)
230 VariableWriteFireDetail::touch(set, object, propertyName);
231 return result;
232}
233
234} // namespace JSC
235
236#endif // JSSymbolTableObject_h
237
238