WasmContext.cpp (4435B)
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 2025 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/WasmContext.h" 20 21 #include "jit/JitRuntime.h" 22 #include "js/friend/StackLimits.h" 23 #include "js/TracingAPI.h" 24 #include "vm/JSContext.h" 25 #include "wasm/WasmPI.h" 26 27 #ifdef XP_WIN 28 // We only need the `windows.h` header, but this file can get unified built 29 // with WasmSignalHandlers.cpp, which requires `winternal.h` to be included 30 // before the `windows.h` header, and so we must include it here for that case. 31 # include <winternl.h> // must include before util/WindowsWrapper.h's `#undef`s 32 33 # include "util/WindowsWrapper.h" 34 #endif 35 36 using namespace js::wasm; 37 38 Context::Context() 39 : triedToInstallSignalHandlers(false), 40 haveSignalHandlers(false), 41 stackLimit(JS::NativeStackLimitMin), 42 mainStackLimit(JS::NativeStackLimitMin) 43 #ifdef ENABLE_WASM_JSPI 44 , 45 activeSuspender_(nullptr) 46 #endif 47 { 48 } 49 50 Context::~Context() { 51 #ifdef ENABLE_WASM_JSPI 52 MOZ_ASSERT(activeSuspender_ == nullptr); 53 MOZ_ASSERT(suspenders_.empty()); 54 #endif 55 } 56 57 void Context::initStackLimit(JSContext* cx) { 58 // The wasm stack limit is the same as the jit stack limit. We also don't 59 // use the stack limit for triggering interrupts. 60 stackLimit = cx->jitStackLimitNoInterrupt; 61 mainStackLimit = stackLimit; 62 63 // See the comment on wasm::Context for why we do this. 64 #ifdef ENABLE_WASM_JSPI 65 # if defined(_WIN32) 66 tib_ = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb()); 67 tibStackBase_ = tib_->StackBase; 68 tibStackLimit_ = tib_->StackLimit; 69 # endif 70 #endif 71 } 72 73 #ifdef ENABLE_WASM_JSPI 74 SuspenderObject* Context::findSuspenderForStackAddress( 75 const void* stackAddress) { 76 // TODO: add a fast path for the main stack that avoids linear search. We 77 // need an accurate main stack base/limit for that. 78 for (auto iter = suspenders_.iter(); !iter.done(); iter.next()) { 79 SuspenderObject* object = iter.get(); 80 if (object->isActive() && object->hasStackAddress(stackAddress)) { 81 return object; 82 } 83 } 84 return nullptr; 85 } 86 87 void Context::trace(JSTracer* trc) { 88 if (activeSuspender_) { 89 TraceEdge(trc, &activeSuspender_, "suspender"); 90 } 91 } 92 93 void Context::traceRoots(JSTracer* trc) { 94 // The suspendedStacks_ contains suspended stacks frames that need to be 95 // traced only during minor GC. The major GC tracing is happening via 96 // SuspenderObject::trace. 97 // Non-suspended stack frames are traced as part of TraceJitActivations. 98 if (!trc->isTenuringTracer()) { 99 return; 100 } 101 gc::AssertRootMarkingPhase(trc); 102 for (auto iter = suspenders_.iter(); !iter.done(); iter.next()) { 103 SuspenderObject* object = iter.get(); 104 if (object->state() == SuspenderState::Suspended) { 105 TraceSuspendableStack(trc, object); 106 } 107 } 108 } 109 110 void Context::enterSuspendableStack(JSContext* cx, SuspenderObject* suspender) { 111 MOZ_ASSERT(!activeSuspender_); 112 activeSuspender_ = suspender; 113 stackLimit = suspender->stackMemoryLimitForJit(); 114 115 // See the comment on wasm::Context for why we do this. 116 # if defined(_WIN32) 117 tibStackBase_ = tib_->StackBase; 118 tibStackLimit_ = tib_->StackLimit; 119 tib_->StackBase = reinterpret_cast<void*>(suspender->stackMemoryBase()); 120 tib_->StackLimit = 121 reinterpret_cast<void*>(suspender->stackMemoryLimitForSystem()); 122 # endif 123 124 # ifdef DEBUG 125 cx->runtime()->jitRuntime()->disallowArbitraryCode(); 126 # endif 127 } 128 129 void Context::leaveSuspendableStack(JSContext* cx) { 130 MOZ_ASSERT(activeSuspender_); 131 activeSuspender_ = nullptr; 132 stackLimit = mainStackLimit; 133 134 // See the comment on wasm::Context for why we do this. 135 # if defined(_WIN32) 136 tib_->StackBase = static_cast<void*>(tibStackBase_); 137 tib_->StackLimit = static_cast<void*>(tibStackLimit_); 138 # endif 139 140 # ifdef DEBUG 141 cx->runtime()->jitRuntime()->clearDisallowArbitraryCode(); 142 # endif 143 } 144 #endif