Skip to content

Commit 5915fb7

Browse files
committed
fix: address PR review comments
- Copy fn pointer out of CUSTOM_MODULES lock before calling module declaration to prevent potential deadlock (spin::Mutex not re-entrant) - Move cargo:rerun-if-env-changed=HYPERLIGHT_JS_RUNTIME_PATH before the match so Cargo tracks the env var even when unset; use canonical path for rerun-if-changed - Lock down globalThis.console binding (non-writable/non-configurable) after Object.freeze to prevent handler code replacing it entirely - Fix test doc comment: built-in overrides work except for require Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
1 parent cd6829d commit 5915fb7

File tree

6 files changed

+55
-21
lines changed

6 files changed

+55
-21
lines changed

src/hyperlight-js-runtime/src/globals/freeze.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,19 @@ use rquickjs::Ctx;
2020
/// Called AFTER custom_globals! so extender crates can modify/extend
2121
/// globals first (e.g. adding console.warn/error/info/debug).
2222
///
23-
/// Frozen: console (Object.freeze), print (non-writable).
23+
/// Frozen: console (Object.freeze + non-writable/non-configurable binding),
24+
/// print (non-writable/non-configurable binding).
2425
/// Already frozen: require (non-configurable from setup),
2526
/// String.bytesFrom (on frozen String constructor).
2627
pub fn setup(ctx: &Ctx<'_>) -> rquickjs::Result<()> {
2728
ctx.eval::<(), _>(
2829
r#"
2930
if (typeof globalThis.console === 'object') {
3031
Object.freeze(globalThis.console);
32+
Object.defineProperty(globalThis, 'console', {
33+
writable: false,
34+
configurable: false
35+
});
3136
}
3237
if (typeof globalThis.print === 'function') {
3338
Object.defineProperty(globalThis, 'print', {

src/hyperlight-js-runtime/src/modules/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ pub struct NativeModuleLoader;
115115
impl Resolver for NativeModuleLoader {
116116
fn resolve(&mut self, _ctx: &Ctx<'_>, base: &str, name: &str) -> Result<String> {
117117
ensure_custom_modules_init();
118-
if CUSTOM_MODULES.lock().contains_key(name) || BUILTIN_MODULES.contains_key(name) {
118+
// Copy result out before dropping the lock to avoid holding it during resolution
119+
let found_custom = CUSTOM_MODULES.lock().contains_key(name);
120+
if found_custom || BUILTIN_MODULES.contains_key(name) {
119121
Ok(name.to_string())
120122
} else {
121123
Err(rquickjs::Error::new_resolving(base, name))
@@ -126,8 +128,12 @@ impl Resolver for NativeModuleLoader {
126128
impl Loader for NativeModuleLoader {
127129
fn load<'js>(&mut self, ctx: &Ctx<'js>, name: &str) -> Result<Module<'js>> {
128130
ensure_custom_modules_init();
129-
// Check custom modules first
130-
if let Some(decl) = CUSTOM_MODULES.lock().get(name) {
131+
// Copy the fn pointer out while holding the lock, then drop the guard
132+
// before calling the declaration. This avoids deadlock if decl()
133+
// triggers a nested module load that tries to lock CUSTOM_MODULES
134+
// (spin::Mutex is not re-entrant).
135+
let custom_decl = CUSTOM_MODULES.lock().get(name).copied();
136+
if let Some(decl) = custom_decl {
131137
return decl(ctx.clone(), name);
132138
}
133139
// Fall back to built-in modules

src/hyperlight-js-runtime/tests/fixtures/extended_runtime/Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hyperlight-js-runtime/tests/fixtures/extended_runtime/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ fn main() -> anyhow::Result<()> {
5050
use std::{env, fs};
5151

5252
let args: Vec<String> = env::args().collect();
53+
if args.len() < 3 {
54+
let program = args.first().map(|s| s.as_str()).unwrap_or("extended_runtime");
55+
eprintln!("Usage: {program} <handler_file> <event_json>");
56+
return Err(anyhow::anyhow!(
57+
"missing required arguments: <handler_file> and <event_json>"
58+
));
59+
}
5360
let file = std::path::PathBuf::from(&args[1]);
5461
let event = &args[2];
5562

src/hyperlight-js-runtime/tests/native_modules.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ limitations under the License.
2020
//! - `register_native_module` adds custom modules to the loader
2121
//! - Built-in modules always work
2222
//! - Custom modules can be imported from JS handlers
23-
//! - Built-in module names cannot be overridden (panics)
23+
//! - Overriding most built-in module names works; overriding `require` panics
2424
//! - The `native_modules!` macro generates correct `init_native_modules`
2525
//! - Full pipeline tests with the extended_runtime fixture binary
2626
@@ -346,7 +346,12 @@ const EXTENDED_RUNTIME_MANIFEST: &str = concat!(
346346

347347
static EXTENDED_RUNTIME_BINARY: LazyLock<PathBuf> = LazyLock::new(|| {
348348
let output = Command::new("cargo")
349-
.args(["build", "--manifest-path", EXTENDED_RUNTIME_MANIFEST])
349+
.args([
350+
"build",
351+
"--locked",
352+
"--manifest-path",
353+
EXTENDED_RUNTIME_MANIFEST,
354+
])
350355
.output()
351356
.expect("Failed to run cargo build for extended-runtime fixture");
352357

src/hyperlight-js/build.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,25 @@ fn build_js_runtime() -> PathBuf {
193193
}
194194

195195
fn bundle_runtime() {
196+
// Always rerun if the environment variable changes, even if it's currently unset.
197+
println!("cargo:rerun-if-env-changed=HYPERLIGHT_JS_RUNTIME_PATH");
198+
196199
let js_runtime_resource = match env::var("HYPERLIGHT_JS_RUNTIME_PATH") {
197200
Ok(path) => {
198-
println!("cargo:warning=Using custom JS runtime: {}", path);
199-
println!("cargo:rerun-if-env-changed=HYPERLIGHT_JS_RUNTIME_PATH");
200-
println!("cargo:rerun-if-changed={}", path);
201-
PathBuf::from(path)
201+
let canonical = PathBuf::from(&path)
202202
.canonicalize()
203-
.expect("HYPERLIGHT_JS_RUNTIME_PATH must point to a valid file")
203+
.expect("HYPERLIGHT_JS_RUNTIME_PATH must point to a valid file");
204+
assert!(
205+
canonical.is_file(),
206+
"HYPERLIGHT_JS_RUNTIME_PATH must point to a file, not a directory: {}",
207+
canonical.display()
208+
);
209+
println!(
210+
"cargo:warning=Using custom JS runtime: {}",
211+
canonical.display()
212+
);
213+
println!("cargo:rerun-if-changed={}", canonical.display());
214+
canonical
204215
}
205216
Err(_) => build_js_runtime(),
206217
};

0 commit comments

Comments
 (0)