1/*
2 * Copyright (C) 2012, 2014-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 *
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#pragma once
30
31#include "JSScope.h"
32#include "PropertyDescriptor.h"
33#include "SymbolTable.h"
34#include "ThrowScope.h"
35#include "VariableWriteFireDetail.h"
36
37namespace JSC {
38
39class JSSymbolTableObject : public JSScope {
40public:
41 typedef JSScope Base;
42 static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames;
43
44 SymbolTable* symbolTable() const { return m_symbolTable.get(); }
45
46 JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
47 JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
48
49 static ptrdiff_t offsetOfSymbolTable() { return OBJECT_OFFSETOF(JSSymbolTableObject, m_symbolTable); }
50
51 DECLARE_EXPORT_INFO;
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 if (auto* singletonScope = symbolTable->singletonScope())
70 singletonScope->notifyWrite(vm, this, "Allocated a scope");
71 m_symbolTable.set(vm, this, symbolTable);
72 }
73
74 static void visitChildren(JSCell*, SlotVisitor&);
75
76private:
77 WriteBarrier<SymbolTable> m_symbolTable;
78};
79
80template<typename SymbolTableObjectType>
81inline bool symbolTableGet(
82 SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot)
83{
84 SymbolTable& symbolTable = *object->symbolTable();
85 ConcurrentJSLocker locker(symbolTable.m_lock);
86 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
87 if (iter == symbolTable.end(locker))
88 return false;
89 SymbolTableEntry::Fast entry = iter->value;
90 ASSERT(!entry.isNull());
91
92 ScopeOffset offset = entry.scopeOffset();
93 // Defend against the inspector asking for a var after it has been optimized out.
94 if (!object->isValidScopeOffset(offset))
95 return false;
96
97 slot.setValue(object, entry.getAttributes() | PropertyAttribute::DontDelete, object->variableAt(offset).get());
98 return true;
99}
100
101template<typename SymbolTableObjectType>
102inline bool symbolTableGet(
103 SymbolTableObjectType* object, PropertyName propertyName, PropertyDescriptor& descriptor)
104{
105 SymbolTable& symbolTable = *object->symbolTable();
106 ConcurrentJSLocker locker(symbolTable.m_lock);
107 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
108 if (iter == symbolTable.end(locker))
109 return false;
110 SymbolTableEntry::Fast entry = iter->value;
111 ASSERT(!entry.isNull());
112
113 ScopeOffset offset = entry.scopeOffset();
114 // Defend against the inspector asking for a var after it has been optimized out.
115 if (!object->isValidScopeOffset(offset))
116 return false;
117
118 descriptor.setDescriptor(object->variableAt(offset).get(), entry.getAttributes() | PropertyAttribute::DontDelete);
119 return true;
120}
121
122template<typename SymbolTableObjectType>
123inline bool symbolTableGet(
124 SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot,
125 bool& slotIsWriteable)
126{
127 SymbolTable& symbolTable = *object->symbolTable();
128 ConcurrentJSLocker locker(symbolTable.m_lock);
129 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
130 if (iter == symbolTable.end(locker))
131 return false;
132 SymbolTableEntry::Fast entry = iter->value;
133 ASSERT(!entry.isNull());
134
135 ScopeOffset offset = entry.scopeOffset();
136 // Defend against the inspector asking for a var after it has been optimized out.
137 if (!object->isValidScopeOffset(offset))
138 return false;
139
140 slot.setValue(object, entry.getAttributes() | PropertyAttribute::DontDelete, object->variableAt(offset).get());
141 slotIsWriteable = !entry.isReadOnly();
142 return true;
143}
144
145template<typename SymbolTableObjectType>
146ALWAYS_INLINE void symbolTablePutTouchWatchpointSet(VM& vm, SymbolTableObjectType* object, PropertyName propertyName, JSValue value, WriteBarrierBase<Unknown>* reg, WatchpointSet* set)
147{
148 reg->set(vm, object, value);
149 if (set)
150 VariableWriteFireDetail::touch(vm, set, object, propertyName);
151}
152
153template<typename SymbolTableObjectType>
154ALWAYS_INLINE void symbolTablePutInvalidateWatchpointSet(VM& vm, SymbolTableObjectType* object, PropertyName propertyName, JSValue value, WriteBarrierBase<Unknown>* reg, WatchpointSet* set)
155{
156 reg->set(vm, object, value);
157 if (set)
158 set->invalidate(vm, VariableWriteFireDetail(object, propertyName)); // Don't mess around - if we had found this statically, we would have invalidated it.
159}
160
161enum class SymbolTablePutMode {
162 Touch,
163 Invalidate
164};
165
166template<SymbolTablePutMode symbolTablePutMode, typename SymbolTableObjectType>
167inline bool symbolTablePut(SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value, bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors, bool& putResult)
168{
169 VM& vm = exec->vm();
170 auto scope = DECLARE_THROW_SCOPE(vm);
171
172 WatchpointSet* set = nullptr;
173 WriteBarrierBase<Unknown>* reg;
174 {
175 SymbolTable& symbolTable = *object->symbolTable();
176 // FIXME: This is very suspicious. We shouldn't need a GC-safe lock here.
177 // https://bugs.webkit.org/show_bug.cgi?id=134601
178 GCSafeConcurrentJSLocker locker(symbolTable.m_lock, vm.heap);
179 SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
180 if (iter == symbolTable.end(locker))
181 return false;
182 bool wasFat;
183 SymbolTableEntry::Fast fastEntry = iter->value.getFast(wasFat);
184 ASSERT(!fastEntry.isNull());
185 if (fastEntry.isReadOnly() && !ignoreReadOnlyErrors) {
186 if (shouldThrowReadOnlyError)
187 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
188 putResult = false;
189 return true;
190 }
191
192 ScopeOffset offset = fastEntry.scopeOffset();
193
194 // Defend against the inspector asking for a var after it has been optimized out.
195 if (!object->isValidScopeOffset(offset))
196 return false;
197
198 set = iter->value.watchpointSet();
199 reg = &object->variableAt(offset);
200 }
201 // I'd prefer we not hold lock while executing barriers, since I prefer to reserve
202 // the right for barriers to be able to trigger GC. And I don't want to hold VM
203 // locks while GC'ing.
204 if (symbolTablePutMode == SymbolTablePutMode::Invalidate)
205 symbolTablePutInvalidateWatchpointSet(vm, object, propertyName, value, reg, set);
206 else
207 symbolTablePutTouchWatchpointSet(vm, object, propertyName, value, reg, set);
208 putResult = true;
209 return true;
210}
211
212template<typename SymbolTableObjectType>
213inline bool symbolTablePutTouchWatchpointSet(
214 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value,
215 bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors, bool& putResult)
216{
217 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
218 return symbolTablePut<SymbolTablePutMode::Touch>(object, exec, propertyName, value, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
219}
220
221template<typename SymbolTableObjectType>
222inline bool symbolTablePutInvalidateWatchpointSet(
223 SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value,
224 bool shouldThrowReadOnlyError, bool ignoreReadOnlyErrors, bool& putResult)
225{
226 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
227 return symbolTablePut<SymbolTablePutMode::Invalidate>(object, exec, propertyName, value, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
228}
229
230} // namespace JSC
231