From a95861d5960633dc0cb294a150b8b5f5d6f2aac5 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Thu, 17 Oct 2019 14:40:55 -0400 Subject: [PATCH] Stack continuations can now directly manipulate the return value of an ActionScript function. --- core/src/avm1.rs | 44 ++++++++++++++++------------- core/src/avm1/activation.rs | 8 +++--- core/src/avm1/scope_continuation.rs | 12 ++++++++ 3 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 core/src/avm1/scope_continuation.rs diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 8a7982971..a6a6b4839 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -23,6 +23,7 @@ pub mod object; mod scope; mod value; +mod scope_continuation; #[cfg(test)] mod test_utils; @@ -248,16 +249,23 @@ impl<'gc> Avm1<'gc> { /// This function will run functions scheduled on the current stack frame's /// `and_then` field. This is intended to allow the runtime to act on the /// results of AVM code to implement things like custom getters/setters. - fn retire_stack_frame(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) { + /// Due to this, this function is given the intended return value and may + /// return a different one. + fn retire_stack_frame( + &mut self, + context: &mut UpdateContext<'_, 'gc, '_>, + mut return_value: Value<'gc>, + ) -> Result, Error> { if let Some(frame) = self.current_stack_frame() { let this = frame.read().this_cell(); if let Some(func) = frame.read().get_then_func() { - let ret_args = vec![self.stack.last().unwrap().to_owned()]; - func(self, context, this, &ret_args); + return_value = func(self, context, this, return_value)?; } } self.stack_frames.pop(); + + Ok(return_value) } /// Execute the AVM stack until it is exhausted. @@ -285,16 +293,15 @@ impl<'gc> Avm1<'gc> { if reader.pos() >= (data.end - data.start) { //Executing beyond the end of a function constitutes an implicit return. - if self + let impl_return = self .current_stack_frame() .unwrap() .read() - .can_implicit_return() - { - self.push(Value::Undefined); + .can_implicit_return(); + let return_value = self.retire_stack_frame(context, Value::Undefined)?; + if impl_return { + self.push(return_value); } - - self.retire_stack_frame(context); } else if let Some(action) = reader.read_action()? { let result = match action { Action::Add => self.action_add(context), @@ -418,16 +425,15 @@ impl<'gc> Avm1<'gc> { } } else { //The explicit end opcode was encountered so return here - if self + let impl_return = self .current_stack_frame() .unwrap() .read() - .can_implicit_return() - { - self.push(Value::Undefined); + .can_implicit_return(); + let return_value = self.retire_stack_frame(context, Value::Undefined)?; + if impl_return { + self.push(return_value); } - - self.retire_stack_frame(context); } Ok(()) @@ -1499,11 +1505,11 @@ impl<'gc> Avm1<'gc> { } fn action_return(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> { - let result = self.pop()?; + let return_value = self.pop()?; + let return_value = self.retire_stack_frame(context, return_value)?; - if self.stack_frames.len() > 1 { - self.push(result); - self.retire_stack_frame(context); + if !self.stack_frames.is_empty() { + self.push(return_value); } Ok(()) diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 2f73b19fb..09cc77bd8 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -1,8 +1,8 @@ //! Activation records -use crate::avm1::function::NativeFunction; use crate::avm1::object::Object; use crate::avm1::scope::Scope; +use crate::avm1::scope_continuation::ScopeContinuation; use crate::avm1::{Avm1, Value}; use crate::context::UpdateContext; use crate::tag_utils::SwfSlice; @@ -95,7 +95,7 @@ pub struct Activation<'gc> { /// /// This facility exists primarily to allow native code to handle the result /// of an AVM function call. - then_func: Option>, + then_func: Option>, } unsafe impl<'gc> gc_arena::Collect for Activation<'gc> { @@ -330,14 +330,14 @@ impl<'gc> Activation<'gc> { } /// Return the function scheduled to be executed, if any. - pub fn get_then_func(&self) -> Option> { + pub fn get_then_func(&self) -> Option> { self.then_func } /// Schedule a native function to execute when this stack frame returns. /// /// Only one native function may be scheduled per activation. - pub fn and_then(&mut self, func: NativeFunction<'gc>) { + pub fn and_then(&mut self, func: ScopeContinuation<'gc>) { self.then_func = Some(func); } } diff --git a/core/src/avm1/scope_continuation.rs b/core/src/avm1/scope_continuation.rs new file mode 100644 index 000000000..fcc34ffd6 --- /dev/null +++ b/core/src/avm1/scope_continuation.rs @@ -0,0 +1,12 @@ +//! GC-compatible scope continuations + +use crate::avm1::{Avm1, Error, Object, Value}; +use crate::context::UpdateContext; +use gc_arena::GcCell; + +pub type ScopeContinuation<'gc> = fn( + &mut Avm1<'gc>, + &mut UpdateContext<'_, 'gc, '_>, + GcCell<'gc, Object<'gc>>, + Value<'gc>, +) -> Result, Error>;