diff --git a/crates/neon/src/context/mod.rs b/crates/neon/src/context/mod.rs index 90d51543d..296fca5fc 100644 --- a/crates/neon/src/context/mod.rs +++ b/crates/neon/src/context/mod.rs @@ -242,9 +242,10 @@ pub trait Context<'a>: ContextInternal<'a> { /// Handles created in the new scope are kept alive only for the duration of the computation and cannot escape. /// /// This method can be useful for limiting the life of temporary values created during long-running computations, to prevent leaks. - fn execute_scoped(&mut self, f: F) -> T + fn execute_scoped<'b, T, F>(&mut self, f: F) -> T where - F: for<'b> FnOnce(ExecuteContext<'b>) -> T, + 'a: 'b, + F: FnOnce(ExecuteContext<'b>) -> T, { let env = self.env(); let scope = unsafe { HandleScope::new(env.to_raw()) }; @@ -263,17 +264,17 @@ pub trait Context<'a>: ContextInternal<'a> { /// Handles created in the new scope are kept alive only for the duration of the computation and cannot escape, with the exception of the result value, which is rooted in the outer context. /// /// This method can be useful for limiting the life of temporary values created during long-running computations, to prevent leaks. - fn compute_scoped(&mut self, f: F) -> JsResult<'a, V> + fn compute_scoped<'b, V, F>(&mut self, f: F) -> JsResult<'a, V> where + 'a: 'b, V: Value, - F: for<'b, 'c> FnOnce(ComputeContext<'b, 'c>) -> JsResult<'b, V>, + F: FnOnce(ComputeContext<'b>) -> JsResult<'b, V>, { let env = self.env(); let scope = unsafe { EscapableHandleScope::new(env.to_raw()) }; let cx = ComputeContext { env, phantom_inner: PhantomData, - phantom_outer: PhantomData, }; let escapee = unsafe { scope.escape(f(cx)?.to_raw()) }; @@ -585,19 +586,18 @@ impl<'a> ContextInternal<'a> for ExecuteContext<'a> { impl<'a> Context<'a> for ExecuteContext<'a> {} /// An execution context of a scope created by [`Context::compute_scoped()`](Context::compute_scoped). -pub struct ComputeContext<'a, 'outer> { +pub struct ComputeContext<'a> { env: Env, phantom_inner: PhantomData<&'a ()>, - phantom_outer: PhantomData<&'outer ()>, } -impl<'a, 'b> ContextInternal<'a> for ComputeContext<'a, 'b> { +impl<'a> ContextInternal<'a> for ComputeContext<'a> { fn env(&self) -> Env { self.env } } -impl<'a, 'b> Context<'a> for ComputeContext<'a, 'b> {} +impl<'a> Context<'a> for ComputeContext<'a> {} /// An execution context of a function call. /// diff --git a/test/napi/lib/functions.js b/test/napi/lib/functions.js index 05a7a0f7b..2f1ce976b 100644 --- a/test/napi/lib/functions.js +++ b/test/napi/lib/functions.js @@ -174,7 +174,10 @@ describe("JsFunction", function () { }); it("computes a value in a scoped computation", function () { + const o = {}; + assert.equal(addon.compute_scoped(), 99); + assert.equal(addon.recompute_scoped(o), o); }); it("catches an exception with cx.try_catch", function () { diff --git a/test/napi/src/js/functions.rs b/test/napi/src/js/functions.rs index 78ac033aa..209bbe8f6 100644 --- a/test/napi/src/js/functions.rs +++ b/test/napi/src/js/functions.rs @@ -215,6 +215,14 @@ pub fn compute_scoped(mut cx: FunctionContext) -> JsResult { Ok(i) } +// Simple identity function to verify that a handle can be moved to `compute_scoped` +// closure and re-escaped. +pub fn recompute_scoped(mut cx: FunctionContext) -> JsResult { + let value = cx.argument::(0)?; + + cx.compute_scoped(move |_| Ok(value)) +} + pub fn throw_and_catch(mut cx: FunctionContext) -> JsResult { let v = cx .argument_opt(0) diff --git a/test/napi/src/lib.rs b/test/napi/src/lib.rs index 0bed7ef10..e35315fb0 100644 --- a/test/napi/src/lib.rs +++ b/test/napi/src/lib.rs @@ -193,6 +193,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("check_string_and_number", check_string_and_number)?; cx.export_function("execute_scoped", execute_scoped)?; cx.export_function("compute_scoped", compute_scoped)?; + cx.export_function("recompute_scoped", recompute_scoped)?; cx.export_function("return_js_array", return_js_array)?; cx.export_function("return_js_array_with_number", return_js_array_with_number)?;