summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmTable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm/WasmTable.cpp')
-rw-r--r--js/src/wasm/WasmTable.cpp211
1 files changed, 211 insertions, 0 deletions
diff --git a/js/src/wasm/WasmTable.cpp b/js/src/wasm/WasmTable.cpp
new file mode 100644
index 000000000..ed0ad4458
--- /dev/null
+++ b/js/src/wasm/WasmTable.cpp
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmTable.h"
+
+#include "mozilla/CheckedInt.h"
+
+#include "jscntxt.h"
+
+#include "wasm/WasmInstance.h"
+#include "wasm/WasmJS.h"
+
+using namespace js;
+using namespace js::wasm;
+using mozilla::CheckedInt;
+
+Table::Table(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject,
+ UniqueByteArray array)
+ : maybeObject_(maybeObject),
+ observers_(cx->zone(), InstanceSet()),
+ array_(Move(array)),
+ kind_(desc.kind),
+ length_(desc.limits.initial),
+ maximum_(desc.limits.maximum),
+ external_(desc.external)
+{}
+
+/* static */ SharedTable
+Table::create(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeObject)
+{
+ // The raw element type of a Table depends on whether it is external: an
+ // external table can contain functions from multiple instances and thus
+ // must store an additional instance pointer in each element.
+ UniqueByteArray array;
+ if (desc.external)
+ array.reset((uint8_t*)cx->pod_calloc<ExternalTableElem>(desc.limits.initial));
+ else
+ array.reset((uint8_t*)cx->pod_calloc<void*>(desc.limits.initial));
+ if (!array)
+ return nullptr;
+
+ return SharedTable(cx->new_<Table>(cx, desc, maybeObject, Move(array)));
+}
+
+void
+Table::tracePrivate(JSTracer* trc)
+{
+ // If this table has a WasmTableObject, then this method is only called by
+ // WasmTableObject's trace hook so maybeObject_ must already be marked.
+ // TraceEdge is called so that the pointer can be updated during a moving
+ // GC. TraceWeakEdge may sound better, but it is less efficient given that
+ // we know object_ is already marked.
+ if (maybeObject_) {
+ MOZ_ASSERT(!gc::IsAboutToBeFinalized(&maybeObject_));
+ TraceEdge(trc, &maybeObject_, "wasm table object");
+ }
+
+ if (external_) {
+ ExternalTableElem* array = externalArray();
+ for (uint32_t i = 0; i < length_; i++) {
+ if (array[i].tls)
+ array[i].tls->instance->trace(trc);
+ else
+ MOZ_ASSERT(!array[i].code);
+ }
+ }
+}
+
+void
+Table::trace(JSTracer* trc)
+{
+ // The trace hook of WasmTableObject will call Table::tracePrivate at
+ // which point we can mark the rest of the children. If there is no
+ // WasmTableObject, call Table::tracePrivate directly. Redirecting through
+ // the WasmTableObject avoids marking the entire Table on each incoming
+ // edge (once per dependent Instance).
+ if (maybeObject_)
+ TraceEdge(trc, &maybeObject_, "wasm table object");
+ else
+ tracePrivate(trc);
+}
+
+void**
+Table::internalArray() const
+{
+ MOZ_ASSERT(!external_);
+ return (void**)array_.get();
+}
+
+ExternalTableElem*
+Table::externalArray() const
+{
+ MOZ_ASSERT(external_);
+ return (ExternalTableElem*)array_.get();
+}
+
+void
+Table::set(uint32_t index, void* code, Instance& instance)
+{
+ if (external_) {
+ ExternalTableElem& elem = externalArray()[index];
+ if (elem.tls)
+ JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
+
+ elem.code = code;
+ elem.tls = &instance.tlsData();
+
+ MOZ_ASSERT(elem.tls->instance->objectUnbarriered()->isTenured(), "no writeBarrierPost");
+ } else {
+ internalArray()[index] = code;
+ }
+}
+
+void
+Table::setNull(uint32_t index)
+{
+ // Only external tables can set elements to null after initialization.
+ ExternalTableElem& elem = externalArray()[index];
+ if (elem.tls)
+ JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
+
+ elem.code = nullptr;
+ elem.tls = nullptr;
+}
+
+uint32_t
+Table::grow(uint32_t delta, JSContext* cx)
+{
+ // This isn't just an optimization: movingGrowable() assumes that
+ // onMovingGrowTable does not fire when length == maximum.
+ if (!delta)
+ return length_;
+
+ uint32_t oldLength = length_;
+
+ CheckedInt<uint32_t> newLength = oldLength;
+ newLength += delta;
+ if (!newLength.isValid())
+ return -1;
+
+ if (maximum_ && newLength.value() > maximum_.value())
+ return -1;
+
+ MOZ_ASSERT(movingGrowable());
+
+ JSRuntime* rt = cx; // Use JSRuntime's MallocProvider to avoid throwing.
+
+ // Note that realloc does not release array_'s pointee (which is returned by
+ // externalArray()) on failure which is exactly what we need here.
+ ExternalTableElem* newArray = rt->pod_realloc(externalArray(), length_, newLength.value());
+ if (!newArray)
+ return -1;
+ Unused << array_.release();
+ array_.reset((uint8_t*)newArray);
+
+ // Realloc does not zero the delta for us.
+ PodZero(newArray + length_, delta);
+ length_ = newLength.value();
+
+ if (observers_.initialized()) {
+ for (InstanceSet::Range r = observers_.all(); !r.empty(); r.popFront())
+ r.front()->instance().onMovingGrowTable();
+ }
+
+ return oldLength;
+}
+
+bool
+Table::movingGrowable() const
+{
+ return !maximum_ || length_ < maximum_.value();
+}
+
+bool
+Table::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance)
+{
+ MOZ_ASSERT(movingGrowable());
+
+ if (!observers_.initialized() && !observers_.init()) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (!observers_.putNew(instance)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+size_t
+Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+ return mallocSizeOf(array_.get());
+}