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
36namespace JSC { namespace Wasm {
37
38uint32_t Table::allocatedLength(uint32_t length)
39{
40 return WTF::roundUpToPowerOfTwo(length);
41}
42
43void 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
51Table::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
69RefPtr<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
83Optional<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
134void 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
146void 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
155JSValue 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
162void 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
170FuncRefTable* Table::asFuncrefTable()
171{
172 return m_type == TableElementType::Funcref ? static_cast<FuncRefTable*>(this) : nullptr;
173}
174
175FuncRefTable::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
190void 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)
204