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 scope;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
mod scope_continuation;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_utils;
|
mod test_utils;
|
||||||
|
|
||||||
|
@ -248,16 +249,23 @@ impl<'gc> Avm1<'gc> {
|
||||||
/// This function will run functions scheduled on the current stack frame's
|
/// 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
|
/// `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.
|
/// 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() {
|
if let Some(frame) = self.current_stack_frame() {
|
||||||
let this = frame.read().this_cell();
|
let this = frame.read().this_cell();
|
||||||
if let Some(func) = frame.read().get_then_func() {
|
if let Some(func) = frame.read().get_then_func() {
|
||||||
let ret_args = vec![self.stack.last().unwrap().to_owned()];
|
return_value = func(self, context, this, return_value)?;
|
||||||
func(self, context, this, &ret_args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack_frames.pop();
|
self.stack_frames.pop();
|
||||||
|
|
||||||
|
Ok(return_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the AVM stack until it is exhausted.
|
/// Execute the AVM stack until it is exhausted.
|
||||||
|
@ -285,16 +293,15 @@ impl<'gc> Avm1<'gc> {
|
||||||
|
|
||||||
if reader.pos() >= (data.end - data.start) {
|
if reader.pos() >= (data.end - data.start) {
|
||||||
//Executing beyond the end of a function constitutes an implicit return.
|
//Executing beyond the end of a function constitutes an implicit return.
|
||||||
if self
|
let impl_return = self
|
||||||
.current_stack_frame()
|
.current_stack_frame()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read()
|
.read()
|
||||||
.can_implicit_return()
|
.can_implicit_return();
|
||||||
{
|
let return_value = self.retire_stack_frame(context, Value::Undefined)?;
|
||||||
self.push(Value::Undefined);
|
if impl_return {
|
||||||
|
self.push(return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.retire_stack_frame(context);
|
|
||||||
} else if let Some(action) = reader.read_action()? {
|
} else if let Some(action) = reader.read_action()? {
|
||||||
let result = match action {
|
let result = match action {
|
||||||
Action::Add => self.action_add(context),
|
Action::Add => self.action_add(context),
|
||||||
|
@ -418,16 +425,15 @@ impl<'gc> Avm1<'gc> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//The explicit end opcode was encountered so return here
|
//The explicit end opcode was encountered so return here
|
||||||
if self
|
let impl_return = self
|
||||||
.current_stack_frame()
|
.current_stack_frame()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read()
|
.read()
|
||||||
.can_implicit_return()
|
.can_implicit_return();
|
||||||
{
|
let return_value = self.retire_stack_frame(context, Value::Undefined)?;
|
||||||
self.push(Value::Undefined);
|
if impl_return {
|
||||||
|
self.push(return_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.retire_stack_frame(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1499,11 +1505,11 @@ impl<'gc> Avm1<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
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()?;
|
let return_value = self.pop()?;
|
||||||
|
let return_value = self.retire_stack_frame(context, return_value)?;
|
||||||
|
|
||||||
if self.stack_frames.len() > 1 {
|
if !self.stack_frames.is_empty() {
|
||||||
self.push(result);
|
self.push(return_value);
|
||||||
self.retire_stack_frame(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! Activation records
|
//! Activation records
|
||||||
|
|
||||||
use crate::avm1::function::NativeFunction;
|
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::scope::Scope;
|
use crate::avm1::scope::Scope;
|
||||||
|
use crate::avm1::scope_continuation::ScopeContinuation;
|
||||||
use crate::avm1::{Avm1, Value};
|
use crate::avm1::{Avm1, Value};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::tag_utils::SwfSlice;
|
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
|
/// This facility exists primarily to allow native code to handle the result
|
||||||
/// of an AVM function call.
|
/// of an AVM function call.
|
||||||
then_func: Option<NativeFunction<'gc>>,
|
then_func: Option<ScopeContinuation<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> gc_arena::Collect for Activation<'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.
|
/// 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
|
self.then_func
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule a native function to execute when this stack frame returns.
|
/// Schedule a native function to execute when this stack frame returns.
|
||||||
///
|
///
|
||||||
/// Only one native function may be scheduled per activation.
|
/// 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);
|
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