Skip to content

Commit 526ed7e

Browse files
Trim comments to be concise and focus on why, not what
The previous two commits' inline comments read like a debugging journal (narrating what was tried, what failed, "confirmed via testing" for nearly every line) rather than code documentation. Rewrote them to state the non-obvious reasoning briefly and let the code show the what - readers can see the diff and the SpiderMonkey headers for themselves. No behavior change; rebuilt and reran the async/Promise regression tests to confirm. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 43205d4 commit 526ed7e

9 files changed

Lines changed: 78 additions & 256 deletions

File tree

‎CMakeLists.txt‎

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,10 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
3030
include(FetchContent)
3131

3232
if (WIN32)
33-
# LOCAL PATCH: pythonmonkey's own compile of its .cc files (and of
34-
# SpiderMonkey's public headers pulled in by them) goes through this
35-
# CMake/clang-cl build directly, not through Mozilla's own moz.build
36-
# system -- which normally defines XP_WIN for every object file it
37-
# compiles. Without it, SpiderMonkey headers that branch on
38-
# `defined(XP_WIN)` (assuming it's always set on Windows, since
39-
# that's Mozilla's own standard "we're building for Windows" macro)
40-
# silently fall through to their POSIX/pthread branch instead,
41-
# confirmed via a real build failure (PlatformMutex.h including the
42-
# nonexistent <pthread.h>, UniquePtrExtensions.h missing Windows
43-
# HANDLE-based types as a result). Defining it globally here fixes
44-
# every such header at once, rather than patching each one
45-
# individually as it's discovered (one already was, in
46-
# BaseProfilerUtils.h, before this more general fix existed).
33+
# This build doesn't go through Mozilla's moz.build system, which
34+
# normally defines XP_WIN on Windows -- without it, SpiderMonkey headers
35+
# that branch on it (e.g. PlatformMutex.h) fall through to a POSIX path
36+
# that doesn't exist here.
4737
SET(COMPILE_FLAGS "/GR- /W0 /DXP_WIN")
4838

4939
SET(OPTIMIZED "/O2")

