Add a notion of "and_then" to activation objects.

This effectively constitutes the ability to assign arbitrary native contiuations to the AVM stack.
This commit is contained in:
David Wendt 2019-10-02 22:07:00 -04:00
parent 24009d4f4a
commit 8485e919db
2 changed files with 42 additions and 6 deletions

View File

@ -244,7 +244,19 @@ impl<'gc> Avm1<'gc> {
}
/// Destroy the current stack frame (if there is one).
fn retire_stack_frame(&mut self) {
///
/// 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, '_>) {
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);
}
}
self.stack_frames.pop();
}
@ -282,7 +294,7 @@ impl<'gc> Avm1<'gc> {
self.push(Value::Undefined);
}
self.retire_stack_frame();
self.retire_stack_frame(context);
} else if let Some(action) = reader.read_action()? {
let result = match action {
Action::Add => self.action_add(context),
@ -415,7 +427,7 @@ impl<'gc> Avm1<'gc> {
self.push(Value::Undefined);
}
self.retire_stack_frame();
self.retire_stack_frame(context);
}
Ok(())
@ -1486,12 +1498,12 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
fn action_return(&mut self, _context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
fn action_return(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
let result = self.pop()?;
if self.stack_frames.len() > 1 {
self.retire_stack_frame();
self.push(result);
self.retire_stack_frame(context);
}
Ok(())

View File

@ -1,8 +1,10 @@
//! Activation records
use crate::avm1::function::NativeFunction;
use crate::avm1::object::Object;
use crate::avm1::scope::Scope;
use crate::avm1::{Avm1, UpdateContext, Value};
use crate::avm1::{Avm1, Value};
use crate::context::UpdateContext;
use crate::tag_utils::SwfSlice;
use gc_arena::{GcCell, MutationContext};
use smallvec::SmallVec;
@ -88,6 +90,12 @@ pub struct Activation<'gc> {
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
/// same register set.
local_registers: Option<GcCell<'gc, RegisterSet<'gc>>>,
/// Native code to execute when the given activation frame returns.
///
/// This facility exists primarily to allow native code to handle the result
/// of an AVM function call.
then_func: Option<NativeFunction<'gc>>,
}
unsafe impl<'gc> gc_arena::Collect for Activation<'gc> {
@ -117,6 +125,7 @@ impl<'gc> Activation<'gc> {
arguments,
is_function: false,
local_registers: None,
then_func: None,
}
}
@ -136,6 +145,7 @@ impl<'gc> Activation<'gc> {
arguments,
is_function: true,
local_registers: None,
then_func: None,
}
}
@ -166,6 +176,7 @@ impl<'gc> Activation<'gc> {
arguments: None,
is_function: false,
local_registers: None,
then_func: None,
}
}
@ -180,6 +191,7 @@ impl<'gc> Activation<'gc> {
arguments: self.arguments,
is_function: false,
local_registers: self.local_registers,
then_func: None,
}
}
@ -316,4 +328,16 @@ impl<'gc> Activation<'gc> {
}
}
}
/// Return the function scheduled to be executed, if any.
pub fn get_then_func(&self) -> Option<NativeFunction<'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>) {
self.then_func = Some(func);
}
}