From e2dcf47c56ea34fdafae6c07d03bcc309128cda5 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Tue, 22 Oct 2019 15:18:40 -0400 Subject: [PATCH] Add a method to force resolve a `ReturnValue` on the Rust stack via recursion. --- core/src/avm1.rs | 37 ++++++++++++++++++++++------------- core/src/avm1/return_value.rs | 20 +++++++++++++++++++ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 98ea731fb..e68df73c5 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -301,23 +301,32 @@ impl<'gc> Avm1<'gc> { pub fn run_current_frame( &mut self, context: &mut UpdateContext<'_, 'gc, '_>, + stop_frame: GcCell<'gc, Activation<'gc>>, ) -> Result<(), Error> { - let stop_frame = *self.stack_frames.last().unwrap(); - let stop_frame_id = self.stack_frames.len() - 1; - - while self - .stack_frames - .get(stop_frame_id) - .map(|fr| GcCell::ptr_eq(stop_frame, *fr)) - .unwrap_or(false) - { - self.with_current_reader_mut(context, |this, r, context| { - this.do_next_action(context, r) - }) - .unwrap()?; + let mut stop_frame_id = None; + for (index, frame) in self.stack_frames.iter().enumerate() { + if GcCell::ptr_eq(stop_frame, *frame) { + stop_frame_id = Some(index); + } } - Ok(()) + if let Some(stop_frame_id) = stop_frame_id { + while self + .stack_frames + .get(stop_frame_id) + .map(|fr| GcCell::ptr_eq(stop_frame, *fr)) + .unwrap_or(false) + { + self.with_current_reader_mut(context, |this, r, context| { + this.do_next_action(context, r) + }) + .unwrap()?; + } + + Ok(()) + } else { + Err("Attempted to run a frame not on the current interpreter stack".into()) + } } /// Run a single action from a given action reader. diff --git a/core/src/avm1/return_value.rs b/core/src/avm1/return_value.rs index 717b9eefb..47f45fcd4 100644 --- a/core/src/avm1/return_value.rs +++ b/core/src/avm1/return_value.rs @@ -109,6 +109,26 @@ impl<'gc> ReturnValue<'gc> { }; } + /// Force a return value to resolve on the Rust stack by recursing back + /// into the AVM. + pub fn resolve( + self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + use ReturnValue::*; + + match self { + Immediate(val) => Ok(val), + ResultOf(frame) => { + avm.run_current_frame(context, frame)?; + + avm.pop() + } + NoResult => Err("Attempted to resolve a no-result return value".into()), + } + } + /// Consumes the given return value. /// /// This exists primarily so that users of return values can indicate that