13313468d160e85e519284e82dadc0a9adf68c03
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmTable.cpp
1 /*
2  * Copyright (C) 2017-2018 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WasmTable.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "JSCJSValueInlines.h"
32 #include <wtf/CheckedArithmetic.h>
33 #include <wtf/StdLibExtras.h>
34 #include <type_traits>
35
36 namespace JSC { namespace Wasm {
37
38 uint32_t Table::allocatedLength(uint32_t length)
39 {
40     return WTF::roundUpToPowerOfTwo(length);
41 }
42
43 void Table::setLength(uint32_t length)
44 {
45     m_length = length;
46     m_mask = WTF::maskForSize(length);
47     ASSERT(isValidLength(length));
48     ASSERT(m_mask == WTF::maskForSize(allocatedLength(length)));
49 }
50
51 Table::Table(uint32_t initial, Optional<uint32_t> maximum, TableElementType type)
52     : m_type(type)
53     , m_maximum(maximum)
54     , m_owner(nullptr)
55 {
56     setLength(initial);
57     ASSERT(!m_maximum || *m_maximum >= m_length);
58
59     // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so.
60     // But for now, we're not doing that.
61     // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425
62     m_jsValues = MallocPtr<WriteBarrier<Unknown>>::malloc((sizeof(WriteBarrier<Unknown>) * Checked<size_t>(allocatedLength(m_length))).unsafeGet());
63     for (uint32_t i = 0; i < allocatedLength(m_length); ++i) {
64         new (&m_jsValues.get()[i]) WriteBarrier<Unknown>();
65         m_jsValues.get()[i].setStartingValue(jsNull());
66     }
67 }
68
69 RefPtr<Table> Table::tryCreate(uint32_t initial, Optional<uint32_t> maximum, TableElementType type)
70 {
71     if (!isValidLength(initial))
72         return nullptr;
73     switch (type) {
74     case TableElementType::Funcref:
75         return adoptRef(new FuncRefTable(initial, maximum));
76     case TableElementType::Anyref:
77         return adoptRef(new Table(initial, maximum));
78     }
79
80     RELEASE_ASSERT_NOT_REACHED();
81 }
82
83 Optional<uint32_t> Table::grow(uint32_t delta)
84 {
85     RELEASE_ASSERT(m_owner);
86     if (delta == 0)
87         return length();
88
89     auto locker = holdLock(m_owner->cellLock());
90
91     using Checked = Checked<uint32_t, RecordOverflow>;
92     Checked newLengthChecked = length();
93     newLengthChecked += delta;
94     uint32_t newLength;
95     if (newLengthChecked.safeGet(newLength) == CheckedState::DidOverflow)
96         return WTF::nullopt;
97
98     if (maximum() && newLength > *maximum())
99         return WTF::nullopt;
100     if (!isValidLength(newLength))
101         return WTF::nullopt;
102
103     auto checkedGrow = [&] (auto& container) {
104         if (newLengthChecked.unsafeGet() > allocatedLength(m_length)) {
105             Checked reallocSizeChecked = allocatedLength(newLengthChecked.unsafeGet());
106             reallocSizeChecked *= sizeof(*container.get());
107             uint32_t reallocSize;
108             if (reallocSizeChecked.safeGet(reallocSize) == CheckedState::DidOverflow)
109                 return false;
110             // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425
111             container.realloc(reallocSize);
112         }
113         for (uint32_t i = m_length; i < allocatedLength(newLength); ++i)
114             new (&container.get()[i]) std::remove_reference_t<decltype(*container.get())>();
115         return true;
116     };
117
118     if (auto* funcRefTable = asFuncrefTable()) {
119         if (!checkedGrow(funcRefTable->m_importableFunctions))
120             return WTF::nullopt;
121         if (!checkedGrow(funcRefTable->m_instances))
122             return WTF::nullopt;
123     }
124
125     if (!checkedGrow(m_jsValues))
126         return WTF::nullopt;
127
128     setLength(newLength);
129     return newLength;
130 }
131
132 void Table::clear(uint32_t index)
133 {
134     RELEASE_ASSERT(index < length());
135     RELEASE_ASSERT(m_owner);
136     if (auto* funcRefTable = asFuncrefTable()) {
137         funcRefTable->m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction();
138         ASSERT(funcRefTable->m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code.
139         funcRefTable->m_instances.get()[index & m_mask] = nullptr;
140     }
141     m_jsValues.get()[index & m_mask].setStartingValue(jsNull());
142 }
143
144 void Table::set(uint32_t index, JSValue value)
145 {
146     RELEASE_ASSERT(index < length());
147     RELEASE_ASSERT(isAnyrefTable());
148     RELEASE_ASSERT(m_owner);
149     clear(index);
150     m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, value);
151 }
152
153 JSValue Table::get(uint32_t index) const
154 {
155     RELEASE_ASSERT(index < length());
156     RELEASE_ASSERT(m_owner);
157     return m_jsValues.get()[index & m_mask].get();
158 }
159
160 void Table::visitChildren(SlotVisitor& visitor)
161 {
162     RELEASE_ASSERT(m_owner);
163     auto locker = holdLock(m_owner->cellLock());
164     for (unsigned i = 0; i < m_length; ++i)
165         visitor.append(m_jsValues.get()[i]);
166 }
167
168 FuncRefTable* Table::asFuncrefTable()
169 {
170     return m_type == TableElementType::Funcref ? static_cast<FuncRefTable*>(this) : nullptr;
171 }
172
173 FuncRefTable::FuncRefTable(uint32_t initial, Optional<uint32_t> maximum)
174     : Table(initial, maximum, TableElementType::Funcref)
175 {
176     // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so.
177     // But for now, we're not doing that.
178     m_importableFunctions = MallocPtr<WasmToWasmImportableFunction>::malloc((sizeof(WasmToWasmImportableFunction) * Checked<size_t>(allocatedLength(m_length))).unsafeGet());
179     // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425
180     m_instances = MallocPtr<Instance*>::malloc((sizeof(Instance*) * Checked<size_t>(allocatedLength(m_length))).unsafeGet());
181     for (uint32_t i = 0; i < allocatedLength(m_length); ++i) {
182         new (&m_importableFunctions.get()[i]) WasmToWasmImportableFunction();
183         ASSERT(m_importableFunctions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code.
184         m_instances.get()[i] = nullptr;
185     }
186 }
187
188 void FuncRefTable::setFunction(uint32_t index, JSObject* optionalWrapper, WasmToWasmImportableFunction function, Instance* instance)
189 {
190     RELEASE_ASSERT(index < length());
191     RELEASE_ASSERT(m_owner);
192     clear(index);
193     if (optionalWrapper)
194         m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, optionalWrapper);
195     m_importableFunctions.get()[index & m_mask] = function;
196     m_instances.get()[index & m_mask] = instance;
197 }
198
199 } } // namespace JSC::Table
200
201 #endif // ENABLE(WEBASSEMBLY)