From 83c832ce864448d208d4a5425cb820068254c246 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Sun, 15 Sep 2019 21:21:57 -0400 Subject: [PATCH] Distinguish between Native and ActionScript functions. --- core/src/avm1.rs | 21 +++++++++------- core/src/avm1/globals/math.rs | 2 +- core/src/avm1/object.rs | 47 +++++++++++++++++++++++++++-------- core/src/avm1/value.rs | 2 +- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 461eee7df..7f445e87c 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -168,7 +168,7 @@ impl<'gc> Avm1<'gc> { stack_frames: vec![], } } - + /// Convert the current locals pool into a set of form values. /// /// This is necessary to support form submission from Flash via a couple of @@ -591,13 +591,16 @@ impl<'gc> Avm1<'gc> { Value::Undefined | Value::Null => { let this = context.active_clip.read().object().as_object()?.to_owned(); let return_value = object.call(self, context, this, &args)?; - self.current_stack_frame_mut().unwrap().stack_mut().push(return_value); + if let Some(instant_return) = return_value { + self.current_stack_frame_mut().unwrap().stack_mut().push(instant_return); + } } Value::String(name) => { if name.is_empty() { - let return_value = - object.call(self, context, object.as_object()?.to_owned(), &args)?; - self.current_stack_frame_mut().unwrap().stack_mut().push(return_value); + let return_value = object.call(self, context, object.as_object()?.to_owned(), &args)?; + if let Some(instant_return) = return_value { + self.current_stack_frame_mut().unwrap().stack_mut().push(instant_return); + } } else { let callable = object.as_object()?.read().get( &name, @@ -610,10 +613,10 @@ impl<'gc> Avm1<'gc> { return Err(format!("Object method {} is not defined", name).into()); } - let return_value = - callable.call(self, context, object.as_object()?.to_owned(), &args)?; - - self.current_stack_frame_mut().unwrap().stack_mut().push(return_value); + let return_value = callable.call(self, context, object.as_object()?.to_owned(), &args)?; + if let Some(instant_return) = return_value { + self.current_stack_frame_mut().unwrap().stack_mut().push(instant_return); + } } } _ => { diff --git a/core/src/avm1/globals/math.rs b/core/src/avm1/globals/math.rs index f0101f9b2..fae401fe8 100644 --- a/core/src/avm1/globals/math.rs +++ b/core/src/avm1/globals/math.rs @@ -99,7 +99,7 @@ mod tests { let function = math.read().get($name, avm, context, math); $( - assert_eq!(function.call(avm, context, math, $args)?, $out); + assert_eq!(function.call(avm, context, math, $args)?, Some($out)); )* Ok(()) diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 97cd092d7..50be99872 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -1,5 +1,6 @@ use crate::avm1::{ActionContext, Avm1, Value}; use crate::display_object::DisplayNode; +use crate::tag_utils::SwfSlice; use core::fmt; use gc_arena::{GcCell, MutationContext}; use std::collections::hash_map::Entry; @@ -13,6 +14,32 @@ pub type NativeFunction<'gc> = fn( &[Value<'gc>], ) -> Value<'gc>; +/// Represents a function that can be defined in the Ruffle runtime or by the +/// AVM1 bytecode itself. +#[derive(Clone)] +pub enum Executable<'gc> { + /// A function provided by the Ruffle runtime and implemented in Rust. + Native(NativeFunction<'gc>), + + /// ActionScript data defined by a previous action. + ActionData +} + +impl<'gc> Executable<'gc> { + /// Execute the given code. + /// + /// Execution is not guaranteed to have completed when this function + /// returns. If on-stack execution is possible, then this function returns + /// a return value you must push onto the stack. Otherwise, you must + /// create a new stack frame and execute the action data yourself. + pub fn exec(&self, avm: &mut Avm1<'gc>, ac: &mut ActionContext<'_, 'gc, '_>, this: GcCell<'gc, Object<'gc>>, args: &[Value<'gc>]) -> Option> { + match self { + Executable::Native(nf) => Some(nf(avm, ac, this, args)), + Executable::ActionData => None + } + } +} + pub const TYPE_OF_OBJECT: &str = "object"; pub const TYPE_OF_FUNCTION: &str = "function"; pub const TYPE_OF_MOVIE_CLIP: &str = "movieclip"; @@ -103,7 +130,7 @@ impl fmt::Debug for Property<'_> { pub struct Object<'gc> { display_node: Option>, values: HashMap>, - function: Option>, + function: Option>, type_of: &'static str, } @@ -138,10 +165,10 @@ impl<'gc> Object<'gc> { result } - pub fn function(function: NativeFunction<'gc>) -> Self { + pub fn native_function(function: NativeFunction<'gc>) -> Self { Self { type_of: TYPE_OF_FUNCTION, - function: Some(function), + function: Some(Executable::Native(function)), display_node: None, values: HashMap::new(), } @@ -188,7 +215,7 @@ impl<'gc> Object<'gc> { .insert(name.to_string(), Property::Stored { value }); } - pub fn set_function( + pub fn set_native_function( &mut self, name: &str, function: NativeFunction<'gc>, @@ -200,7 +227,7 @@ impl<'gc> Object<'gc> { name, Value::Object(GcCell::allocate( context.gc_context, - Object::function(function), + Object::native_function(function), )), avm, context, @@ -216,7 +243,7 @@ impl<'gc> Object<'gc> { ) { self.force_set( name, - Value::Object(GcCell::allocate(gc_context, Object::function(function))), + Value::Object(GcCell::allocate(gc_context, Object::native_function(function))), ) } @@ -247,11 +274,11 @@ impl<'gc> Object<'gc> { context: &mut ActionContext<'_, 'gc, '_>, this: GcCell<'gc, Object<'gc>>, args: &[Value<'gc>], - ) -> Value<'gc> { - if let Some(function) = self.function { - function(avm, context, this, args) + ) -> Option> { + if let Some(function) = &self.function { + function.exec(avm, context, this, args) } else { - Value::Undefined + Some(Value::Undefined) } } diff --git a/core/src/avm1/value.rs b/core/src/avm1/value.rs index 859c4906c..5e67af18d 100644 --- a/core/src/avm1/value.rs +++ b/core/src/avm1/value.rs @@ -166,7 +166,7 @@ impl<'gc> Value<'gc> { context: &mut ActionContext<'_, 'gc, '_>, this: GcCell<'gc, Object<'gc>>, args: &[Value<'gc>], - ) -> Result, Error> { + ) -> Result>, Error> { if let Value::Object(object) = self { Ok(object.read().call(avm, context, this, args)) } else {