From a5ae0bee5f3197c37acc444a5ba944bc3c7ee1f1 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Thu, 13 Aug 2026 18:39:07 -0700 Subject: [PATCH] perf: invoke node directly from the js_binary launcher The launcher ran the program through our Node wrapper script, which does nothing but pass its arguments along to Node and add a `--require` flag for our fs patches. Let's just inline that into our main launcher script so that we avoid spawning another bash process unnecessarily. We still need to keep the Node wrapper around, though, so that if we ever shell out to `node` then we get our wrapper with its patches. Co-Authored-By: Claude Opus 5 (1M context) --- js/private/js_binary.sh.tpl | 20 +++++++++++++------- js/private/test/snapshots/launcher.sh | 20 +++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/js/private/js_binary.sh.tpl b/js/private/js_binary.sh.tpl index a5a50eb4e..a532bdd2d 100644 --- a/js/private/js_binary.sh.tpl +++ b/js/private/js_binary.sh.tpl @@ -385,7 +385,7 @@ for ARG in ${ALL_ARGS[@]+"${ALL_ARGS[@]}"}; do esac done -# Configure JS_BINARY__FS_PATCH_ROOTS for node fs patches which are run via --require in the node wrapper. +# Configure JS_BINARY__FS_PATCH_ROOTS for node fs patches which are run via --require below. # Don't override JS_BINARY__FS_PATCH_ROOTS if already set by an outer js_binary incase a js_binary such # as js_run_deverser runs another js_binary tool. if [ -z "${JS_BINARY__FS_PATCH_ROOTS:-}" ]; then @@ -481,8 +481,14 @@ fi # Run the main program # ============================================================================== +# Invoke node directly rather than through $JS_BINARY__NODE_WRAPPER. This way +# we avoid spawning an extra bash process on every launch. The wrapper is +# still put on the PATH as `node` so that child processes get the patched +# runtime. +node_cmd=("$JS_BINARY__NODE_BINARY" --require "$JS_BINARY__NODE_PATCHES") + if [ "${JS_BINARY__LOG_INFO:-}" ]; then - logf_info "$(echo -n "running" "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"})" + logf_info "$(echo -n "running" "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"})" fi # De-export capture-related vars so child processes (e.g. a nested js_binary) @@ -502,18 +508,18 @@ if [ -z "${JS_BINARY__EXPECTED_EXIT_CODE:-}" ] && [ -z "${JS_BINARY__EXIT_CODE_O # Nothing must run after node exits, so replace this shell with node. Signals # and terminal control are then delivered directly to node instead of being # proxied through a backgrounded child. - exec "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} + exec "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} exit 127 fi if [ "${STDOUT_CAPTURE:-}" ] && [ "${STDERR_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" 2>>"$STDERR_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" 2>>"$STDERR_CAPTURE" & elif [ "${STDOUT_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" & elif [ "${STDERR_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 2>>"$STDERR_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 2>>"$STDERR_CAPTURE" & else - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 & fi # ============================================================================== diff --git a/js/private/test/snapshots/launcher.sh b/js/private/test/snapshots/launcher.sh index ee471fe6d..20ba918af 100644 --- a/js/private/test/snapshots/launcher.sh +++ b/js/private/test/snapshots/launcher.sh @@ -505,7 +505,7 @@ for ARG in ${ALL_ARGS[@]+"${ALL_ARGS[@]}"}; do esac done -# Configure JS_BINARY__FS_PATCH_ROOTS for node fs patches which are run via --require in the node wrapper. +# Configure JS_BINARY__FS_PATCH_ROOTS for node fs patches which are run via --require below. # Don't override JS_BINARY__FS_PATCH_ROOTS if already set by an outer js_binary incase a js_binary such # as js_run_deverser runs another js_binary tool. if [ -z "${JS_BINARY__FS_PATCH_ROOTS:-}" ]; then @@ -601,8 +601,14 @@ fi # Run the main program # ============================================================================== +# Invoke node directly rather than through $JS_BINARY__NODE_WRAPPER. This way +# we avoid spawning an extra bash process on every launch. The wrapper is +# still put on the PATH as `node` so that child processes get the patched +# runtime. +node_cmd=("$JS_BINARY__NODE_BINARY" --require "$JS_BINARY__NODE_PATCHES") + if [ "${JS_BINARY__LOG_INFO:-}" ]; then - logf_info "$(echo -n "running" "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"})" + logf_info "$(echo -n "running" "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"})" fi # De-export capture-related vars so child processes (e.g. a nested js_binary) @@ -622,18 +628,18 @@ if [ -z "${JS_BINARY__EXPECTED_EXIT_CODE:-}" ] && [ -z "${JS_BINARY__EXIT_CODE_O # Nothing must run after node exits, so replace this shell with node. Signals # and terminal control are then delivered directly to node instead of being # proxied through a backgrounded child. - exec "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} + exec "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} exit 127 fi if [ "${STDOUT_CAPTURE:-}" ] && [ "${STDERR_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" 2>>"$STDERR_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" 2>>"$STDERR_CAPTURE" & elif [ "${STDOUT_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 >>"$STDOUT_CAPTURE" & elif [ "${STDERR_CAPTURE:-}" ]; then - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 2>>"$STDERR_CAPTURE" & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 2>>"$STDERR_CAPTURE" & else - "$JS_BINARY__NODE_WRAPPER" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 & + "${node_cmd[@]}" ${JS_BINARY__NODE_OPTIONS[@]+"${JS_BINARY__NODE_OPTIONS[@]}"} -- "$entry_point" ${ARGS[@]+"${ARGS[@]}"} <&0 & fi # ==============================================================================