From f547cc56630996d87ebb49032b149b990eff27a1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 4 Sep 2026 10:15:07 -0700 Subject: [PATCH 1/6] Integrate NightMonkey. This integrates the work from bytecodealliance/firefox#2; see that PR for the full description. The main interesting/relevant integration bit is that because NightMonkey puts the compiler logic in a native binary (that runs outside the Wasm snapshot, and processes it), somewhat analogous to the `weval` tool, we need to build those binaries too. Unlike `weval`, there is not a clean and version-drift-tolerant interface between the engine and the external processing tool; rather, the compiler needs to be built from exactly the same tree as the in-Wasm-module runtime. So this repo builds those binaries now and will ship them as release artifacts alongside the Wasm runtime modules. WPT and E2E tests pass locally with the NightMonkey mode enabled. --- .github/workflows/main.yml | 61 ++++++++++++++- .github/workflows/release-please.yml | 50 ++++++++++++ CMakeLists.txt | 6 ++ builtins/web/fetch/fetch_event.cpp | 8 ++ cmake/spidermonkey.cmake | 41 +++++++--- componentize.sh.in | 77 ++++++++++++++++++- justfile | 6 +- runtime/engine.cpp | 8 ++ runtime/js.cpp | 8 ++ runtime/script_loader.cpp | 14 ++++ .../expect_serve_body.txt | 1 + .../nightmonkey-fallback.js | 7 ++ .../expect_serve_stderr_nightmonkey.txt | 4 + tests/test.sh | 3 + tests/tests.cmake | 11 ++- tests/wpt-harness/wpt.cmake | 2 + 16 files changed, 286 insertions(+), 21 deletions(-) create mode 100644 tests/e2e/nightmonkey-fallback/expect_serve_body.txt create mode 100644 tests/e2e/nightmonkey-fallback/nightmonkey-fallback.js create mode 100644 tests/e2e/runtime-err/expect_serve_stderr_nightmonkey.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5147099f..d4749102 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - build: [release, debug, weval] + build: [release, debug, weval, nightmonkey] os: [ubuntu-latest] outputs: SM_TAG_EXISTS: ${{ steps.check-sm-release.outputs.SM_TAG_EXISTS }} @@ -32,6 +32,7 @@ jobs: SM_CACHE_KEY_debug: ${{ steps.check-sm-release.outputs.SM_CACHE_KEY_debug }} SM_CACHE_KEY_release: ${{ steps.check-sm-release.outputs.SM_CACHE_KEY_release }} SM_CACHE_KEY_weval: ${{ steps.check-sm-release.outputs.SM_CACHE_KEY_weval }} + SM_CACHE_KEY_nightmonkey: ${{ steps.check-sm-release.outputs.SM_CACHE_KEY_nightmonkey }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -73,8 +74,9 @@ jobs: - name: Configure StarlingMonkey run: | cmake -S . -B cmake-build-${{ matrix.build }}\ - -DCMAKE_BUILD_TYPE=${{ (matrix.build == 'release' || matrix.build == 'weval') && 'Release' || 'Debug' }}\ - ${{matrix.build == 'weval' && '-DUSE_WASM_OPT=OFF -DWEVAL=ON' || ''}} + -DCMAKE_BUILD_TYPE=${{ (matrix.build == 'release' || matrix.build == 'weval' || matrix.build == 'nightmonkey') && 'Release' || 'Debug' }}\ + ${{matrix.build == 'weval' && '-DUSE_WASM_OPT=OFF -DWEVAL=ON' || ''}}\ + ${{matrix.build == 'nightmonkey' && '-DNIGHTMONKEY=ON' || ''}} - name: Build StarlingMonkey run: | @@ -153,6 +155,14 @@ jobs: spidermonkey-dist-weval key: ${{ needs.test.outputs.SM_CACHE_KEY_weval }} fail-on-cache-miss: true + - name: Restore SpiderMonkey NightMonkey Cache + uses: actions/cache/restore@v4 + id: sm-cache-nightmonkey + with: + path: | + spidermonkey-dist-nightmonkey + key: ${{ needs.test.outputs.SM_CACHE_KEY_nightmonkey }} + fail-on-cache-miss: true - name: Create SpiderMonkey Tar Balls run: | @@ -160,6 +170,7 @@ jobs: tar -a -cf release-artifacts/spidermonkey-static-debug.tar.gz spidermonkey-dist-debug/* tar -a -cf release-artifacts/spidermonkey-static-release.tar.gz spidermonkey-dist-release/* tar -a -cf release-artifacts/spidermonkey-static-release_weval.tar.gz spidermonkey-dist-weval/* + tar -a -cf release-artifacts/spidermonkey-static-release_nightmonkey.tar.gz spidermonkey-dist-nightmonkey/* tree release-artifacts - name: Do the Release @@ -171,3 +182,47 @@ jobs: stability or availability guarantees. tag_name: ${{ needs.test.outputs.SM_TAG }} files: release-artifacts/* + + nightmonkey-compilers: + name: NightMonkey compiler (${{ matrix.artifact }}) + needs: test + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + artifact: nightmonkey-x86_64-linux + binary: nightmonkey + - os: ubuntu-24.04-arm + artifact: nightmonkey-aarch64-linux + binary: nightmonkey + - os: macos-14 + artifact: nightmonkey-aarch64-macos + binary: nightmonkey + - os: windows-2022 + artifact: nightmonkey-x86_64-windows.exe + binary: nightmonkey.exe + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Read Firefox revision + id: firefox-revision + shell: bash + run: | + revision="$(awk '/^set\(SM_TAG / {gsub(/set\(SM_TAG |\)/, ""); print}' cmake/spidermonkey.cmake)" + echo "revision=$revision" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@v4 + with: + repository: bytecodealliance/firefox + ref: ${{ steps.firefox-revision.outputs.revision }} + path: firefox + - name: Build compiler + run: | + cargo build --release --manifest-path firefox/js/src/night/nightmonkey/Cargo.toml + compiler="firefox/js/src/night/nightmonkey/target/release/${{ matrix.binary }}" + "$compiler" --help + cp "$compiler" "${{ matrix.artifact }}" + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: ${{ matrix.artifact }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 05b30f19..cf289c8b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -55,6 +55,7 @@ jobs: just mode=release builddir=build-release build starling just mode=relwithdebinfo builddir=build-debug build starling just mode=weval builddir=build-weval build starling-ics.wevalcache + just mode=nightmonkey builddir=build-nightmonkey build starling - name: Prepare Artifacts run: | @@ -64,6 +65,7 @@ jobs: cp build-release/starling-raw.wasm release-artifacts/starling-raw.wasm cp build-debug/starling-raw.wasm release-artifacts/starling-raw-debug.wasm cp build-weval/starling-raw.wasm release-artifacts/starling-raw-weval.wasm + cp build-nightmonkey/starling-raw.wasm release-artifacts/starling-raw-nightmonkey.wasm # Component-versions of the runtime cp build-release/starling.wasm release-artifacts/starling.wasm @@ -72,6 +74,7 @@ jobs: # Supporting artifacts cp build-release/preview1-adapter.wasm release-artifacts cp build-weval/starling-ics.wevalcache release-artifacts + cp build-nightmonkey/componentize.sh release-artifacts echo "Release artifacts:" ls -la release-artifacts/ @@ -82,6 +85,53 @@ jobs: tag_name: ${{ needs.release-please.outputs.tag_name }} files: release-artifacts/* + build-nightmonkey-compilers: + needs: release-please + if: ${{ needs.release-please.outputs.release_created }} + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + artifact: nightmonkey-x86_64-linux + binary: nightmonkey + - os: ubuntu-24.04-arm + artifact: nightmonkey-aarch64-linux + binary: nightmonkey + - os: macos-14 + artifact: nightmonkey-aarch64-macos + binary: nightmonkey + - os: windows-2022 + artifact: nightmonkey-x86_64-windows.exe + binary: nightmonkey.exe + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Read Firefox revision + id: firefox-revision + shell: bash + run: | + revision="$(awk '/^set\(SM_TAG / {gsub(/set\(SM_TAG |\)/, ""); print}' cmake/spidermonkey.cmake)" + echo "revision=$revision" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@v4 + with: + repository: bytecodealliance/firefox + ref: ${{ steps.firefox-revision.outputs.revision }} + path: firefox + - name: Build compiler + run: | + cargo build --release --manifest-path firefox/js/src/night/nightmonkey/Cargo.toml + compiler="firefox/js/src/night/nightmonkey/target/release/${{ matrix.binary }}" + "$compiler" --help + cp "$compiler" "${{ matrix.artifact }}" + - name: Upload Release Asset + uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 #2.3.2 + with: + tag_name: ${{ needs.release-please.outputs.tag_name }} + files: ${{ matrix.artifact }} + publish-extension: needs: release-please if: ${{ needs.release-please.outputs.debugger_release_created }} diff --git a/CMakeLists.txt b/CMakeLists.txt index a44ab177..d0ae7c82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,12 @@ else() set(AOT 0) endif() +if(NIGHTMONKEY) + set(NIGHTMONKEY_AOT 1) +else() + set(NIGHTMONKEY_AOT 0) +endif() + set(RUNTIME_FILE "starling-raw.wasm") set(ADAPTER_FILE "preview1-adapter.wasm") configure_file("componentize.sh.in" "${CMAKE_CURRENT_BINARY_DIR}/componentize.sh" @ONLY) diff --git a/builtins/web/fetch/fetch_event.cpp b/builtins/web/fetch/fetch_event.cpp index 3258ba96..7188156b 100644 --- a/builtins/web/fetch/fetch_event.cpp +++ b/builtins/web/fetch/fetch_event.cpp @@ -12,6 +12,9 @@ #include #include #include +#ifdef ENABLE_JS_NIGHTMONKEY +# include +#endif #include #include @@ -493,6 +496,11 @@ static void dispatch_fetch_event(HandleObject event, double *total_compute) { } bool handle_incoming_request(host_api::HttpIncomingRequest *request) { +#ifdef ENABLE_JS_NIGHTMONKEY + if (!JS::NightActivate(ENGINE->cx())) { + return false; + } +#endif #ifdef DEBUG std::println(stderr, "Warning: Using a DEBUG build. Expect things to be SLOW."); #endif diff --git a/cmake/spidermonkey.cmake b/cmake/spidermonkey.cmake index 7ce5cf82..edb4b1d3 100644 --- a/cmake/spidermonkey.cmake +++ b/cmake/spidermonkey.cmake @@ -1,4 +1,5 @@ -set(SM_TAG FIREFOX_147_0_4_RELEASE_STARLING) +set(SM_TAG 9f1a4ce9a631039e6898985d842afaf16cebc89e) +set(SM_REPO_URL https://github.com/bytecodealliance/firefox.git) include("manage-git-source") @@ -9,9 +10,16 @@ else() endif() option(WEVAL "Build with a SpiderMonkey variant that supports weval-based AOT compilation" OFF) +option(NIGHTMONKEY "Build with NightMonkey AOT compilation support" OFF) + +if (WEVAL AND NIGHTMONKEY) + message(FATAL_ERROR "WEVAL and NIGHTMONKEY cannot be enabled together") +endif() if (WEVAL) set(SM_BUILD_TYPE "${SM_BUILD_TYPE}_weval") +elseif (NIGHTMONKEY) + set(SM_BUILD_TYPE "${SM_BUILD_TYPE}_nightmonkey") endif() # If the developer has specified an alternate local set of SpiderMonkey @@ -53,16 +61,20 @@ if (DEFINED SM_LIB_DIR) target_include_directories(spidermonkey INTERFACE ${SM_INCLUDE_DIR}) target_link_libraries(spidermonkey INTERFACE ${SM_LIB_DIR}/libspidermonkey.a) else() - # Clone SpiderMonkey source using git directly for shallow clone - # Use deps folder in project root for shared access across build directories - set(SM_SOURCE_DIR ${CMAKE_SOURCE_DIR}/deps/spidermonkey-source) - - manage_git_source( - NAME spidermonkey - REPO_URL https://github.com/bytecodealliance/firefox.git - TAG ${SM_TAG} - SOURCE_DIR ${SM_SOURCE_DIR} - ) + set(SM_SOURCE_DIR "${CMAKE_SOURCE_DIR}/deps/spidermonkey-source" CACHE PATH + "Path to a local Firefox source checkout") + if (SM_SOURCE_DIR STREQUAL "${CMAKE_SOURCE_DIR}/deps/spidermonkey-source") + manage_git_source( + NAME spidermonkey + REPO_URL ${SM_REPO_URL} + TAG ${SM_TAG} + SOURCE_DIR ${SM_SOURCE_DIR} + ) + elseif (NOT EXISTS "${SM_SOURCE_DIR}/mach") + message(FATAL_ERROR "SM_SOURCE_DIR does not contain a Firefox checkout: ${SM_SOURCE_DIR}") + else() + message(STATUS "Using SpiderMonkey source from ${SM_SOURCE_DIR}") + endif() # Each build configuration gets its own object directory set(SM_OBJ_DIR ${CMAKE_CURRENT_BINARY_DIR}/spidermonkey-obj) @@ -151,6 +163,8 @@ mk_add_options AUTOCLOBBER=1 string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-aot-ics\n") string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-aot-ics-force\n") string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-pbl-weval\n") + elseif(NIGHTMONKEY) + string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-nightmonkey\n") endif() file(GENERATE OUTPUT ${MOZCONFIG} CONTENT "${MOZCONFIG_CONTENT}") @@ -188,6 +202,11 @@ mk_add_options AUTOCLOBBER=1 ) add_custom_target(spidermonkey_build DEPENDS ${LIB_SM}) + if(NIGHTMONKEY) + set(NIGHTMONKEY_BIN "${SM_OBJ_DIR}/dist/host/bin/nightmonkey" CACHE FILEPATH + "Path to the NightMonkey compiler" FORCE) + endif() + add_library(spidermonkey INTERFACE) add_dependencies(spidermonkey spidermonkey_build) target_include_directories(spidermonkey INTERFACE ${SM_INCLUDE_DIR}) diff --git a/componentize.sh.in b/componentize.sh.in index c51b0d71..3d83d387 100755 --- a/componentize.sh.in +++ b/componentize.sh.in @@ -1,26 +1,52 @@ #!/usr/bin/env bash -#set -euo pipefail +set -euo pipefail wizer="${WIZER:-@WASMTIME_DIR@/wasmtime wizer}" wasm_tools="${WASM_TOOLS:-@WASM_TOOLS_BIN@}" weval="${WEVAL:-@WEVAL_BIN@}" +configured_nightmonkey="@NIGHTMONKEY_BIN@" aot=@AOT@ +nightmonkey_aot=@NIGHTMONKEY_AOT@ preopen_dir="${PREOPEN_DIR:-}" usage() { - echo "Usage: $(basename "$0") [--verbose] [-i,--initializer-script-path path] [--strip-path-prefix prefix] [--legacy-script] [input.js] [-o output.wasm]" + echo "Usage: $(basename "$0") [--verbose] [--enable-nightmonkey] [-i,--initializer-script-path path] [--strip-path-prefix prefix] [--legacy-script] [input.js] [-o output.wasm]" echo " Providing an input file but no output uses the input base name with a .wasm extension" echo " Providing an output file but no input creates a component without running any top-level script" echo " Specifying '--verbose' causes the detailed output during initialization and execution" echo " Specifying '-i' or '--initializer-script-path' allows specifying an initializer script" echo " Specifying '--strip-path-prefix' will cause the provided prefix to be stripped from paths in stack traces and the debugger" echo " Specifying '--legacy-script' causes evaluation as a legacy JS script instead of a module" + echo " Specifying '--enable-nightmonkey' AOT-compiles the wizened core module with NightMonkey" echo " Specifying '--wpt-mode' enables WPT compatibility mode" echo " Specifying '--init-location url' allows setting the URL to use for 'globalThis.location' during initialization" exit 1 } +find_nightmonkey() { + if [[ -n ${NIGHTMONKEY:-} ]]; then + printf '%s\n' "$NIGHTMONKEY" + return + fi + if [[ -n $configured_nightmonkey && -x $configured_nightmonkey ]]; then + printf '%s\n' "$configured_nightmonkey" + return + fi + + local platform machine artifact + platform="$(uname -s)" + machine="$(uname -m)" + case "$platform:$machine" in + Linux:x86_64) artifact=nightmonkey-x86_64-linux ;; + Linux:aarch64|Linux:arm64) artifact=nightmonkey-aarch64-linux ;; + Darwin:arm64) artifact=nightmonkey-aarch64-macos ;; + MINGW*:x86_64|MSYS*:x86_64|CYGWIN*:x86_64) artifact=nightmonkey-x86_64-windows.exe ;; + *) echo "No released NightMonkey compiler is available for $platform/$machine; set NIGHTMONKEY" >&2; return 1 ;; + esac + printf '%s/%s\n' "$(dirname "$0")" "$artifact" +} + if [ $# -lt 1 ] then usage @@ -31,10 +57,15 @@ OUT_FILE="" LEGACY_SCRIPT_PARAM="" STARLING_ARGS="" VERBOSE=0 +ENABLE_NIGHTMONKEY=0 while [ $# -gt 0 ] do case "$1" in + --enable-nightmonkey) + ENABLE_NIGHTMONKEY=1 + shift + ;; --legacy-script) LEGACY_SCRIPT_PARAM="$1 " IN_FILE="$2" @@ -83,6 +114,27 @@ then usage fi +nightmonkey="" +if [[ $ENABLE_NIGHTMONKEY -ne 0 ]]; then + if [[ $nightmonkey_aot -eq 0 ]]; then + echo "This runtime was not built with NightMonkey support" >&2 + exit 1 + fi + if [[ $aot -ne 0 ]]; then + echo "NightMonkey and weval cannot be enabled together" >&2 + exit 1 + fi + if [[ -z $IN_FILE ]]; then + echo "NightMonkey requires an input script to snapshot" >&2 + exit 1 + fi + nightmonkey="$(find_nightmonkey)" + if [[ ! -x $nightmonkey ]]; then + echo "NightMonkey compiler not found or not executable: $nightmonkey; set NIGHTMONKEY to the matching compiler from this StarlingMonkey release" >&2 + exit 1 + fi +fi + # Use the -o param as output file if provided, otherwise use the input base name with a .wasm # extension. if [ -z "$OUT_FILE" ] @@ -122,6 +174,27 @@ if [[ -n "$IN_FILE" ]]; then $preopen_dir -o "$OUT_FILE" \ "$(dirname "$0")/starling-raw.wasm" fi + + if [[ $ENABLE_NIGHTMONKEY -ne 0 ]]; then + SNAPSHOT_FILE="$(mktemp "${TMPDIR:-/tmp}/nightmonkey-snapshot.XXXXXX")" + COMPILED_FILE="$(mktemp "${TMPDIR:-/tmp}/nightmonkey-output.XXXXXX")" + cleanup_nightmonkey() { + rm -f "$SNAPSHOT_FILE" "$COMPILED_FILE" + } + trap cleanup_nightmonkey EXIT + NIGHTMONKEY_ARGS=() + if [[ $VERBOSE -ne 0 ]]; then + NIGHTMONKEY_ARGS+=(--stats --keep-names) + fi + cp "$OUT_FILE" "$SNAPSHOT_FILE" + if [[ -n ${NIGHTMONKEY_KEEP_SNAPSHOT:-} ]]; then + cp "$SNAPSHOT_FILE" "$NIGHTMONKEY_KEEP_SNAPSHOT" + fi + "$nightmonkey" "${NIGHTMONKEY_ARGS[@]}" "$SNAPSHOT_FILE" -o "$COMPILED_FILE" + mv "$COMPILED_FILE" "$OUT_FILE" + cleanup_nightmonkey + trap - EXIT + fi else echo "Creating runtime-eval component $OUT_FILE" cp "$(dirname "$0")/starling-raw.wasm" "$OUT_FILE" diff --git a/justfile b/justfile index f0be229b..c201ce5d 100644 --- a/justfile +++ b/justfile @@ -20,13 +20,13 @@ default: build target="all" *flags: #!/usr/bin/env bash set -euo pipefail - echo 'Setting build directory to {{ builddir }}, build type {{ if mode == "weval" { "Release (weval)" } else { capitalize(mode) } }}' + echo 'Setting build directory to {{ builddir }}, build type {{ if mode == "weval" { "Release (weval)" } else if mode == "nightmonkey" { "Release (NightMonkey)" } else { capitalize(mode) } }}' # Only run configure step if build directory doesn't exist yet if ! {{ path_exists(builddir) }} || {{ reconfigure }} = 'true'; then cmake -S . -B {{ builddir }} {{ flags }} \ - -DCMAKE_BUILD_TYPE={{ if mode == "weval" { "Release" } else { capitalize(mode) } }} \ - {{ if mode == "weval" { "-DUSE_WASM_OPT=OFF -DWEVAL=ON" } else { "" } }} + -DCMAKE_BUILD_TYPE={{ if mode == "weval" || mode == "nightmonkey" { "Release" } else { capitalize(mode) } }} \ + {{ if mode == "weval" { "-DUSE_WASM_OPT=OFF -DWEVAL=ON" } else if mode == "nightmonkey" { "-DNIGHTMONKEY=ON" } else { "" } }} else echo 'build directory already exists, skipping cmake configure' fi diff --git a/runtime/engine.cpp b/runtime/engine.cpp index e9da55dd..c4667f5f 100644 --- a/runtime/engine.cpp +++ b/runtime/engine.cpp @@ -7,6 +7,9 @@ #include "js/CompilationAndEvaluation.h" #include "js/Modules.h" +#ifdef ENABLE_JS_NIGHTMONKEY +# include "js/NightMonkey.h" +#endif #include "js/ForOfIterator.h" #include "js/Initialization.h" #include "js/Promise.h" @@ -524,6 +527,11 @@ const mozilla::Maybe &Engine::init_location() const { void Engine::finish_pre_initialization() { MOZ_ASSERT(state_ == EngineState::ScriptPreInitializing); +#ifdef ENABLE_JS_NIGHTMONKEY + if (!JS::NightCaptureSnapshotHeap(cx())) { + abort("capturing NightMonkey snapshot state"); + } +#endif js::ResetMathRandomSeed(ENGINE->cx()); state_ = EngineState::Initialized; } diff --git a/runtime/js.cpp b/runtime/js.cpp index dd3a9774..cbc81c65 100644 --- a/runtime/js.cpp +++ b/runtime/js.cpp @@ -11,6 +11,9 @@ #include "wasi/api.h" #include "wasi/libc-environ.h" #include "wizer.h" +#ifdef ENABLE_JS_NIGHTMONKEY +# include "js/NightMonkey.h" +#endif #ifdef MEM_STATS #include #endif @@ -86,6 +89,11 @@ WIZER_INIT(wizen); * load the file `./index.js` and run it as the top-level module script. */ extern "C" bool exports_wasi_cli_run_run() { +#ifdef ENABLE_JS_NIGHTMONKEY + if (ENGINE && !JS::NightActivate(ENGINE->cx())) { + return false; + } +#endif auto arg_strings = host_api::environment_get_arguments(); std::vector args; args.reserve(arg_strings.size()); diff --git a/runtime/script_loader.cpp b/runtime/script_loader.cpp index e7d50eb9..9d0a937c 100644 --- a/runtime/script_loader.cpp +++ b/runtime/script_loader.cpp @@ -1,4 +1,8 @@ #include "script_loader.h" + +#ifdef ENABLE_JS_NIGHTMONKEY +# include "js/NightMonkey.h" +#endif #include "encode.h" #include @@ -542,6 +546,16 @@ bool ScriptLoader::eval_top_level_script(std::string_view path, } // Execute the top-level module script. +#ifdef ENABLE_JS_NIGHTMONKEY + if (ENGINE->state() == api::EngineState::ScriptPreInitializing) { + JS::RootedScript root(cx, MODULE_MODE ? JS::GetModuleScript(module) : script.get()); + if (!root || !JS::NightRegisterRoot(cx, root, true) || + !JS::NightCaptureSnapshotExtras(cx, root)) { + return false; + } + } +#endif + if (!MODULE_MODE) { return JS_ExecuteScript(cx, script, result); } diff --git a/tests/e2e/nightmonkey-fallback/expect_serve_body.txt b/tests/e2e/nightmonkey-fallback/expect_serve_body.txt new file mode 100644 index 00000000..c60cf781 --- /dev/null +++ b/tests/e2e/nightmonkey-fallback/expect_serve_body.txt @@ -0,0 +1 @@ +fallback 42 diff --git a/tests/e2e/nightmonkey-fallback/nightmonkey-fallback.js b/tests/e2e/nightmonkey-fallback/nightmonkey-fallback.js new file mode 100644 index 00000000..23345f45 --- /dev/null +++ b/tests/e2e/nightmonkey-fallback/nightmonkey-fallback.js @@ -0,0 +1,7 @@ +function interpreted(value) { + return eval("value + 1"); +} + +addEventListener("fetch", event => { + event.respondWith(new Response(`fallback ${interpreted(41)}\n`)); +}); diff --git a/tests/e2e/runtime-err/expect_serve_stderr_nightmonkey.txt b/tests/e2e/runtime-err/expect_serve_stderr_nightmonkey.txt new file mode 100644 index 00000000..82d4e5a1 --- /dev/null +++ b/tests/e2e/runtime-err/expect_serve_stderr_nightmonkey.txt @@ -0,0 +1,4 @@ +stderr [0] :: Error while running request handler: runtime error +stderr [0] :: Caused by: error cause +stderr [0] :: Stack: +stderr [0] :: diff --git a/tests/test.sh b/tests/test.sh index 84f22d95..7849f9e4 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -15,6 +15,9 @@ test_serve_body_expectation="$test_dir/expect_serve_body.txt" test_serve_headers_expectation="$test_dir/expect_serve_headers.txt" test_serve_stdout_expectation="$test_dir/expect_serve_stdout.txt" test_serve_stderr_expectation="$test_dir/expect_serve_stderr.txt" +if [[ "$componentize_flags" == *--enable-nightmonkey* ]] && [ -f "$test_dir/expect_serve_stderr_nightmonkey.txt" ]; then + test_serve_stderr_expectation="$test_dir/expect_serve_stderr_nightmonkey.txt" +fi test_serve_status_expectation=$(cat "$test_dir/expect_serve_status.txt" 2> /dev/null || echo "200") body_log="$test_dir/body.log" diff --git a/tests/tests.cmake b/tests/tests.cmake index d7d6b086..e6356a3e 100644 --- a/tests/tests.cmake +++ b/tests/tests.cmake @@ -4,10 +4,16 @@ find_program(BASH_PROGRAM bash) include("wasmtime") include("weval") +if(NIGHTMONKEY) + set(TEST_COMPONENTIZE_FLAGS "--enable-nightmonkey") +else() + set(TEST_COMPONENTIZE_FLAGS "") +endif() + function(test_e2e TEST_NAME) get_target_property(RUNTIME_DIR starling-raw.wasm BINARY_DIR) add_test(e2e-${TEST_NAME} ${BASH_PROGRAM} ${CMAKE_SOURCE_DIR}/tests/test.sh ${RUNTIME_DIR} ${CMAKE_SOURCE_DIR}/tests/e2e/${TEST_NAME}) - set_property(TEST e2e-${TEST_NAME} PROPERTY ENVIRONMENT "WASMTIME=${WASMTIME};WASM_TOOLS=${WASM_TOOLS_DIR}/wasm-tools") + set_property(TEST e2e-${TEST_NAME} PROPERTY ENVIRONMENT "WASMTIME=${WASMTIME};WASM_TOOLS=${WASM_TOOLS_DIR}/wasm-tools;COMPONENTIZE_FLAGS=${TEST_COMPONENTIZE_FLAGS}") set_tests_properties(e2e-${TEST_NAME} PROPERTIES TIMEOUT 120) endfunction() @@ -31,7 +37,7 @@ function(integration_tests) add_custom_command( OUTPUT test-server.wasm WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E env "WASM_TOOLS=${WASM_TOOLS_DIR}/wasm-tools" env "PREOPEN_DIR=${CMAKE_SOURCE_DIR}/tests" ${RUNTIME_DIR}/componentize.sh ${TESTS_DIR}/test-server.js test-server.wasm + COMMAND ${CMAKE_COMMAND} -E env "WASM_TOOLS=${WASM_TOOLS_DIR}/wasm-tools" env "PREOPEN_DIR=${CMAKE_SOURCE_DIR}/tests" ${RUNTIME_DIR}/componentize.sh ${TEST_COMPONENTIZE_FLAGS} ${TESTS_DIR}/test-server.js test-server.wasm DEPENDS ${DEPS} VERBATIM ) @@ -61,6 +67,7 @@ test_e2e(teed-stream-as-outgoing-body) test_e2e(init-script) test_e2e(no-init-location) test_e2e(init-location) +test_e2e(nightmonkey-fallback) integration_tests( blob diff --git a/tests/wpt-harness/wpt.cmake b/tests/wpt-harness/wpt.cmake index a00b08cc..afe8eece 100644 --- a/tests/wpt-harness/wpt.cmake +++ b/tests/wpt-harness/wpt.cmake @@ -8,6 +8,8 @@ include("manage-git-source") if(WEVAL) set(COMPONENTIZE_FLAGS "--aot") +elseif(NIGHTMONKEY) + set(COMPONENTIZE_FLAGS "--enable-nightmonkey") else() set(COMPONENTIZE_FLAGS "") endif() From e483dea00388d16bb8c490004eb74599d5cc195d Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 22 Sep 2026 17:11:02 -0700 Subject: [PATCH 2/6] Build NightMonkey from its own repository. --- .github/workflows/main.yml | 33 ++++-- .github/workflows/release-please.yml | 23 ++-- CMakeLists.txt | 3 +- builtins/web/fetch/fetch_event.cpp | 2 +- cmake/manage-git-source.cmake | 107 +++++++++++++----- cmake/night-runtime-compile.cmake | 106 ++++++++++++++++++ cmake/nightmonkey.cmake | 159 +++++++++++++++++++++++++++ cmake/spidermonkey.cmake | 26 +++-- runtime/engine.cpp | 10 +- runtime/js.cpp | 2 +- runtime/script_loader.cpp | 7 +- 11 files changed, 418 insertions(+), 60 deletions(-) create mode 100644 cmake/night-runtime-compile.cmake create mode 100644 cmake/nightmonkey.cmake diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d4749102..3d9f975d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -104,7 +104,15 @@ jobs: mkdir -p spidermonkey-dist-${{ matrix.build }} cp -a cmake-build-${{ matrix.build }}/spidermonkey-obj/dist/libspidermonkey.a spidermonkey-dist-${{ matrix.build }}/ cp -aL cmake-build-${{ matrix.build }}/spidermonkey-obj/dist/include spidermonkey-dist-${{ matrix.build }}/ - tree spidermonkey-dist-${{ matrix.build }} + if [ "${{ matrix.build }}" = "nightmonkey" ]; then + # The NightMonkey runtime is compiled against the engine's private + # headers and with the flags libjs was built with, which a + # --enable-external-compiler-hooks build exports alongside the + # public headers (see cmake/nightmonkey.cmake). + cp -aL cmake-build-${{ matrix.build }}/spidermonkey-obj/dist/include-private spidermonkey-dist-${{ matrix.build }}/ + cp -aL cmake-build-${{ matrix.build }}/spidermonkey-obj/dist/system_wrappers spidermonkey-dist-${{ matrix.build }}/ + fi + tree -L 2 spidermonkey-dist-${{ matrix.build }} # Upload tarball as an artifact of the github action run, so the output # can be inspected for pull requests. @@ -183,9 +191,10 @@ jobs: tag_name: ${{ needs.test.outputs.SM_TAG }} files: release-artifacts/* + # The NightMonkey compiler is a host binary that transforms a wizened + # snapshot of the (NightMonkey-enabled) runtime. nightmonkey-compilers: name: NightMonkey compiler (${{ matrix.artifact }}) - needs: test strategy: fail-fast: false matrix: @@ -205,21 +214,25 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Read Firefox revision - id: firefox-revision + - name: Read NightMonkey pins + id: nightmonkey shell: bash run: | - revision="$(awk '/^set\(SM_TAG / {gsub(/set\(SM_TAG |\)/, ""); print}' cmake/spidermonkey.cmake)" + revision="$(awk '/^set\(NIGHTMONKEY_TAG / {gsub(/set\(NIGHTMONKEY_TAG |\)/, ""); print}' cmake/nightmonkey.cmake)" + engine="$(awk '/^set\(NIGHTMONKEY_ENGINE_VERSION / {gsub(/"/, "", $2); print $2}' cmake/nightmonkey.cmake)" echo "revision=$revision" >> "$GITHUB_OUTPUT" + echo "engine=$engine" >> "$GITHUB_OUTPUT" - uses: actions/checkout@v4 with: - repository: bytecodealliance/firefox - ref: ${{ steps.firefox-revision.outputs.revision }} - path: firefox + repository: bytecodealliance/nightmonkey + ref: ${{ steps.nightmonkey.outputs.revision }} + path: nightmonkey - name: Build compiler + shell: bash run: | - cargo build --release --manifest-path firefox/js/src/night/nightmonkey/Cargo.toml - compiler="firefox/js/src/night/nightmonkey/target/release/${{ matrix.binary }}" + cargo build --release -p nightmonkey --manifest-path nightmonkey/Cargo.toml \ + --no-default-features --features "${{ steps.nightmonkey.outputs.engine }}" + compiler="nightmonkey/target/release/${{ matrix.binary }}" "$compiler" --help cp "$compiler" "${{ matrix.artifact }}" - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index cf289c8b..e9d32079 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -85,6 +85,9 @@ jobs: tag_name: ${{ needs.release-please.outputs.tag_name }} files: release-artifacts/* + # The NightMonkey compiler for each supported host, built as in the CI + # workflow (see the nightmonkey-compilers job in main.yml) and uploaded next + # to the starling-raw-nightmonkey.wasm runtime it belongs to. build-nightmonkey-compilers: needs: release-please if: ${{ needs.release-please.outputs.release_created }} @@ -109,21 +112,25 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Read Firefox revision - id: firefox-revision + - name: Read NightMonkey pins + id: nightmonkey shell: bash run: | - revision="$(awk '/^set\(SM_TAG / {gsub(/set\(SM_TAG |\)/, ""); print}' cmake/spidermonkey.cmake)" + revision="$(awk '/^set\(NIGHTMONKEY_TAG / {gsub(/set\(NIGHTMONKEY_TAG |\)/, ""); print}' cmake/nightmonkey.cmake)" + engine="$(awk '/^set\(NIGHTMONKEY_ENGINE_VERSION / {gsub(/"/, "", $2); print $2}' cmake/nightmonkey.cmake)" echo "revision=$revision" >> "$GITHUB_OUTPUT" + echo "engine=$engine" >> "$GITHUB_OUTPUT" - uses: actions/checkout@v4 with: - repository: bytecodealliance/firefox - ref: ${{ steps.firefox-revision.outputs.revision }} - path: firefox + repository: bytecodealliance/nightmonkey + ref: ${{ steps.nightmonkey.outputs.revision }} + path: nightmonkey - name: Build compiler + shell: bash run: | - cargo build --release --manifest-path firefox/js/src/night/nightmonkey/Cargo.toml - compiler="firefox/js/src/night/nightmonkey/target/release/${{ matrix.binary }}" + cargo build --release -p nightmonkey --manifest-path nightmonkey/Cargo.toml \ + --no-default-features --features "${{ steps.nightmonkey.outputs.engine }}" + compiler="nightmonkey/target/release/${{ matrix.binary }}" "$compiler" --help cp "$compiler" "${{ matrix.artifact }}" - name: Upload Release Asset diff --git a/CMakeLists.txt b/CMakeLists.txt index d0ae7c82..5638ac41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ include("wasmtime") include("cbindgen") include("spidermonkey") +include("nightmonkey") include("openssl") include("${HOST_API}/host_api.cmake") include("build-crates") @@ -49,7 +50,7 @@ add_library(extension_api INTERFACE include/extension-api.h runtime/encode.h run if (ENABLE_JS_DEBUGGER) target_compile_definitions(extension_api INTERFACE ENABLE_JS_DEBUGGER) endif() -target_link_libraries(extension_api INTERFACE rust-url spidermonkey) +target_link_libraries(extension_api INTERFACE rust-url spidermonkey nightmonkey) target_include_directories(extension_api INTERFACE include deps/include runtime) include("builtins") diff --git a/builtins/web/fetch/fetch_event.cpp b/builtins/web/fetch/fetch_event.cpp index 7188156b..ffd94b39 100644 --- a/builtins/web/fetch/fetch_event.cpp +++ b/builtins/web/fetch/fetch_event.cpp @@ -13,7 +13,7 @@ #include #include #ifdef ENABLE_JS_NIGHTMONKEY -# include +# include "runtime/NightRegistration.h" #endif #include diff --git a/cmake/manage-git-source.cmake b/cmake/manage-git-source.cmake index 50cef2c9..047d1ea8 100644 --- a/cmake/manage-git-source.cmake +++ b/cmake/manage-git-source.cmake @@ -1,4 +1,5 @@ -# Function to manage git-based source dependencies with shallow cloning and tag management +# Function to manage git-based source dependencies with shallow cloning and tag management. +# TAG may be either a git tag name or a full 40-character commit hash. function(manage_git_source) cmake_parse_arguments( GIT_SRC @@ -17,19 +18,37 @@ function(manage_git_source) # Use file locking to prevent concurrent clone operations file(LOCK ${LOCK_FILE} GUARD FUNCTION) + # CMake's regex syntax has no bounded repetition, hence the length check. + string(LENGTH "${GIT_SRC_TAG}" GIT_SRC_TAG_LENGTH) + if(GIT_SRC_TAG_LENGTH EQUAL 40 AND GIT_SRC_TAG MATCHES "^[0-9a-f]+$") + set(GIT_SRC_IS_COMMIT TRUE) + else() + set(GIT_SRC_IS_COMMIT FALSE) + endif() + # Check if source directory already exists and has the correct tag set(NEED_CLONE TRUE) set(NEED_CHECKOUT FALSE) if(EXISTS ${GIT_SRC_SOURCE_DIR}/.git) - # Check current tag - execute_process( - COMMAND git -C ${GIT_SRC_SOURCE_DIR} describe --tags --exact-match HEAD - OUTPUT_VARIABLE CURRENT_TAG - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - RESULT_VARIABLE TAG_CHECK_RESULT - ) + # Check current revision + if(GIT_SRC_IS_COMMIT) + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} rev-parse HEAD + OUTPUT_VARIABLE CURRENT_TAG + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE TAG_CHECK_RESULT + ) + else() + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} describe --tags --exact-match HEAD + OUTPUT_VARIABLE CURRENT_TAG + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE TAG_CHECK_RESULT + ) + endif() if(TAG_CHECK_RESULT EQUAL 0 AND CURRENT_TAG STREQUAL ${GIT_SRC_TAG}) set(NEED_CLONE FALSE) @@ -49,50 +68,86 @@ function(manage_git_source) file(REMOVE_RECURSE ${GIT_SRC_SOURCE_DIR}) endif() - # Perform shallow clone of specific tag - execute_process( - COMMAND git clone --depth 1 --branch ${GIT_SRC_TAG} - ${GIT_SRC_REPO_URL} - ${GIT_SRC_SOURCE_DIR} - RESULT_VARIABLE CLONE_RESULT - ERROR_VARIABLE CLONE_ERROR - ) + if(GIT_SRC_IS_COMMIT) + # `git clone --branch` only accepts branch and tag names, so a + # commit is fetched into a fresh repository instead. + file(MAKE_DIRECTORY ${GIT_SRC_SOURCE_DIR}) + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} init --quiet + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} remote add origin ${GIT_SRC_REPO_URL} + COMMAND_ERROR_IS_FATAL ANY + ) + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} fetch --depth 1 origin ${GIT_SRC_TAG} + RESULT_VARIABLE CLONE_RESULT + ERROR_VARIABLE CLONE_ERROR + ) + if(CLONE_RESULT EQUAL 0) + execute_process( + COMMAND git -C ${GIT_SRC_SOURCE_DIR} checkout --detach FETCH_HEAD + RESULT_VARIABLE CLONE_RESULT + ERROR_VARIABLE CLONE_ERROR + ) + endif() + else() + # Perform shallow clone of specific tag + execute_process( + COMMAND git clone --depth 1 --branch ${GIT_SRC_TAG} + ${GIT_SRC_REPO_URL} + ${GIT_SRC_SOURCE_DIR} + RESULT_VARIABLE CLONE_RESULT + ERROR_VARIABLE CLONE_ERROR + ) + endif() if(NOT CLONE_RESULT EQUAL 0) message(FATAL_ERROR "Failed to clone ${GIT_SRC_NAME} source: ${CLONE_ERROR}") endif() elseif(NEED_CHECKOUT) - # Check if the tag already exists locally + # Check if the revision already exists locally + if(GIT_SRC_IS_COMMIT) + set(GIT_SRC_REF "${GIT_SRC_TAG}^{commit}") + else() + set(GIT_SRC_REF "refs/tags/${GIT_SRC_TAG}") + endif() execute_process( - COMMAND git -C ${GIT_SRC_SOURCE_DIR} rev-parse --verify "refs/tags/${GIT_SRC_TAG}" + COMMAND git -C ${GIT_SRC_SOURCE_DIR} rev-parse --verify --quiet "${GIT_SRC_REF}" OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE TAG_EXISTS_RESULT ) if(NOT TAG_EXISTS_RESULT EQUAL 0) - # Tag doesn't exist locally, fetch it - message(STATUS "Fetching tag ${GIT_SRC_TAG}") + # Revision doesn't exist locally, fetch it + message(STATUS "Fetching ${GIT_SRC_TAG}") + if(GIT_SRC_IS_COMMIT) + set(FETCH_REF ${GIT_SRC_TAG}) + else() + set(FETCH_REF tag ${GIT_SRC_TAG}) + endif() execute_process( - COMMAND git -C ${GIT_SRC_SOURCE_DIR} fetch --depth 1 origin tag ${GIT_SRC_TAG} + COMMAND git -C ${GIT_SRC_SOURCE_DIR} fetch --depth 1 ${GIT_SRC_REPO_URL} ${FETCH_REF} RESULT_VARIABLE FETCH_RESULT ERROR_VARIABLE FETCH_ERROR ) if(NOT FETCH_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to fetch tag ${GIT_SRC_TAG}: ${FETCH_ERROR}") + message(FATAL_ERROR "Failed to fetch ${GIT_SRC_TAG}: ${FETCH_ERROR}") endif() endif() - # Checkout the tag (whether it was already local or just fetched) + # Checkout the revision (whether it was already local or just fetched) execute_process( - COMMAND git -C ${GIT_SRC_SOURCE_DIR} checkout ${GIT_SRC_TAG} + COMMAND git -C ${GIT_SRC_SOURCE_DIR} checkout --detach ${GIT_SRC_TAG} RESULT_VARIABLE CHECKOUT_RESULT ERROR_VARIABLE CHECKOUT_ERROR ) if(NOT CHECKOUT_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to checkout tag ${GIT_SRC_TAG}: ${CHECKOUT_ERROR}") + message(FATAL_ERROR "Failed to checkout ${GIT_SRC_TAG}: ${CHECKOUT_ERROR}") endif() endif() endfunction() diff --git a/cmake/night-runtime-compile.cmake b/cmake/night-runtime-compile.cmake new file mode 100644 index 00000000..4e200ccd --- /dev/null +++ b/cmake/night-runtime-compile.cmake @@ -0,0 +1,106 @@ +# Compiles one NightMonkey runtime source file against SpiderMonkey's private +# headers, with the flags libjs itself was compiled with, so that the object, +# shape, string and script layouts the runtime bakes into its code match the +# engine it is linked with. Both come from dist/include-private, which a +# `--enable-external-compiler-hooks` build of SpiderMonkey exports: +# js-build-config.json records the flags, and the headers mirror js/src. +# +# Run in script mode from cmake/nightmonkey.cmake: +# cmake -DCXX= -DSPIDERMONKEY_DIST= -DNIGHTMONKEY_SOURCE_DIR= +# -DSOURCE= -DOBJECT= [-DDEPFILE=] +# -P night-runtime-compile.cmake +# +# This mirrors the flag handling of NightMonkey's own CMakeLists.txt, except +# that the compiler is the one StarlingMonkey builds with (the same one that +# built libjs) rather than the path recorded in the JSON, which does not +# survive being moved to another machine as pre-built artifacts. +cmake_minimum_required(VERSION 3.27) + +foreach(var CXX SPIDERMONKEY_DIST NIGHTMONKEY_SOURCE_DIR SOURCE OBJECT) + if (NOT DEFINED ${var}) + message(FATAL_ERROR "night-runtime-compile.cmake: ${var} is required") + endif() +endforeach() + +set(SM_CONFIG_JSON "${SPIDERMONKEY_DIST}/include-private/js-build-config.json") +if (NOT EXISTS "${SM_CONFIG_JSON}") + message(FATAL_ERROR "${SM_CONFIG_JSON} not found: SpiderMonkey must be built with --enable-external-compiler-hooks") +endif() +file(READ "${SM_CONFIG_JSON}" SM_JSON) + +function(sm_json_list out key) + string(JSON n LENGTH "${SM_JSON}" ${key}) + set(result) + if (n GREATER 0) + math(EXPR last "${n} - 1") + foreach(i RANGE ${last}) + string(JSON v GET "${SM_JSON}" ${key} ${i}) + list(APPEND result "${v}") + endforeach() + endif() + set(${out} "${result}" PARENT_SCOPE) +endfunction() + +sm_json_list(SM_CXX_BASE_FLAGS cxx_base_flags) +sm_json_list(SM_OS_CXXFLAGS os_cxxflags) +sm_json_list(SM_OPTIMIZE_FLAGS optimize_flags) +sm_json_list(SM_DEBUG_FLAGS debug_flags) +sm_json_list(SM_EXTRA_CXXFLAGS extra_cxxflags) +sm_json_list(SM_WARNINGS_CXXFLAGS warnings_cxxflags) +sm_json_list(SM_DEBUG_DEFINES debug_defines) +sm_json_list(SM_LIBRARY_DEFINES library_defines) +sm_json_list(SM_FORCE_INCLUDES force_includes) +sm_json_list(SM_INCLUDE_DIRS include_dirs) + +# The base flags are the target, standard and (if one was configured) the +# sysroot. A sysroot recorded on another machine is dropped in favor of the +# compiler's own. +set(FLAGS) +set(skip_next FALSE) +foreach(flag ${SM_CXX_BASE_FLAGS}) + if (skip_next) + set(skip_next FALSE) + if (EXISTS "${flag}") + list(APPEND FLAGS "--sysroot" "${flag}") + endif() + elseif (flag STREQUAL "--sysroot") + set(skip_next TRUE) + elseif (flag MATCHES "^--sysroot=(.*)$") + if (EXISTS "${CMAKE_MATCH_1}") + list(APPEND FLAGS "${flag}") + endif() + else() + list(APPEND FLAGS "${flag}") + endif() +endforeach() + +list(APPEND FLAGS ${SM_OS_CXXFLAGS} ${SM_OPTIMIZE_FLAGS} ${SM_DEBUG_FLAGS} ${SM_EXTRA_CXXFLAGS}) +foreach(define ${SM_DEBUG_DEFINES}) + list(APPEND FLAGS "-D${define}=1") +endforeach() +foreach(define ${SM_LIBRARY_DEFINES}) + list(APPEND FLAGS "-D${define}") +endforeach() +list(APPEND FLAGS "-DENABLE_JS_NIGHTMONKEY=1") +# The configuration headers every engine TU sees. +foreach(header ${SM_FORCE_INCLUDES}) + list(APPEND FLAGS "-include" "${SPIDERMONKEY_DIST}/include-private/${header}") +endforeach() +list(APPEND FLAGS "-I${NIGHTMONKEY_SOURCE_DIR}") +foreach(dir ${SM_INCLUDE_DIRS}) + list(APPEND FLAGS "-I${SPIDERMONKEY_DIST}/${dir}") +endforeach() +# The engine's own warning set (its headers are not clean under a plain -Wall), +# plus what the runtime's sources need. +list(APPEND FLAGS ${SM_WARNINGS_CXXFLAGS} -Wno-invalid-offsetof -Wno-unused-private-field) +if (DEFINED DEPFILE) + list(APPEND FLAGS -MD -MF "${DEPFILE}") +endif() + +execute_process( + COMMAND ${CXX} ${FLAGS} -c "${SOURCE}" -o "${OBJECT}" + RESULT_VARIABLE result +) +if (NOT result EQUAL 0) + message(FATAL_ERROR "Compiling ${SOURCE} failed") +endif() diff --git a/cmake/nightmonkey.cmake b/cmake/nightmonkey.cmake new file mode 100644 index 00000000..63c8468b --- /dev/null +++ b/cmake/nightmonkey.cmake @@ -0,0 +1,159 @@ +# NightMonkey: an ahead-of-time JS-to-Wasm compiler layered on top of +# SpiderMonkey (https://github.com/bytecodealliance/nightmonkey). +# +# NightMonkey has two halves, both built here from a pinned checkout of its +# repository: +# +# - The runtime (`runtime/*.cpp` in the NightMonkey tree): the helpers that +# compiled code calls into, the snapshot registration and activation, and the +# engine's external compiler hook table. It is compiled against the private +# headers and with the exact flags of the SpiderMonkey build it is linked +# with, both of which a `--enable-external-compiler-hooks` build exports under +# dist/include-private, and linked into starling-raw.wasm. +# - The `nightmonkey` host binary: a cargo build of the compiler that +# transforms a wizened snapshot of the runtime into one with compiled bodies. +# componentize.sh runs it when `--enable-nightmonkey` is passed. +# +# The compiler and the runtime share layout facts with the engine, so all +# three must come from the same trees: the NightMonkey pin below and the +# SpiderMonkey pin in cmake/spidermonkey.cmake move together. The compiler +# carries a checked-in opcode table per engine version, and the build checks +# the selected one against the engine's Opcodes.h (below). +set(NIGHTMONKEY_TAG 9f9170b0b2faf12541f3f31048ce0264bd7b0b05) +set(NIGHTMONKEY_REPO_URL https://github.com/bytecodealliance/nightmonkey.git) +set(NIGHTMONKEY_ENGINE_VERSION "ff147" CACHE STRING + "Engine version NightMonkey is built for (compiler/src/opcodes/.rs in its tree)") + +include("manage-git-source") + +# Everything that links against SpiderMonkey also links against this target; +# it is empty unless NIGHTMONKEY is enabled. +add_library(nightmonkey INTERFACE) + +if (NOT NIGHTMONKEY) + return() +endif() + +# Like SM_SOURCE_DIR, this can point at a local checkout for in-tree +# development of NightMonkey itself. +set(NIGHTMONKEY_SOURCE_DIR "${CMAKE_SOURCE_DIR}/deps/nightmonkey-source" CACHE PATH + "Path to a local NightMonkey source checkout") +if (NIGHTMONKEY_SOURCE_DIR STREQUAL "${CMAKE_SOURCE_DIR}/deps/nightmonkey-source") + manage_git_source( + NAME nightmonkey + REPO_URL ${NIGHTMONKEY_REPO_URL} + TAG ${NIGHTMONKEY_TAG} + SOURCE_DIR ${NIGHTMONKEY_SOURCE_DIR} + ) +elseif (NOT EXISTS "${NIGHTMONKEY_SOURCE_DIR}/runtime/Night.h") + message(FATAL_ERROR "NIGHTMONKEY_SOURCE_DIR does not contain a NightMonkey checkout: ${NIGHTMONKEY_SOURCE_DIR}") +else() + message(STATUS "Using NightMonkey source from ${NIGHTMONKEY_SOURCE_DIR}") +endif() + +# The SpiderMonkey dist/ directory: `include/`, `include-private/` (with +# js-build-config.json) and `system_wrappers/`, whether built from source or +# unpacked from pre-built artifacts. +set(NIGHTMONKEY_SM_DIST ${SM_LIB_DIR}) +set(NIGHTMONKEY_OBJ_DIR ${CMAKE_CURRENT_BINARY_DIR}/nightmonkey-obj) +file(MAKE_DIRECTORY ${NIGHTMONKEY_OBJ_DIR}) + +# --- the opcode table check -------------------------------------------------- +# +# NightMonkey's compiler does not read the engine at build time: its opcode +# table is generated ahead of time and checked in per engine version, +# selected by the cargo feature of the same name. This verifies that the +# selected table is what the SpiderMonkey being built against generates, so +# an engine whose bytecode differs is refused rather than miscompiled. The +# runtime and compiler builds depend on it (and it on the engine build). +find_program(NIGHTMONKEY_PYTHON python3 REQUIRED DOC "python3, for NightMonkey's scripts/gen_opcodes.py") +add_custom_target(nightmonkey_opcodes_check + COMMAND ${NIGHTMONKEY_PYTHON} ${NIGHTMONKEY_SOURCE_DIR}/scripts/gen_opcodes.py check + ${NIGHTMONKEY_ENGINE_VERSION} ${NIGHTMONKEY_SM_DIST}/include-private/vm/Opcodes.h + COMMENT "Checking NightMonkey's ${NIGHTMONKEY_ENGINE_VERSION} opcode table against SpiderMonkey's Opcodes.h" + VERBATIM +) +if (TARGET spidermonkey_build) + add_dependencies(nightmonkey_opcodes_check spidermonkey_build) +endif() + +# --- the runtime ------------------------------------------------------------- +# +# The compile flags come from js-build-config.json, which only exists once +# SpiderMonkey has been built, so each source is compiled by a script that +# reads them at build time (cmake/night-runtime-compile.cmake). The engine +# build is ordered first through a target-level dependency (a file-level one +# on the engine library would make the Makefile generator run the engine build +# once per consuming target); the headers each object actually depends on, +# including the engine's, are tracked through the compiler's depfile. +file(GLOB NIGHT_RUNTIME_SOURCES ${NIGHTMONKEY_SOURCE_DIR}/runtime/*.cpp) +# The in-process compilation lane (the jit-test harness) is not part of the +# snapshot flow. +list(FILTER NIGHT_RUNTIME_SOURCES EXCLUDE REGEX "/NightInproc[A-Za-z]*\\.cpp$") +set(NIGHT_RUNTIME_COMPILE_SCRIPT ${CMAKE_SOURCE_DIR}/cmake/night-runtime-compile.cmake) + +set(NIGHT_RUNTIME_OBJS) +foreach(source ${NIGHT_RUNTIME_SOURCES}) + get_filename_component(name ${source} NAME_WE) + set(object ${NIGHTMONKEY_OBJ_DIR}/${name}.o) + add_custom_command( + OUTPUT ${object} + COMMAND ${CMAKE_COMMAND} + -DCXX=${CMAKE_CXX_COMPILER} + -DSPIDERMONKEY_DIST=${NIGHTMONKEY_SM_DIST} + -DNIGHTMONKEY_SOURCE_DIR=${NIGHTMONKEY_SOURCE_DIR} + -DSOURCE=${source} + -DOBJECT=${object} + -DDEPFILE=${object}.d + -P ${NIGHT_RUNTIME_COMPILE_SCRIPT} + DEPENDS ${source} ${NIGHT_RUNTIME_COMPILE_SCRIPT} + DEPFILE ${object}.d + COMMENT "Compiling NightMonkey runtime: ${name}.cpp" + VERBATIM + ) + list(APPEND NIGHT_RUNTIME_OBJS ${object}) +endforeach() + +set(NIGHT_RUNTIME_LIB ${NIGHTMONKEY_OBJ_DIR}/libnight_runtime.a) +add_custom_command( + OUTPUT ${NIGHT_RUNTIME_LIB} + COMMAND ${CMAKE_COMMAND} -E rm -f ${NIGHT_RUNTIME_LIB} + COMMAND ${CMAKE_AR} qcs ${NIGHT_RUNTIME_LIB} ${NIGHT_RUNTIME_OBJS} + DEPENDS ${NIGHT_RUNTIME_OBJS} + COMMENT "Creating NightMonkey runtime library" + VERBATIM +) +add_custom_target(night_runtime_build DEPENDS ${NIGHT_RUNTIME_LIB}) +add_dependencies(night_runtime_build nightmonkey_opcodes_check) + +# --- the compiler ------------------------------------------------------------ +find_program(CARGO_BIN cargo REQUIRED DOC "cargo, for building the NightMonkey compiler") +set(NIGHTMONKEY_CARGO_TARGET_DIR ${CMAKE_CURRENT_BINARY_DIR}/nightmonkey-cargo) +set(NIGHTMONKEY_BIN "${NIGHTMONKEY_CARGO_TARGET_DIR}/release/nightmonkey" CACHE FILEPATH + "Path to the NightMonkey compiler" FORCE) +add_custom_target(nightmonkey_compiler + # Self-contained apart from the engine version, which selects the + # checked-in opcode table. + COMMAND ${CARGO_BIN} build --release -p nightmonkey + --no-default-features --features ${NIGHTMONKEY_ENGINE_VERSION} + --manifest-path ${NIGHTMONKEY_SOURCE_DIR}/Cargo.toml + --target-dir ${NIGHTMONKEY_CARGO_TARGET_DIR} + BYPRODUCTS ${NIGHTMONKEY_BIN} + # Run from the StarlingMonkey tree so rustup picks up its rust-toolchain.toml. + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Building the NightMonkey compiler" + VERBATIM +) +add_dependencies(nightmonkey_compiler nightmonkey_opcodes_check) + +# --- the interface target ---------------------------------------------------- +add_dependencies(nightmonkey night_runtime_build nightmonkey_compiler) +# `runtime/Night.h` etc. are included from the NightMonkey tree, and gate their +# declarations on ENABLE_JS_NIGHTMONKEY. +target_include_directories(nightmonkey INTERFACE ${NIGHTMONKEY_SOURCE_DIR}) +target_compile_definitions(nightmonkey INTERFACE ENABLE_JS_NIGHTMONKEY=1) +# The whole runtime is linked in: the compiled bodies the snapshot transform +# appends call its exported helpers by name, so nothing in it may be dropped as +# unreferenced. +target_link_libraries(nightmonkey INTERFACE + -Wl,--whole-archive ${NIGHT_RUNTIME_LIB} -Wl,--no-whole-archive) diff --git a/cmake/spidermonkey.cmake b/cmake/spidermonkey.cmake index edb4b1d3..1605dcdc 100644 --- a/cmake/spidermonkey.cmake +++ b/cmake/spidermonkey.cmake @@ -1,4 +1,7 @@ -set(SM_TAG 9f1a4ce9a631039e6898985d842afaf16cebc89e) +# A tag or full commit hash in SM_REPO_URL. The NightMonkey build needs a +# revision that carries `--enable-external-compiler-hooks` (the `wasi-ff147` +# branch). +set(SM_TAG f0c060c52260d41fc631a14cda05372ad1c2cbe1) set(SM_REPO_URL https://github.com/bytecodealliance/firefox.git) include("manage-git-source") @@ -10,7 +13,7 @@ else() endif() option(WEVAL "Build with a SpiderMonkey variant that supports weval-based AOT compilation" OFF) -option(NIGHTMONKEY "Build with NightMonkey AOT compilation support" OFF) +option(NIGHTMONKEY "Build with NightMonkey AOT compilation support (see cmake/nightmonkey.cmake)" OFF) if (WEVAL AND NIGHTMONKEY) message(FATAL_ERROR "WEVAL and NIGHTMONKEY cannot be enabled together") @@ -57,6 +60,15 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/null.cpp "") if (DEFINED SM_LIB_DIR) set(SM_INCLUDE_DIR ${SM_LIB_DIR}/include) + # NightMonkey's runtime is compiled against the engine's private headers, + # which a `--enable-external-compiler-hooks` build exports next to the + # public ones (see cmake/nightmonkey.cmake). + if (NIGHTMONKEY AND NOT EXISTS ${SM_LIB_DIR}/include-private/js-build-config.json) + message(FATAL_ERROR "The pre-built SpiderMonkey artifacts in ${SM_LIB_DIR} do not include the \ + private headers NightMonkey needs (include-private/). Use artifacts from a NightMonkey \ + build, or unset SPIDERMONKEY_BINARIES to build SpiderMonkey from source.") + endif() + add_library(spidermonkey INTERFACE) target_include_directories(spidermonkey INTERFACE ${SM_INCLUDE_DIR}) target_link_libraries(spidermonkey INTERFACE ${SM_LIB_DIR}/libspidermonkey.a) @@ -164,7 +176,10 @@ mk_add_options AUTOCLOBBER=1 string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-aot-ics-force\n") string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-pbl-weval\n") elseif(NIGHTMONKEY) - string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-nightmonkey\n") + # The external compiler hook surface NightMonkey plugs into, plus the + # export of the engine's private headers and compile flags that its + # runtime is built against. + string(APPEND MOZCONFIG_CONTENT "ac_add_options --enable-external-compiler-hooks\n") endif() file(GENERATE OUTPUT ${MOZCONFIG} CONTENT "${MOZCONFIG_CONTENT}") @@ -202,11 +217,6 @@ mk_add_options AUTOCLOBBER=1 ) add_custom_target(spidermonkey_build DEPENDS ${LIB_SM}) - if(NIGHTMONKEY) - set(NIGHTMONKEY_BIN "${SM_OBJ_DIR}/dist/host/bin/nightmonkey" CACHE FILEPATH - "Path to the NightMonkey compiler" FORCE) - endif() - add_library(spidermonkey INTERFACE) add_dependencies(spidermonkey spidermonkey_build) target_include_directories(spidermonkey INTERFACE ${SM_INCLUDE_DIR}) diff --git a/runtime/engine.cpp b/runtime/engine.cpp index c4667f5f..1feabb93 100644 --- a/runtime/engine.cpp +++ b/runtime/engine.cpp @@ -8,7 +8,8 @@ #include "js/CompilationAndEvaluation.h" #include "js/Modules.h" #ifdef ENABLE_JS_NIGHTMONKEY -# include "js/NightMonkey.h" +# include "runtime/Night.h" +# include "runtime/NightHooks.h" #endif #include "js/ForOfIterator.h" #include "js/Initialization.h" @@ -311,6 +312,11 @@ bool init_js(const EngineConfig& config) { if (!cx) { return false; } +#ifdef ENABLE_JS_NIGHTMONKEY + // NightMonkey plugs into the engine through its external compiler hook + // table, which has to be registered before any script runs. + js::night::NightInstallHooks(JS_GetRuntime(cx)); +#endif CONTEXT = cx; SCRIPT_VALUE.init(cx); @@ -528,7 +534,7 @@ const mozilla::Maybe &Engine::init_location() const { void Engine::finish_pre_initialization() { MOZ_ASSERT(state_ == EngineState::ScriptPreInitializing); #ifdef ENABLE_JS_NIGHTMONKEY - if (!JS::NightCaptureSnapshotHeap(cx())) { + if (!js::NightSnapshotCaptureHeap(cx())) { abort("capturing NightMonkey snapshot state"); } #endif diff --git a/runtime/js.cpp b/runtime/js.cpp index cbc81c65..be886b43 100644 --- a/runtime/js.cpp +++ b/runtime/js.cpp @@ -12,7 +12,7 @@ #include "wasi/libc-environ.h" #include "wizer.h" #ifdef ENABLE_JS_NIGHTMONKEY -# include "js/NightMonkey.h" +# include "runtime/NightRegistration.h" #endif #ifdef MEM_STATS #include diff --git a/runtime/script_loader.cpp b/runtime/script_loader.cpp index 9d0a937c..4e30e19d 100644 --- a/runtime/script_loader.cpp +++ b/runtime/script_loader.cpp @@ -1,7 +1,8 @@ #include "script_loader.h" #ifdef ENABLE_JS_NIGHTMONKEY -# include "js/NightMonkey.h" +# include "runtime/Night.h" +# include "runtime/NightRegistration.h" #endif #include "encode.h" @@ -549,8 +550,8 @@ bool ScriptLoader::eval_top_level_script(std::string_view path, #ifdef ENABLE_JS_NIGHTMONKEY if (ENGINE->state() == api::EngineState::ScriptPreInitializing) { JS::RootedScript root(cx, MODULE_MODE ? JS::GetModuleScript(module) : script.get()); - if (!root || !JS::NightRegisterRoot(cx, root, true) || - !JS::NightCaptureSnapshotExtras(cx, root)) { + if (!root || !JS::NightRegisterRoot(cx, root, /* executedAtInit = */ true) || + !js::NightSnapshotCaptureExtras(cx, root)) { return false; } } From 239ff92cea944fdd1094df84b891a01c8ea2f659 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 22 Sep 2026 23:20:03 -0700 Subject: [PATCH 3/6] Fix justfile --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index c201ce5d..d71194db 100644 --- a/justfile +++ b/justfile @@ -25,7 +25,7 @@ build target="all" *flags: # Only run configure step if build directory doesn't exist yet if ! {{ path_exists(builddir) }} || {{ reconfigure }} = 'true'; then cmake -S . -B {{ builddir }} {{ flags }} \ - -DCMAKE_BUILD_TYPE={{ if mode == "weval" || mode == "nightmonkey" { "Release" } else { capitalize(mode) } }} \ + -DCMAKE_BUILD_TYPE={{ if mode == "weval" { "Release" } else if mode == "nightmonkey" { "Release" } else { capitalize(mode) } }} \ {{ if mode == "weval" { "-DUSE_WASM_OPT=OFF -DWEVAL=ON" } else if mode == "nightmonkey" { "-DNIGHTMONKEY=ON" } else { "" } }} else echo 'build directory already exists, skipping cmake configure' From b9a989074939bd07986d49f9683edb27036afa20 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 22 Sep 2026 23:24:24 -0700 Subject: [PATCH 4/6] Avoid verbose NightMonkey output. --- componentize.sh.in | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/componentize.sh.in b/componentize.sh.in index 3d83d387..585e7862 100755 --- a/componentize.sh.in +++ b/componentize.sh.in @@ -19,6 +19,7 @@ usage() { echo " Specifying '--strip-path-prefix' will cause the provided prefix to be stripped from paths in stack traces and the debugger" echo " Specifying '--legacy-script' causes evaluation as a legacy JS script instead of a module" echo " Specifying '--enable-nightmonkey' AOT-compiles the wizened core module with NightMonkey" + echo " Set NIGHTMONKEY_FLAGS (e.g. '--stats') to pass extra diagnostic options to the NightMonkey compiler" echo " Specifying '--wpt-mode' enables WPT compatibility mode" echo " Specifying '--init-location url' allows setting the URL to use for 'globalThis.location' during initialization" exit 1 @@ -184,7 +185,13 @@ if [[ -n "$IN_FILE" ]]; then trap cleanup_nightmonkey EXIT NIGHTMONKEY_ARGS=() if [[ $VERBOSE -ne 0 ]]; then - NIGHTMONKEY_ARGS+=(--stats --keep-names) + NIGHTMONKEY_ARGS+=(--keep-names) + fi + # Compiler diagnostics (e.g. `--stats`) are opt-in via NIGHTMONKEY_FLAGS + # rather than tied to --verbose, since they are very noisy. + if [[ -n ${NIGHTMONKEY_FLAGS:-} ]]; then + read -r -a NIGHTMONKEY_EXTRA_ARGS <<< "$NIGHTMONKEY_FLAGS" + NIGHTMONKEY_ARGS+=("${NIGHTMONKEY_EXTRA_ARGS[@]}") fi cp "$OUT_FILE" "$SNAPSHOT_FILE" if [[ -n ${NIGHTMONKEY_KEEP_SNAPSHOT:-} ]]; then From fa4d65f8bf19a78d5d06de270395f7e11bdebf1a Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 23 Sep 2026 11:39:24 -0700 Subject: [PATCH 5/6] Fix the NightMonkey build when StarlingMonkey is a CMake subproject, and edit NightMonkey tag. --- cmake/nightmonkey.cmake | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmake/nightmonkey.cmake b/cmake/nightmonkey.cmake index 63c8468b..c1db3b31 100644 --- a/cmake/nightmonkey.cmake +++ b/cmake/nightmonkey.cmake @@ -19,7 +19,7 @@ # SpiderMonkey pin in cmake/spidermonkey.cmake move together. The compiler # carries a checked-in opcode table per engine version, and the build checks # the selected one against the engine's Opcodes.h (below). -set(NIGHTMONKEY_TAG 9f9170b0b2faf12541f3f31048ce0264bd7b0b05) +set(NIGHTMONKEY_TAG 751695b96cb77becbd8be5d2a5497f7ba1d54fe2) set(NIGHTMONKEY_REPO_URL https://github.com/bytecodealliance/nightmonkey.git) set(NIGHTMONKEY_ENGINE_VERSION "ff147" CACHE STRING "Engine version NightMonkey is built for (compiler/src/opcodes/.rs in its tree)") @@ -90,7 +90,7 @@ file(GLOB NIGHT_RUNTIME_SOURCES ${NIGHTMONKEY_SOURCE_DIR}/runtime/*.cpp) # The in-process compilation lane (the jit-test harness) is not part of the # snapshot flow. list(FILTER NIGHT_RUNTIME_SOURCES EXCLUDE REGEX "/NightInproc[A-Za-z]*\\.cpp$") -set(NIGHT_RUNTIME_COMPILE_SCRIPT ${CMAKE_SOURCE_DIR}/cmake/night-runtime-compile.cmake) +set(NIGHT_RUNTIME_COMPILE_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/night-runtime-compile.cmake) set(NIGHT_RUNTIME_OBJS) foreach(source ${NIGHT_RUNTIME_SOURCES}) @@ -139,8 +139,9 @@ add_custom_target(nightmonkey_compiler --manifest-path ${NIGHTMONKEY_SOURCE_DIR}/Cargo.toml --target-dir ${NIGHTMONKEY_CARGO_TARGET_DIR} BYPRODUCTS ${NIGHTMONKEY_BIN} - # Run from the StarlingMonkey tree so rustup picks up its rust-toolchain.toml. - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + # Run from the StarlingMonkey tree so rustup picks up its rust-toolchain.toml + # (which is not CMAKE_SOURCE_DIR when StarlingMonkey is a subproject). + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/.. COMMENT "Building the NightMonkey compiler" VERBATIM ) From 0425ec4110702b4d8f8a622018496aa4eec205b2 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 24 Sep 2026 08:44:10 -0700 Subject: [PATCH 6/6] Review feedback. --- .github/workflows/release-please.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index e9d32079..5fd68b5b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -74,7 +74,6 @@ jobs: # Supporting artifacts cp build-release/preview1-adapter.wasm release-artifacts cp build-weval/starling-ics.wevalcache release-artifacts - cp build-nightmonkey/componentize.sh release-artifacts echo "Release artifacts:" ls -la release-artifacts/