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

@ -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);
}
}
}
_ => {

View File

@ -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(())

View File

@ -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<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_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<DisplayNode<'gc>>,
values: HashMap<String, Property<'gc>>,
function: Option<NativeFunction<'gc>>,
function: Option<Executable<'gc>>,
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<Value<'gc>> {
if let Some(function) = &self.function {
function.exec(avm, context, this, args)
} else {
Value::Undefined
Some(Value::Undefined)
}
}

View File

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