Skip to content

Commit 5e5e6c8

Browse files
committed
Introduce NightMonkey: an ahead-of-time JS-to-Wasm compiler.
**NightMonkey** is an ahead-of-time JS-to-Wasm compilation tier built inside SpiderMonkey. NIGHT expands to *Nonlocal Inference with Guiding Heuristics for Types*: an optimistic whole-program type analysis guides code generation, with dynamic guards for correctness. (The night monkey, genus *Aotus*, is the only truly nocturnal monkey: it does its work in the night, before the program runs during the day.) Each JS function's bytecode is compiled to a WebAssembly function that runs alongside the runtime compiled to Wasm. There are two modes of use: - **Snapshot** (the shipping flow): the `nightmonkey` host binary drives Wizer in-process to snapshot the runtime plus loaded user program (or processes an existing Wizer snapshot), reads out JS bytecode and heap objects (such as prototype objects), and rewrites that snapshot with compiled bodies. - **In-process** (the testing flow): the JS shell compiled to Wasm runs under `wasm-jit-runner`, walks its own live heap, compiles the script tree, and injects the bodies into its running instance via runner hostcalls (`--night-inprocess`). A drop-in shell for jit-tests. NightMonkey has a two-part structure: an *optimistic static type analysis* and a *guard-based codegen backend*. The idea is that we: 1. "Predict" types statically, using a model of JavaScript semantics that is intentionally optimistic (elides corner-cases). We call this the "likelier-types analysis" (in a nod to the initial version of the analysis, the "likely-types analysis"; this one is a little better). 2. Generate an optimistic Wasm body for a given JS function bytecode body, using those predicted types. 3. Insert dynamic guards checking those assumptions, with fallbacks to a fully generic (but still compiled!) Wasm body. The *key constraint* that NightMonkey adheres to, and attempts to solve: we cannot derive type information, or any other profiling information, by observing a running program. In other words, unlike the standard JIT approach based on the "JIT hypothesis" (that a warmed-up program will reach a steady state with stable types, which we can then specialize for), we must decide any specialization we will do ahead-of-time, based on whatever analysis or heuristics we can come up with. The thing we permit ourselves in return is much more analysis time: unlike a JIT engine, we do not need to compile in milliseconds. NightMonkey performs its analysis using a whole-program, call-sensitive, points-to (heap abstraction) + callgraph analysis, over a lattice that is a hybrid of a Steensgaard (union-find-based) and capped Andersen (points-to-set/membership-based) design. The codegen using the types that come out of this analysis is then a "two-track" approach: there is one optimistic track that adheres to "type contexts" that are maximally optimal, and one fully generic track. (Earlier experiments tried to do more multiversioning, a la Static Basic Block Versioning, but that did not converge well.) As of 2026-09-04, comparing to native IonMonkey and baseline tiers, and against Wasm-hosted interpreter and weval+PBL execution: ```plain bench native-ion nat-baseline wasm-interp weval aot aot/wasm-int aot/weval weval/wasm-int ion/weval ion/aot baseline/aot richards 29205 6489 377 936 11893 31.55 12.71 2.48 31.20 2.46 0.55 deltablue 28179 6870 395 978 6678 16.91 6.83 2.48 28.81 4.22 1.03 crypto 42755 5654 714 949 15696 21.98 16.54 1.33 45.05 2.72 0.36 raytrace 58549 11458 1045 1815 11964 11.45 6.59 1.74 32.26 4.89 0.96 earley-boyer 83262 21153 1510 3982 15088 9.99 3.79 2.64 20.91 5.52 1.40 navier-stokes 43926 8269 1223 2090 24980 20.43 11.95 1.71 21.02 1.76 0.33 splay 29291 23303 5248 6853 10693 2.04 1.56 1.31 4.27 2.74 2.18 regexp 18601 7223 596 766 2484 4.17 3.24 1.29 24.28 7.49 2.91 pdfjs 95804 40738 4116 6185 24743 6.01 4.00 1.50 15.49 3.87 1.65 mandreel 73940 11619 865 1269 19545 22.60 15.40 1.47 58.27 3.78 0.59 code-load 70224 69259 37108 37005 37271 1.00 1.01 1.00 1.90 1.88 1.86 box2d 99321 22370 1896 4135 25999 13.71 6.29 2.18 24.02 3.82 0.86 react-bench 0.631 1.415 15.026 10.421 2.862 5.25 3.64 1.44 16.52 4.54 2.02 geomean 49233 14111 1527 2569 14247 8.93 5.37 1.66 18.94 3.53 1.05 (octane = Score higher-better; react-bench = ms/render lower-better; best-of-3, taskset -c 1) (ratio cols = speedup of A over B, direction-corrected for react-bench; geomean row: lane cols over octane scores only, ratio cols over all benches) ``` We can conclude that NightMonkey is ~9x faster than the Wasm interpreter on average, or ~5x faster than weval+PBL. It is nearly on par with the native baseline compiler, and within ~3.5x of the IonMonkey optimized native-code ceiling (while running within a Wasm engine). On benchmarks where type-based specialization works especially well, NightMonkey comes within ~2.5x (e.g. Richards) of native Ion. This work has been done over the past ~6 months with the use of LLM-assisted code generation (mainly Claude Opus and Fable) but with careful line-by-line review and obsessive review of analysis results and generated code. I've read through the whole implementation, checked it to the best of my ability and will continue to do so. It is "a lot" but it does pass jit-tests cleanly. This commit message and the README are fully human-authored.
1 parent b8f4a57 commit 5e5e6c8

