Distinguish between Native and ActionScript functions.

This commit is contained in:
David Wendt 2019-09-15 21:21:57 -04:00 committed by Mike Welsh
parent 728c3d18db
commit 83c832ce86
4 changed files with 51 additions and 21 deletions

View File

@ -168,7 +168,7 @@ impl<'gc> Avm1<'gc> {
stack_frames: vec![], stack_frames: vec![],
} }
} }
/// Convert the current locals pool into a set of form values. /// Convert the current locals pool into a set of form values.
/// ///
/// This is necessary to support form submission from Flash via a couple of /// 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 => { Value::Undefined | Value::Null => {
let this = context.active_clip.read().object().as_object()?.to_owned(); let this = context.active_clip.read().object().as_object()?.to_owned();
let return_value = object.call(self, context, this, &args)?; 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) => { Value::String(name) => {
if name.is_empty() { if name.is_empty() {
let return_value = let return_value = object.call(self, context, object.as_object()?.to_owned(), &args)?;
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(return_value); self.current_stack_frame_mut().unwrap().stack_mut().push(instant_return);
}
} else { } else {
let callable = object.as_object()?.read().get( let callable = object.as_object()?.read().get(
&name, &name,
@ -610,10 +613,10 @@ impl<'gc> Avm1<'gc> {
return Err(format!("Object method {} is not defined", name).into()); return Err(format!("Object method {} is not defined", name).into());
} }
let return_value = let return_value = callable.call(self, context, object.as_object()?.to_owned(), &args)?;
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);
self.current_stack_frame_mut().unwrap().stack_mut().push(return_value); }
} }
} }
_ => { _ => {

View File

@ -99,7 +99,7 @@ mod tests {
let function = math.read().get($name, avm, context, math); 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(()) Ok(())

View File

@ -1,5 +1,6 @@
use crate::avm1::{ActionContext, Avm1, Value}; use crate::avm1::{ActionContext, Avm1, Value};
use crate::display_object::DisplayNode; use crate::display_object::DisplayNode;
use crate::tag_utils::SwfSlice;
use core::fmt; use core::fmt;
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
@ -13,6 +14,32 @@ pub type NativeFunction<'gc> = fn(
&[Value<'gc>], &[Value<'gc>],
) -> 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<Value<'gc>> {
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_OBJECT: &str = "object";
pub const TYPE_OF_FUNCTION: &str = "function"; pub const TYPE_OF_FUNCTION: &str = "function";
pub const TYPE_OF_MOVIE_CLIP: &str = "movieclip"; pub const TYPE_OF_MOVIE_CLIP: &str = "movieclip";
@ -103,7 +130,7 @@ impl fmt::Debug for Property<'_> {
pub struct Object<'gc> { pub struct Object<'gc> {
display_node: Option<DisplayNode<'gc>>, display_node: Option<DisplayNode<'gc>>,
values: HashMap<String, Property<'gc>>, values: HashMap<String, Property<'gc>>,
function: Option<NativeFunction<'gc>>, function: Option<Executable<'gc>>,
type_of: &'static str, type_of: &'static str,
} }
@ -138,10 +165,10 @@ impl<'gc> Object<'gc> {
result result
} }
pub fn function(function: NativeFunction<'gc>) -> Self { pub fn native_function(function: NativeFunction<'gc>) -> Self {
Self { Self {
type_of: TYPE_OF_FUNCTION, type_of: TYPE_OF_FUNCTION,
function: Some(function), function: Some(Executable::Native(function)),
display_node: None, display_node: None,
values: HashMap::new(), values: HashMap::new(),
} }
@ -188,7 +215,7 @@ impl<'gc> Object<'gc> {
.insert(name.to_string(), Property::Stored { value }); .insert(name.to_string(), Property::Stored { value });
} }
pub fn set_function( pub fn set_native_function(
&mut self, &mut self,
name: &str, name: &str,
function: NativeFunction<'gc>, function: NativeFunction<'gc>,
@ -200,7 +227,7 @@ impl<'gc> Object<'gc> {
name, name,
Value::Object(GcCell::allocate( Value::Object(GcCell::allocate(
context.gc_context, context.gc_context,
Object::function(function), Object::native_function(function),
)), )),
avm, avm,
context, context,
@ -216,7 +243,7 @@ impl<'gc> Object<'gc> {
) { ) {
self.force_set( self.force_set(
name, 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, '_>, context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>, this: GcCell<'gc, Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Value<'gc> { ) -> Option<Value<'gc>> {
if let Some(function) = self.function { if let Some(function) = &self.function {
function(avm, context, this, args) function.exec(avm, context, this, args)
} else { } else {
Value::Undefined Some(Value::Undefined)
} }
} }

View File

@ -166,7 +166,7 @@ impl<'gc> Value<'gc> {
context: &mut ActionContext<'_, 'gc, '_>, context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>, this: GcCell<'gc, Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Option<Value<'gc>>, Error> {
if let Value::Object(object) = self { if let Value::Object(object) = self {
Ok(object.read().call(avm, context, this, args)) Ok(object.read().call(avm, context, this, args))
} else { } else {