diff options
Diffstat (limited to 'js/src/wasm/WasmTable.cpp')
-rw-r--r-- | js/src/wasm/WasmTable.cpp | 211 |
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()); +} |