Mypal/js/src/wasm/WasmTable.cpp
2021-02-04 16:48:36 +02:00

211 lines
5.9 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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());
}