From df7b5a751cb480f3d6ccb3c10bb4c4ee40339d55 Mon Sep 17 00:00:00 2001 From: Elior Nguyen Date: Mon, 16 Mar 2026 12:26:26 +0700 Subject: [PATCH 1/5] fix: pass previousRealm to AsyncGeneratorCompleteStep in AsyncGeneratorYield --- core/engine/src/builtins/generator/mod.rs | 4 ++ core/engine/src/tests/async_generator.rs | 48 ++++++++++++++++++- core/engine/src/vm/call_frame/mod.rs | 5 ++ .../src/vm/opcode/generator/yield_stm.rs | 23 +++++---- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index 6b010d42c09..f96ca3a8003 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -100,6 +100,9 @@ impl GeneratorContext { resume_kind: GeneratorResumeKind, context: &mut Context, ) -> CompletionRecord { + // Capture the caller's realm for AsyncGeneratorYield (spec §27.6.3.8 step 8). + let caller_realm = Some(context.realm().clone()); + std::mem::swap(&mut context.vm.stack, &mut self.stack); let frame = self.call_frame.take().expect("should have a call frame"); let fp = frame.fp; @@ -109,6 +112,7 @@ impl GeneratorContext { let frame = context.vm.frame_mut(); frame.fp = fp; frame.rp = rp; + frame.caller_realm = caller_realm; frame.set_exit_early(true); if let Some(value) = value { diff --git a/core/engine/src/tests/async_generator.rs b/core/engine/src/tests/async_generator.rs index a263d555272..20021667bff 100644 --- a/core/engine/src/tests/async_generator.rs +++ b/core/engine/src/tests/async_generator.rs @@ -1,5 +1,5 @@ use crate::{ - Context, JsValue, TestAction, builtins::promise::PromiseState, object::JsPromise, + Context, JsValue, Source, TestAction, builtins::promise::PromiseState, object::JsPromise, run_test_actions, }; use boa_macros::js_str; @@ -120,3 +120,49 @@ fn return_on_then_queue() { TestAction::assert_eq("count", JsValue::from(2)), ]); } + +#[test] +fn cross_realm_async_generator_yield() { + // Exercises AsyncGeneratorYield spec steps 6-8 (previousRealm handling) + // by creating a generator in one realm and consuming it from another. + let mut context = Context::default(); + + let generator_realm = context.create_realm().unwrap(); + + let old_realm = context.enter_realm(generator_realm); + let generator = context + .eval(Source::from_bytes( + b"(async function* g() { yield 42; yield 99; })()", + )) + .unwrap(); + context.enter_realm(old_realm); + + let next_fn = generator + .as_object() + .unwrap() + .get(js_str!("next"), &mut context) + .unwrap(); + + let call_next = |ctx: &mut Context| -> JsValue { + let result = next_fn + .as_callable() + .unwrap() + .call(&generator, &[], ctx) + .unwrap(); + ctx.run_jobs().unwrap(); + result + }; + + assert_promise_iter_value( + &call_next(&mut context), + &JsValue::from(42), + false, + &mut context, + ); + assert_promise_iter_value( + &call_next(&mut context), + &JsValue::from(99), + false, + &mut context, + ); +} diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 8a2e839a113..b5ab30d8ce3 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -77,6 +77,10 @@ pub struct CallFrame { /// \[\[Realm\]\] pub(crate) realm: Realm, + /// The caller's realm, captured during generator resume. + /// Used by `AsyncGeneratorYield` to pass `previousRealm` to `AsyncGeneratorCompleteStep`. + pub(crate) caller_realm: Option, + // SAFETY: Nothing in `CallFrameFlags` requires tracing, so this is safe. #[unsafe_ignore_trace] pub(crate) flags: CallFrameFlags, @@ -154,6 +158,7 @@ impl CallFrame { active_runnable, environments, realm, + caller_realm: None, flags: CallFrameFlags::empty(), } } diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index 8e91aa3fbdf..a1b8c0b4cf0 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -66,13 +66,19 @@ impl AsyncGeneratorYield { let value = context.vm.get_register(value.into()); let completion = Ok(value.clone()); - // TODO: 6. Assert: The execution context stack has at least two elements. - // TODO: 7. Let previousContext be the second to top element of the execution context stack. - // TODO: 8. Let previousRealm be previousContext's Realm. + // 6. Assert: The execution context stack has at least two elements. + // 7. Let previousContext be the second to top element of the execution context stack. + // 8. Let previousRealm be previousContext's Realm. + let previous_realm = context.vm.frame().caller_realm.clone(); + // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm). - if let Err(err) = - AsyncGenerator::complete_step(&async_generator_object, completion, false, None, context) - { + if let Err(err) = AsyncGenerator::complete_step( + &async_generator_object, + completion, + false, + previous_realm, + context, + ) { return context.handle_error(err); } @@ -114,8 +120,9 @@ impl AsyncGeneratorYield { // a. Set generator.[[AsyncGeneratorState]] to suspended-yield. r#gen.data_mut().state = AsyncGeneratorState::SuspendedYield; - // TODO: b. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. - // TODO: c. Let callerContext be the running execution context. + // b. Remove genContext from the execution context stack and restore the execution context + // that is at the top of the execution context stack as the running execution context. + // c. Let callerContext be the running execution context. // d. Resume callerContext passing undefined. If genContext is ever resumed again, let resumptionValue be the Completion Record with which it is resumed. // e. Assert: If control reaches here, then genContext is the running execution context again. // f. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue). From dcbd8da92b3746e2d4b0650e1f5c8687e1e4fe47 Mon Sep 17 00:00:00 2001 From: Elior Nguyen Date: Mon, 16 Mar 2026 13:20:04 +0700 Subject: [PATCH 2/5] refactor: read previousRealm from frame stack instead of storing in CallFrame --- core/engine/src/builtins/generator/mod.rs | 4 ---- core/engine/src/vm/call_frame/mod.rs | 5 ----- core/engine/src/vm/opcode/generator/yield_stm.rs | 6 +++++- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index f96ca3a8003..6b010d42c09 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -100,9 +100,6 @@ impl GeneratorContext { resume_kind: GeneratorResumeKind, context: &mut Context, ) -> CompletionRecord { - // Capture the caller's realm for AsyncGeneratorYield (spec §27.6.3.8 step 8). - let caller_realm = Some(context.realm().clone()); - std::mem::swap(&mut context.vm.stack, &mut self.stack); let frame = self.call_frame.take().expect("should have a call frame"); let fp = frame.fp; @@ -112,7 +109,6 @@ impl GeneratorContext { let frame = context.vm.frame_mut(); frame.fp = fp; frame.rp = rp; - frame.caller_realm = caller_realm; frame.set_exit_early(true); if let Some(value) = value { diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 9867d29ad24..cd560f18594 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -77,10 +77,6 @@ pub struct CallFrame { /// \[\[Realm\]\] pub(crate) realm: Realm, - /// The caller's realm, captured during generator resume. - /// Used by `AsyncGeneratorYield` to pass `previousRealm` to `AsyncGeneratorCompleteStep`. - pub(crate) caller_realm: Option, - // SAFETY: Nothing in `CallFrameFlags` requires tracing, so this is safe. #[unsafe_ignore_trace] pub(crate) flags: CallFrameFlags, @@ -158,7 +154,6 @@ impl CallFrame { active_runnable, environments, realm, - caller_realm: None, flags: CallFrameFlags::empty(), } } diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index a1b8c0b4cf0..a3c25419aac 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -69,7 +69,11 @@ impl AsyncGeneratorYield { // 6. Assert: The execution context stack has at least two elements. // 7. Let previousContext be the second to top element of the execution context stack. // 8. Let previousRealm be previousContext's Realm. - let previous_realm = context.vm.frame().caller_realm.clone(); + let previous_realm = context + .vm + .frames + .get(context.vm.frames.len() - 2) + .map(|frame| frame.realm.clone()); // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm). if let Err(err) = AsyncGenerator::complete_step( From 8d302ec2b3e68ed4a6be1b7c3197b7e9d4e108a6 Mon Sep 17 00:00:00 2001 From: Elior Nguyen Date: Tue, 17 Mar 2026 10:56:10 +0700 Subject: [PATCH 3/5] fix: capture previousRealm correctly in AsyncGeneratorYield --- core/engine/src/builtins/generator/mod.rs | 15 ++++++- core/engine/src/native_function/mod.rs | 3 ++ core/engine/src/tests/async_generator.rs | 44 ++++++++++++++----- core/engine/src/vm/call_frame/mod.rs | 5 +++ core/engine/src/vm/mod.rs | 6 +++ .../src/vm/opcode/generator/yield_stm.rs | 9 ++-- 6 files changed, 65 insertions(+), 17 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index 6b010d42c09..1ad0be7e196 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -100,8 +100,21 @@ impl GeneratorContext { resume_kind: GeneratorResumeKind, context: &mut Context, ) -> CompletionRecord { + // Capture the caller's realm before the stack swap. + // `native_function_call` stores the pre-swap realm in `native_caller_realm`, + // which is the actual caller's realm (not the function's realm). + // AsyncGeneratorYield uses this as `previousRealm` (spec §27.6.3.8, step 8). + // + // Only set on the initial resume (from `next()`); subsequent resumes + // (e.g., await continuations from `run_jobs()`) must preserve the + // previously captured caller realm. + let caller_realm = context.vm.native_caller_realm.clone(); + std::mem::swap(&mut context.vm.stack, &mut self.stack); - let frame = self.call_frame.take().expect("should have a call frame"); + let mut frame = self.call_frame.take().expect("should have a call frame"); + if frame.caller_realm.is_none() { + frame.caller_realm = caller_realm; + } let fp = frame.fp; let rp = frame.rp; context.vm.push_frame(frame); diff --git a/core/engine/src/native_function/mod.rs b/core/engine/src/native_function/mod.rs index c54e75ae9c9..f3dccd9f116 100644 --- a/core/engine/src/native_function/mod.rs +++ b/core/engine/src/native_function/mod.rs @@ -360,6 +360,8 @@ pub(crate) fn native_function_call( context.swap_realm(&mut realm); context.vm.native_active_function = Some(this_function_object); + let previous_native_caller_realm = context.vm.native_caller_realm.take(); + context.vm.native_caller_realm = Some(realm.clone()); let result = if constructor.is_some() { function.call(&JsValue::undefined(), &args, context) @@ -369,6 +371,7 @@ pub(crate) fn native_function_call( .map_err(|err| err.inject_realm(context.realm().clone())); context.vm.native_active_function = None; + context.vm.native_caller_realm = previous_native_caller_realm; context.swap_realm(&mut realm); context.vm.shadow_stack.pop(); diff --git a/core/engine/src/tests/async_generator.rs b/core/engine/src/tests/async_generator.rs index 20021667bff..a0220e504c6 100644 --- a/core/engine/src/tests/async_generator.rs +++ b/core/engine/src/tests/async_generator.rs @@ -125,6 +125,8 @@ fn return_on_then_queue() { fn cross_realm_async_generator_yield() { // Exercises AsyncGeneratorYield spec steps 6-8 (previousRealm handling) // by creating a generator in one realm and consuming it from another. + // Also verifies that the iter result objects are created in the caller's + // realm by checking their prototype against old_realm's Object.prototype. let mut context = Context::default(); let generator_realm = context.create_realm().unwrap(); @@ -135,7 +137,10 @@ fn cross_realm_async_generator_yield() { b"(async function* g() { yield 42; yield 99; })()", )) .unwrap(); - context.enter_realm(old_realm); + context.enter_realm(old_realm.clone()); + + // Grab Object.prototype from the caller's realm (old_realm). + let caller_object_proto = old_realm.intrinsics().constructors().object().prototype(); let next_fn = generator .as_object() @@ -153,16 +158,33 @@ fn cross_realm_async_generator_yield() { result }; - assert_promise_iter_value( - &call_next(&mut context), - &JsValue::from(42), - false, - &mut context, + // First yield: value 42 + let first = call_next(&mut context); + assert_promise_iter_value(&first, &JsValue::from(42), false, &mut context); + + // Verify the iter result was created in old_realm. + let first_promise = JsPromise::from_object(first.as_object().unwrap().clone()).unwrap(); + let PromiseState::Fulfilled(first_result) = first_promise.state() else { + panic!("promise was not fulfilled"); + }; + assert_eq!( + first_result.as_object().unwrap().prototype(), + Some(caller_object_proto.clone()), + "iter result prototype should be old_realm's Object.prototype" ); - assert_promise_iter_value( - &call_next(&mut context), - &JsValue::from(99), - false, - &mut context, + + // Second yield: value 99 + let second = call_next(&mut context); + assert_promise_iter_value(&second, &JsValue::from(99), false, &mut context); + + // Verify the iter result was created in old_realm. + let second_promise = JsPromise::from_object(second.as_object().unwrap().clone()).unwrap(); + let PromiseState::Fulfilled(second_result) = second_promise.state() else { + panic!("promise was not fulfilled"); + }; + assert_eq!( + second_result.as_object().unwrap().prototype(), + Some(caller_object_proto), + "iter result prototype should be old_realm's Object.prototype" ); } diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index cd560f18594..cf91437c22f 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -77,6 +77,10 @@ pub struct CallFrame { /// \[\[Realm\]\] pub(crate) realm: Realm, + /// The caller's realm, captured before a generator stack swap. + /// Used by `AsyncGeneratorYield` to pass `previousRealm` to `complete_step`. + pub(crate) caller_realm: Option, + // SAFETY: Nothing in `CallFrameFlags` requires tracing, so this is safe. #[unsafe_ignore_trace] pub(crate) flags: CallFrameFlags, @@ -154,6 +158,7 @@ impl CallFrame { active_runnable, environments, realm, + caller_realm: None, flags: CallFrameFlags::empty(), } } diff --git a/core/engine/src/vm/mod.rs b/core/engine/src/vm/mod.rs index f01c9bb47dd..46c1dbc03de 100644 --- a/core/engine/src/vm/mod.rs +++ b/core/engine/src/vm/mod.rs @@ -89,6 +89,11 @@ pub struct Vm { /// because we don't push a frame for them. pub(crate) native_active_function: Option, + /// The caller's realm before a native function's `swap_realm`. + /// Used by `GeneratorContext::resume()` to capture `previousRealm` + /// for `AsyncGeneratorYield`. + pub(crate) native_caller_realm: Option, + /// Number of nested host calls that re-enter the VM via `Context::run()`. /// /// This is incremented by high-level host entry points such as @@ -347,6 +352,7 @@ impl Vm { pending_exception: None, runtime_limits: RuntimeLimits::default(), native_active_function: None, + native_caller_realm: None, host_call_depth: 0, shadow_stack: ShadowStack::default(), #[cfg(feature = "trace")] diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index a3c25419aac..3adc55ba031 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -69,11 +69,10 @@ impl AsyncGeneratorYield { // 6. Assert: The execution context stack has at least two elements. // 7. Let previousContext be the second to top element of the execution context stack. // 8. Let previousRealm be previousContext's Realm. - let previous_realm = context - .vm - .frames - .get(context.vm.frames.len() - 2) - .map(|frame| frame.realm.clone()); + let previous_realm = context.vm.frame().caller_realm.clone(); + + // Clear caller_realm so the next `next()` call can set a fresh one. + context.vm.frame_mut().caller_realm = None; // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm). if let Err(err) = AsyncGenerator::complete_step( From b1a71c48261172a2e6ca30d7c0ada6fc66233f9a Mon Sep 17 00:00:00 2001 From: Elior Nguyen Date: Tue, 17 Mar 2026 11:26:58 +0700 Subject: [PATCH 4/5] fix: resolve merge conflict - remove duplicate call_frame.take() --- core/engine/src/builtins/generator/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index fcc4e02987b..030601b97ff 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -112,13 +112,12 @@ impl GeneratorContext { let caller_realm = context.vm.native_caller_realm.clone(); std::mem::swap(&mut context.vm.stack, &mut self.stack); - let mut frame = self.call_frame.take().expect("should have a call frame"); + let Some(mut frame) = self.call_frame.take() else { + return CompletionRecord::Throw(PanicError::new("should have a call frame").into()); + }; if frame.caller_realm.is_none() { frame.caller_realm = caller_realm; } - let Some(frame) = self.call_frame.take() else { - return CompletionRecord::Throw(PanicError::new("should have a call frame").into()); - }; let fp = frame.fp; let rp = frame.rp; context.vm.push_frame(frame); From c37288f374ecee549ad7e1befb6546851f73745e Mon Sep 17 00:00:00 2001 From: Elior Nguyen Date: Tue, 17 Mar 2026 15:41:48 +0700 Subject: [PATCH 5/5] refactor: read previousRealm from frame stack, remove hack fields --- core/engine/src/builtins/generator/mod.rs | 15 +--------- core/engine/src/native_function/mod.rs | 3 -- core/engine/src/tests/async_generator.rs | 30 +++++++++++-------- core/engine/src/vm/call_frame/mod.rs | 5 ---- core/engine/src/vm/mod.rs | 6 ---- .../src/vm/opcode/generator/yield_stm.rs | 9 +++--- 6 files changed, 24 insertions(+), 44 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index 030601b97ff..74ad7a0feec 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -101,23 +101,10 @@ impl GeneratorContext { resume_kind: GeneratorResumeKind, context: &mut Context, ) -> CompletionRecord { - // Capture the caller's realm before the stack swap. - // `native_function_call` stores the pre-swap realm in `native_caller_realm`, - // which is the actual caller's realm (not the function's realm). - // AsyncGeneratorYield uses this as `previousRealm` (spec §27.6.3.8, step 8). - // - // Only set on the initial resume (from `next()`); subsequent resumes - // (e.g., await continuations from `run_jobs()`) must preserve the - // previously captured caller realm. - let caller_realm = context.vm.native_caller_realm.clone(); - std::mem::swap(&mut context.vm.stack, &mut self.stack); - let Some(mut frame) = self.call_frame.take() else { + let Some(frame) = self.call_frame.take() else { return CompletionRecord::Throw(PanicError::new("should have a call frame").into()); }; - if frame.caller_realm.is_none() { - frame.caller_realm = caller_realm; - } let fp = frame.fp; let rp = frame.rp; context.vm.push_frame(frame); diff --git a/core/engine/src/native_function/mod.rs b/core/engine/src/native_function/mod.rs index f3dccd9f116..c54e75ae9c9 100644 --- a/core/engine/src/native_function/mod.rs +++ b/core/engine/src/native_function/mod.rs @@ -360,8 +360,6 @@ pub(crate) fn native_function_call( context.swap_realm(&mut realm); context.vm.native_active_function = Some(this_function_object); - let previous_native_caller_realm = context.vm.native_caller_realm.take(); - context.vm.native_caller_realm = Some(realm.clone()); let result = if constructor.is_some() { function.call(&JsValue::undefined(), &args, context) @@ -371,7 +369,6 @@ pub(crate) fn native_function_call( .map_err(|err| err.inject_realm(context.realm().clone())); context.vm.native_active_function = None; - context.vm.native_caller_realm = previous_native_caller_realm; context.swap_realm(&mut realm); context.vm.shadow_stack.pop(); diff --git a/core/engine/src/tests/async_generator.rs b/core/engine/src/tests/async_generator.rs index a0220e504c6..188ddcc325f 100644 --- a/core/engine/src/tests/async_generator.rs +++ b/core/engine/src/tests/async_generator.rs @@ -125,22 +125,28 @@ fn return_on_then_queue() { fn cross_realm_async_generator_yield() { // Exercises AsyncGeneratorYield spec steps 6-8 (previousRealm handling) // by creating a generator in one realm and consuming it from another. - // Also verifies that the iter result objects are created in the caller's - // realm by checking their prototype against old_realm's Object.prototype. + // Per spec, previousRealm is the realm of the second-to-top execution + // context (the `next()` / AwaitFulfilled handler), which has the same + // realm as the generator. The iter result prototype should match the + // generator realm's Object.prototype. let mut context = Context::default(); let generator_realm = context.create_realm().unwrap(); - let old_realm = context.enter_realm(generator_realm); + let old_realm = context.enter_realm(generator_realm.clone()); let generator = context .eval(Source::from_bytes( b"(async function* g() { yield 42; yield 99; })()", )) .unwrap(); - context.enter_realm(old_realm.clone()); + context.enter_realm(old_realm); - // Grab Object.prototype from the caller's realm (old_realm). - let caller_object_proto = old_realm.intrinsics().constructors().object().prototype(); + // Grab Object.prototype from the generator's realm (previousRealm per spec). + let gen_realm_object_proto = generator_realm + .intrinsics() + .constructors() + .object() + .prototype(); let next_fn = generator .as_object() @@ -162,29 +168,29 @@ fn cross_realm_async_generator_yield() { let first = call_next(&mut context); assert_promise_iter_value(&first, &JsValue::from(42), false, &mut context); - // Verify the iter result was created in old_realm. + // Verify the iter result was created in the generator's realm (previousRealm). let first_promise = JsPromise::from_object(first.as_object().unwrap().clone()).unwrap(); let PromiseState::Fulfilled(first_result) = first_promise.state() else { panic!("promise was not fulfilled"); }; assert_eq!( first_result.as_object().unwrap().prototype(), - Some(caller_object_proto.clone()), - "iter result prototype should be old_realm's Object.prototype" + Some(gen_realm_object_proto.clone()), + "iter result prototype should be generator realm's Object.prototype" ); // Second yield: value 99 let second = call_next(&mut context); assert_promise_iter_value(&second, &JsValue::from(99), false, &mut context); - // Verify the iter result was created in old_realm. + // Verify the iter result was created in the generator's realm (previousRealm). let second_promise = JsPromise::from_object(second.as_object().unwrap().clone()).unwrap(); let PromiseState::Fulfilled(second_result) = second_promise.state() else { panic!("promise was not fulfilled"); }; assert_eq!( second_result.as_object().unwrap().prototype(), - Some(caller_object_proto), - "iter result prototype should be old_realm's Object.prototype" + Some(gen_realm_object_proto), + "iter result prototype should be generator realm's Object.prototype" ); } diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index cf91437c22f..cd560f18594 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -77,10 +77,6 @@ pub struct CallFrame { /// \[\[Realm\]\] pub(crate) realm: Realm, - /// The caller's realm, captured before a generator stack swap. - /// Used by `AsyncGeneratorYield` to pass `previousRealm` to `complete_step`. - pub(crate) caller_realm: Option, - // SAFETY: Nothing in `CallFrameFlags` requires tracing, so this is safe. #[unsafe_ignore_trace] pub(crate) flags: CallFrameFlags, @@ -158,7 +154,6 @@ impl CallFrame { active_runnable, environments, realm, - caller_realm: None, flags: CallFrameFlags::empty(), } } diff --git a/core/engine/src/vm/mod.rs b/core/engine/src/vm/mod.rs index 46c1dbc03de..f01c9bb47dd 100644 --- a/core/engine/src/vm/mod.rs +++ b/core/engine/src/vm/mod.rs @@ -89,11 +89,6 @@ pub struct Vm { /// because we don't push a frame for them. pub(crate) native_active_function: Option, - /// The caller's realm before a native function's `swap_realm`. - /// Used by `GeneratorContext::resume()` to capture `previousRealm` - /// for `AsyncGeneratorYield`. - pub(crate) native_caller_realm: Option, - /// Number of nested host calls that re-enter the VM via `Context::run()`. /// /// This is incremented by high-level host entry points such as @@ -352,7 +347,6 @@ impl Vm { pending_exception: None, runtime_limits: RuntimeLimits::default(), native_active_function: None, - native_caller_realm: None, host_call_depth: 0, shadow_stack: ShadowStack::default(), #[cfg(feature = "trace")] diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index 3adc55ba031..a3c25419aac 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -69,10 +69,11 @@ impl AsyncGeneratorYield { // 6. Assert: The execution context stack has at least two elements. // 7. Let previousContext be the second to top element of the execution context stack. // 8. Let previousRealm be previousContext's Realm. - let previous_realm = context.vm.frame().caller_realm.clone(); - - // Clear caller_realm so the next `next()` call can set a fresh one. - context.vm.frame_mut().caller_realm = None; + let previous_realm = context + .vm + .frames + .get(context.vm.frames.len() - 2) + .map(|frame| frame.realm.clone()); // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm). if let Err(err) = AsyncGenerator::complete_step(