Stack continuations can now directly manipulate the return value of an ActionScript function.
This commit is contained in:
parent
8485e919db
commit
a95861d596
|
@ -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(())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>;
|
Loading…
Reference in New Issue