diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 7f445e87c..e31947acf 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -648,13 +648,22 @@ impl<'gc> Avm1<'gc> { fn action_define_function( &mut self, - _context: &mut ActionContext, - _name: &str, - _params: &[&str], - _actions: &[u8], + context: &mut ActionContext<'_, 'gc, '_>, + name: &str, + params: &[&str], + actions: &[u8], ) -> Result<(), Error> { - // TODO(Herschel) - Err("Unimplemented action: DefineFunction".into()) + let swf_version = self.current_stack_frame().unwrap().swf_version(); + let func_data = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap(); + let func = Value::Object(GcCell::allocate(context.gc_context, Object::action_function(swf_version, func_data, name, params))); + + if name == "" { + self.current_stack_frame_mut().unwrap().stack_mut().push(func); + } else { + self.current_stack_frame_mut().unwrap().locals_mut().insert(name.to_string(), func); + } + + Ok(()) } fn action_define_local(&mut self, _context: &mut ActionContext) -> Result<(), Error> { diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 50be99872..efb54cd62 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -14,15 +14,47 @@ pub type NativeFunction<'gc> = fn( &[Value<'gc>], ) -> Value<'gc>; +/// Represents a function defined in the AVM1 runtime. +#[derive(Clone)] +struct Avm1Function { + /// The file format version of the SWF that generated this function. + swf_version: u8, + + /// A reference to the underlying SWF data. + data: SwfSlice, + + /// The name of the function, if not anonymous. + name: Option, + + /// The names of the function parameters. + params: Vec, +} + +impl Avm1Function { + pub fn new(swf_version: u8, actions: SwfSlice, name: &str, params: &[&str]) -> Avm1Function { + let name = match name { + "" => None, + name => Some(name.to_string()) + }; + + Avm1Function { + swf_version: swf_version, + data: actions, + name: name, + params: params.into_iter().map(|s| s.to_string()).collect() + } + } +} + /// Represents a function that can be defined in the Ruffle runtime or by the /// AVM1 bytecode itself. #[derive(Clone)] -pub enum Executable<'gc> { +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 + Action(Avm1Function) } impl<'gc> Executable<'gc> { @@ -35,7 +67,7 @@ impl<'gc> Executable<'gc> { 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 + Executable::Action(af) => None } } } @@ -174,6 +206,15 @@ impl<'gc> Object<'gc> { } } + pub fn action_function(swf_version: u8, actions: SwfSlice, name: &str, params: &[&str]) -> Self { + Self { + type_of: TYPE_OF_FUNCTION, + function: Some(Executable::Action(Avm1Function::new(swf_version, actions, name, params))), + display_node: None, + values: HashMap::new() + } + } + pub fn set_display_node(&mut self, display_node: DisplayNode<'gc>) { self.display_node = Some(display_node); } diff --git a/core/src/tag_utils.rs b/core/src/tag_utils.rs index e4f5ce433..877be9ec0 100644 --- a/core/src/tag_utils.rs +++ b/core/src/tag_utils.rs @@ -3,6 +3,7 @@ use swf::TagCode; pub type DecodeResult = Result<(), Box>; pub type SwfStream = swf::read::Reader>; +/// A shared-ownership reference to some portion of an immutable datastream. #[derive(Debug, Clone)] pub struct SwfSlice { pub data: std::sync::Arc>, @@ -16,6 +17,27 @@ impl AsRef<[u8]> for SwfSlice { } } +impl SwfSlice { + /// Construct a new SwfSlice from a regular slice. + /// + /// This function returns None if the given slice is not a subslice of the + /// current slice. + pub fn to_subslice(&self, slice: &[u8]) -> Option { + let self_pval = self.data.as_ptr() as usize; + let slice_pval = slice.as_ptr() as usize; + + if (self_pval + self.start) <= slice_pval && slice_pval < (self_pval + self.end) { + Some(SwfSlice { + data: self.data.clone(), + start: slice_pval - self_pval, + end: (slice_pval - self_pval) + slice.len() + }) + } else { + None + } + } +} + pub fn decode_tags<'a, R, F>( reader: &'a mut SwfStream, mut tag_callback: F,