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 <wtf/CheckedArithmetic.h> |
32 | #include <wtf/StdLibExtras.h> |
33 | #include <type_traits> |
34 | |
35 | namespace JSC { namespace Wasm { |
36 | |
37 | uint32_t Table::allocatedLength(uint32_t length) |
38 | { |
39 | return WTF::roundUpToPowerOfTwo(length); |
40 | } |
41 | |
42 | void Table::setLength(uint32_t length) |
43 | { |
44 | m_length = length; |
45 | m_mask = WTF::maskForSize(length); |
46 | ASSERT(isValidLength(length)); |
47 | ASSERT(m_mask == WTF::maskForSize(allocatedLength(length))); |
48 | } |
49 | |
50 | RefPtr<Table> Table::tryCreate(uint32_t initial, Optional<uint32_t> maximum) |
51 | { |
52 | if (!isValidLength(initial)) |
53 | return nullptr; |
54 | return adoptRef(new (NotNull, fastMalloc(sizeof(Table))) Table(initial, maximum)); |
55 | } |
56 | |
57 | Table::~Table() |
58 | { |
59 | } |
60 | |
61 | Table::Table(uint32_t initial, Optional<uint32_t> maximum) |
62 | { |
63 | setLength(initial); |
64 | m_maximum = maximum; |
65 | ASSERT(!m_maximum || *m_maximum >= m_length); |
66 | |
67 | // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so. |
68 | // But for now, we're not doing that. |
69 | m_importableFunctions = MallocPtr<WasmToWasmImportableFunction>::malloc((sizeof(WasmToWasmImportableFunction) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); |
70 | // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 |
71 | m_instances = MallocPtr<Instance*>::malloc((sizeof(Instance*) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); |
72 | for (uint32_t i = 0; i < allocatedLength(m_length); ++i) { |
73 | new (&m_importableFunctions.get()[i]) WasmToWasmImportableFunction(); |
74 | ASSERT(m_importableFunctions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. |
75 | m_instances.get()[i] = nullptr; |
76 | } |
77 | } |
78 | |
79 | Optional<uint32_t> Table::grow(uint32_t delta) |
80 | { |
81 | if (delta == 0) |
82 | return length(); |
83 | |
84 | using Checked = Checked<uint32_t, RecordOverflow>; |
85 | Checked newLengthChecked = length(); |
86 | newLengthChecked += delta; |
87 | uint32_t newLength; |
88 | if (newLengthChecked.safeGet(newLength) == CheckedState::DidOverflow) |
89 | return WTF::nullopt; |
90 | |
91 | if (maximum() && newLength > *maximum()) |
92 | return WTF::nullopt; |
93 | if (!isValidLength(newLength)) |
94 | return WTF::nullopt; |
95 | |
96 | auto checkedGrow = [&] (auto& container) { |
97 | if (newLengthChecked.unsafeGet() > allocatedLength(m_length)) { |
98 | Checked reallocSizeChecked = allocatedLength(newLengthChecked.unsafeGet()); |
99 | reallocSizeChecked *= sizeof(*container.get()); |
100 | uint32_t reallocSize; |
101 | if (reallocSizeChecked.safeGet(reallocSize) == CheckedState::DidOverflow) |
102 | return false; |
103 | // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 |
104 | container.realloc(reallocSize); |
105 | } |
106 | for (uint32_t i = m_length; i < allocatedLength(newLength); ++i) |
107 | new (&container.get()[i]) std::remove_reference_t<decltype(*container.get())>(); |
108 | return true; |
109 | }; |
110 | |
111 | if (!checkedGrow(m_importableFunctions)) |
112 | return WTF::nullopt; |
113 | if (!checkedGrow(m_instances)) |
114 | return WTF::nullopt; |
115 | |
116 | setLength(newLength); |
117 | |
118 | return newLength; |
119 | } |
120 | |
121 | void Table::clearFunction(uint32_t index) |
122 | { |
123 | RELEASE_ASSERT(index < length()); |
124 | m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction(); |
125 | ASSERT(m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. |
126 | m_instances.get()[index & m_mask] = nullptr; |
127 | } |
128 | |
129 | void Table::setFunction(uint32_t index, WasmToWasmImportableFunction function, Instance* instance) |
130 | { |
131 | RELEASE_ASSERT(index < length()); |
132 | m_importableFunctions.get()[index & m_mask] = function; |
133 | m_instances.get()[index & m_mask] = instance; |
134 | } |
135 | |
136 | } } // namespace JSC::Table |
137 | |
138 | #endif // ENABLE(WEBASSEMBLY) |
139 | |