201 files changed

Lines changed: 98053 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: CI
2+
concurrency:
3+
group: ${{ github.head_ref || github.ref }}-${{ github.workflow }}
4+
cancel-in-progress: true
5+
on:
6+
push:
7+
branches: [main]
8+
pull_request:
9+
workflow_dispatch:
10+
defaults:
11+
run:
12+
shell: bash
13+
14+
# NightMonkey builds against a pinned branch of the SpiderMonkey fork that
15+
# carries the external compiler hook API.
16+
env:
17+
FIREFOX_REPO: bytecodealliance/firefox
18+
FIREFOX_REF: wasi-ff147
19+
SM_OBJDIR: obj-nightmonkey-sm
20+
21+
jobs:
22+
jit-test:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Resolve the SpiderMonkey revision
28+
id: smrev
29+
run: |
30+
sha=$(git ls-remote "https://github.com/$FIREFOX_REPO" "refs/heads/$FIREFOX_REF" | cut -f1)
31+
if [ -z "$sha" ]; then
32+
echo "::error::branch $FIREFOX_REF not found on https://github.com/$FIREFOX_REPO"
33+
exit 1
34+
fi
35+
echo "sha=$sha" >> "$GITHUB_OUTPUT"
36+
37+
- name: Cache the SpiderMonkey checkout
38+
id: cache-src
39+
uses: actions/cache@v4
40+
with:
41+
path: firefox
42+
key: firefox-src-${{ steps.smrev.outputs.sha }}
43+
44+
- name: Fetch SpiderMonkey (shallow)
45+
if: steps.cache-src.outputs.cache-hit != 'true'
46+
run: |
47+
git init firefox
48+
cd firefox
49+
git remote add origin "https://github.com/$FIREFOX_REPO"
50+
git fetch --depth 1 origin "${{ steps.smrev.outputs.sha }}"
51+
git checkout --detach FETCH_HEAD
52+
53+
- name: Cache the mach toolchains
54+
uses: actions/cache@v4
55+
with:
56+
path: ~/.mozbuild
57+
key: mozbuild-${{ runner.os }}-${{ steps.smrev.outputs.sha }}
58+
restore-keys: mozbuild-${{ runner.os }}-
59+
60+
- name: Bootstrap SpiderMonkey
61+
run: |
62+
cd firefox
63+
./mach --no-interactive bootstrap --application-choice=js
64+
# mach bootstrap only fetches host-target toolchains; the wasm32-wasi
65+
# sysroot (crt1.o, libc) must be fetched explicitly into ~/.mozbuild.
66+
rustup component add rust-src
67+
rustup target add wasm32-wasip1
68+
cd ~/.mozbuild && "$GITHUB_WORKSPACE/firefox/mach" --log-no-times artifact toolchain --from-build sysroot-wasm32-wasi
69+
70+
- name: Cache the SpiderMonkey objdir
71+
uses: actions/cache@v4
72+
with:
73+
path: firefox/${{ env.SM_OBJDIR }}
74+
key: firefox-obj-${{ runner.os }}-${{ steps.smrev.outputs.sha }}-${{ hashFiles('spidermonkey/mozconfig') }}
75+
76+
- name: Build SpiderMonkey
77+
run: scripts/build-spidermonkey.sh firefox
78+
79+
- name: Cache cargo
80+
uses: actions/cache@v4
81+
with:
82+
path: |
83+
~/.cargo/registry
84+
~/.cargo/git
85+
build/cargo
86+
key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
87+
restore-keys: cargo-${{ runner.os }}-
88+
89+
- name: Build NightMonkey
90+
run: |
91+
cmake -S . -B build -DSPIDERMONKEY_DIST="$GITHUB_WORKSPACE/firefox/$SM_OBJDIR/dist"
92+
cmake --build build
93+
94+
- name: jit-tests (AOT lane)
95+
run: scripts/run-jit-tests.sh firefox build -- --format=automation

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build*/
2+
target/
3+
__pycache__/
4+
*.pyc

