diff options
Diffstat (limited to 'js/src/wasm/WasmCompartment.cpp')
-rw-r--r-- | js/src/wasm/WasmCompartment.cpp | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/js/src/wasm/WasmCompartment.cpp b/js/src/wasm/WasmCompartment.cpp new file mode 100644 index 000000000..46b2b23b2 --- /dev/null +++ b/js/src/wasm/WasmCompartment.cpp @@ -0,0 +1,180 @@ +/* -*- 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); +} |