1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  *
4  * Copyright 2016 Mozilla Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "wasm/WasmRealm.h"
20 
21 #include "vm/Realm.h"
22 #include "wasm/WasmInstance.h"
23 
24 #include "debugger/DebugAPI-inl.h"
25 
26 using namespace js;
27 using namespace wasm;
28 
Realm(JSRuntime * rt)29 wasm::Realm::Realm(JSRuntime* rt) : runtime_(rt) {}
30 
~Realm()31 wasm::Realm::~Realm() { MOZ_ASSERT(instances_.empty()); }
32 
33 struct InstanceComparator {
34   const Instance& target;
InstanceComparatorInstanceComparator35   explicit InstanceComparator(const Instance& target) : target(target) {}
36 
operator ()InstanceComparator37   int operator()(const Instance* instance) const {
38     if (instance == &target) {
39       return 0;
40     }
41 
42     // Instances can share code, so the segments can be equal (though they
43     // can't partially overlap).  If the codeBases are equal, we sort by
44     // Instance address.  Thus a Code may map to many instances.
45 
46     // Compare by the first tier, always.
47 
48     Tier instanceTier = instance->code().stableTier();
49     Tier targetTier = target.code().stableTier();
50 
51     if (instance->codeBase(instanceTier) == target.codeBase(targetTier)) {
52       return instance < &target ? -1 : 1;
53     }
54 
55     return target.codeBase(targetTier) < instance->codeBase(instanceTier) ? -1
56                                                                           : 1;
57   }
58 };
59 
registerInstance(JSContext * cx,HandleWasmInstanceObject instanceObj)60 bool wasm::Realm::registerInstance(JSContext* cx,
61                                    HandleWasmInstanceObject instanceObj) {
62   MOZ_ASSERT(runtime_ == cx->runtime());
63 
64   Instance& instance = instanceObj->instance();
65   MOZ_ASSERT(this == &instance.realm()->wasm);
66 
67   instance.ensureProfilingLabels(cx->runtime()->geckoProfiler().enabled());
68 
69   if (instance.debugEnabled() &&
70       instance.realm()->debuggerObservesAllExecution()) {
71     instance.debug().ensureEnterFrameTrapsState(cx, true);
72   }
73 
74   {
75     if (!instances_.reserve(instances_.length() + 1)) {
76       return false;
77     }
78 
79     auto runtimeInstances = cx->runtime()->wasmInstances.lock();
80     if (!runtimeInstances->reserve(runtimeInstances->length() + 1)) {
81       return false;
82     }
83 
84     // To avoid implementing rollback, do not fail after mutations start.
85 
86     InstanceComparator cmp(instance);
87     size_t index;
88 
89     MOZ_ALWAYS_FALSE(
90         BinarySearchIf(instances_, 0, instances_.length(), cmp, &index));
91     MOZ_ALWAYS_TRUE(instances_.insert(instances_.begin() + index, &instance));
92 
93     MOZ_ALWAYS_FALSE(BinarySearchIf(runtimeInstances.get(), 0,
94                                     runtimeInstances->length(), cmp, &index));
95     MOZ_ALWAYS_TRUE(
96         runtimeInstances->insert(runtimeInstances->begin() + index, &instance));
97   }
98 
99   // Notify the debugger after wasmInstances is unlocked.
100   DebugAPI::onNewWasmInstance(cx, instanceObj);
101   return true;
102 }
103 
unregisterInstance(Instance & instance)104 void wasm::Realm::unregisterInstance(Instance& instance) {
105   InstanceComparator cmp(instance);
106   size_t index;
107 
108   if (BinarySearchIf(instances_, 0, instances_.length(), cmp, &index)) {
109     instances_.erase(instances_.begin() + index);
110   }
111 
112   auto runtimeInstances = runtime_->wasmInstances.lock();
113   if (BinarySearchIf(runtimeInstances.get(), 0, runtimeInstances->length(), cmp,
114                      &index)) {
115     runtimeInstances->erase(runtimeInstances->begin() + index);
116   }
117 }
118 
ensureProfilingLabels(bool profilingEnabled)119 void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
120   for (Instance* instance : instances_) {
121     instance->ensureProfilingLabels(profilingEnabled);
122   }
123 }
124 
addSizeOfExcludingThis(MallocSizeOf mallocSizeOf,size_t * realmTables)125 void wasm::Realm::addSizeOfExcludingThis(MallocSizeOf mallocSizeOf,
126                                          size_t* realmTables) {
127   *realmTables += instances_.sizeOfExcludingThis(mallocSizeOf);
128 }
129 
InterruptRunningCode(JSContext * cx)130 void wasm::InterruptRunningCode(JSContext* cx) {
131   auto runtimeInstances = cx->runtime()->wasmInstances.lock();
132   for (Instance* instance : runtimeInstances.get()) {
133     instance->tlsData()->setInterrupt();
134   }
135 }
136 
ResetInterruptState(JSContext * cx)137 void wasm::ResetInterruptState(JSContext* cx) {
138   auto runtimeInstances = cx->runtime()->wasmInstances.lock();
139   for (Instance* instance : runtimeInstances.get()) {
140     instance->tlsData()->resetInterrupt(cx);
141   }
142 }
143