‎CMakeLists.txt‎

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# NightMonkey: an AOT compiler from JavaScript to Wasm, layered on top of
2+
# SpiderMonkey. This build consumes the dist/ directory of a SpiderMonkey
3+
# build made with --enable-external-compiler-hooks (spidermonkey/mozconfig):
4+
# its private headers, the compile flags libjs was built with
5+
# (include-private/js-build-config.json) and the archives under lib/.
6+
#
7+
# cmake -S . -B build -DSPIDERMONKEY_DIST=/path/to/firefox/obj-nightmonkey-sm/dist
8+
# cmake --build build
9+
#
10+
# Produces, under build/bin:
11+
# js the NightMonkey wasm shell (the snapshot flow's shell)
12+
# js-inproc the same shell with the in-process compiler linked in
13+
# (the test lane; NIGHT_INPROCESS)
14+
# nightmonkey the host AOT compiler (snapshot in, module out)
15+
# wasm-jit-runner the host that runs the in-process test lane
16+
# inproc-shell.sh the shell wrapper the test harnesses use
17+
18+
cmake_minimum_required(VERSION 3.20)
19+
20+
set(SPIDERMONKEY_DIST "" CACHE PATH
21+
"dist/ directory of a SpiderMonkey build with --enable-external-compiler-hooks")
22+
option(NIGHT_INPROCESS "Build the in-process test lane (guest compiler, wasm-jit-runner)" ON)
23+
option(NIGHT_DEBUG "Runtime diagnostics and crash-on-failure in the in-process lane" OFF)
24+
set(NIGHT_CARGO "cargo" CACHE STRING "cargo executable")
25+
set(NIGHT_STACK_SIZE "67108864" CACHE STRING "linear-memory stack size of the shells, in bytes")
26+
set(NIGHT_CARGO_TARGET_DIR "" CACHE PATH "cargo target directory (default: <build>/cargo; share it between build trees)")
27+
28+
if(NOT SPIDERMONKEY_DIST)
29+
message(FATAL_ERROR "SPIDERMONKEY_DIST is required: the dist/ directory of a SpiderMonkey build made with --enable-external-compiler-hooks (see spidermonkey/mozconfig)")
30+
endif()
31+
get_filename_component(SPIDERMONKEY_DIST "${SPIDERMONKEY_DIST}" ABSOLUTE)
32+
set(SM_CONFIG_JSON "${SPIDERMONKEY_DIST}/include-private/js-build-config.json")
33+
if(NOT EXISTS "${SM_CONFIG_JSON}")
34+
message(FATAL_ERROR "${SM_CONFIG_JSON} not found: is this a --enable-external-compiler-hooks build?")
35+
endif()
36+
file(READ "${SM_CONFIG_JSON}" SM_JSON)
37+
38+
function(sm_json_list out key)
39+
string(JSON n LENGTH "${SM_JSON}" ${key})
40+
set(result)
41+
if(n GREATER 0)
42+
math(EXPR last "${n} - 1")
43+
foreach(i RANGE ${last})
44+
string(JSON v GET "${SM_JSON}" ${key} ${i})
45+
list(APPEND result "${v}")
46+
endforeach()
47+
endif()
48+
set(${out} "${result}" PARENT_SCOPE)
49+
endfunction()
50+
51+
# The compiler command lines libjs was built with: the executable plus the
52+
# sysroot, standard and target flags.
53+
string(JSON SM_CXX_CMD GET "${SM_JSON}" cxx)
54+
string(JSON SM_CC_CMD GET "${SM_JSON}" cc)
55+
separate_arguments(SM_CXX_CMD NATIVE_COMMAND "${SM_CXX_CMD}")
56+
separate_arguments(SM_CC_CMD NATIVE_COMMAND "${SM_CC_CMD}")
57+
list(GET SM_CXX_CMD 0 SM_CXX)
58+
list(GET SM_CC_CMD 0 SM_CC)
59+
list(REMOVE_AT SM_CXX_CMD 0)
60+
list(REMOVE_AT SM_CC_CMD 0)
61+
sm_json_list(SM_OS_CXXFLAGS os_cxxflags)
62+
sm_json_list(SM_OPTIMIZE_FLAGS optimize_flags)
63+
sm_json_list(SM_DEBUG_FLAGS debug_flags)
64+
sm_json_list(SM_EXTRA_CXXFLAGS extra_cxxflags)
65+
sm_json_list(SM_DEBUG_DEFINES debug_defines)
66+
sm_json_list(SM_LIBRARY_DEFINES library_defines)
67+
sm_json_list(SM_FORCE_INCLUDES force_includes)
68+
sm_json_list(SM_INCLUDE_DIRS include_dirs)
69+
sm_json_list(SM_LIBRARIES libraries)
70+
string(JSON SM_RUST_TARGET GET "${SM_JSON}" rust_target)
71+
string(JSON SM_OS_ARCH GET "${SM_JSON}" os_arch)
72+
73+
set(CMAKE_SYSTEM_NAME Generic)
74+
set(CMAKE_SYSTEM_PROCESSOR wasm32)
75+
set(CMAKE_C_COMPILER "${SM_CC}")
76+
set(CMAKE_CXX_COMPILER "${SM_CXX}")
77+
set(CMAKE_C_COMPILER_WORKS TRUE)
78+
set(CMAKE_CXX_COMPILER_WORKS TRUE)
79+
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
80+
set(CMAKE_EXECUTABLE_SUFFIX "")
81+
82+
project(nightmonkey C CXX)
83+
84+
# Every engine-facing TU is compiled exactly as libjs was: same sysroot,
85+
# standard, target, ABI-affecting flags, defines and force-included
86+
# configuration headers, so the private headers describe the same layouts.
87+
set(NIGHT_CXX_FLAGS ${SM_CXX_CMD} ${SM_OS_CXXFLAGS} ${SM_OPTIMIZE_FLAGS}
88+
${SM_DEBUG_FLAGS} ${SM_EXTRA_CXXFLAGS})
89+
set(NIGHT_DEFINES ENABLE_JS_NIGHTMONKEY=1)
90+
foreach(d ${SM_DEBUG_DEFINES})
91+
list(APPEND NIGHT_DEFINES "${d}=1")
92+
endforeach()
93+
foreach(d ${SM_LIBRARY_DEFINES})
94+
list(APPEND NIGHT_DEFINES "${d}")
95+
endforeach()
96+
if(NIGHT_DEBUG)
97+
list(APPEND NIGHT_DEFINES NIGHTMONKEY_DEBUG=1)
98+
endif()
99+
set(NIGHT_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}")
100+
foreach(d ${SM_INCLUDE_DIRS})
101+
list(APPEND NIGHT_INCLUDE_DIRS "${SPIDERMONKEY_DIST}/${d}")
102+
endforeach()
103+
set(NIGHT_FORCE_INCLUDE_FLAGS)
104+
foreach(h ${SM_FORCE_INCLUDES})
105+
# SHELL: keeps CMake from de-duplicating the repeated -include.
106+
list(APPEND NIGHT_FORCE_INCLUDE_FLAGS "SHELL:-include ${SPIDERMONKEY_DIST}/include-private/${h}")
107+
endforeach()
108+
109+
function(night_engine_target name)
110+
target_compile_options(${name} PRIVATE ${NIGHT_CXX_FLAGS} ${NIGHT_FORCE_INCLUDE_FLAGS}
111+
-Wall -Wno-invalid-offsetof -Wno-unused-private-field)
112+
target_compile_definitions(${name} PRIVATE ${NIGHT_DEFINES})
113+
target_include_directories(${name} PRIVATE ${NIGHT_INCLUDE_DIRS})
114+
endfunction()
115+
116+
# The in-process sources are compiled into the runtime archive but only pulled
117+
# into a shell that references them (js-inproc); the snapshot shell stays free
118+
# of the runner's hostcall imports.
119+
function(night_inprocess_target name)
120+
target_compile_definitions(${name} PRIVATE ENABLE_JS_NIGHTMONKEY_INPROCESS=1)
121+
endfunction()
122+
123+
# --- the night runtime -------------------------------------------------------
124+
set(NIGHT_RUNTIME_SOURCES
125+
runtime/NightEntry.cpp
126+
runtime/NightGenerator.cpp
127+
runtime/NightHooks.cpp
128+
runtime/NightInlineCaches.cpp
129+
runtime/NightInlineHeap.cpp
130+
runtime/NightOps.cpp
131+
runtime/NightRegExp.cpp
132+
runtime/NightRegistration.cpp
133+
runtime/NightRuntime.cpp
134+
runtime/NightRuntimeSlots.cpp
135+
runtime/NightSnapshotExtras.cpp
136+
runtime/NightStack.cpp)
137+
if(NIGHT_INPROCESS)
138+
list(APPEND NIGHT_RUNTIME_SOURCES runtime/NightInproc.cpp runtime/NightInprocHost.cpp)
139+
endif()
140+
add_library(night_runtime STATIC ${NIGHT_RUNTIME_SOURCES})
141+
night_engine_target(night_runtime)
142+
if(NIGHT_INPROCESS)
143+
night_inprocess_target(night_runtime)
144+
endif()
145+
146+
# --- cargo-built pieces ----------------------------------------------------------
147+
set(CARGO_TARGET_DIR "${CMAKE_BINARY_DIR}/cargo")
148+
if(NIGHT_CARGO_TARGET_DIR)
149+
set(CARGO_TARGET_DIR "${NIGHT_CARGO_TARGET_DIR}")
150+
endif()
151+
set(CARGO_ENV "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" "SPIDERMONKEY_DIST=${SPIDERMONKEY_DIST}")
152+
set(NIGHT_BIN_DIR "${CMAKE_BINARY_DIR}/bin")
153+
file(MAKE_DIRECTORY "${NIGHT_BIN_DIR}")
154+
155+
# The host AOT compiler (snapshot in, module out) with wizer driving built in.
156+
add_custom_target(nightmonkey_host ALL
157+
COMMAND ${CMAKE_COMMAND} -E env ${CARGO_ENV} RUSTFLAGS=-Cdebuginfo=0
158+
${NIGHT_CARGO} build --release -p nightmonkey --features wizen
159+
COMMAND ${CMAKE_COMMAND} -E copy_if_different
160+
"${CARGO_TARGET_DIR}/release/nightmonkey" "${NIGHT_BIN_DIR}/nightmonkey"
161+
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
162+
BYPRODUCTS "${NIGHT_BIN_DIR}/nightmonkey"
163+
COMMENT "cargo: nightmonkey (host)"
164+
VERBATIM)
165+
166+
if(NIGHT_INPROCESS)
167+
# The guest compiler: night-compiler and night-snapshot as one staticlib
168+
# for the shell's target, linked into the shell next to libjsrust.a.
169+
set(NIGHT_GUEST_LIB "${CARGO_TARGET_DIR}/${SM_RUST_TARGET}/release/libnight_guest.a")
170+
add_custom_target(night_guest ALL
171+
COMMAND ${CMAKE_COMMAND} -E env ${CARGO_ENV} RUSTFLAGS=-Cdebuginfo=0
172+
${NIGHT_CARGO} build --release -p night-guest --target ${SM_RUST_TARGET}
173+
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
174+
BYPRODUCTS "${NIGHT_GUEST_LIB}"
175+
COMMENT "cargo: night-guest (${SM_RUST_TARGET})"
176+
VERBATIM)
177+
178+
# The host that runs the in-process lane (hostcalls that inject compiled
179+
# bodies into the running shell instance).
180+
add_custom_target(wasm_jit_runner ALL
181+
COMMAND ${CMAKE_COMMAND} -E env ${CARGO_ENV} RUSTFLAGS=-Cdebuginfo=0
182+
${NIGHT_CARGO} build --release -p wasm-jit-runner
183+
COMMAND ${CMAKE_COMMAND} -E copy_if_different
184+
"${CARGO_TARGET_DIR}/release/wasm-jit-runner" "${NIGHT_BIN_DIR}/wasm-jit-runner"
185+
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
186+
BYPRODUCTS "${NIGHT_BIN_DIR}/wasm-jit-runner"
187+
COMMENT "cargo: wasm-jit-runner (host)"
188+
VERBATIM)
189+
endif()
190+
191+
# --- the shells --------------------------------------------------------------------
192+
set(NIGHT_SM_LIBS)
193+
foreach(l ${SM_LIBRARIES})
194+
list(APPEND NIGHT_SM_LIBS "${SPIDERMONKEY_DIST}/${l}")
195+
endforeach()
196+
197+
# Links a NightMonkey shell: the runtime, SpiderMonkey's shell/engine
198+
# archives and the libs. The memory and funcref table are exported (and the
199+
# table growable) so appended and injected code can share them; the deep
200+
# stack is for the in-process compiler's lowering, which recurses on the main
201+
# stack. The linker driver needs the same sysroot/target as the compiler.
202+
function(night_shell name)
203+
add_executable(${name} shell/nightshell.cpp)
204+
night_engine_target(${name})
205+
target_include_directories(${name} PRIVATE "${CMAKE_SOURCE_DIR}/third_party/wizer")
206+
set_target_properties(${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${NIGHT_BIN_DIR}")
207+
target_link_libraries(${name} PRIVATE night_runtime ${NIGHT_SM_LIBS} ${ARGN} m)
208+
target_link_options(${name} PRIVATE ${SM_CXX_CMD}
209+
-Wl,-z,stack-size=${NIGHT_STACK_SIZE} -Wl,--stack-first
210+
-Wl,--export-memory -Wl,--export-table -Wl,--growable-table
211+
-lwasi-emulated-process-clocks -lwasi-emulated-getpid)
212+
endfunction()
213+
214+
# The snapshot flow's shell: what `nightmonkey --shell` wizens.
215+
night_shell(js)
216+
217+
if(NIGHT_INPROCESS)
218+
# The test lane's shell, with the guest compiler linked in. Two Rust static
219+
# libraries meet here: SpiderMonkey's libjsrust.a, whose LTO-merged object
220+
# carries its own copy of std's unmangled runtime symbols
221+
# (rust_eh_personality, the panic and allocator shims), and the guest
222+
# compiler with the prebuilt std. The first definition wins and every copy
223+
# routes through the same allocator shim.
224+
night_shell(js-inproc "${NIGHT_GUEST_LIB}")
225+
night_inprocess_target(js-inproc)
226+
add_dependencies(js-inproc night_guest)
227+
target_link_options(js-inproc PRIVATE -Wl,--allow-multiple-definition)
228+
229+
# The harness wrapper: runs js-inproc under wasm-jit-runner.
230+
configure_file(scripts/inproc-shell.sh.in "${NIGHT_BIN_DIR}/inproc-shell.sh" @ONLY)
231+
file(CHMOD "${NIGHT_BIN_DIR}/inproc-shell.sh" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
232+
endif()

0 commit comments

Comments
 (0)