‎include/JobQueue.hh‎

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -53,40 +53,26 @@ bool getHostDefinedData(JSContext *cx, JS::MutableHandle<JSObject *> incumbentGl
5353

5454
/**
5555
* @brief Ask the embedding for the host defined global to use when running
56-
* a JS microtask (LOCAL PATCH: new pure-virtual method added alongside the
57-
* SpiderMonkey 157a1 JobQueue redesign -- see runJobs() below for context).
56+
* a JS microtask.
5857
*
59-
* Mirrors the "we don't track this" stance already taken in
60-
* getHostDefinedData() above: we have no host defined global of our own, so
61-
* SpiderMonkey falls back to its own default (the microtask's execution
62-
* global, from GetExecutionGlobalFromJSMicroTask). Matches SpiderMonkey's
63-
* own reference embedding, InternalJobQueue::getHostDefinedGlobal, which
64-
* does exactly this (js/src/vm/JSContext.cpp).
58+
* Same "we don't track this" stance as getHostDefinedData() above -- falls
59+
* back to SpiderMonkey's own default, matching the reference embedding
60+
* (InternalJobQueue::getHostDefinedGlobal, js/src/vm/JSContext.cpp).
6561
*/
6662
bool getHostDefinedGlobal(JSContext *cx, JS::MutableHandle<JSObject *> out) const override;
6763

6864
/**
6965
* @brief Pull every job SpiderMonkey has queued internally since the last
7066
* call, and forward each one to the Python event-loop for execution.
7167
*
72-
* LOCAL PATCH (SpiderMonkey 157a1 API change): `JobQueue::enqueuePromiseJob`
73-
* -- the old per-job push callback this class used to override -- was
74-
* removed from the base class entirely. SpiderMonkey now enqueues promise
75-
* reaction jobs into its own internal queue as it creates them (see
76-
* EnqueueJob() in js/src/builtin/Promise.cpp), without notifying the
77-
* embedding. The embedding is instead expected to pull queued jobs itself,
78-
* here, whenever it wants a "microtask checkpoint" to happen -- triggered
79-
* by the embedder calling the free function js::RunJobs(cx) (declared in
80-
* jsfriendapi.h; NOT the same thing as this method, despite the identical
81-
* name -- js::RunJobs(cx) is what calls cx->jobQueue->runJobs(cx), i.e.
82-
* this override). PythonMonkey calls js::RunJobs(GLOBAL_CX) once after each
83-
* top-level JS_ExecuteScript() call, in pythonmonkey.cc.
84-
*
85-
* This preserves the original behaviour -- JS promise reactions execute as
86-
* Python asyncio callbacks, not synchronously inline -- by draining
87-
* SpiderMonkey's internal queue and re-creating the same "hand this job to
88-
* Python's event loop" forwarding enqueuePromiseJob used to do per-job, just
89-
* done here in a pull/batch fashion instead.
68+
* SpiderMonkey no longer pushes promise jobs to the embedding as they're
69+
* created (the old enqueuePromiseJob); it queues them internally and
70+
* expects the embedder to pull them here on demand, via the free function
71+
* js::RunJobs(cx) (jsfriendapi.h -- not the same thing as this method: it's
72+
* what calls cx->jobQueue->runJobs(cx)). PythonMonkey calls
73+
* js::RunJobs(GLOBAL_CX) after every top-level JS_ExecuteScript(), plus a
74+
* few call sites where JS callbacks resolve promises outside of script
75+
* execution (see JSFunctionProxy.cc, PromiseType.cc).
9076
*
9177
* Calling this method at the wrong time can break the web. The HTML spec
9278
* indicates exactly when the job queue should be drained (in HTML jargon,
@@ -138,15 +124,8 @@ js::UniquePtr<JS::JobQueue::SavedJobQueue> saveJobQueue(JSContext *) override;
138124
* see https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/public/Promise.h#l580
139125
* https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/OffThreadPromiseRuntimeState.cpp#l160
140126
*
141-
* LOCAL PATCH (SpiderMonkey 157a1 API change): `JS::InitDispatchToEventLoop`
142-
* (2-callback init) was replaced by `JS::InitAsyncTaskCallbacks`, which now
143-
* mandates both a `DispatchToEventLoopCallback` AND a
144-
* `DelayedDispatchToEventLoopCallback` (see delayedDispatchToEventLoop()
145-
* below). The callback signature itself also changed: it now takes ownership
146-
* of the Dispatchable via `js::UniquePtr<Dispatchable>&&` instead of a raw
147-
* pointer, and `Dispatchable::run()` is now `protected` -- callers must go
148-
* through the new public static `Dispatchable::Run(cx, task, shuttingDown)`
149-
* instead of calling `->run()` directly.
127+
* Takes ownership of the Dispatchable (run via the public static
128+
* Dispatchable::Run, since Dispatchable::run() is protected).
150129
*
151130
* @param closure - closure, currently the javascript context
152131
* @param dispatchable - the Dispatchable to be called; ownership transferred to this callback
@@ -156,27 +135,15 @@ static bool dispatchToEventLoop(void *closure, js::UniquePtr<JS::Dispatchable> &
156135

157136
/**
158137
* @brief The callback for dispatching an off-thread promise to the event
159-
* loop after a delay (LOCAL PATCH: newly mandatory as of the same API
160-
* change described on dispatchToEventLoop() above -- previously this
161-
* concept didn't need to exist as a separate callback for this embedding).
138+
* loop after a delay.
162139
*
163-
* NEEDS REVIEW: this embedding has no cross-thread-safe delayed-dispatch
164-
* mechanism (PyEventLoop::enqueueWithDelay exists but calls
165-
* asyncio.loop.call_later, which -- unlike call_soon_threadsafe, used
166-
* elsewhere in this codebase -- is not documented as safe to call from a
167-
* thread other than the one running the loop; this callback, per its
168-
* declaration in js/public/Promise.h, must be safe to call from ANY
169-
* thread). Per that same header's documented contract ("If a timeout
170-
* manager is not available for given context, it should return false"),
171-
* this always returns false, i.e. this embedding declines to service
172-
* engine-level delayed dispatch. This should only affect internal
173-
* SpiderMonkey features that specifically need a delayed off-thread
174-
* callback (e.g. an Atomics.waitAsync timeout) -- ordinary JS
175-
* `setTimeout`/`setInterval` in pythonmonkey go through a separate,
176-
* already-working path (PyEventLoop::enqueueWithDelay called from JS-exposed
177-
* timer functions, not this SpiderMonkey-internal callback) and are
178-
* unaffected. Not verified against a real Atomics.waitAsync-with-timeout
179-
* test case.
140+
* Always returns false (no timeout manager available), which
141+
* js/public/Promise.h documents as a valid response when the embedding
142+
* can't service delayed cross-thread dispatch. Only affects SpiderMonkey
143+
* features needing a delayed off-thread callback (e.g. an
144+
* Atomics.waitAsync timeout) -- ordinary setTimeout/setInterval go through
145+
* PyEventLoop::enqueueWithDelay instead and are unaffected. NEEDS REVIEW:
146+
* not verified against a real Atomics.waitAsync-with-timeout case.
180147
*
181148
* @param closure - closure, currently the javascript context
182149
* @param dispatchable - the Dispatchable that would be called; ownership transferred to this callback

‎setup.sh‎

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,8 @@ else
2626
exit 1
2727
fi
2828
# Install rust compiler
29-
# LOCAL PATCH: like the Poetry skip below, this step was unconditional --
30-
# no check for whether rust/the 1.85 toolchain is already installed. On a
31-
# machine where it already is, re-running rustup-init.sh downloads a fresh
32-
# installer exe into a temp dir and executes it, which on this Windows
33-
# machine gets blocked ("Permission denied", almost certainly Defender/
34-
# SmartScreen refusing to run a newly-downloaded, unsigned exe straight out
35-
# of a temp directory) -- a real, reproducible failure, not a flake. Skip
36-
# the whole block if rustup + the 1.85 toolchain are already present.
29+
# Skip if already installed: re-running rustup-init.sh here downloads and
30+
# runs a fresh installer exe, which Defender/SmartScreen blocks on this box.
3731
if command -v rustup >/dev/null && rustup toolchain list 2>/dev/null | grep -q '^1\.85'; then
3832
echo "Rust 1.85 toolchain already installed, skipping rustup-init"
3933
else
@@ -52,15 +46,9 @@ if [[ "$OSTYPE" == "msys"* || "$OSTYPE" == "cygwin"* ]]; then # Windows
5246
else
5347
POETRY_BIN="$HOME/.local/bin/poetry"
5448
fi
55-
# LOCAL PATCH: like the rustup step above, made idempotent (skip if already
56-
# installed) rather than always re-running the installer. Also, this
57-
# machine has no `python3` on PATH (only `python`), which made the real
58-
# installer command (`python3 - --version ...`) fail outright -- confirmed
59-
# via a real failure, not speculative. Poetry itself is still needed: the
60-
# `.git/hooks/pre-commit` dev-tooling branch further down calls
61-
# `$POETRY_BIN run pip install autopep8`, so skipping this setup entirely
62-
# (an earlier version of this patch did) would silently break that branch
63-
# for anyone whose clone takes it.
49+
# Skip if already installed (same idempotency reasoning as rustup above).
50+
# Falls back to `python` since this machine has no `python3` on PATH.
51+
# Poetry is still needed below by the .git/hooks/pre-commit branch.
6452
if command -v "$POETRY_BIN" >/dev/null || [ -x "$POETRY_BIN" ]; then
6553
echo "Poetry already installed, skipping"
6654
else
@@ -74,19 +62,10 @@ echo "Done installing dependencies"
7462
echo "Downloading spidermonkey source code"
7563
# Read the commit hash for mozilla-central from the `mozcentral.version` file
7664
MOZCENTRAL_VERSION=$(cat mozcentral.version)
77-
# LOCAL PATCH: this download+extract is not idempotent as originally
78-
# written -- it always re-extracts and always re-`mv`s, which fails once
79-
# firefox-source already exists from a prior (possibly failed-later) run.
80-
# Since this script needs re-running whenever a later step fails (and we've
81-
# hit several unrelated Windows-environment issues after this point), skip
82-
# entirely once firefox-source is already present.
65+
# Skip if already extracted -- lets this script be re-run after a later
66+
# step fails without re-downloading/re-extracting every time.
8367
if [ ! -d firefox-source ]; then
84-
# LOCAL PATCH: wget.exe (MSYS2's, and presumably any other copy) is
85-
# blocked outright on this machine by a Windows Defender Application
86-
# Control policy ("An Application Control policy has blocked this
87-
# file" -- confirmed directly, not a PATH/permission-bits issue).
88-
# curl is unaffected (checked both Windows' own and MSYS2's) -- use it
89-
# instead. unzip is also unaffected, kept as-is.
68+
# curl instead of wget: wget.exe is blocked by this machine's WDAC policy.
9069
curl -fsSL -o firefox-source-${MOZCENTRAL_VERSION}.zip https://github.com/mozilla-firefox/firefox/archive/${MOZCENTRAL_VERSION}.zip
9170
unzip -q firefox-source-${MOZCENTRAL_VERSION}.zip && mv firefox-${MOZCENTRAL_VERSION} firefox-source
9271
else
@@ -111,7 +90,7 @@ sed -i'' -e '/MOZ_CRASH_UNSAFE_PRINTF/,/__PRETTY_FUNCTION__);/d' ./mfbt/LinkedLi
11190
sed -i'' -e '/MOZ_ASSERT(stackRootPtr == nullptr);/d' ./js/src/vm/JSContext.cpp # would assert false in Debug Build since we extensively use `new JS::Rooted`
11291
sed -i'' -e 's/"-fuse-ld=ld"/"-ld64" if c_compiler.version > "14.0.0" else "-fuse-ld=ld"/' ./build/moz.configure/toolchain.configure # XCode 15 changed the linker behaviour. See https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Linking
11392
sed -i'' -e 's/defined(XP_WIN)/defined(_WIN32)/' ./mozglue/baseprofiler/public/BaseProfilerUtils.h # this header file is introduced to js/Debug.h in https://phabricator.services.mozilla.com/D221102, but it would be compiled without XP_WIN in this building configuration
114-
sed -i'' -e 's/os\.environ\["MOZILLABUILD"\]/os.environ.get("MOZILLABUILD", "")/g' ./python/mozbuild/mozbuild/backend/visualstudio.py # LOCAL PATCH: this VS-project-file-generation convenience feature (not needed for a command-line-only build) does an unguarded os.environ["MOZILLABUILD"] lookup and crashes with KeyError when it's unset, which it is here (we don't use the official Mozilla Build package) -- confirmed via a real build failure, not speculative
93+
sed -i'' -e 's/os\.environ\["MOZILLABUILD"\]/os.environ.get("MOZILLABUILD", "")/g' ./python/mozbuild/mozbuild/backend/visualstudio.py # avoid KeyError: we don't use the official Mozilla Build package, so this is never set
11594

11695
cd js/src
11796
mkdir -p _build
@@ -126,16 +105,8 @@ mkdir -p ../../../../_spidermonkey_install/
126105
--disable-tests \
127106
$(if [[ "$OSTYPE" == "darwin"* ]]; then echo "--enable-linker=ld64"; fi) \
128107
--enable-optimize
129-
# LOCAL PATCH: the original --disable-explicit-resource-management flag
130-
# (worked around Bugzilla 1940342, a header/lib enum mismatch from when
131-
# the `using` syntax was newly landing in nightly circa early 2025) is
132-
# now an unrecognized configure option on this newer mozilla-central
133-
# snapshot -- confirmed via a real `InvalidOptionError: Unknown option`
134-
# build failure. The explicit-resource-management feature has evidently
135-
# shipped/stabilized since, taking the flag (and presumably the bug it
136-
# worked around) with it. Removed rather than guessing at a replacement
137-
# flag; if header/lib enum mismatches resurface, that bug tracker is the
138-
# place to check first.
108+
# --disable-explicit-resource-management (worked around Bugzilla 1940342)
109+
# is no longer a recognized flag -- the feature it gated has since shipped.
139110
make -j$CPUS
140111
echo "Done building spidermonkey"
141112

‎src/BufferType.cc‎

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -94,28 +94,14 @@ PyObject *BufferType::fromJsTypedArray(JSContext *cx, JS::HandleObject typedArra
9494
return nullptr;
9595
}
9696

97-
// LOCAL PATCH (SpiderMonkey 157a1 API change, needs team review -- see
98-
// handover doc): JS_GetArrayBufferViewFixedData was removed upstream;
99-
// JS_GetArrayBufferViewData is its replacement, but trades the old
100-
// function's own "return nullptr if the data is still inline/movable"
101-
// runtime guard for a caller-supplied JS::AutoRequireNoGC token instead.
102-
// AutoRequireNoGC (js/GCAPI.h) is a trivial marker type with no runtime
103-
// behaviour of its own -- it's a compile-time "I've verified this is
104-
// safe" token, not an active GC suppressor. The safety property the old
105-
// function's guard provided (never returning a pointer into GC-movable
106-
// inline TypedArray storage) is still expected to hold here because of
107-
// the JS_GetArrayBufferViewBuffer() call above: per ITS OWN comment, it
108-
// forces any inline/movable data to be promoted to a real, stably
109-
// allocated ArrayBuffer first. This reasoning has NOT been independently
110-
// verified against SpiderMonkey's actual GC internals (e.g. by stress
111-
// testing with --enable-gczeal / a compacting-GC configuration) -- do
112-
// that before trusting this for anything beyond experimentation.
113-
// AutoRequireNoGC's own ctor/dtor are protected (it's a base marker type,
114-
// not directly instantiable) -- use AutoAssertNoGC instead, which is
115-
// publicly constructible AND (in diagnostic builds) actually verifies at
116-
// runtime that no GC happens while it's alive, rather than being a pure
117-
// no-op marker. Strictly better for confidence in this fix than the bare
118-
// base class would have been even if it were public.
97+
// NEEDS REVIEW: JS_GetArrayBufferViewFixedData was removed upstream; its
98+
// replacement trades the old "return nullptr if data is still inline/
99+
// movable" runtime guard for a caller-supplied no-GC token. Safety here
100+
// relies on JS_GetArrayBufferViewBuffer() above having already promoted
101+
// any inline data to a stable allocation -- not independently verified
102+
// against SpiderMonkey's GC (e.g. via --enable-gczeal). AutoAssertNoGC,
103+
// not the base AutoRequireNoGC (protected ctor), since it actually
104+
// asserts at runtime in diagnostic builds instead of being a bare marker.
119105
JS::AutoAssertNoGC nogc(cx);
120106
bool isSharedMemory2; // redundant with isSharedMemory above; required by this function's signature
121107
uint8_t *data = static_cast<uint8_t *>(JS_GetArrayBufferViewData(typedArray, &isSharedMemory2, nogc));

‎src/JSFunctionProxy.cc‎

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,9 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self,
6060
return NULL;
6161
}
6262

63-
// LOCAL PATCH (SpiderMonkey 157a1 JobQueue redesign, found via retesting
64-
// the claims in SPIDERMONKEY_VERSION_BUMP.md -- fix #7's checkpoint list
65-
// missed this site): this is the generic entry point Python uses to call
66-
// back into any JS function it was handed -- e.g. a `setTimeout` callback
67-
// dispatched from PyEventLoop, or a JS event-listener invoked directly by
68-
// Python code. If the JS function just called resolved/rejected a Promise
69-
// with already-attached reactions (the common case: `resolve(...)` inside
70-
// a `setTimeout` callback), that enqueues a job into cx->microTaskQueues
71-
// with nothing else scheduled to drain it -- this call happens outside of
72-
// JS_ExecuteScript() and outside PromiseType.cc's two checkpoints. Confirmed
73-
// via a real hang: awaiting a JS Promise that resolves via `setTimeout`
74-
// never returned until this checkpoint was added here.
63+
// This is the generic entry point for any Python->JS callback (e.g. a
64+
// setTimeout callback), so a Promise resolved here has nothing else
65+
// scheduled to drain its reaction jobs. See JobQueue.cc's runJobs.
7566
js::RunJobs(cx);
7667

7768
if (PyErr_Occurred()) {

‎src/JSMethodProxy.cc‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO
7171
return NULL;
7272
}
7373

74-
// LOCAL PATCH (SpiderMonkey 157a1 JobQueue redesign): same missing
75-
// checkpoint as JSFunctionProxy_call in JSFunctionProxy.cc -- see the
76-
// comment there for the full explanation and the real hang that surfaced
77-
// it. This is the same "Python calls back into a JS callable" bridge, just
78-
// for bound methods instead of plain functions.
74+
// Same checkpoint as JSFunctionProxy_call, for bound methods.
7975
js::RunJobs(cx);
8076

8177
if (PyErr_Occurred()) {

0 commit comments

Comments
 (0)