1/*
2 * Copyright (C) 2012, 2014 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 "VariableWatchpointSetInlines.h"
36
37namespace JSC {
38
39class JSSymbolTableObject : public JSScope {
40public:
41    typedef JSScope Base;
42
43    SymbolTable* symbolTable() const { return m_symbolTable.get(); }
44
45    JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
46    JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
47
48protected:
49    static const unsigned StructureFlags = IsEnvironmentRecord | OverridesVisitChildren | OverridesGetPropertyNames | Base::StructureFlags;
50
51    JSSymbolTableObject(VM& vm, Structure* structure, JSScope* scope, SymbolTable* symbolTable = 0)
52        : Base(vm, structure, scope)
53    {
54        if (symbolTable)
55            m_symbolTable.set(vm, this, symbolTable);
56    }
57
58    void finishCreation(VM& vm)
59    {
60        Base::finishCreation(vm);
61        if (!m_symbolTable)
62            m_symbolTable.set(vm, this, SymbolTable::create(vm));
63    }
64
65    static void visitChildren(JSCell*, SlotVisitor&);
66
67    WriteBarrier<SymbolTable> m_symbolTable;
68};
69
70template<typename SymbolTableObjectType>
71inline bool symbolTableGet(
72    SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot)
73{
74    SymbolTable& symbolTable = *object->symbolTable();
75    ConcurrentJITLocker locker(symbolTable.m_lock);
76    SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
77    if (iter == symbolTable.end(locker))
78        return false;
79    SymbolTableEntry::Fast entry = iter->value;
80    ASSERT(!entry.isNull());
81    slot.setValue(object, entry.getAttributes() | DontDelete, object->registerAt(entry.getIndex()).get());
82    return true;
83}
84
85template<typename SymbolTableObjectType>
86inline bool symbolTableGet(
87    SymbolTableObjectType* object, PropertyName propertyName, PropertyDescriptor& descriptor)
88{
89    SymbolTable& symbolTable = *object->symbolTable();
90    ConcurrentJITLocker locker(symbolTable.m_lock);
91    SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
92    if (iter == symbolTable.end(locker))
93        return false;
94    SymbolTableEntry::Fast entry = iter->value;
95    ASSERT(!entry.isNull());
96    descriptor.setDescriptor(
97        object->registerAt(entry.getIndex()).get(), entry.getAttributes() | DontDelete);
98    return true;
99}
100
101template<typename SymbolTableObjectType>
102inline bool symbolTableGet(
103    SymbolTableObjectType* object, PropertyName propertyName, PropertySlot& slot,
104    bool& slotIsWriteable)
105{
106    SymbolTable& symbolTable = *object->symbolTable();
107    ConcurrentJITLocker locker(symbolTable.m_lock);
108    SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
109    if (iter == symbolTable.end(locker))
110        return false;
111    SymbolTableEntry::Fast entry = iter->value;
112    ASSERT(!entry.isNull());
113    slot.setValue(object, entry.getAttributes() | DontDelete, object->registerAt(entry.getIndex()).get());
114    slotIsWriteable = !entry.isReadOnly();
115    return true;
116}
117
118template<typename SymbolTableObjectType>
119inline bool symbolTablePut(
120    SymbolTableObjectType* object, ExecState* exec, PropertyName propertyName, JSValue value,
121    bool shouldThrow)
122{
123    VM& vm = exec->vm();
124    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
125
126    WriteBarrierBase<Unknown>* reg;
127    {
128        SymbolTable& symbolTable = *object->symbolTable();
129        // FIXME: This is very suspicious. We shouldn't need a GC-safe lock here.
130        // https://bugs.webkit.org/show_bug.cgi?id=134601
131        GCSafeConcurrentJITLocker locker(symbolTable.m_lock, exec->vm().heap);
132        SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
133        if (iter == symbolTable.end(locker))
134            return false;
135        bool wasFat;
136        SymbolTableEntry::Fast fastEntry = iter->value.getFast(wasFat);
137        ASSERT(!fastEntry.isNull());
138        if (fastEntry.isReadOnly()) {
139            if (shouldThrow)
140                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
141            return true;
142        }
143        if (VariableWatchpointSet* set = iter->value.watchpointSet()) {
144            // FIXME: It's strange that we're doing this while holding the symbol table's lock.
145            // https://bugs.webkit.org/show_bug.cgi?id=134601
146            set->notifyWrite(vm, value);
147        }
148        reg = &object->registerAt(fastEntry.getIndex());
149    }
150    // I'd prefer we not hold lock while executing barriers, since I prefer to reserve
151    // the right for barriers to be able to trigger GC. And I don't want to hold VM
152    // locks while GC'ing.
153    reg->set(vm, object, value);
154    return true;
155}
156
157template<typename SymbolTableObjectType>
158inline bool symbolTablePutWithAttributes(
159    SymbolTableObjectType* object, VM& vm, PropertyName propertyName,
160    JSValue value, unsigned attributes)
161{
162    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
163
164    WriteBarrierBase<Unknown>* reg;
165    {
166        SymbolTable& symbolTable = *object->symbolTable();
167        ConcurrentJITLocker locker(symbolTable.m_lock);
168        SymbolTable::Map::iterator iter = symbolTable.find(locker, propertyName.uid());
169        if (iter == symbolTable.end(locker))
170            return false;
171        SymbolTableEntry& entry = iter->value;
172        ASSERT(!entry.isNull());
173        if (VariableWatchpointSet* set = entry.watchpointSet())
174            set->notifyWrite(vm, value);
175        entry.setAttributes(attributes);
176        reg = &object->registerAt(entry.getIndex());
177    }
178    reg->set(vm, object, value);
179    return true;
180}
181
182} // namespace JSC
183
184#endif // JSSymbolTableObject_h
185
186