Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2016 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmTable.h"
20 :
21 : #include "mozilla/CheckedInt.h"
22 :
23 : #include "jscntxt.h"
24 : #include "jscompartment.h"
25 :
26 : #include "wasm/WasmInstance.h"
27 : #include "wasm/WasmJS.h"
28 :
29 : using namespace js;
30 : using namespace js::wasm;
31 : using mozilla::CheckedInt;
32 :
33 0 : Table::Table(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject,
34 0 : UniqueByteArray array)
35 : : maybeObject_(maybeObject),
36 : observers_(cx->zone()),
37 0 : array_(Move(array)),
38 0 : kind_(desc.kind),
39 0 : length_(desc.limits.initial),
40 : maximum_(desc.limits.maximum),
41 0 : external_(desc.external)
42 0 : {}
43 :
44 : /* static */ SharedTable
45 0 : Table::create(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject)
46 : {
47 : // The raw element type of a Table depends on whether it is external: an
48 : // external table can contain functions from multiple instances and thus
49 : // must store an additional instance pointer in each element.
50 0 : UniqueByteArray array;
51 0 : if (desc.external)
52 0 : array.reset((uint8_t*)cx->pod_calloc<ExternalTableElem>(desc.limits.initial));
53 : else
54 0 : array.reset((uint8_t*)cx->pod_calloc<void*>(desc.limits.initial));
55 0 : if (!array)
56 0 : return nullptr;
57 :
58 0 : return SharedTable(cx->new_<Table>(cx, desc, maybeObject, Move(array)));
59 : }
60 :
61 : void
62 0 : Table::tracePrivate(JSTracer* trc)
63 : {
64 : // If this table has a WasmTableObject, then this method is only called by
65 : // WasmTableObject's trace hook so maybeObject_ must already be marked.
66 : // TraceEdge is called so that the pointer can be updated during a moving
67 : // GC. TraceWeakEdge may sound better, but it is less efficient given that
68 : // we know object_ is already marked.
69 0 : if (maybeObject_) {
70 0 : MOZ_ASSERT(!gc::IsAboutToBeFinalized(&maybeObject_));
71 0 : TraceEdge(trc, &maybeObject_, "wasm table object");
72 : }
73 :
74 0 : if (external_) {
75 0 : ExternalTableElem* array = externalArray();
76 0 : for (uint32_t i = 0; i < length_; i++) {
77 0 : if (array[i].tls)
78 0 : array[i].tls->instance->trace(trc);
79 : else
80 0 : MOZ_ASSERT(!array[i].code);
81 : }
82 : }
83 0 : }
84 :
85 : void
86 0 : Table::trace(JSTracer* trc)
87 : {
88 : // The trace hook of WasmTableObject will call Table::tracePrivate at
89 : // which point we can mark the rest of the children. If there is no
90 : // WasmTableObject, call Table::tracePrivate directly. Redirecting through
91 : // the WasmTableObject avoids marking the entire Table on each incoming
92 : // edge (once per dependent Instance).
93 0 : if (maybeObject_)
94 0 : TraceEdge(trc, &maybeObject_, "wasm table object");
95 : else
96 0 : tracePrivate(trc);
97 0 : }
98 :
99 : void**
100 0 : Table::internalArray() const
101 : {
102 0 : MOZ_ASSERT(!external_);
103 0 : return (void**)array_.get();
104 : }
105 :
106 : ExternalTableElem*
107 0 : Table::externalArray() const
108 : {
109 0 : MOZ_ASSERT(external_);
110 0 : return (ExternalTableElem*)array_.get();
111 : }
112 :
113 : void
114 0 : Table::set(uint32_t index, void* code, Instance& instance)
115 : {
116 0 : if (external_) {
117 0 : ExternalTableElem& elem = externalArray()[index];
118 0 : if (elem.tls)
119 0 : JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
120 :
121 0 : elem.code = code;
122 0 : elem.tls = instance.tlsData();
123 :
124 0 : MOZ_ASSERT(elem.tls->instance->objectUnbarriered()->isTenured(), "no writeBarrierPost");
125 : } else {
126 0 : internalArray()[index] = code;
127 : }
128 0 : }
129 :
130 : void
131 0 : Table::setNull(uint32_t index)
132 : {
133 : // Only external tables can set elements to null after initialization.
134 0 : ExternalTableElem& elem = externalArray()[index];
135 0 : if (elem.tls)
136 0 : JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
137 :
138 0 : elem.code = nullptr;
139 0 : elem.tls = nullptr;
140 0 : }
141 :
142 : uint32_t
143 0 : Table::grow(uint32_t delta, JSContext* cx)
144 : {
145 : // This isn't just an optimization: movingGrowable() assumes that
146 : // onMovingGrowTable does not fire when length == maximum.
147 0 : if (!delta)
148 0 : return length_;
149 :
150 0 : uint32_t oldLength = length_;
151 :
152 0 : CheckedInt<uint32_t> newLength = oldLength;
153 0 : newLength += delta;
154 0 : if (!newLength.isValid())
155 0 : return -1;
156 :
157 0 : if (maximum_ && newLength.value() > maximum_.value())
158 0 : return -1;
159 :
160 0 : MOZ_ASSERT(movingGrowable());
161 :
162 0 : JSRuntime* rt = cx->runtime(); // Use JSRuntime's MallocProvider to avoid throwing.
163 :
164 : // Note that realloc does not release array_'s pointee (which is returned by
165 : // externalArray()) on failure which is exactly what we need here.
166 0 : ExternalTableElem* newArray = rt->pod_realloc(externalArray(), length_, newLength.value());
167 0 : if (!newArray)
168 0 : return -1;
169 0 : Unused << array_.release();
170 0 : array_.reset((uint8_t*)newArray);
171 :
172 : // Realloc does not zero the delta for us.
173 0 : PodZero(newArray + length_, delta);
174 0 : length_ = newLength.value();
175 :
176 0 : if (observers_.initialized()) {
177 0 : for (InstanceSet::Range r = observers_.all(); !r.empty(); r.popFront())
178 0 : r.front()->instance().onMovingGrowTable();
179 : }
180 :
181 0 : return oldLength;
182 : }
183 :
184 : bool
185 0 : Table::movingGrowable() const
186 : {
187 0 : return !maximum_ || length_ < maximum_.value();
188 : }
189 :
190 : bool
191 0 : Table::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance)
192 : {
193 0 : MOZ_ASSERT(movingGrowable());
194 :
195 0 : if (!observers_.initialized() && !observers_.init()) {
196 0 : ReportOutOfMemory(cx);
197 0 : return false;
198 : }
199 :
200 0 : if (!observers_.putNew(instance)) {
201 0 : ReportOutOfMemory(cx);
202 0 : return false;
203 : }
204 :
205 0 : return true;
206 : }
207 :
208 : size_t
209 0 : Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
210 : {
211 0 : return mallocSizeOf(array_.get());
212 : }
|