b54645c642ee8b5ee0f32fb04ba4fb4fd802619c
[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, auto initializer) {
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             initializer(container.get()[i]);
116         }
117         return true;
118     };
119
120     if (auto* funcRefTable = asFuncrefTable()) {
121         if (!checkedGrow(funcRefTable->m_importableFunctions, [] (auto&) { }))
122             return WTF::nullopt;
123         if (!checkedGrow(funcRefTable->m_instances, [] (auto&) { }))
124             return WTF::nullopt;
125     }
126
127     if (!checkedGrow(m_jsValues, [] (WriteBarrier<Unknown>& slot) { slot.setStartingValue(jsNull()); }))
128         return WTF::nullopt;
129
130     setLength(newLength);
131     return newLength;
132 }
133
134 void Table::clear(uint32_t index)
135 {
136     RELEASE_ASSERT(index < length());
137     RELEASE_ASSERT(m_owner);
138     if (auto* funcRefTable = asFuncrefTable()) {
139         funcRefTable->m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction();
140         ASSERT(funcRefTable->m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code.
141         funcRefTable->m_instances.get()[index & m_mask] = nullptr;
142     }
143     m_jsValues.get()[index & m_mask].setStartingValue(jsNull());
144 }
145
146 void Table::set(uint32_t index, JSValue value)
147 {
148     RELEASE_ASSERT(index < length());
149     RELEASE_ASSERT(isAnyrefTable());
150     RELEASE_ASSERT(m_owner);
151     clear(index);
152     m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, value);
153 }
154
155 JSValue Table::get(uint32_t index) const
156 {
157     RELEASE_ASSERT(index < length());
158     RELEASE_ASSERT(m_owner);
159     return m_jsValues.get()[index & m_mask].get();
160 }
161
162 void Table::visitAggregate(SlotVisitor& visitor)
163 {
164     RELEASE_ASSERT(m_owner);
165     auto locker = holdLock(m_owner->cellLock());
166     for (unsigned i = 0; i < m_length; ++i)
167         visitor.append(m_jsValues.get()[i]);
168 }
169
170 FuncRefTable* Table::asFuncrefTable()
171 {
172     return m_type == TableElementType::Funcref ? static_cast<FuncRefTable*>(this) : nullptr;
173 }
174
175 FuncRefTable::FuncRefTable(uint32_t initial, Optional<uint32_t> maximum)
176     : Table(initial, maximum, TableElementType::Funcref)
177 {
178     // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so.
179     // But for now, we're not doing that.
180     m_importableFunctions = MallocPtr<WasmToWasmImportableFunction>::malloc((sizeof(WasmToWasmImportableFunction) * Checked<size_t>(allocatedLength(m_length))).unsafeGet());
181     // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425
182     m_instances = MallocPtr<Instance*>::malloc((sizeof(Instance*) * Checked<size_t>(allocatedLength(m_length))).unsafeGet());
183     for (uint32_t i = 0; i < allocatedLength(m_length); ++i) {
184         new (&m_importableFunctions.get()[i]) WasmToWasmImportableFunction();
185         ASSERT(m_importableFunctions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code.
186         m_instances.get()[i] = nullptr;
187     }
188 }
189
190 void FuncRefTable::setFunction(uint32_t index, JSObject* optionalWrapper, WasmToWasmImportableFunction function, Instance* instance)
191 {
192     RELEASE_ASSERT(index < length());
193     RELEASE_ASSERT(m_owner);
194     clear(index);
195     if (optionalWrapper)
196         m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, optionalWrapper);
197     m_importableFunctions.get()[index & m_mask] = function;
198     m_instances.get()[index & m_mask] = instance;
199 }
200
201 } } // namespace JSC::Table
202
203 #endif // ENABLE(WEBASSEMBLY)