181 lines
5.3 KiB
C++
181 lines
5.3 KiB
C++
/* -*- 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/WasmCompartment.h"
|
|
|
|
#include "jscompartment.h"
|
|
|
|
#include "wasm/WasmInstance.h"
|
|
|
|
#include "vm/Debugger-inl.h"
|
|
|
|
using namespace js;
|
|
using namespace wasm;
|
|
|
|
Compartment::Compartment(Zone* zone)
|
|
: mutatingInstances_(false),
|
|
activationCount_(0),
|
|
profilingEnabled_(false)
|
|
{}
|
|
|
|
Compartment::~Compartment()
|
|
{
|
|
MOZ_ASSERT(activationCount_ == 0);
|
|
MOZ_ASSERT(instances_.empty());
|
|
MOZ_ASSERT(!mutatingInstances_);
|
|
}
|
|
|
|
struct InstanceComparator
|
|
{
|
|
const Instance& target;
|
|
explicit InstanceComparator(const Instance& target) : target(target) {}
|
|
|
|
int operator()(const Instance* instance) const {
|
|
if (instance == &target)
|
|
return 0;
|
|
MOZ_ASSERT(!target.codeSegment().containsCodePC(instance->codeBase()));
|
|
MOZ_ASSERT(!instance->codeSegment().containsCodePC(target.codeBase()));
|
|
return target.codeBase() < instance->codeBase() ? -1 : 1;
|
|
}
|
|
};
|
|
|
|
void
|
|
Compartment::trace(JSTracer* trc)
|
|
{
|
|
// A WasmInstanceObject that was initially reachable when called can become
|
|
// unreachable while executing on the stack. Since wasm does not otherwise
|
|
// scan the stack during GC to identify live instances, we mark all instance
|
|
// objects live if there is any running wasm in the compartment.
|
|
if (activationCount_) {
|
|
for (Instance* i : instances_)
|
|
i->trace(trc);
|
|
}
|
|
}
|
|
|
|
bool
|
|
Compartment::registerInstance(JSContext* cx, HandleWasmInstanceObject instanceObj)
|
|
{
|
|
Instance& instance = instanceObj->instance();
|
|
MOZ_ASSERT(this == &instance.compartment()->wasm);
|
|
|
|
if (!instance.ensureProfilingState(cx, profilingEnabled_))
|
|
return false;
|
|
|
|
size_t index;
|
|
if (BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index))
|
|
MOZ_CRASH("duplicate registration");
|
|
|
|
{
|
|
AutoMutateInstances guard(*this);
|
|
if (!instances_.insert(instances_.begin() + index, &instance)) {
|
|
ReportOutOfMemory(cx);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Debugger::onNewWasmInstance(cx, instanceObj);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Compartment::unregisterInstance(Instance& instance)
|
|
{
|
|
size_t index;
|
|
if (!BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index))
|
|
return;
|
|
|
|
AutoMutateInstances guard(*this);
|
|
instances_.erase(instances_.begin() + index);
|
|
}
|
|
|
|
struct PCComparator
|
|
{
|
|
const void* pc;
|
|
explicit PCComparator(const void* pc) : pc(pc) {}
|
|
|
|
int operator()(const Instance* instance) const {
|
|
if (instance->codeSegment().containsCodePC(pc))
|
|
return 0;
|
|
return pc < instance->codeBase() ? -1 : 1;
|
|
}
|
|
};
|
|
|
|
Code*
|
|
Compartment::lookupCode(const void* pc) const
|
|
{
|
|
Instance* instance = lookupInstanceDeprecated(pc);
|
|
return instance ? &instance->code() : nullptr;
|
|
}
|
|
|
|
Instance*
|
|
Compartment::lookupInstanceDeprecated(const void* pc) const
|
|
{
|
|
// lookupInstanceDeprecated can be called asynchronously from the interrupt
|
|
// signal handler. In that case, the signal handler is just asking whether
|
|
// the pc is in wasm code. If instances_ is being mutated then we can't be
|
|
// executing wasm code so returning nullptr is fine.
|
|
if (mutatingInstances_)
|
|
return nullptr;
|
|
|
|
size_t index;
|
|
if (!BinarySearchIf(instances_, 0, instances_.length(), PCComparator(pc), &index))
|
|
return nullptr;
|
|
|
|
return instances_[index];
|
|
}
|
|
|
|
bool
|
|
Compartment::ensureProfilingState(JSContext* cx)
|
|
{
|
|
bool newProfilingEnabled = cx->spsProfiler.enabled();
|
|
if (profilingEnabled_ == newProfilingEnabled)
|
|
return true;
|
|
|
|
// Since one Instance can call another Instance in the same compartment
|
|
// directly without calling through Instance::callExport(), when profiling
|
|
// is enabled, enable it for the entire compartment at once. It is only safe
|
|
// to enable profiling when the wasm is not on the stack, so delay enabling
|
|
// profiling until there are no live WasmActivations in this compartment.
|
|
|
|
if (activationCount_ > 0)
|
|
return true;
|
|
|
|
for (Instance* instance : instances_) {
|
|
if (!instance->ensureProfilingState(cx, newProfilingEnabled))
|
|
return false;
|
|
}
|
|
|
|
profilingEnabled_ = newProfilingEnabled;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Compartment::profilingEnabled() const
|
|
{
|
|
// Profiling can asynchronously interrupt the mutation of the instances_
|
|
// vector which is used by lookupCode() during stack-walking. To handle
|
|
// this rare case, disable profiling during mutation.
|
|
return profilingEnabled_ && !mutatingInstances_;
|
|
}
|
|
|
|
void
|
|
Compartment::addSizeOfExcludingThis(MallocSizeOf mallocSizeOf, size_t* compartmentTables)
|
|
{
|
|
*compartmentTables += instances_.sizeOfExcludingThis(mallocSizeOf);
|
|
}
|