Distinguish between Native and ActionScript functions.
This commit is contained in:
parent
728c3d18db
commit
83c832ce86
|
@ -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);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue