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
35namespace JSC { namespace Wasm {
36
37uint32_t Table::allocatedLength(uint32_t length)
38{
39 return WTF::roundUpToPowerOfTwo(length);
40}
41
42void 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
50RefPtr<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
57Table::~Table()
58{
59}
60
61Table::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
79Optional<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
121void 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
129void 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