Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp
Warning:line 1608, column 5
Value stored to 'funcBytecodeOffset' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_wasm4.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -resource-dir /usr/lib/llvm-20/lib/clang/20 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/wasm -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-20-090804-167946-1 -x c++ Unified_cpp_js_src_wasm4.cpp
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/WasmPI.h"
20
21#include "builtin/Promise.h"
22#include "debugger/DebugAPI.h"
23#include "debugger/Debugger.h"
24#include "jit/arm/Simulator-arm.h"
25#include "jit/JitRuntime.h"
26#include "jit/MIRGenerator.h"
27#include "js/CallAndConstruct.h"
28#include "js/Printf.h"
29#include "vm/Iteration.h"
30#include "vm/JSContext.h"
31#include "vm/JSObject.h"
32#include "vm/NativeObject.h"
33#include "vm/PromiseObject.h"
34#include "wasm/WasmConstants.h"
35#include "wasm/WasmContext.h"
36#include "wasm/WasmFeatures.h"
37#include "wasm/WasmGenerator.h"
38#include "wasm/WasmIonCompile.h" // IonPlatformSupport
39#include "wasm/WasmValidate.h"
40
41#include "vm/JSObject-inl.h"
42#include "wasm/WasmGcObject-inl.h"
43#include "wasm/WasmInstance-inl.h"
44
45#ifdef JS_CODEGEN_ARM64
46# include "jit/arm64/vixl/Simulator-vixl.h"
47#endif
48
49#ifdef XP_WIN
50# include "util/WindowsWrapper.h"
51#endif
52
53using namespace js;
54using namespace js::jit;
55
56#ifdef ENABLE_WASM_JSPI1
57namespace js::wasm {
58
59SuspenderObjectData::SuspenderObjectData(void* stackMemory)
60 : stackMemory_(stackMemory),
61 suspendableFP_(nullptr),
62 suspendableSP_(static_cast<uint8_t*>(stackMemory) +
63 SuspendableStackPlusRedZoneSize),
64 state_(SuspenderState::Initial),
65 suspendedBy_(nullptr) {}
66
67void SuspenderObjectData::releaseStackMemory() {
68 js_free(stackMemory_);
69 stackMemory_ = nullptr;
70}
71
72# if defined(_WIN32)
73// On WIN64, the Thread Information Block stack limits has to be modified to
74// avoid failures on SP checks.
75void SuspenderObjectData::updateTIBStackFields() {
76 _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
77 savedStackBase_ = tib->StackBase;
78 savedStackLimit_ = tib->StackLimit;
79 uintptr_t stack_limit = (uintptr_t)stackMemory_;
80 uintptr_t stack_base = stack_limit + SuspendableStackPlusRedZoneSize;
81 tib->StackBase = (void*)stack_base;
82 tib->StackLimit = (void*)stack_limit;
83}
84
85void SuspenderObjectData::restoreTIBStackFields() {
86 _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(::NtCurrentTeb());
87 tib->StackBase = savedStackBase_;
88 tib->StackLimit = savedStackLimit_;
89}
90# endif
91
92# ifdef JS_SIMULATOR_ARM64
93void SuspenderObjectData::switchSimulatorToMain() {
94 auto* sim = Simulator::Current();
95 suspendableSP_ = (void*)sim->xreg(Registers::sp, vixl::Reg31IsStackPointer);
96 suspendableFP_ = (void*)sim->xreg(Registers::fp);
97 sim->set_xreg(Registers::sp, (int64_t)mainSP_, vixl::Debugger::LogRegWrites,
98 vixl::Reg31IsStackPointer);
99 sim->set_xreg(Registers::fp, (int64_t)mainFP_);
100}
101
102void SuspenderObjectData::switchSimulatorToSuspendable() {
103 auto* sim = Simulator::Current();
104 mainSP_ = (void*)sim->xreg(Registers::sp, vixl::Reg31IsStackPointer);
105 mainFP_ = (void*)sim->xreg(Registers::fp);
106 sim->set_xreg(Registers::sp, (int64_t)suspendableSP_,
107 vixl::Debugger::LogRegWrites, vixl::Reg31IsStackPointer);
108 sim->set_xreg(Registers::fp, (int64_t)suspendableFP_);
109}
110# endif
111
112# ifdef JS_SIMULATOR_ARM
113void SuspenderObjectData::switchSimulatorToMain() {
114 suspendableSP_ = (void*)Simulator::Current()->get_register(Simulator::sp);
115 suspendableFP_ = (void*)Simulator::Current()->get_register(Simulator::fp);
116 Simulator::Current()->set_register(Simulator::sp, (int)mainSP_);
117 Simulator::Current()->set_register(Simulator::fp, (int)mainFP_);
118}
119
120void SuspenderObjectData::switchSimulatorToSuspendable() {
121 mainSP_ = (void*)Simulator::Current()->get_register(Simulator::sp);
122 mainFP_ = (void*)Simulator::Current()->get_register(Simulator::fp);
123 Simulator::Current()->set_register(Simulator::sp, (int)suspendableSP_);
124 Simulator::Current()->set_register(Simulator::fp, (int)suspendableFP_);
125}
126# endif
127
128// Slots that used in various JSFunctionExtended below.
129const size_t SUSPENDER_SLOT = 0;
130const size_t WRAPPED_FN_SLOT = 1;
131const size_t CONTINUE_ON_SUSPENDABLE_SLOT = 1;
132const size_t PROMISE_SLOT = 2;
133
134SuspenderContext::SuspenderContext()
135 : activeSuspender_(nullptr), suspendedStacks_() {}
136
137SuspenderContext::~SuspenderContext() {
138 MOZ_ASSERT(activeSuspender_ == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(activeSuspender_ == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(activeSuspender_ == nullptr)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("activeSuspender_ == nullptr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 138); AnnotateMozCrashReason("MOZ_ASSERT" "(" "activeSuspender_ == nullptr"
")"); do { *((volatile int*)__null) = 138; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
139 MOZ_ASSERT(suspendedStacks_.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(suspendedStacks_.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(suspendedStacks_.isEmpty()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("suspendedStacks_.isEmpty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "suspendedStacks_.isEmpty()"
")"); do { *((volatile int*)__null) = 139; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
140}
141
142SuspenderObject* SuspenderContext::activeSuspender() {
143 return activeSuspender_;
144}
145
146void SuspenderContext::setActiveSuspender(SuspenderObject* obj) {
147 activeSuspender_.set(obj);
148}
149
150void SuspenderContext::trace(JSTracer* trc) {
151 if (activeSuspender_) {
152 TraceEdge(trc, &activeSuspender_, "suspender");
153 }
154}
155
156static void TraceSuspendableStack(JSTracer* trc,
157 const SuspenderObjectData& data) {
158 void* startFP = data.suspendableFP();
159 void* returnAddress = data.suspendedReturnAddress();
160 void* exitFP = data.suspendableExitFP();
161 MOZ_ASSERT(startFP != exitFP)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(startFP != exitFP)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(startFP != exitFP))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("startFP != exitFP"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startFP != exitFP"
")"); do { *((volatile int*)__null) = 161; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
162
163 WasmFrameIter iter(static_cast<FrameWithInstances*>(startFP), returnAddress);
164 MOZ_ASSERT(iter.currentFrameStackSwitched())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.currentFrameStackSwitched())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.currentFrameStackSwitched
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("iter.currentFrameStackSwitched()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 164); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.currentFrameStackSwitched()"
")"); do { *((volatile int*)__null) = 164; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
165 uintptr_t highestByteVisitedInPrevWasmFrame = 0;
166 while (true) {
167 MOZ_ASSERT(!iter.done())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.done())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.done()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!iter.done()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 167); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter.done()"
")"); do { *((volatile int*)__null) = 167; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
168 uint8_t* nextPC = iter.resumePCinCurrentFrame();
169 Instance* instance = iter.instance();
170 TraceInstanceEdge(trc, instance, "WasmFrameIter instance");
171 highestByteVisitedInPrevWasmFrame = instance->traceFrame(
172 trc, iter, nextPC, highestByteVisitedInPrevWasmFrame);
173 if (iter.frame() == exitFP) {
174 break;
175 }
176 ++iter;
177 if (iter.currentFrameStackSwitched()) {
178 highestByteVisitedInPrevWasmFrame = 0;
179 }
180 }
181}
182
183void SuspenderContext::traceRoots(JSTracer* trc) {
184 // The suspendedStacks_ contains suspended stacks frames that need to be
185 // traced only during minor GC. The major GC tracing is happening via
186 // SuspenderObject::trace.
187 // Non-suspended stack frames are traced as part of TraceJitActivations.
188 if (!trc->isTenuringTracer()) {
189 return;
190 }
191 gc::AssertRootMarkingPhase(trc);
192 for (const SuspenderObjectData& data : suspendedStacks_) {
193 TraceSuspendableStack(trc, data);
194 }
195}
196
197static_assert(JS_STACK_GROWTH_DIRECTION(-1) < 0,
198 "JS-PI implemented only for native stacks that grows towards 0");
199
200static void DecrementSuspendableStacksCount(JSContext* cx) {
201 for (;;) {
202 uint32_t currentCount = cx->wasm().suspendableStacksCount;
203 MOZ_ASSERT(currentCount > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(currentCount > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(currentCount > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("currentCount > 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "currentCount > 0"
")"); do { *((volatile int*)__null) = 203; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
204 if (cx->wasm().suspendableStacksCount.compareExchange(currentCount,
205 currentCount - 1)) {
206 break;
207 }
208 // Failed to decrement suspendableStacksCount, repeat.
209 }
210}
211
212class SuspenderObject : public NativeObject {
213 public:
214 static const JSClass class_;
215
216 enum { DataSlot, PromisingPromiseSlot, SuspendingReturnTypeSlot, SlotCount };
217
218 enum class ReturnType : int32_t { Unknown, Promise, Value, Exception };
219
220 static SuspenderObject* create(JSContext* cx) {
221 for (;;) {
222 uint32_t currentCount = cx->wasm().suspendableStacksCount;
223 if (currentCount >= SuspendableStacksMaxCount) {
224 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
225 JSMSG_JSPI_SUSPENDER_LIMIT);
226 return nullptr;
227 }
228 if (cx->wasm().suspendableStacksCount.compareExchange(currentCount,
229 currentCount + 1)) {
230 break;
231 }
232 // Failed to increment suspendableStacksCount, repeat.
233 }
234
235 Rooted<SuspenderObject*> suspender(
236 cx, NewBuiltinClassInstance<SuspenderObject>(cx));
237 if (!suspender) {
238 DecrementSuspendableStacksCount(cx);
239 return nullptr;
240 }
241
242 void* stackMemory = js_malloc(SuspendableStackPlusRedZoneSize);
243 if (!stackMemory) {
244 DecrementSuspendableStacksCount(cx);
245 ReportOutOfMemory(cx);
246 return nullptr;
247 }
248
249 SuspenderObjectData* data = js_new<SuspenderObjectData>(stackMemory);
250 if (!data) {
251 js_free(stackMemory);
252 DecrementSuspendableStacksCount(cx);
253 ReportOutOfMemory(cx);
254 return nullptr;
255 }
256 MOZ_RELEASE_ASSERT(data->state() != SuspenderState::Moribund)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(data->state() != SuspenderState::Moribund)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(data->state() != SuspenderState::Moribund))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("data->state() != SuspenderState::Moribund"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 256); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "data->state() != SuspenderState::Moribund"
")"); do { *((volatile int*)__null) = 256; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
257
258 suspender->initReservedSlot(DataSlot, PrivateValue(data));
259 suspender->initReservedSlot(PromisingPromiseSlot, NullValue());
260 suspender->initReservedSlot(SuspendingReturnTypeSlot,
261 Int32Value(int32_t(ReturnType::Unknown)));
262 return suspender;
263 }
264
265 PromiseObject* promisingPromise() const {
266 return &getReservedSlot(PromisingPromiseSlot)
267 .toObject()
268 .as<PromiseObject>();
269 }
270
271 void setPromisingPromise(Handle<PromiseObject*> promise) {
272 setReservedSlot(PromisingPromiseSlot, ObjectOrNullValue(promise));
273 }
274
275 ReturnType suspendingReturnType() const {
276 return ReturnType(getReservedSlot(SuspendingReturnTypeSlot).toInt32());
277 }
278
279 void setSuspendingReturnType(ReturnType type) {
280 // The SuspendingReturnTypeSlot will change after result is defined,
281 // and becomes invalid after GetSuspendingPromiseResult. The assert is
282 // checking if the result was processed by GetSuspendingPromiseResult.
283 MOZ_ASSERT((type == ReturnType::Unknown) !=do { static_assert( mozilla::detail::AssertionConditionType<
decltype((type == ReturnType::Unknown) != (suspendingReturnType
() == ReturnType::Unknown))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((type == ReturnType::Unknown
) != (suspendingReturnType() == ReturnType::Unknown)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(type == ReturnType::Unknown) != (suspendingReturnType() == ReturnType::Unknown)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(type == ReturnType::Unknown) != (suspendingReturnType() == ReturnType::Unknown)"
")"); do { *((volatile int*)__null) = 284; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
284 (suspendingReturnType() == ReturnType::Unknown))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((type == ReturnType::Unknown) != (suspendingReturnType
() == ReturnType::Unknown))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((type == ReturnType::Unknown
) != (suspendingReturnType() == ReturnType::Unknown)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(type == ReturnType::Unknown) != (suspendingReturnType() == ReturnType::Unknown)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(type == ReturnType::Unknown) != (suspendingReturnType() == ReturnType::Unknown)"
")"); do { *((volatile int*)__null) = 284; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
285
286 setReservedSlot(SuspendingReturnTypeSlot, Int32Value(int32_t(type)));
287 }
288
289 JS::NativeStackLimit getStackMemoryLimit() {
290 return JS::NativeStackLimit(data()->stackMemory()) + SuspendableRedZoneSize;
291 }
292
293 SuspenderState state() { return data()->state(); }
294
295 inline bool hasData() { return !getReservedSlot(DataSlot).isUndefined(); }
296
297 inline SuspenderObjectData* data() {
298 return static_cast<SuspenderObjectData*>(
299 getReservedSlot(DataSlot).toPrivate());
300 }
301
302 void setMoribund(JSContext* cx);
303 void setActive(JSContext* cx);
304 void setSuspended(JSContext* cx);
305
306 void enter(JSContext* cx);
307 void suspend(JSContext* cx);
308 void resume(JSContext* cx);
309 void leave(JSContext* cx);
310
311 // Modifies frames to inject the suspendable stack back into the main one.
312 void forwardToSuspendable();
313
314 private:
315 static const JSClassOps classOps_;
316
317 static void finalize(JS::GCContext* gcx, JSObject* obj);
318 static void trace(JSTracer* trc, JSObject* obj);
319};
320
321static_assert(SuspenderObjectDataSlot == SuspenderObject::DataSlot);
322
323const JSClass SuspenderObject::class_ = {
324 "SuspenderObject",
325 JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_FOREGROUND_FINALIZE,
326 &SuspenderObject::classOps_,
327};
328
329const JSClassOps SuspenderObject::classOps_ = {
330 nullptr, // addProperty
331 nullptr, // delProperty
332 nullptr, // enumerate
333 nullptr, // newEnumerate
334 nullptr, // resolve
335 nullptr, // mayResolve
336 finalize, // finalize
337 nullptr, // call
338 nullptr, // construct
339 trace, // trace
340};
341
342/* static */
343void SuspenderObject::finalize(JS::GCContext* gcx, JSObject* obj) {
344 SuspenderObject& suspender = obj->as<SuspenderObject>();
345 if (!suspender.hasData()) {
346 return;
347 }
348 SuspenderObjectData* data = suspender.data();
349 if (data->state() == SuspenderState::Moribund) {
350 MOZ_RELEASE_ASSERT(!data->stackMemory())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!data->stackMemory())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!data->stackMemory()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!data->stackMemory()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 350); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!data->stackMemory()"
")"); do { *((volatile int*)__null) = 350; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
351 } else {
352 // Cleaning stack memory and removing from suspendableStacks_.
353 data->releaseStackMemory();
354 if (SuspenderContext* scx = data->suspendedBy()) {
355 scx->suspendedStacks_.remove(data);
356 }
357 }
358 js_free(data);
359}
360
361/* static */
362void SuspenderObject::trace(JSTracer* trc, JSObject* obj) {
363 SuspenderObject& suspender = obj->as<SuspenderObject>();
364 if (!suspender.hasData()) {
365 return;
366 }
367 SuspenderObjectData& data = *suspender.data();
368 // The SuspenderObjectData refers stacks frames that need to be traced
369 // only during major GC to determine if SuspenderObject content is
370 // reachable from JS. The frames must be suspended -- non-suspended
371 // stack frames are traced as part of TraceJitActivations.
372 if (!data.traceable() || trc->isTenuringTracer()) {
373 return;
374 }
375 TraceSuspendableStack(trc, data);
376}
377
378void SuspenderObject::setMoribund(JSContext* cx) {
379 MOZ_ASSERT(state() == SuspenderState::Active)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(state() == SuspenderState::Active)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(state() == SuspenderState::Active
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"state() == SuspenderState::Active", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "state() == SuspenderState::Active"
")"); do { *((volatile int*)__null) = 379; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
380 ResetInstanceStackLimits(cx);
381# if defined(_WIN32)
382 data()->restoreTIBStackFields();
383# endif
384 SuspenderObjectData* data = this->data();
385 data->setState(SuspenderState::Moribund);
386 data->releaseStackMemory();
387 DecrementSuspendableStacksCount(cx);
388 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 390); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
")"); do { *((volatile int*)__null) = 390; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
389 !cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 390); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
")"); do { *((volatile int*)__null) = 390; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
390 data))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList
( data)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 390); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( data)"
")"); do { *((volatile int*)__null) = 390; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
391}
392
393void SuspenderObject::setActive(JSContext* cx) {
394 data()->setState(SuspenderState::Active);
395 UpdateInstanceStackLimitsForSuspendableStack(cx, getStackMemoryLimit());
396# if defined(_WIN32)
397 data()->updateTIBStackFields();
398# endif
399}
400
401void SuspenderObject::setSuspended(JSContext* cx) {
402 data()->setState(SuspenderState::Suspended);
403 ResetInstanceStackLimits(cx);
404# if defined(_WIN32)
405 data()->restoreTIBStackFields();
406# endif
407}
408
409void SuspenderObject::enter(JSContext* cx) {
410 MOZ_ASSERT(state() == SuspenderState::Initial)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(state() == SuspenderState::Initial)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(state() == SuspenderState::Initial
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"state() == SuspenderState::Initial", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 410); AnnotateMozCrashReason("MOZ_ASSERT" "(" "state() == SuspenderState::Initial"
")"); do { *((volatile int*)__null) = 410; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
411 cx->wasm().promiseIntegration.setActiveSuspender(this);
412 setActive(cx);
413# ifdef DEBUG1
414 cx->runtime()->jitRuntime()->disallowArbitraryCode();
415# endif
416}
417
418void SuspenderObject::suspend(JSContext* cx) {
419 MOZ_ASSERT(state() == SuspenderState::Active)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(state() == SuspenderState::Active)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(state() == SuspenderState::Active
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"state() == SuspenderState::Active", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 419); AnnotateMozCrashReason("MOZ_ASSERT" "(" "state() == SuspenderState::Active"
")"); do { *((volatile int*)__null) = 419; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
420 setSuspended(cx);
421 cx->wasm().promiseIntegration.suspendedStacks_.pushFront(data());
422 data()->setSuspendedBy(&cx->wasm().promiseIntegration);
423 cx->wasm().promiseIntegration.setActiveSuspender(nullptr);
424# ifdef DEBUG1
425 cx->runtime()->jitRuntime()->clearDisallowArbitraryCode();
426# endif
427
428 if (cx->realm()->isDebuggee()) {
429 WasmFrameIter iter(cx->activation()->asJit());
430 while (true) {
431 MOZ_ASSERT(!iter.done())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.done())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.done()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!iter.done()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter.done()"
")"); do { *((volatile int*)__null) = 431; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
432 if (iter.debugEnabled()) {
433 DebugAPI::onSuspendWasmFrame(cx, iter.debugFrame());
434 }
435 ++iter;
436 if (iter.currentFrameStackSwitched()) {
437 break;
438 }
439 }
440 }
441}
442
443void SuspenderObject::resume(JSContext* cx) {
444 MOZ_ASSERT(state() == SuspenderState::Suspended)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(state() == SuspenderState::Suspended)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(state() == SuspenderState::Suspended
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"state() == SuspenderState::Suspended", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 444); AnnotateMozCrashReason("MOZ_ASSERT" "(" "state() == SuspenderState::Suspended"
")"); do { *((volatile int*)__null) = 444; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
445 cx->wasm().promiseIntegration.setActiveSuspender(this);
446 setActive(cx);
447 data()->setSuspendedBy(nullptr);
448 cx->wasm().promiseIntegration.suspendedStacks_.remove(data());
449# ifdef DEBUG1
450 cx->runtime()->jitRuntime()->disallowArbitraryCode();
451# endif
452
453 if (cx->realm()->isDebuggee()) {
454 for (FrameIter iter(cx);; ++iter) {
455 MOZ_RELEASE_ASSERT(!iter.done(), "expecting stackSwitched()")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.done())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.done()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!iter.done()" " ("
"expecting stackSwitched()" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 455); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!iter.done()"
") (" "expecting stackSwitched()" ")"); do { *((volatile int
*)__null) = 455; __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
456 if (iter.isWasm()) {
457 WasmFrameIter& wasmIter = iter.wasmFrame();
458 if (wasmIter.currentFrameStackSwitched()) {
459 break;
460 }
461 if (wasmIter.debugEnabled()) {
462 DebugAPI::onResumeWasmFrame(cx, iter);
463 }
464 }
465 }
466 }
467}
468
469void SuspenderObject::leave(JSContext* cx) {
470 cx->wasm().promiseIntegration.setActiveSuspender(nullptr);
471# ifdef DEBUG1
472 cx->runtime()->jitRuntime()->clearDisallowArbitraryCode();
473# endif
474 // We are exiting alternative stack if state is active,
475 // otherwise the stack was just suspended.
476 switch (state()) {
477 case SuspenderState::Active:
478 setMoribund(cx);
479 break;
480 case SuspenderState::Suspended:
481 break;
482 case SuspenderState::Initial:
483 case SuspenderState::Moribund:
484 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 484); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 484; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
485 }
486}
487
488void SuspenderObject::forwardToSuspendable() {
489 // Injecting suspendable stack back into main one at the exit frame.
490 SuspenderObjectData* data = this->data();
491 uint8_t* mainExitFP = (uint8_t*)data->mainExitFP();
492 *reinterpret_cast<void**>(mainExitFP + Frame::callerFPOffset()) =
493 data->suspendableFP();
494 *reinterpret_cast<void**>(mainExitFP + Frame::returnAddressOffset()) =
495 data->suspendedReturnAddress();
496}
497
498bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data) {
499 Rooted<SuspenderObject*> suspender(
500 cx, cx->wasm().promiseIntegration.activeSuspender());
501 SuspenderObjectData* stacks = suspender->data();
502
503 cx->wasm().promiseIntegration.setActiveSuspender(nullptr);
504
505 MOZ_ASSERT(suspender->state() == SuspenderState::Active)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(suspender->state() == SuspenderState::Active)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(suspender->state() == SuspenderState::Active))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("suspender->state() == SuspenderState::Active"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "suspender->state() == SuspenderState::Active"
")"); do { *((volatile int*)__null) = 505; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
506 suspender->setSuspended(cx);
507
508# ifdef JS_SIMULATOR
509# if defined(JS_SIMULATOR_ARM64) || defined(JS_SIMULATOR_ARM)
510 // The simulator is using its own stack, however switching is needed for
511 // virtual registers.
512 stacks->switchSimulatorToMain();
513 bool res = fn(data);
514 stacks->switchSimulatorToSuspendable();
515# else
516# error "not supported"
517# endif
518# else
519 // The platform specific code below inserts offsets as strings into inline
520 // assembly. CHECK_OFFSETS verifies the specified literals in macros below.
521# define CHECK_OFFSETS(MAIN_FP_OFFSET, MAIN_SP_OFFSET, \
522 SUSPENDABLE_FP_OFFSET, SUSPENDABLE_SP_OFFSET) \
523 static_assert( \
524 (MAIN_FP_OFFSET) == SuspenderObjectData::offsetOfMainFP() && \
525 (MAIN_SP_OFFSET) == SuspenderObjectData::offsetOfMainSP() && \
526 (SUSPENDABLE_FP_OFFSET) == \
527 SuspenderObjectData::offsetOfSuspendableFP() && \
528 (SUSPENDABLE_SP_OFFSET) == \
529 SuspenderObjectData::offsetOfSuspendableSP());
530
531 // The following assembly code temporarily switches FP/SP pointers to be on
532 // main stack, while maintaining frames linking. After
533 // `CallImportData::Call` execution suspendable stack FP/SP will be restored.
534 //
535 // Because the assembly sequences contain a call, the trashed-register list
536 // must contain all the caller saved registers. They must also contain "cc"
537 // and "memory" since both of those state elements could be modified by the
538 // call. They also need a "volatile" qualifier to ensure that the they don't
539 // get optimised out or otherwise messed with by clang/gcc.
540 //
541 // `Registers::VolatileMask` (in the assembler complex) is useful in that it
542 // lists the caller-saved registers.
543
544 uintptr_t res;
545
546 // clang-format off
547#if defined(_M_ARM64) || defined(__aarch64__)
548# define CALLER_SAVED_REGS \
549 "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", "x10", \
550 "x11", "x12", "x13", "x14", "x15", "x16", "x17", /* "x18", */ \
551 "x19", "x20", /* it's unclear who saves these two, so be safe */ \
552 /* claim that all the vector regs are caller-saved, for safety */ \
553 "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10", \
554 "v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", \
555 "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", \
556 "v29", "v30", "v31"
557# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
558 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
559 asm volatile( \
560 "\n mov x0, %1" \
561 "\n mov x27, sp " \
562 "\n str x29, [x0, #" #SUSPENDABLE_FP "] " \
563 "\n str x27, [x0, #" #SUSPENDABLE_SP "] " \
564 \
565 "\n ldr x29, [x0, #" #MAIN_FP "] " \
566 "\n ldr x27, [x0, #" #MAIN_SP "] " \
567 "\n mov sp, x27 " \
568 \
569 "\n stp x0, x27, [sp, #-16]! " \
570 \
571 "\n mov x0, %3" \
572 "\n blr %2 " \
573 \
574 "\n ldp x3, x27, [sp], #16 " \
575 \
576 "\n mov x27, sp " \
577 "\n str x29, [x3, #" #MAIN_FP "] " \
578 "\n str x27, [x3, #" #MAIN_SP "] " \
579 \
580 "\n ldr x29, [x3, #" #SUSPENDABLE_FP "] " \
581 "\n ldr x27, [x3, #" #SUSPENDABLE_SP "] " \
582 "\n mov sp, x27 " \
583 "\n mov %0, x0" \
584 : "=r"(res) \
585 : "r"(stacks), "r"(fn), "r"(data) \
586 : "x0", "x3", "x27", CALLER_SAVED_REGS, "cc", "memory")
587 INLINED_ASM(24, 32, 40, 48);
588
589# elif defined(_WIN64) && defined(_M_X64)
590# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
591 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
592 asm("\n mov %1, %%rcx" \
593 "\n mov %%rbp, " #SUSPENDABLE_FP "(%%rcx)" \
594 "\n mov %%rsp, " #SUSPENDABLE_SP "(%%rcx)" \
595 \
596 "\n mov " #MAIN_FP "(%%rcx), %%rbp" \
597 "\n mov " #MAIN_SP "(%%rcx), %%rsp" \
598 \
599 "\n push %%rcx" \
600 "\n push %%rdx" \
601 \
602 "\n mov %3, %%rcx" \
603 "\n call *%2" \
604 \
605 "\n pop %%rdx" \
606 "\n pop %%rcx" \
607 \
608 "\n mov %%rbp, " #MAIN_FP "(%%rcx)" \
609 "\n mov %%rsp, " #MAIN_SP "(%%rcx)" \
610 \
611 "\n mov " #SUSPENDABLE_FP "(%%rcx), %%rbp" \
612 "\n mov " #SUSPENDABLE_SP "(%%rcx), %%rsp" \
613 \
614 "\n movq %%rax, %0" \
615 : "=r"(res) \
616 : "r"(stacks), "r"(fn), "r"(data) \
617 : "rcx", "rax", "cc", "memory")
618 INLINED_ASM(24, 32, 40, 48);
619
620# elif defined(__x86_64__1)
621# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
622 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
623 asm("\n mov %1, %%rdi" \
624 "\n mov %%rbp, " #SUSPENDABLE_FP "(%%rdi)" \
625 "\n mov %%rsp, " #SUSPENDABLE_SP "(%%rdi)" \
626 \
627 "\n mov " #MAIN_FP "(%%rdi), %%rbp" \
628 "\n mov " #MAIN_SP "(%%rdi), %%rsp" \
629 \
630 "\n push %%rdi" \
631 "\n push %%rdx" \
632 \
633 "\n mov %3, %%rdi" \
634 "\n call *%2" \
635 \
636 "\n pop %%rdx" \
637 "\n pop %%rdi" \
638 \
639 "\n mov %%rbp, " #MAIN_FP "(%%rdi)" \
640 "\n mov %%rsp, " #MAIN_SP "(%%rdi)" \
641 \
642 "\n mov " #SUSPENDABLE_FP "(%%rdi), %%rbp" \
643 "\n mov " #SUSPENDABLE_SP "(%%rdi), %%rsp" \
644 \
645 "\n movq %%rax, %0" \
646 : "=r"(res) \
647 : "r"(stacks), "r"(fn), "r"(data) \
648 : "rdi", "rax", "cc", "memory")
649 INLINED_ASM(24, 32, 40, 48);
650# elif defined(__i386__) || defined(_M_IX86)
651# define CALLER_SAVED_REGS "eax", "ecx", "edx"
652# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
653 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
654 asm("\n mov %1, %%edx" \
655 "\n mov %%ebp, " #SUSPENDABLE_FP "(%%edx)" \
656 "\n mov %%esp, " #SUSPENDABLE_SP "(%%edx)" \
657 \
658 "\n mov " #MAIN_FP "(%%edx), %%ebp" \
659 "\n mov " #MAIN_SP "(%%edx), %%esp" \
660 \
661 "\n push %%edx" \
662 "\n sub $8, %%esp" \
663 "\n push %3" \
664 "\n call *%2" \
665 "\n add $12, %%esp" \
666 "\n pop %%edx" \
667 \
668 "\n mov %%ebp, " #MAIN_FP "(%%edx)" \
669 "\n mov %%esp, " #MAIN_SP "(%%edx)" \
670 \
671 "\n mov " #SUSPENDABLE_FP "(%%edx), %%ebp" \
672 "\n mov " #SUSPENDABLE_SP "(%%edx), %%esp" \
673 \
674 "\n mov %%eax, %0" \
675 : "=r"(res) \
676 : "r"(stacks), "r"(fn), "r"(data) \
677 : CALLER_SAVED_REGS, "cc", "memory")
678 INLINED_ASM(12, 16, 20, 24);
679
680# elif defined(__arm__)
681# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
682 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
683 asm("\n mov r0, %1" \
684 "\n mov r1, sp" \
685 "\n str r11, [r0, #" #SUSPENDABLE_FP "]" \
686 "\n str r1, [r0, #" #SUSPENDABLE_SP "]" \
687 \
688 "\n ldr r11, [r0, #" #MAIN_FP "]" \
689 "\n ldr r1, [r0, #" #MAIN_SP "]" \
690 "\n mov sp, r1" \
691 \
692 "\n str r0, [sp, #-8]! " \
693 \
694 "\n mov r0, %3" \
695 "\n blx %2" \
696 \
697 "\n ldr r2, [sp], #8 " \
698 \
699 "\n mov r1, sp" \
700 "\n str r11, [r2, #" #MAIN_FP "]" \
701 "\n str r1, [r2, #" #MAIN_SP "]" \
702 \
703 "\n ldr r11, [r2, #" #SUSPENDABLE_FP "]" \
704 "\n ldr r1, [r2, #" #SUSPENDABLE_SP "]" \
705 "\n mov sp, r1" \
706 "\n mov %0, r0" \
707 : "=r"(res) \
708 : "r"(stacks), "r"(fn), "r"(data) \
709 : "r0", "r1", "r2", "r3", "cc", "memory")
710 INLINED_ASM(12, 16, 20, 24);
711
712#elif defined(__loongarch_lp64)
713# define CALLER_SAVED_REGS \
714 "$ra", "$a0", "$a1", "$a2", "$a3", "$a4", "$a5", "$a6", "$a7", \
715 "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8", \
716 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", \
717 "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", \
718 "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23"
719# define INLINED_ASM(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP) \
720 CHECK_OFFSETS(MAIN_FP, MAIN_SP, SUSPENDABLE_FP, SUSPENDABLE_SP); \
721 asm volatile( \
722 "\n move $a0, %1" \
723 "\n st.d $fp, $a0, " #SUSPENDABLE_FP \
724 "\n st.d $sp, $a0, " #SUSPENDABLE_SP \
725 \
726 "\n ld.d $fp, $a0, " #MAIN_FP \
727 "\n ld.d $sp, $a0, " #MAIN_SP \
728 \
729 "\n addi.d $sp, $sp, -16" \
730 "\n st.d $a0, $sp, 8" \
731 \
732 "\n move $a0, %3" \
733 "\n jirl $ra, %2, 0" \
734 \
735 "\n ld.d $a3, $sp, 8" \
736 "\n addi.d $sp, $sp, 16" \
737 \
738 "\n st.d $fp, $a3, " #MAIN_FP \
739 "\n st.d $sp, $a3, " #MAIN_SP \
740 \
741 "\n ld.d $fp, $a3, " #SUSPENDABLE_FP \
742 "\n ld.d $sp, $a3, " #SUSPENDABLE_SP \
743 "\n move %0, $a0" \
744 : "=r"(res) \
745 : "r"(stacks), "r"(fn), "r"(data) \
746 : "$a0", "$a3", CALLER_SAVED_REGS, "cc", "memory")
747 INLINED_ASM(24, 32, 40, 48);
748
749# else
750 MOZ_CRASH("Not supported for this platform")do { do { } while (false); MOZ_ReportCrash("" "Not supported for this platform"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 750); AnnotateMozCrashReason("MOZ_CRASH(" "Not supported for this platform"
")"); do { *((volatile int*)__null) = 750; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
751# endif
752 // clang-format on
753# endif // JS_SIMULATOR
754
755 bool ok = res;
756 suspender->setActive(cx);
757 cx->wasm().promiseIntegration.setActiveSuspender(suspender);
758
759# undef INLINED_ASM
760# undef CHECK_OFFSETS
761# undef CALLER_SAVED_REGS
762
763 return ok;
764}
765
766static void CleanupActiveSuspender(JSContext* cx) {
767 SuspenderObject* suspender = cx->wasm().promiseIntegration.activeSuspender();
768 MOZ_ASSERT(suspender)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(suspender)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(suspender))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("suspender", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 768); AnnotateMozCrashReason("MOZ_ASSERT" "(" "suspender" ")"
); do { *((volatile int*)__null) = 768; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
769 cx->wasm().promiseIntegration.setActiveSuspender(nullptr);
770 suspender->setMoribund(cx);
771}
772
773// Suspending
774
775// Builds a wasm module with following structure:
776// (module
777// (type $params (struct (field ..)*)))
778// (type $results (struct (field ..)*)))
779// (import "" "" (func $suspending.wrappedfn ..))
780// (func $suspending.exported .. )
781// (func $suspending.trampoline ..)
782// (func $suspending.continue-on-suspendable ..)
783// (export "" (func $suspending.exported))
784// )
785//
786// The module provides logic for the state transitions (see the SMDOC):
787// - Invoke Suspending Import via $suspending.exported
788// - Suspending Function Returns a Promise via $suspending.trampoline
789// - Promise Resolved transitions via $suspending.continue-on-suspendable
790//
791class SuspendingFunctionModuleFactory {
792 public:
793 enum TypeIdx {
794 ParamsTypeIndex,
795 ResultsTypeIndex,
796 };
797
798 enum FnIdx {
799 WrappedFnIndex,
800 ExportedFnIndex,
801 TrampolineFnIndex,
802 ContinueOnSuspendableFnIndex
803 };
804
805 private:
806 // Builds function that will be imported to wasm module:
807 // (func $suspending.exported
808 // (param ..)* (result ..)*
809 // (local $suspender externref)
810 // (local $results (ref $results))
811 // call $builtin.current-suspender
812 // local.tee $suspender
813 // ref.func $suspending.trampoline
814 // local.get $i*
815 // stuct.new $param-type
816 // stack-switch SwitchToMain ;; <- (suspender,fn,data)
817 // local.get $suspender
818 // call $builtin.get-suspending-promise-result
819 // ref.cast $results-type
820 // local.set $results
821 // (struct.get $results (local.get $results))*
822 // )
823 bool encodeExportedFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
824 uint32_t resultSize, uint32_t paramsOffset,
825 RefType resultType, Bytes& bytecode) {
826 Encoder encoder(bytecode, *codeMeta.types);
827 ValTypeVector locals;
828 if (!locals.emplaceBack(RefType::extern_())) {
829 return false;
830 }
831 if (!locals.emplaceBack(resultType)) {
832 return false;
833 }
834 if (!EncodeLocalEntries(encoder, locals)) {
835 return false;
836 }
837
838 const int suspenderIndex = paramsSize;
839 if (!encoder.writeOp(Op::I32Const) || !encoder.writeVarU32(0)) {
840 return false;
841 }
842 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
843 !encoder.writeVarU32((uint32_t)BuiltinModuleFuncId::CurrentSuspender)) {
844 return false;
845 }
846 if (!encoder.writeOp(Op::LocalTee) ||
847 !encoder.writeVarU32(suspenderIndex)) {
848 return false;
849 }
850
851 // Results local is located after all params and suspender.
852 const int resultsIndex = paramsSize + 1;
853
854 if (!encoder.writeOp(Op::RefFunc) ||
855 !encoder.writeVarU32(TrampolineFnIndex)) {
856 return false;
857 }
858 for (uint32_t i = 0; i < paramsSize; i++) {
859 if (!encoder.writeOp(Op::LocalGet) ||
860 !encoder.writeVarU32(i + paramsOffset)) {
861 return false;
862 }
863 }
864 if (!encoder.writeOp(GcOp::StructNew) ||
865 !encoder.writeVarU32(ParamsTypeIndex)) {
866 return false;
867 }
868
869 if (!encoder.writeOp(MozOp::StackSwitch) ||
870 !encoder.writeVarU32(uint32_t(StackSwitchKind::SwitchToMain))) {
871 return false;
872 }
873
874 if (!encoder.writeOp(Op::LocalGet) ||
875 !encoder.writeVarU32(suspenderIndex)) {
876 return false;
877 }
878 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
879 !encoder.writeVarU32(
880 (uint32_t)BuiltinModuleFuncId::GetSuspendingPromiseResult)) {
881 return false;
882 }
883 if (!encoder.writeOp(GcOp::RefCast) ||
884 !encoder.writeVarU32(ResultsTypeIndex) ||
885 !encoder.writeOp(Op::LocalSet) || !encoder.writeVarU32(resultsIndex)) {
886 return false;
887 }
888 for (uint32_t i = 0; i < resultSize; i++) {
889 if (!encoder.writeOp(Op::LocalGet) ||
890 !encoder.writeVarU32(resultsIndex) ||
891 !encoder.writeOp(GcOp::StructGet) ||
892 !encoder.writeVarU32(ResultsTypeIndex) || !encoder.writeVarU32(i)) {
893 return false;
894 }
895 }
896 return encoder.writeOp(Op::End);
897 }
898
899 // Builds function that is called on main stack:
900 // (func $suspending.trampoline
901 // (param $params (ref $suspender)) (param $param (ref $param-type))
902 // (result anyref)
903 // local.get $suspender ;; for $builtin.forward-exn-to-suspended below
904 // block (result exnref)
905 // try_table (catch_all_ref 0)
906 // local.get $suspender ;; for call $add-promise-reactions
907 // (struct.get $param-type $i (local.get $param))*
908 // call $suspending.wrappedfn
909 // ref.func $suspending.continue-on-suspendable
910 // call $builtin.add-promise-reactions
911 // return
912 // end
913 // unreachable
914 // end
915 // call $builtin.forward-exn-to-suspended
916 // )
917 // The function calls suspending import and returns into the
918 // $promising.exported function because that was the top function
919 // on the main stack.
920 bool encodeTrampolineFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
921 Bytes& bytecode) {
922 Encoder encoder(bytecode, *codeMeta.types);
923 if (!EncodeLocalEntries(encoder, ValTypeVector())) {
924 return false;
925 }
926 const uint32_t SuspenderIndex = 0;
927 const uint32_t ParamsIndex = 1;
928
929 if (!encoder.writeOp(Op::LocalGet) ||
930 !encoder.writeVarU32(SuspenderIndex)) {
931 return false;
932 }
933
934 if (!encoder.writeOp(Op::Block) ||
935 !encoder.writeFixedU8(uint8_t(TypeCode::ExnRef))) {
936 return false;
937 }
938
939 if (!encoder.writeOp(Op::TryTable) ||
940 !encoder.writeFixedU8(uint8_t(TypeCode::BlockVoid)) ||
941 !encoder.writeVarU32(1) ||
942 !encoder.writeFixedU8(/* catch_all_ref = */ 0x03) ||
943 !encoder.writeVarU32(0)) {
944 return false;
945 }
946
947 // For AddPromiseReactions call below.
948 if (!encoder.writeOp(Op::LocalGet) ||
949 !encoder.writeVarU32(SuspenderIndex)) {
950 return false;
951 }
952
953 for (uint32_t i = 0; i < paramsSize; i++) {
954 if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ParamsIndex)) {
955 return false;
956 }
957 if (!encoder.writeOp(GcOp::StructGet) ||
958 !encoder.writeVarU32(ParamsTypeIndex) || !encoder.writeVarU32(i)) {
959 return false;
960 }
961 }
962 if (!encoder.writeOp(Op::Call) || !encoder.writeVarU32(WrappedFnIndex)) {
963 return false;
964 }
965 if (!encoder.writeOp(Op::RefFunc) ||
966 !encoder.writeVarU32(ContinueOnSuspendableFnIndex)) {
967 return false;
968 }
969
970 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
971 !encoder.writeVarU32(
972 (uint32_t)BuiltinModuleFuncId::AddPromiseReactions)) {
973 return false;
974 }
975
976 if (!encoder.writeOp(Op::Return) || !encoder.writeOp(Op::End) ||
977 !encoder.writeOp(Op::Unreachable) || !encoder.writeOp(Op::End)) {
978 return false;
979 }
980
981 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
982 !encoder.writeVarU32(
983 (uint32_t)BuiltinModuleFuncId::ForwardExceptionToSuspended)) {
984 return false;
985 }
986
987 return encoder.writeOp(Op::End);
988 }
989
990 // Builds function that is called on main stack:
991 // (func $suspending.continue-on-suspendable
992 // (param $params (ref $suspender)) (param $results externref)
993 // (result externref)
994 // local.get $suspender
995 // ref.null funcref
996 // local.get $results
997 // any.convert_extern
998 // stack-switch ContinueOnSuspendable
999 // )
1000 bool encodeContinueOnSuspendableFunction(CodeMetadata& codeMeta,
1001 uint32_t resultsSize,
1002 Bytes& bytecode) {
1003 Encoder encoder(bytecode, *codeMeta.types);
1004 if (!EncodeLocalEntries(encoder, ValTypeVector())) {
1005 return false;
1006 }
1007
1008 const uint32_t SuspenderIndex = 0;
1009 const uint32_t ResultsIndex = 1;
1010
1011 if (!encoder.writeOp(Op::LocalGet) ||
1012 !encoder.writeVarU32(SuspenderIndex)) {
1013 return false;
1014 }
1015 if (!encoder.writeOp(Op::RefNull) ||
1016 !encoder.writeValType(ValType(RefType::func()))) {
1017 return false;
1018 }
1019 if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ResultsIndex) ||
1020 !encoder.writeOp(GcOp::AnyConvertExtern)) {
1021 return false;
1022 }
1023
1024 if (!encoder.writeOp(MozOp::StackSwitch) ||
1025 !encoder.writeVarU32(
1026 uint32_t(StackSwitchKind::ContinueOnSuspendable))) {
1027 return false;
1028 }
1029
1030 return encoder.writeOp(Op::End);
1031 }
1032
1033 public:
1034 SharedModule build(JSContext* cx, HandleObject func, ValTypeVector&& params,
1035 ValTypeVector&& results) {
1036 FeatureOptions options;
1037 options.isBuiltinModule = true;
1038 options.requireExnref = true;
1039
1040 ScriptedCaller scriptedCaller;
1041 SharedCompileArgs compileArgs =
1042 CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
1043 if (!compileArgs) {
1044 return nullptr;
1045 }
1046
1047 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
1048 if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
1049 return nullptr;
1050 }
1051 MutableCodeMetadata codeMeta = moduleMeta->codeMeta;
1052
1053 MOZ_ASSERT(IonPlatformSupport())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IonPlatformSupport())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IonPlatformSupport()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IonPlatformSupport()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IonPlatformSupport()"
")"); do { *((volatile int*)__null) = 1053; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1054 CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
1055 DebugEnabled::False);
1056 compilerEnv.computeParameters();
1057
1058 RefType suspenderType = RefType::extern_();
1059 RefType promiseType = RefType::extern_();
1060
1061 ValTypeVector paramsWithoutSuspender;
1062
1063 const size_t resultsSize = results.length();
1064 const size_t paramsSize = params.length();
1065 const size_t paramsOffset = 0;
1066 if (!paramsWithoutSuspender.append(params.begin(), params.end())) {
1067 ReportOutOfMemory(cx);
1068 return nullptr;
1069 }
1070
1071 ValTypeVector resultsRef;
1072 if (!resultsRef.emplaceBack(promiseType)) {
1073 ReportOutOfMemory(cx);
1074 return nullptr;
1075 }
1076
1077 StructType boxedParamsStruct;
1078 if (!StructType::createImmutable(paramsWithoutSuspender,
1079 &boxedParamsStruct)) {
1080 ReportOutOfMemory(cx);
1081 return nullptr;
1082 }
1083 MOZ_ASSERT(codeMeta->types->length() == ParamsTypeIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->types->length() == ParamsTypeIndex)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->types->length() == ParamsTypeIndex))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->types->length() == ParamsTypeIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1083); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->types->length() == ParamsTypeIndex"
")"); do { *((volatile int*)__null) = 1083; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1084 if (!codeMeta->types->addType(std::move(boxedParamsStruct))) {
1085 return nullptr;
1086 }
1087
1088 StructType boxedResultType;
1089 if (!StructType::createImmutable(results, &boxedResultType)) {
1090 ReportOutOfMemory(cx);
1091 return nullptr;
1092 }
1093 MOZ_ASSERT(codeMeta->types->length() == ResultsTypeIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->types->length() == ResultsTypeIndex)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->types->length() == ResultsTypeIndex)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->types->length() == ResultsTypeIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1093); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->types->length() == ResultsTypeIndex"
")"); do { *((volatile int*)__null) = 1093; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1094 if (!codeMeta->types->addType(std::move(boxedResultType))) {
1095 return nullptr;
1096 }
1097
1098 MOZ_ASSERT(codeMeta->funcs.length() == WrappedFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == WrappedFnIndex)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == WrappedFnIndex))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == WrappedFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1098); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == WrappedFnIndex"
")"); do { *((volatile int*)__null) = 1098; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1099 if (!moduleMeta->addDefinedFunc(std::move(paramsWithoutSuspender),
1100 std::move(resultsRef))) {
1101 return nullptr;
1102 }
1103
1104 // Imports names are not important, declare functions above as imports.
1105 codeMeta->numFuncImports = codeMeta->funcs.length();
1106
1107 // We will be looking up and using the exports function by index so
1108 // the name doesn't matter.
1109 MOZ_ASSERT(codeMeta->funcs.length() == ExportedFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == ExportedFnIndex)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == ExportedFnIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == ExportedFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == ExportedFnIndex"
")"); do { *((volatile int*)__null) = 1109; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1110 if (!moduleMeta->addDefinedFunc(std::move(params), std::move(results),
1111 /*declareForRef = */ true,
1112 mozilla::Some(CacheableName()))) {
1113 return nullptr;
1114 }
1115
1116 ValTypeVector paramsTrampoline, resultsTrampoline;
1117 if (!paramsTrampoline.emplaceBack(suspenderType) ||
1118 !paramsTrampoline.emplaceBack(RefType::fromTypeDef(
1119 &(*codeMeta->types)[ParamsTypeIndex], false)) ||
1120 !resultsTrampoline.emplaceBack(RefType::any())) {
1121 ReportOutOfMemory(cx);
1122 return nullptr;
1123 }
1124 MOZ_ASSERT(codeMeta->funcs.length() == TrampolineFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == TrampolineFnIndex)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == TrampolineFnIndex))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == TrampolineFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1124); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == TrampolineFnIndex"
")"); do { *((volatile int*)__null) = 1124; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1125 if (!moduleMeta->addDefinedFunc(std::move(paramsTrampoline),
1126 std::move(resultsTrampoline),
1127 /*declareForRef = */ true)) {
1128 return nullptr;
1129 }
1130
1131 ValTypeVector paramsContinueOnSuspendable, resultsContinueOnSuspendable;
1132 if (!paramsContinueOnSuspendable.emplaceBack(suspenderType) ||
1133 !paramsContinueOnSuspendable.emplaceBack(RefType::extern_())) {
1134 ReportOutOfMemory(cx);
1135 return nullptr;
1136 }
1137 MOZ_ASSERT(codeMeta->funcs.length() == ContinueOnSuspendableFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == ContinueOnSuspendableFnIndex
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == ContinueOnSuspendableFnIndex
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"codeMeta->funcs.length() == ContinueOnSuspendableFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == ContinueOnSuspendableFnIndex"
")"); do { *((volatile int*)__null) = 1137; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1138 if (!moduleMeta->addDefinedFunc(std::move(paramsContinueOnSuspendable),
1139 std::move(resultsContinueOnSuspendable),
1140 /*declareForRef = */ true)) {
1141 return nullptr;
1142 }
1143
1144 if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
1145 return nullptr;
1146 }
1147
1148 ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(),
1149 nullptr, nullptr, nullptr);
1150 if (!mg.initializeCompleteTier()) {
1151 return nullptr;
1152 }
1153 // Build functions and keep bytecodes around until the end.
1154 uint32_t funcBytecodeOffset = CallSite::FIRST_VALID_BYTECODE_OFFSET;
1155 Bytes bytecode;
1156 if (!encodeExportedFunction(
1157 *codeMeta, paramsSize, resultsSize, paramsOffset,
1158 RefType::fromTypeDef(&(*codeMeta->types)[ResultsTypeIndex], false),
1159 bytecode)) {
1160 ReportOutOfMemory(cx);
1161 return nullptr;
1162 }
1163 if (!mg.compileFuncDef(ExportedFnIndex, funcBytecodeOffset,
1164 bytecode.begin(),
1165 bytecode.begin() + bytecode.length())) {
1166 return nullptr;
1167 }
1168 funcBytecodeOffset += bytecode.length();
1169
1170 Bytes bytecode2;
1171 if (!encodeTrampolineFunction(*codeMeta, paramsSize, bytecode2)) {
1172 ReportOutOfMemory(cx);
1173 return nullptr;
1174 }
1175 if (!mg.compileFuncDef(TrampolineFnIndex, funcBytecodeOffset,
1176 bytecode2.begin(),
1177 bytecode2.begin() + bytecode2.length())) {
1178 return nullptr;
1179 }
1180 funcBytecodeOffset += bytecode2.length();
1181
1182 Bytes bytecode3;
1183 if (!encodeContinueOnSuspendableFunction(*codeMeta, paramsSize,
1184 bytecode3)) {
1185 ReportOutOfMemory(cx);
1186 return nullptr;
1187 }
1188 if (!mg.compileFuncDef(ContinueOnSuspendableFnIndex, funcBytecodeOffset,
1189 bytecode3.begin(),
1190 bytecode3.begin() + bytecode3.length())) {
1191 return nullptr;
1192 }
1193 funcBytecodeOffset += bytecode3.length();
1194
1195 if (!mg.finishFuncDefs()) {
1196 return nullptr;
1197 }
1198
1199 SharedBytes shareableBytes = js_new<ShareableBytes>();
1200 if (!shareableBytes) {
1201 ReportOutOfMemory(cx);
1202 return nullptr;
1203 }
1204 return mg.finishModule(*shareableBytes, moduleMeta,
1205 /*maybeCompleteTier2Listener=*/nullptr);
1206 }
1207};
1208
1209// Reaction on resolved/rejected suspending promise.
1210static bool WasmPISuspendTaskContinue(JSContext* cx, unsigned argc, Value* vp) {
1211 CallArgs args = CallArgsFromVp(argc, vp);
1212 // The arg[0] has result of resolved promise, or rejection reason.
1213 Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
1214 RootedValue suspender(cx, callee->getExtendedSlot(SUSPENDER_SLOT));
1215 RootedValue suspendingPromise(cx, callee->getExtendedSlot(PROMISE_SLOT));
1216
1217 // Convert result of the promise into the parameters/arguments for the
1218 // $suspending.continue-on-suspendable.
1219 RootedFunction continueOnSuspendable(
1220 cx, &callee->getExtendedSlot(CONTINUE_ON_SUSPENDABLE_SLOT)
1221 .toObject()
1222 .as<JSFunction>());
1223 JS::RootedValueArray<2> argv(cx);
1224 argv[0].set(suspender);
1225 argv[1].set(suspendingPromise);
1226
1227 JS::Rooted<JS::Value> rval(cx);
1228 if (Call(cx, UndefinedHandleValue, continueOnSuspendable, argv, &rval)) {
1229 return true;
1230 }
1231
1232 // The stack was unwound during exception -- time to release resources.
1233 CleanupActiveSuspender(cx);
1234
1235 if (cx->isThrowingOutOfMemory()) {
1236 return false;
1237 }
1238 Rooted<PromiseObject*> promise(
1239 cx, suspender.toObject().as<SuspenderObject>().promisingPromise());
1240 return RejectPromiseWithPendingError(cx, promise);
1241}
1242
1243static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
1244 if (!aValue.isObject()) {
1245 return false;
1246 }
1247
1248 // We only care about Promise here, so CheckedUnwrapStatic is fine.
1249 JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(&aValue.toObject()));
1250 if (!obj) {
1251 return false;
1252 }
1253
1254 return JS::IsPromiseObject(obj);
1255}
1256
1257// Wraps original import to catch all exceptions and convert result to a
1258// promise.
1259// Seen as $suspending.wrappedfn in wasm.
1260static bool WasmPIWrapSuspendingImport(JSContext* cx, unsigned argc,
1261 Value* vp) {
1262 CallArgs args = CallArgsFromVp(argc, vp);
1263 Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
1264 RootedValue originalImportFunc(cx, callee->getExtendedSlot(WRAPPED_FN_SLOT));
1265
1266 // Catching exceptions here.
1267 RootedValue rval(cx);
1268 if (Call(cx, UndefinedHandleValue, originalImportFunc, args, &rval)) {
1269 // Convert the result to a resolved promise later in AddPromiseReactions.
1270 args.rval().set(rval);
1271 return true;
1272 }
1273
1274 // Deferring pending exception to the handler in the
1275 // $suspending.trampoline.
1276 return false;
1277}
1278
1279JSFunction* WasmSuspendingFunctionCreate(JSContext* cx, HandleObject func,
1280 ValTypeVector&& params,
1281 ValTypeVector&& results) {
1282 MOZ_ASSERT(IsCallable(ObjectValue(*func)) &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper
(func))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper
(func)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper(func)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper(func)"
")"); do { *((volatile int*)__null) = 1283; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1283 !IsCrossCompartmentWrapper(func))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper
(func))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper
(func)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper(func)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsCallable(ObjectValue(*func)) && !IsCrossCompartmentWrapper(func)"
")"); do { *((volatile int*)__null) = 1283; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1284
1285 SuspendingFunctionModuleFactory moduleFactory;
1286 SharedModule module =
1287 moduleFactory.build(cx, func, std::move(params), std::move(results));
1288 if (!module) {
1289 return nullptr;
1290 }
1291
1292 // Instantiate the module.
1293 Rooted<ImportValues> imports(cx);
1294
1295 // Add $suspending.wrappedfn to imports.
1296 RootedFunction funcWrapper(
1297 cx, NewNativeFunction(cx, WasmPIWrapSuspendingImport, 0, nullptr,
1298 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1299 if (!funcWrapper) {
1300 return nullptr;
1301 }
1302 funcWrapper->initExtendedSlot(WRAPPED_FN_SLOT, ObjectValue(*func));
1303 if (!imports.get().funcs.append(funcWrapper)) {
1304 ReportOutOfMemory(cx);
1305 return nullptr;
1306 }
1307
1308 Rooted<WasmInstanceObject*> instance(cx);
1309 if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
1310 // Can also trap on invalid input function.
1311 return nullptr;
1312 }
1313
1314 // Returns the $suspending.exported function.
1315 RootedFunction wasmFunc(cx);
1316 if (!WasmInstanceObject::getExportedFunction(
1317 cx, instance, SuspendingFunctionModuleFactory::ExportedFnIndex,
1318 &wasmFunc)) {
1319 return nullptr;
1320 }
1321 return wasmFunc;
1322}
1323
1324JSFunction* WasmSuspendingFunctionCreate(JSContext* cx, HandleObject func,
1325 const FuncType& type) {
1326 ValTypeVector params, results;
1327 if (!params.append(type.args().begin(), type.args().end()) ||
1328 !results.append(type.results().begin(), type.results().end())) {
1329 ReportOutOfMemory(cx);
1330 return nullptr;
1331 }
1332 return WasmSuspendingFunctionCreate(cx, func, std::move(params),
1333 std::move(results));
1334}
1335
1336// Promising
1337
1338// Builds a wasm module with following structure:
1339// (module
1340// (type $params (struct (field ..)*))
1341// (type $results (struct (field ..)*))
1342// (type $create-suspender-result (struct (field externref externref)))
1343// (import "" "" (func $promising.wrappedfn ..))
1344// (func $promising.exported .. )
1345// (func $promising.trampoline ..)
1346// (export "" (func $promising.exported))
1347// )
1348//
1349// The module provides logic for the Invoke Promising Import state transition
1350// via $promising.exported and $promising.trampoline (see the SMDOC).
1351//
1352class PromisingFunctionModuleFactory {
1353 public:
1354 enum TypeIdx {
1355 ParamsTypeIndex,
1356 ResultsTypeIndex,
1357 };
1358
1359 enum FnIdx {
1360 WrappedFnIndex,
1361 ExportedFnIndex,
1362 TrampolineFnIndex,
1363 };
1364
1365 private:
1366 // Builds function that will be exported for JS:
1367 // (func $promising.exported
1368 // (param ..)* (result externref)
1369 // (local $suspender externref)
1370 // call $builtin.create-suspender
1371 // local.tee $suspender
1372 // call $builtin.create-promising-promise ;; -> (promise)
1373 // local.get $suspender
1374 // ref.func $promising.trampoline
1375 // local.get $i*
1376 // stuct.new $param-type
1377 // stack-switch SwitchToSuspendable ;; <- (suspender,fn,data)
1378 // )
1379 bool encodeExportedFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
1380 Bytes& bytecode) {
1381 Encoder encoder(bytecode, *codeMeta.types);
1382 ValTypeVector locals;
1383 if (!locals.emplaceBack(RefType::extern_())) {
1384 return false;
1385 }
1386 if (!EncodeLocalEntries(encoder, locals)) {
1387 return false;
1388 }
1389
1390 const uint32_t SuspenderIndex = paramsSize;
1391 if (!encoder.writeOp(Op::I32Const) || !encoder.writeVarU32(0)) {
1392 return false;
1393 }
1394 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
1395 !encoder.writeVarU32((uint32_t)BuiltinModuleFuncId::CreateSuspender)) {
1396 return false;
1397 }
1398
1399 if (!encoder.writeOp(Op::LocalTee) ||
1400 !encoder.writeVarU32(SuspenderIndex)) {
1401 return false;
1402 }
1403 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
1404 !encoder.writeVarU32(
1405 (uint32_t)BuiltinModuleFuncId::CreatePromisingPromise)) {
1406 return false;
1407 }
1408
1409 if (!encoder.writeOp(Op::LocalGet) ||
1410 !encoder.writeVarU32(SuspenderIndex)) {
1411 return false;
1412 }
1413 if (!encoder.writeOp(Op::RefFunc) ||
1414 !encoder.writeVarU32(TrampolineFnIndex)) {
1415 return false;
1416 }
1417 for (uint32_t i = 0; i < paramsSize; i++) {
1418 if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(i)) {
1419 return false;
1420 }
1421 }
1422 if (!encoder.writeOp(GcOp::StructNew) ||
1423 !encoder.writeVarU32(ParamsTypeIndex)) {
1424 return false;
1425 }
1426 if (!encoder.writeOp(MozOp::StackSwitch) ||
1427 !encoder.writeVarU32(uint32_t(StackSwitchKind::SwitchToSuspendable))) {
1428 return false;
1429 }
1430
1431 return encoder.writeOp(Op::End);
1432 }
1433
1434 // Builds function that is called on alternative stack:
1435 // (func $promising.trampoline
1436 // (param $suspender externref) (param $params (ref $param-type))
1437 // (result externref)
1438 // local.get $suspender ;; for call $set-results
1439 // (local.get $suspender)?
1440 // (struct.get $param-type $i (local.get $param))*
1441 // (local.get $suspender)?
1442 // call $promising.wrappedfn
1443 // struct.new $result-type
1444 // call $builtin.set-promising-promise-results
1445 // )
1446 bool encodeTrampolineFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
1447 Bytes& bytecode) {
1448 Encoder encoder(bytecode, *codeMeta.types);
1449 if (!EncodeLocalEntries(encoder, ValTypeVector())) {
1450 return false;
1451 }
1452 const uint32_t SuspenderIndex = 0;
1453 const uint32_t ParamsIndex = 1;
1454
1455 // Reserved for SetResultsFnIndex call at the end
1456 if (!encoder.writeOp(Op::LocalGet) ||
1457 !encoder.writeVarU32(SuspenderIndex)) {
1458 return false;
1459 }
1460
1461 for (uint32_t i = 0; i < paramsSize; i++) {
1462 if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ParamsIndex)) {
1463 return false;
1464 }
1465 if (!encoder.writeOp(GcOp::StructGet) ||
1466 !encoder.writeVarU32(ParamsTypeIndex) || !encoder.writeVarU32(i)) {
1467 return false;
1468 }
1469 }
1470 if (!encoder.writeOp(Op::Call) || !encoder.writeVarU32(WrappedFnIndex)) {
1471 return false;
1472 }
1473
1474 if (!encoder.writeOp(GcOp::StructNew) ||
1475 !encoder.writeVarU32(ResultsTypeIndex)) {
1476 return false;
1477 }
1478 if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
1479 !encoder.writeVarU32(
1480 (uint32_t)BuiltinModuleFuncId::SetPromisingPromiseResults)) {
1481 return false;
1482 }
1483
1484 return encoder.writeOp(Op::End);
1485 }
1486
1487 public:
1488 SharedModule build(JSContext* cx, HandleFunction fn, ValTypeVector&& params,
1489 ValTypeVector&& results) {
1490 const FuncType& fnType = fn->wasmTypeDef()->funcType();
1491 size_t paramsSize = params.length();
1492
1493 RefType suspenderType = RefType::extern_();
1494
1495 FeatureOptions options;
1496 options.isBuiltinModule = true;
1497
1498 ScriptedCaller scriptedCaller;
1499 SharedCompileArgs compileArgs =
1500 CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
1501 if (!compileArgs) {
1502 return nullptr;
1503 }
1504
1505 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
1506 if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
1507 return nullptr;
1508 }
1509 MutableCodeMetadata codeMeta = moduleMeta->codeMeta;
1510
1511 MOZ_ASSERT(IonPlatformSupport())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IonPlatformSupport())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IonPlatformSupport()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IonPlatformSupport()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IonPlatformSupport()"
")"); do { *((volatile int*)__null) = 1511; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1512 CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
1513 DebugEnabled::False);
1514 compilerEnv.computeParameters();
1515
1516 StructType boxedParamsStruct;
1517 if (!StructType::createImmutable(params, &boxedParamsStruct)) {
1518 ReportOutOfMemory(cx);
1519 return nullptr;
1520 }
1521 MOZ_ASSERT(codeMeta->types->length() == ParamsTypeIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->types->length() == ParamsTypeIndex)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->types->length() == ParamsTypeIndex))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->types->length() == ParamsTypeIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->types->length() == ParamsTypeIndex"
")"); do { *((volatile int*)__null) = 1521; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1522 if (!codeMeta->types->addType(std::move(boxedParamsStruct))) {
1523 return nullptr;
1524 }
1525
1526 StructType boxedResultType;
1527 if (!StructType::createImmutable(fnType.results(), &boxedResultType)) {
1528 ReportOutOfMemory(cx);
1529 return nullptr;
1530 }
1531 MOZ_ASSERT(codeMeta->types->length() == ResultsTypeIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->types->length() == ResultsTypeIndex)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->types->length() == ResultsTypeIndex)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->types->length() == ResultsTypeIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1531); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->types->length() == ResultsTypeIndex"
")"); do { *((volatile int*)__null) = 1531; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1532 if (!codeMeta->types->addType(std::move(boxedResultType))) {
1533 return nullptr;
1534 }
1535
1536 ValTypeVector paramsForWrapper, resultsForWrapper;
1537 if (!paramsForWrapper.append(fnType.args().begin(), fnType.args().end()) ||
1538 !resultsForWrapper.append(fnType.results().begin(),
1539 fnType.results().end())) {
1540 ReportOutOfMemory(cx);
1541 return nullptr;
1542 }
1543 MOZ_ASSERT(codeMeta->funcs.length() == WrappedFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == WrappedFnIndex)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == WrappedFnIndex))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == WrappedFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1543); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == WrappedFnIndex"
")"); do { *((volatile int*)__null) = 1543; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1544 if (!moduleMeta->addDefinedFunc(std::move(paramsForWrapper),
1545 std::move(resultsForWrapper))) {
1546 return nullptr;
1547 }
1548
1549 // Imports names are not important, declare functions above as imports.
1550 codeMeta->numFuncImports = codeMeta->funcs.length();
1551
1552 // We will be looking up and using the exports function by index so
1553 // the name doesn't matter.
1554 MOZ_ASSERT(codeMeta->funcs.length() == ExportedFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == ExportedFnIndex)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == ExportedFnIndex))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == ExportedFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1554); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == ExportedFnIndex"
")"); do { *((volatile int*)__null) = 1554; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1555 if (!moduleMeta->addDefinedFunc(std::move(params), std::move(results),
1556 /* declareFoRef = */ true,
1557 mozilla::Some(CacheableName()))) {
1558 return nullptr;
1559 }
1560
1561 ValTypeVector paramsTrampoline, resultsTrampoline;
1562 if (!paramsTrampoline.emplaceBack(suspenderType) ||
1563 !paramsTrampoline.emplaceBack(RefType::fromTypeDef(
1564 &(*codeMeta->types)[ParamsTypeIndex], false))) {
1565 ReportOutOfMemory(cx);
1566 return nullptr;
1567 }
1568 MOZ_ASSERT(codeMeta->funcs.length() == TrampolineFnIndex)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(codeMeta->funcs.length() == TrampolineFnIndex)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(codeMeta->funcs.length() == TrampolineFnIndex))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("codeMeta->funcs.length() == TrampolineFnIndex"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "codeMeta->funcs.length() == TrampolineFnIndex"
")"); do { *((volatile int*)__null) = 1568; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1569 if (!moduleMeta->addDefinedFunc(std::move(paramsTrampoline),
1570 std::move(resultsTrampoline),
1571 /* declareFoRef = */ true)) {
1572 return nullptr;
1573 }
1574
1575 if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
1576 return nullptr;
1577 }
1578
1579 ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(),
1580 nullptr, nullptr, nullptr);
1581 if (!mg.initializeCompleteTier()) {
1582 return nullptr;
1583 }
1584 // Build functions and keep bytecodes around until the end.
1585 Bytes bytecode;
1586 uint32_t funcBytecodeOffset = CallSite::FIRST_VALID_BYTECODE_OFFSET;
1587 if (!encodeExportedFunction(*codeMeta, paramsSize, bytecode)) {
1588 ReportOutOfMemory(cx);
1589 return nullptr;
1590 }
1591 if (!mg.compileFuncDef(ExportedFnIndex, funcBytecodeOffset,
1592 bytecode.begin(),
1593 bytecode.begin() + bytecode.length())) {
1594 return nullptr;
1595 }
1596 funcBytecodeOffset += bytecode.length();
1597
1598 Bytes bytecode2;
1599 if (!encodeTrampolineFunction(*codeMeta, paramsSize, bytecode2)) {
1600 ReportOutOfMemory(cx);
1601 return nullptr;
1602 }
1603 if (!mg.compileFuncDef(TrampolineFnIndex, funcBytecodeOffset,
1604 bytecode2.begin(),
1605 bytecode2.begin() + bytecode2.length())) {
1606 return nullptr;
1607 }
1608 funcBytecodeOffset += bytecode2.length();
Value stored to 'funcBytecodeOffset' is never read
1609
1610 if (!mg.finishFuncDefs()) {
1611 return nullptr;
1612 }
1613
1614 SharedBytes shareableBytes = js_new<ShareableBytes>();
1615 if (!shareableBytes) {
1616 ReportOutOfMemory(cx);
1617 return nullptr;
1618 }
1619 return mg.finishModule(*shareableBytes, moduleMeta,
1620 /*maybeCompleteTier2Listener=*/nullptr);
1621 }
1622};
1623
1624// Wraps call to wasm $promising.exported function to catch an exception and
1625// return a promise instead.
1626static bool WasmPIPromisingFunction(JSContext* cx, unsigned argc, Value* vp) {
1627 CallArgs args = CallArgsFromVp(argc, vp);
1628 Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
1629 RootedFunction fn(
1630 cx,
1631 &callee->getExtendedSlot(WRAPPED_FN_SLOT).toObject().as<JSFunction>());
1632
1633 // Catching exceptions here.
1634 if (Call(cx, UndefinedHandleValue, fn, args, args.rval())) {
1635 return true;
1636 }
1637
1638 // During an exception the stack was unwound -- time to release resources.
1639 CleanupActiveSuspender(cx);
1640
1641 if (cx->isThrowingOutOfMemory()) {
1642 return false;
1643 }
1644
1645 RootedObject promiseObject(cx, NewPromiseObject(cx, nullptr));
1646 if (!promiseObject) {
1647 return false;
1648 }
1649 args.rval().setObject(*promiseObject);
1650
1651 Rooted<PromiseObject*> promise(cx, &promiseObject->as<PromiseObject>());
1652 return RejectPromiseWithPendingError(cx, promise);
1653}
1654
1655JSFunction* WasmPromisingFunctionCreate(JSContext* cx, HandleObject func,
1656 ValTypeVector&& params,
1657 ValTypeVector&& results) {
1658 RootedFunction wrappedWasmFunc(cx, &func->as<JSFunction>());
1659 MOZ_ASSERT(wrappedWasmFunc->isWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wrappedWasmFunc->isWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(wrappedWasmFunc->isWasm()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"wrappedWasmFunc->isWasm()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1659); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wrappedWasmFunc->isWasm()"
")"); do { *((volatile int*)__null) = 1659; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1660 const FuncType& wrappedWasmFuncType =
1661 wrappedWasmFunc->wasmTypeDef()->funcType();
1662
1663 MOZ_ASSERT(results.length() == 0 && params.length() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(results.length() == 0 && params.length() == 0
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(results.length() == 0 && params.length() == 0
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"results.length() == 0 && params.length() == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1663); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 0 && params.length() == 0"
")"); do { *((volatile int*)__null) = 1663; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1664 if (!results.append(RefType::extern_())) {
1665 ReportOutOfMemory(cx);
1666 return nullptr;
1667 }
1668 if (!params.append(wrappedWasmFuncType.args().begin(),
1669 wrappedWasmFuncType.args().end())) {
1670 ReportOutOfMemory(cx);
1671 return nullptr;
1672 }
1673
1674 PromisingFunctionModuleFactory moduleFactory;
1675 SharedModule module = moduleFactory.build(
1676 cx, wrappedWasmFunc, std::move(params), std::move(results));
1677 // Instantiate the module.
1678 Rooted<ImportValues> imports(cx);
1679
1680 // Add wrapped function ($promising.wrappedfn) to imports.
1681 if (!imports.get().funcs.append(func)) {
1682 ReportOutOfMemory(cx);
1683 return nullptr;
1684 }
1685
1686 Rooted<WasmInstanceObject*> instance(cx);
1687 if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
1688 MOZ_ASSERT(cx->isThrowingOutOfMemory())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cx->isThrowingOutOfMemory())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(cx->isThrowingOutOfMemory
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("cx->isThrowingOutOfMemory()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1688); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cx->isThrowingOutOfMemory()"
")"); do { *((volatile int*)__null) = 1688; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1689 return nullptr;
1690 }
1691
1692 // Wrap $promising.exported function for exceptions/traps handling.
1693 RootedFunction wasmFunc(cx);
1694 if (!WasmInstanceObject::getExportedFunction(
1695 cx, instance, PromisingFunctionModuleFactory::ExportedFnIndex,
1696 &wasmFunc)) {
1697 return nullptr;
1698 }
1699
1700 RootedFunction wasmFuncWrapper(
1701 cx, NewNativeFunction(cx, WasmPIPromisingFunction, 0, nullptr,
1702 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1703 if (!wasmFuncWrapper) {
1704 return nullptr;
1705 }
1706 wasmFuncWrapper->initExtendedSlot(WRAPPED_FN_SLOT, ObjectValue(*wasmFunc));
1707 return wasmFuncWrapper;
1708}
1709
1710// Gets active suspender.
1711// The reserved parameter is a workaround for limitation in the
1712// WasmBuiltinModule.yaml generator to always have params.
1713// Seen as $builtin.current-suspender to wasm.
1714SuspenderObject* CurrentSuspender(Instance* instance, int32_t reserved) {
1715 MOZ_ASSERT(SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1715; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1716 JSContext* cx = instance->cx();
1717 SuspenderObject* suspender = cx->wasm().promiseIntegration.activeSuspender();
1718 if (!suspender) {
1719 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1720 JSMSG_JSPI_INVALID_STATE);
1721 return nullptr;
1722 }
1723 return suspender;
1724}
1725
1726// Creates a suspender and promise (that will be returned to JS code).
1727// Seen as $builtin.create-suspender to wasm.
1728SuspenderObject* CreateSuspender(Instance* instance, int32_t reserved) {
1729 MOZ_ASSERT(SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1729); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1729; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1730 JSContext* cx = instance->cx();
1731 return SuspenderObject::create(cx);
1732}
1733
1734// Creates a promise that will be returned at promising call.
1735// Seen as $builtin.create-promising-promise to wasm.
1736PromiseObject* CreatePromisingPromise(Instance* instance,
1737 SuspenderObject* suspender) {
1738 MOZ_ASSERT(SASigCreatePromisingPromise.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigCreatePromisingPromise.failureMode == FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(SASigCreatePromisingPromise.failureMode
== FailureMode::FailOnNullPtr))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("SASigCreatePromisingPromise.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigCreatePromisingPromise.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1739; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1739 FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigCreatePromisingPromise.failureMode == FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(SASigCreatePromisingPromise.failureMode
== FailureMode::FailOnNullPtr))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("SASigCreatePromisingPromise.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigCreatePromisingPromise.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1739; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1740 JSContext* cx = instance->cx();
1741
1742 Rooted<SuspenderObject*> suspenderObject(cx, suspender);
1743 RootedObject promiseObject(cx, NewPromiseObject(cx, nullptr));
1744 if (!promiseObject) {
1745 return nullptr;
1746 }
1747
1748 Rooted<PromiseObject*> promise(cx, &promiseObject->as<PromiseObject>());
1749 suspenderObject->setPromisingPromise(promise);
1750 return promise.get();
1751}
1752
1753// Converts promise results into actual function result, or exception/trap
1754// if rejected.
1755// Seen as $builtin.get-suspending-promise-result to wasm.
1756JSObject* GetSuspendingPromiseResult(Instance* instance, void* result,
1757 SuspenderObject* suspender) {
1758 MOZ_ASSERT(SASigGetSuspendingPromiseResult.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigGetSuspendingPromiseResult.failureMode == FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(SASigGetSuspendingPromiseResult
.failureMode == FailureMode::FailOnNullPtr))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigGetSuspendingPromiseResult.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1759); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigGetSuspendingPromiseResult.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1759; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1759 FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigGetSuspendingPromiseResult.failureMode == FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(SASigGetSuspendingPromiseResult
.failureMode == FailureMode::FailOnNullPtr))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigGetSuspendingPromiseResult.failureMode == FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1759); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigGetSuspendingPromiseResult.failureMode == FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 1759; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1760 JSContext* cx = instance->cx();
1761 Rooted<SuspenderObject*> suspenderObject(cx, suspender);
1762 RootedAnyRef resultRef(cx, AnyRef::fromCompiledCode(result));
1763
1764 SuspenderObject::ReturnType returnType =
1765 suspenderObject->suspendingReturnType();
1766 MOZ_ASSERT(returnType != SuspenderObject::ReturnType::Unknown)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(returnType != SuspenderObject::ReturnType::Unknown)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(returnType != SuspenderObject::ReturnType::Unknown))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("returnType != SuspenderObject::ReturnType::Unknown"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "returnType != SuspenderObject::ReturnType::Unknown"
")"); do { *((volatile int*)__null) = 1766; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1767 Rooted<PromiseObject*> promise(
1768 cx, returnType == SuspenderObject::ReturnType::Promise
1769 ? &resultRef.toJSObject().as<PromiseObject>()
1770 : nullptr);
1771
1772# ifdef DEBUG1
1773 auto resetReturnType = mozilla::MakeScopeExit([&suspenderObject]() {
1774 suspenderObject->setSuspendingReturnType(
1775 SuspenderObject::ReturnType::Unknown);
1776 });
1777# endif
1778
1779 if (promise ? promise->state() == JS::PromiseState::Rejected
1780 : returnType == SuspenderObject::ReturnType::Exception) {
1781 // Promise was rejected or an exception was thrown, set pending exception
1782 // and fail.
1783 RootedValue reason(
1784 cx, promise ? promise->reason() : resultRef.get().toJSValue());
1785 cx->setPendingException(reason, ShouldCaptureStack::Maybe);
1786 return nullptr;
1787 }
1788
1789 // Construct the results object.
1790 Rooted<WasmStructObject*> results(
1791 cx, instance->constantStructNewDefault(
1792 cx, SuspendingFunctionModuleFactory::ResultsTypeIndex));
1793 const FieldTypeVector& fields = results->typeDef().structType().fields_;
1794
1795 if (fields.length() > 0) {
1796 MOZ_ASSERT_IF(promise, promise->state() == JS::PromiseState::Fulfilled)do { if (promise) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(promise->state() == JS::PromiseState::Fulfilled
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(promise->state() == JS::PromiseState::Fulfilled))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("promise->state() == JS::PromiseState::Fulfilled"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1796); AnnotateMozCrashReason("MOZ_ASSERT" "(" "promise->state() == JS::PromiseState::Fulfilled"
")"); do { *((volatile int*)__null) = 1796; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1797 RootedValue jsValue(cx);
1798 if (promise) {
1799 jsValue.set(promise->value());
1800 } else {
1801 jsValue.set(resultRef.get().toJSValue());
1802 }
1803
1804 // The struct object is constructed based on returns of exported function.
1805 // It is the only way we can get ValType for Val::fromJSValue call.
1806 const wasm::FuncType& sig = instance->codeMeta().getFuncType(
1807 SuspendingFunctionModuleFactory::ExportedFnIndex);
1808
1809 if (fields.length() == 1) {
1810 RootedVal val(cx);
1811 MOZ_ASSERT(sig.result(0).storageType() == fields[0].type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sig.result(0).storageType() == fields[0].type)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sig.result(0).storageType() == fields[0].type))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sig.result(0).storageType() == fields[0].type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1811); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sig.result(0).storageType() == fields[0].type"
")"); do { *((volatile int*)__null) = 1811; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1812 if (!Val::fromJSValue(cx, sig.result(0), jsValue, &val)) {
1813 return nullptr;
1814 }
1815 results->storeVal(val, 0);
1816 } else {
1817 // The multi-value result is wrapped into ArrayObject/Iterable.
1818 Rooted<ArrayObject*> array(cx);
1819 if (!IterableToArray(cx, jsValue, &array)) {
1820 return nullptr;
1821 }
1822 if (fields.length() != array->length()) {
1823 UniqueChars expected(JS_smprintf("%zu", fields.length()));
1824 UniqueChars got(JS_smprintf("%u", array->length()));
1825 if (!expected || !got) {
1826 ReportOutOfMemory(cx);
1827 return nullptr;
1828 }
1829
1830 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1831 JSMSG_WASM_WRONG_NUMBER_OF_VALUES,
1832 expected.get(), got.get());
1833 return nullptr;
1834 }
1835
1836 for (size_t i = 0; i < fields.length(); i++) {
1837 RootedVal val(cx);
1838 RootedValue v(cx, array->getDenseElement(i));
1839 MOZ_ASSERT(sig.result(i).storageType() == fields[i].type)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sig.result(i).storageType() == fields[i].type)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(sig.result(i).storageType() == fields[i].type))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("sig.result(i).storageType() == fields[i].type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1839); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sig.result(i).storageType() == fields[i].type"
")"); do { *((volatile int*)__null) = 1839; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1840 if (!Val::fromJSValue(cx, sig.result(i), v, &val)) {
1841 return nullptr;
1842 }
1843 results->storeVal(val, i);
1844 }
1845 }
1846 }
1847 return results;
1848}
1849
1850// Collects returned suspending promising, and registers callbacks to
1851// react on it using WasmPISuspendTaskContinue.
1852// Seen as $builtin.add-promise-reactions to wasm.
1853void* AddPromiseReactions(Instance* instance, SuspenderObject* suspender,
1854 void* result, JSFunction* continueOnSuspendable) {
1855 MOZ_ASSERT(SASigAddPromiseReactions.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigAddPromiseReactions.failureMode == FailureMode::
FailOnInvalidRef)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(SASigAddPromiseReactions.failureMode
== FailureMode::FailOnInvalidRef))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("SASigAddPromiseReactions.failureMode == FailureMode::FailOnInvalidRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigAddPromiseReactions.failureMode == FailureMode::FailOnInvalidRef"
")"); do { *((volatile int*)__null) = 1856; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1856 FailureMode::FailOnInvalidRef)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigAddPromiseReactions.failureMode == FailureMode::
FailOnInvalidRef)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(SASigAddPromiseReactions.failureMode
== FailureMode::FailOnInvalidRef))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("SASigAddPromiseReactions.failureMode == FailureMode::FailOnInvalidRef"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigAddPromiseReactions.failureMode == FailureMode::FailOnInvalidRef"
")"); do { *((volatile int*)__null) = 1856; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1857 JSContext* cx = instance->cx();
1858
1859 RootedAnyRef resultRef(cx, AnyRef::fromCompiledCode(result));
1860 RootedValue resultValue(cx, resultRef.get().toJSValue());
1861 Rooted<SuspenderObject*> suspenderObject(cx, suspender);
1862 RootedFunction fn(cx, continueOnSuspendable);
1863
1864 if (!IsPromiseValue(cx, resultValue)) {
1865 suspenderObject->forwardToSuspendable();
1866 suspenderObject->setSuspendingReturnType(
1867 SuspenderObject::ReturnType::Value);
1868 return resultRef.get().forCompiledCode();
1869 }
1870
1871 // Wrap a promise.
1872 RootedObject promiseConstructor(cx, GetPromiseConstructor(cx));
1873 RootedObject promiseObj(cx,
1874 PromiseResolve(cx, promiseConstructor, resultValue));
1875 if (!promiseObj) {
1876 return AnyRef::invalid().forCompiledCode();
1877 }
1878 Rooted<PromiseObject*> promiseObject(cx, &promiseObj->as<PromiseObject>());
1879
1880 suspenderObject->setSuspendingReturnType(
1881 SuspenderObject::ReturnType::Promise);
1882
1883 // Add promise reactions
1884 RootedFunction then_(
1885 cx, NewNativeFunction(cx, WasmPISuspendTaskContinue, 1, nullptr,
1886 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1887 then_->initExtendedSlot(SUSPENDER_SLOT, ObjectValue(*suspenderObject));
1888 then_->initExtendedSlot(CONTINUE_ON_SUSPENDABLE_SLOT, ObjectValue(*fn));
1889 then_->initExtendedSlot(PROMISE_SLOT, ObjectValue(*promiseObject));
1890 if (!JS::AddPromiseReactions(cx, promiseObject, then_, then_)) {
1891 return AnyRef::invalid().forCompiledCode();
1892 }
1893 return AnyRef::fromJSObject(*promiseObject).forCompiledCode();
1894}
1895
1896// Changes exit stack frame pointers to suspendable stack and recast exception
1897// to wasm reference. Seen as $builtin.forward-exn-to-suspended to wasm.
1898void* ForwardExceptionToSuspended(Instance* instance,
1899 SuspenderObject* suspender, void* exception) {
1900 MOZ_ASSERT(SASigForwardExceptionToSuspended.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigForwardExceptionToSuspended.failureMode == FailureMode
::Infallible)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(SASigForwardExceptionToSuspended.
failureMode == FailureMode::Infallible))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigForwardExceptionToSuspended.failureMode == FailureMode::Infallible"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigForwardExceptionToSuspended.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 1901; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1901 FailureMode::Infallible)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigForwardExceptionToSuspended.failureMode == FailureMode
::Infallible)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(SASigForwardExceptionToSuspended.
failureMode == FailureMode::Infallible))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigForwardExceptionToSuspended.failureMode == FailureMode::Infallible"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigForwardExceptionToSuspended.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 1901; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1902
1903 suspender->forwardToSuspendable();
1904 suspender->setSuspendingReturnType(SuspenderObject::ReturnType::Exception);
1905 return exception;
1906}
1907
1908// Resolves the promise using results packed by wasm.
1909// Seen as $builtin.set-promising-promise-results to wasm.
1910int32_t SetPromisingPromiseResults(Instance* instance,
1911 SuspenderObject* suspender,
1912 WasmStructObject* results) {
1913 MOZ_ASSERT(SASigSetPromisingPromiseResults.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigSetPromisingPromiseResults.failureMode == FailureMode
::FailOnNegI32)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(SASigSetPromisingPromiseResults
.failureMode == FailureMode::FailOnNegI32))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigSetPromisingPromiseResults.failureMode == FailureMode::FailOnNegI32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1914); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigSetPromisingPromiseResults.failureMode == FailureMode::FailOnNegI32"
")"); do { *((volatile int*)__null) = 1914; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1914 FailureMode::FailOnNegI32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigSetPromisingPromiseResults.failureMode == FailureMode
::FailOnNegI32)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(SASigSetPromisingPromiseResults
.failureMode == FailureMode::FailOnNegI32))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("SASigSetPromisingPromiseResults.failureMode == FailureMode::FailOnNegI32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1914); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigSetPromisingPromiseResults.failureMode == FailureMode::FailOnNegI32"
")"); do { *((volatile int*)__null) = 1914; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1915 JSContext* cx = instance->cx();
1916 Rooted<WasmStructObject*> res(cx, results);
1917 Rooted<SuspenderObject*> suspenderObject(cx, suspender);
1918 RootedObject promise(cx, suspenderObject->promisingPromise());
1919
1920 const StructType& resultType = res->typeDef().structType();
1921 RootedValue val(cx);
1922 // Unbox the result value from the struct, if any.
1923 switch (resultType.fields_.length()) {
1924 case 0:
1925 break;
1926 case 1: {
1927 if (!res->getField(cx, /*index=*/0, &val)) {
1928 return false;
1929 }
1930 } break;
1931 default: {
1932 Rooted<ArrayObject*> array(cx, NewDenseEmptyArray(cx));
1933 if (!array) {
1934 return false;
1935 }
1936 for (size_t i = 0; i < resultType.fields_.length(); i++) {
1937 RootedValue item(cx);
1938 if (!res->getField(cx, i, &item)) {
1939 return false;
1940 }
1941 if (!NewbornArrayPush(cx, array, item)) {
1942 return false;
1943 }
1944 }
1945 val.setObject(*array);
1946 } break;
1947 }
1948 ResolvePromise(cx, promise, val);
1949 return 0;
1950}
1951
1952void UpdateSuspenderState(Instance* instance, SuspenderObject* suspender,
1953 UpdateSuspenderStateAction action) {
1954 MOZ_ASSERT(SASigUpdateSuspenderState.failureMode == FailureMode::Infallible)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(SASigUpdateSuspenderState.failureMode == FailureMode
::Infallible)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(SASigUpdateSuspenderState.failureMode
== FailureMode::Infallible))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("SASigUpdateSuspenderState.failureMode == FailureMode::Infallible"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1954); AnnotateMozCrashReason("MOZ_ASSERT" "(" "SASigUpdateSuspenderState.failureMode == FailureMode::Infallible"
")"); do { *((volatile int*)__null) = 1954; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1955
1956 JSContext* cx = instance->cx();
1957 switch (action) {
1958 case UpdateSuspenderStateAction::Enter:
1959 suspender->enter(cx);
1960 break;
1961 case UpdateSuspenderStateAction::Suspend:
1962 suspender->suspend(cx);
1963 break;
1964 case UpdateSuspenderStateAction::Resume:
1965 suspender->resume(cx);
1966 break;
1967 case UpdateSuspenderStateAction::Leave:
1968 suspender->leave(cx);
1969 break;
1970 default:
1971 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/wasm/WasmPI.cpp"
, 1971); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 1971; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
1972 }
1973}
1974
1975} // namespace js::wasm
1976#endif // ENABLE_WASM_JSPI