Stack continuations can now directly manipulate the return value of an ActionScript function.

This commit is contained in:
David Wendt 2019-10-17 14:40:55 -04:00
parent 8485e919db
commit a95861d596
3 changed files with 41 additions and 23 deletions

View File

@ -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<Value<'gc>, 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(())

View File

@ -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<NativeFunction<'gc>>,
then_func: Option<ScopeContinuation<'gc>>,
}
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<NativeFunction<'gc>> {
pub fn get_then_func(&self) -> Option<ScopeContinuation<'gc>> {
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);
}
}

View File

@ -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<Value<'gc>, Error>;