From f547cc56630996d87ebb49032b149b990eff27a1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 4 Sep 2026 10:15:07 -0700 Subject: [PATCH] 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()