From 1c3e4406b393ddf2658a64c955e272ecdde56eeb Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Sun, 6 Oct 2019 14:45:14 -0700 Subject: [PATCH] chore: Fix clippy lints * Remove clone calls from Copy objects * Used Iterator::cloned() instead of manually cloning * Pass swf::Function into AvmFunction2::new() * Use action_clone_sprite --- core/src/avm1.rs | 377 +++++++++++++++++++++++++----------- core/src/avm1/activation.rs | 90 +++++---- core/src/avm1/function.rs | 158 ++++++++------- core/src/avm1/scope.rs | 162 +++++++++------- 4 files changed, 512 insertions(+), 275 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 7861abb9c..a590fe242 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -1,31 +1,31 @@ +use crate::avm1::function::Avm1Function2; use crate::avm1::globals::create_globals; use crate::avm1::object::Object; -use crate::avm1::function::Avm1Function2; use crate::backend::navigator::NavigationMethod; use crate::prelude::*; use gc_arena::{GcCell, MutationContext}; use rand::{rngs::SmallRng, Rng}; -use std::convert::TryInto; use std::collections::HashMap; +use std::convert::TryInto; use swf::avm1::read::Reader; use swf::avm1::types::{Action, Function}; use crate::tag_utils::SwfSlice; +mod activation; mod fscommand; mod function; -mod scope; -mod activation; mod globals; pub mod movie_clip; pub mod object; +mod scope; mod value; -pub use value::Value; -use scope::Scope; use activation::Activation; +use scope::Scope; +pub use value::Value; pub struct ActionContext<'a, 'gc, 'gc_context> { pub gc_context: gc_arena::MutationContext<'gc, 'gc_context>, @@ -80,7 +80,7 @@ pub struct Avm1<'gc> { /// Used to enforce the restriction that no more than one mutable current /// reader be active at once. - is_reading: bool + is_reading: bool, } unsafe impl<'gc> gc_arena::Collect for Avm1<'gc> { @@ -101,8 +101,13 @@ impl<'gc> Avm1<'gc> { globals: GcCell::allocate(gc_context, create_globals(gc_context)), stack_frames: vec![], stack: vec![], - registers: [Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined], - is_reading: false + registers: [ + Value::Undefined, + Value::Undefined, + Value::Undefined, + Value::Undefined, + ], + is_reading: false, } } @@ -113,7 +118,13 @@ impl<'gc> Avm1<'gc> { pub fn locals_into_form_values(&self) -> HashMap { let mut form_values = HashMap::new(); - for (k, v) in self.current_stack_frame().unwrap().scope().locals().iter_values() { + for (k, v) in self + .current_stack_frame() + .unwrap() + .scope() + .locals() + .iter_values() + { form_values.insert(k.clone(), v.clone().into_string()); } @@ -121,11 +132,34 @@ impl<'gc> Avm1<'gc> { } /// Add a stack frame that executes code in timeline scope - pub fn insert_stack_frame_for_action(&mut self, swf_version: u8, code: SwfSlice, action_context: &mut ActionContext<'_, 'gc, '_>) { - let global_scope = GcCell::allocate(action_context.gc_context, Scope::from_global_object(self.globals)); - let clip_obj = action_context.active_clip.read().object().as_object().unwrap().to_owned(); - let child_scope = GcCell::allocate(action_context.gc_context, Scope::new(global_scope, scope::ScopeClass::Target, clip_obj)); - self.stack_frames.push(Activation::from_action(swf_version, code, child_scope, clip_obj, None)); + pub fn insert_stack_frame_for_action( + &mut self, + swf_version: u8, + code: SwfSlice, + action_context: &mut ActionContext<'_, 'gc, '_>, + ) { + let global_scope = GcCell::allocate( + action_context.gc_context, + Scope::from_global_object(self.globals), + ); + let clip_obj = action_context + .active_clip + .read() + .object() + .as_object() + .unwrap() + .to_owned(); + let child_scope = GcCell::allocate( + action_context.gc_context, + Scope::new(global_scope, scope::ScopeClass::Target, clip_obj), + ); + self.stack_frames.push(Activation::from_action( + swf_version, + code, + child_scope, + clip_obj, + None, + )); } /// Add a stack frame for any arbitrary code. @@ -134,14 +168,14 @@ impl<'gc> Avm1<'gc> { } /// Retrieve the current AVM execution frame. - /// + /// /// Yields None if there is no stack frame. pub fn current_stack_frame(&self) -> Option<&Activation<'gc>> { self.stack_frames.last() } /// Retrieve the current AVM execution frame for mutation. - /// + /// /// Yields None if there is no stack frame. pub fn current_stack_frame_mut(&mut self) -> Option<&mut Activation<'gc>> { self.stack_frames.last_mut() @@ -153,34 +187,40 @@ impl<'gc> Avm1<'gc> { } /// Perform some action with the current stack frame's reader. - /// + /// /// This function constructs a reader based off the current stack frame's /// reader. You are permitted to mutate the stack frame as you wish. If the /// stack frame we started with still exists in the same location on the /// stack, it's PC will be updated to the Reader's current PC. - /// + /// /// Stack frame identity (for the purpose of the above paragraph) is /// determined by the data pointed to by the `SwfSlice` of a given frame. - /// + /// /// # Warnings - /// + /// /// It is incorrect to call this function multiple times in the same stack. /// Doing so will result in any changes in duplicate readers being ignored. /// Always pass the borrowed reader into functions that need it. - fn with_current_reader_mut(&mut self, func: F) -> Option where F: FnOnce(&mut Self, &mut Reader<'_>) -> R { + fn with_current_reader_mut(&mut self, func: F) -> Option + where + F: FnOnce(&mut Self, &mut Reader<'_>) -> R, + { if self.is_reading { - log::error!("Two mutable readers are open at the same time. Please correct this error."); + log::error!( + "Two mutable readers are open at the same time. Please correct this error." + ); } self.is_reading = true; - let current_stack_id = self.stack_frames.len() - 1; - let (swf_version, data, pc) = self.stack_frames.last().map(|frame| (frame.swf_version(), frame.data(), frame.pc()))?; + let (swf_version, data, pc) = self + .stack_frames + .last() + .map(|frame| (frame.swf_version(), frame.data(), frame.pc()))?; let mut read = Reader::new(data.as_ref(), swf_version); read.seek(pc.try_into().unwrap()); let r = func(self, &mut read); - //this took an hour of fighting borrowck to figure out was necessary if let Some(new_stack) = self.stack_frames.get_mut(current_stack_id) { if new_stack.is_identical_fn(&data) { @@ -199,16 +239,24 @@ impl<'gc> Avm1<'gc> { } /// Execute the AVM stack until it is exhausted. - pub fn run_stack_till_empty(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { - while self.stack_frames.len() > 0 { - self.with_current_reader_mut(|this, r| this.do_next_action(context, r)).unwrap()?; + pub fn run_stack_till_empty( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { + while !self.stack_frames.is_empty() { + self.with_current_reader_mut(|this, r| this.do_next_action(context, r)) + .unwrap()?; } Ok(()) } /// Run a single action from a given action reader. - fn do_next_action(&mut self, context: &mut ActionContext<'_, 'gc, '_>, reader: &mut Reader<'_>) -> Result<(), Error> { + fn do_next_action( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + reader: &mut Reader<'_>, + ) -> Result<(), Error> { let data = self.current_stack_frame().unwrap().data(); if reader.pos() >= (data.end - data.start) { @@ -234,6 +282,7 @@ impl<'gc> Avm1<'gc> { Action::CallFunction => self.action_call_function(context), Action::CallMethod => self.action_call_method(context), Action::CharToAscii => self.action_char_to_ascii(context), + Action::CloneSprite => self.action_clone_sprite(context), Action::ConstantPool(constant_pool) => { self.action_constant_pool(context, &constant_pool[..]) } @@ -346,8 +395,8 @@ impl<'gc> Avm1<'gc> { Ok(()) } - pub fn variable_name_is_slash_path<'s>(path: &'s str) -> bool { - path.contains(":") || path.contains(".") + pub fn variable_name_is_slash_path(path: &str) -> bool { + path.contains(':') || path.contains('.') } pub fn resolve_slash_path( @@ -410,25 +459,45 @@ impl<'gc> Avm1<'gc> { } /// Retrieve a given register value. - /// + /// /// If a given register does not exist, this function yields /// Value::Undefined, which is also a valid register value. pub fn current_register(&self, id: u8) -> Value<'gc> { - if self.current_stack_frame().map(|sf| sf.has_local_registers()).unwrap_or(false) { + if self + .current_stack_frame() + .map(|sf| sf.has_local_registers()) + .unwrap_or(false) + { self.current_stack_frame().unwrap().local_register(id) } else { - self.registers.get(id as usize).map(|v| v.clone()).unwrap_or(Value::Undefined) + self.registers + .get(id as usize) + .cloned() + .unwrap_or(Value::Undefined) } } /// Set a register to a given value. - /// + /// /// If a given register does not exist, this function does nothing. - pub fn set_current_register(&mut self, id: u8, value: Value<'gc>, context: &mut ActionContext<'_, 'gc, '_>) { - if self.current_stack_frame().map(|sf| sf.has_local_registers()).unwrap_or(false) { - self.current_stack_frame_mut().unwrap().set_local_register(id, value, context.gc_context); - } else { - self.registers.get_mut(id as usize).map(|v| *v = value); + pub fn set_current_register( + &mut self, + id: u8, + value: Value<'gc>, + context: &mut ActionContext<'_, 'gc, '_>, + ) { + if self + .current_stack_frame() + .map(|sf| sf.has_local_registers()) + .unwrap_or(false) + { + self.current_stack_frame_mut().unwrap().set_local_register( + id, + value, + context.gc_context, + ); + } else if let Some(v) = self.registers.get_mut(id as usize) { + *v = value; } } @@ -472,7 +541,10 @@ impl<'gc> Avm1<'gc> { let a = self.pop()?; let b = self.pop()?; let result = b.into_number_v1() != 0.0 && a.into_number_v1() != 0.0; - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -554,15 +626,21 @@ impl<'gc> Avm1<'gc> { Err("Unimplemented action: Call".into()) } - fn action_call_function(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { + fn action_call_function( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { let fn_name = self.pop()?; let mut args = Vec::new(); let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count? for _ in 0..num_args { args.push(self.pop()?); } - - let target_fn = self.current_stack_frame_mut().unwrap().resolve(fn_name.as_string()?); + + let target_fn = self + .current_stack_frame_mut() + .unwrap() + .resolve(fn_name.as_string()?); let this = context.active_clip.read().object().as_object()?.to_owned(); let return_value = target_fn.call(self, context, this, &args)?; if let Some(instant_return) = return_value { @@ -572,7 +650,10 @@ impl<'gc> Avm1<'gc> { Ok(()) } - fn action_call_method(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { + fn action_call_method( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { let method_name = self.pop()?; let object = self.pop()?; let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count? @@ -591,7 +672,8 @@ impl<'gc> Avm1<'gc> { } Value::String(name) => { if name.is_empty() { - let return_value = object.call(self, context, object.as_object()?.to_owned(), &args)?; + let return_value = + object.call(self, context, object.as_object()?.to_owned(), &args)?; if let Some(instant_return) = return_value { self.push(instant_return); } @@ -607,7 +689,8 @@ 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)?; + let return_value = + callable.call(self, context, object.as_object()?.to_owned(), &args)?; if let Some(instant_return) = return_value { self.push(instant_return); } @@ -648,14 +731,26 @@ impl<'gc> Avm1<'gc> { actions: &[u8], ) -> Result<(), Error> { let swf_version = self.current_stack_frame().unwrap().swf_version(); - let func_data = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap(); - let scope = Scope::new_closure_scope(self.current_stack_frame().unwrap().scope_cell(), context.gc_context); - let func = Value::Object(GcCell::allocate(context.gc_context, Object::action_function(swf_version, func_data, name, params, scope))); - + let func_data = self + .current_stack_frame() + .unwrap() + .data() + .to_subslice(actions) + .unwrap(); + let scope = Scope::new_closure_scope( + self.current_stack_frame().unwrap().scope_cell(), + context.gc_context, + ); + let func = Value::Object(GcCell::allocate( + context.gc_context, + Object::action_function(swf_version, func_data, name, params, scope), + )); if name == "" { self.push(func); } else { - self.current_stack_frame_mut().unwrap().define(name, func, context.gc_context); + self.current_stack_frame_mut() + .unwrap() + .define(name, func, context.gc_context); } Ok(()) @@ -664,49 +759,61 @@ impl<'gc> Avm1<'gc> { fn action_define_function_2( &mut self, context: &mut ActionContext<'_, 'gc, '_>, - action_func: &Function + action_func: &Function, ) -> Result<(), Error> { let swf_version = self.current_stack_frame().unwrap().swf_version(); - let func_data = self.current_stack_frame().unwrap().data().to_subslice(action_func.actions).unwrap(); - let scope = Scope::new_closure_scope(self.current_stack_frame().unwrap().scope_cell(), context.gc_context); - let func2 = Avm1Function2::new( - swf_version, - func_data, - action_func.name, - action_func.params.capacity() as u8, //TODO: this needs to be refactored - action_func.preload_parent, - action_func.preload_root, - action_func.suppress_super, - action_func.preload_super, - action_func.suppress_arguments, - action_func.preload_arguments, - action_func.suppress_this, - action_func.preload_this, - action_func.preload_global, - &action_func.params, - scope + let func_data = self + .current_stack_frame() + .unwrap() + .data() + .to_subslice(action_func.actions) + .unwrap(); + let scope = Scope::new_closure_scope( + self.current_stack_frame().unwrap().scope_cell(), + context.gc_context, ); - let func_obj = Value::Object(GcCell::allocate(context.gc_context, Object::action_function2(func2))); - + let func2 = Avm1Function2::new(swf_version, func_data, action_func, scope); + let func_obj = Value::Object(GcCell::allocate( + context.gc_context, + Object::action_function2(func2), + )); if action_func.name == "" { self.push(func_obj); } else { - self.current_stack_frame_mut().unwrap().define(action_func.name, func_obj, context.gc_context); + self.current_stack_frame_mut().unwrap().define( + action_func.name, + func_obj, + context.gc_context, + ); } Ok(()) } - fn action_define_local(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { + fn action_define_local( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { let value = self.pop()?; let name = self.pop()?; - self.current_stack_frame_mut().unwrap().define(name.as_string()?, value, context.gc_context); + self.current_stack_frame_mut().unwrap().define( + name.as_string()?, + value, + context.gc_context, + ); Ok(()) } - fn action_define_local_2(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { + fn action_define_local_2( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { let name = self.pop()?; - self.current_stack_frame_mut().unwrap().define(name.as_string()?, Value::Undefined, context.gc_context); + self.current_stack_frame_mut().unwrap().define( + name.as_string()?, + Value::Undefined, + context.gc_context, + ); Ok(()) } @@ -718,7 +825,6 @@ impl<'gc> Avm1<'gc> { //Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns //a boolean based on if the delete actually deleted something. let did_exist = Value::Bool(object.read().has_property(name)); - object.write(context.gc_context).delete(name); self.push(did_exist); @@ -733,7 +839,10 @@ impl<'gc> Avm1<'gc> { //a boolean based on if the delete actually deleted something. let did_exist = Value::Bool(self.current_stack_frame().unwrap().is_defined(name)); - self.current_stack_frame().unwrap().scope().delete(name, context.gc_context); + self.current_stack_frame() + .unwrap() + .scope() + .delete(name, context.gc_context); self.push(did_exist); Ok(()) @@ -762,13 +871,12 @@ impl<'gc> Avm1<'gc> { let name = self.pop()?; let name = name.as_string()?; self.push(Value::Null); // Sentinel that indicates end of enumeration - let ob = match self.current_stack_frame().unwrap().resolve(name) { Value::Object(ob) => ob, _ => { log::error!("Cannot enumerate properties of {}", name); - return Ok(()) //TODO: This is NOT OK(()). + return Ok(()); //TODO: This is NOT OK(()). } }; @@ -785,7 +893,10 @@ impl<'gc> Avm1<'gc> { let a = self.pop()?; let b = self.pop()?; let result = b.into_number_v1() == a.into_number_v1(); - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -906,7 +1017,6 @@ impl<'gc> Avm1<'gc> { let is_slashpath = Self::variable_name_is_slash_path(path); let mut result = None; - if is_slashpath { if let Some((node, var_name)) = Self::resolve_slash_path_variable(context.target_clip, context.root, path) @@ -1063,7 +1173,7 @@ impl<'gc> Avm1<'gc> { &mut self, _context: &mut ActionContext, jump_offset: i16, - reader: &mut Reader<'_> + reader: &mut Reader<'_>, ) -> Result<(), Error> { let val = self.pop()?; if val.as_bool() { @@ -1103,7 +1213,7 @@ impl<'gc> Avm1<'gc> { &mut self, _context: &mut ActionContext, jump_offset: i16, - reader: &mut Reader<'_> + reader: &mut Reader<'_>, ) -> Result<(), Error> { // TODO(Herschel): Handle out-of-bounds. reader.seek(jump_offset.into()); @@ -1115,7 +1225,10 @@ impl<'gc> Avm1<'gc> { let a = self.pop()?; let b = self.pop()?; let result = b.into_number_v1() < a.into_number_v1(); - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -1186,7 +1299,10 @@ impl<'gc> Avm1<'gc> { // AS1 logical not let val = self.pop()?; let result = val.into_number_v1() == 0.0; - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -1229,7 +1345,10 @@ impl<'gc> Avm1<'gc> { let a = self.pop()?; let b = self.pop()?; let result = b.into_number_v1() != 0.0 || a.into_number_v1() != 0.0; - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -1339,9 +1458,10 @@ impl<'gc> Avm1<'gc> { let name = name_val.as_string()?; let object = self.pop()?.as_object()?; let this = self.current_stack_frame().unwrap().this_cell(); - - object.write(context.gc_context).set(name, value, self, context, this); + object + .write(context.gc_context) + .set(name, value, self, context, this); Ok(()) } @@ -1397,10 +1517,13 @@ impl<'gc> Avm1<'gc> { Self::resolve_slash_path_variable(context.target_clip, context.root, var_path) { if let Some(clip) = node.write(context.gc_context).as_movie_clip_mut() { - clip.object() - .as_object()? - .write(context.gc_context) - .set(var_name, value.clone(), self, context, this); + clip.object().as_object()?.write(context.gc_context).set( + var_name, + value.clone(), + self, + context, + this, + ); } } } else { @@ -1408,7 +1531,9 @@ impl<'gc> Avm1<'gc> { let scope = self.current_stack_frame().unwrap().scope_cell(); let unused_value = scope.read().overwrite(var_path, value, self, context, this); if let Some(value) = unused_value { - self.current_stack_frame().unwrap().define(var_path, value, context.gc_context); + self.current_stack_frame() + .unwrap() + .define(var_path, value, context.gc_context); } } @@ -1444,13 +1569,24 @@ impl<'gc> Avm1<'gc> { } let scope = self.current_stack_frame().unwrap().scope_cell(); - let clip_obj = context.active_clip.read().object().as_object().unwrap().to_owned(); + let clip_obj = context + .active_clip + .read() + .object() + .as_object() + .unwrap() + .to_owned(); - self.current_stack_frame_mut().unwrap().set_scope(Scope::new_target_scope(scope, clip_obj, context.gc_context)); + self.current_stack_frame_mut() + .unwrap() + .set_scope(Scope::new_target_scope(scope, clip_obj, context.gc_context)); Ok(()) } - fn action_set_target2(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { + fn action_set_target2( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Result<(), Error> { let target = self.pop()?; if let Ok(target) = target.as_string() { self.action_set_target(context, target)?; @@ -1508,7 +1644,6 @@ impl<'gc> Avm1<'gc> { ) -> Result<(), Error> { // Does NOT pop the value from the stack. let val = self.stack.last().ok_or("Stack underflow")?.clone(); - self.set_current_register(register, val, context); Ok(()) @@ -1529,7 +1664,10 @@ impl<'gc> Avm1<'gc> { let a = self.pop()?; let b = self.pop()?; let result = b.into_string() == a.into_string(); - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -1566,7 +1704,10 @@ impl<'gc> Avm1<'gc> { let b = self.pop()?; // This is specifically a non-UTF8 aware comparison. let result = b.into_string().bytes().lt(a.into_string().bytes()); - self.push(Value::from_bool_v1(result, self.current_swf_version().unwrap())); + self.push(Value::from_bool_v1( + result, + self.current_swf_version().unwrap(), + )); Ok(()) } @@ -1627,7 +1768,7 @@ impl<'gc> Avm1<'gc> { _context: &mut ActionContext, _frame: u16, num_actions_to_skip: u8, - r: &mut Reader<'_> + r: &mut Reader<'_>, ) -> Result<(), Error> { // TODO(Herschel): Always true for now. let loaded = true; @@ -1643,7 +1784,7 @@ impl<'gc> Avm1<'gc> { &mut self, _context: &mut ActionContext, num_actions_to_skip: u8, - r: &mut Reader<'_> + r: &mut Reader<'_>, ) -> Result<(), Error> { // TODO(Herschel): Always true for now. let _frame_num = self.pop()?.as_f64()? as u16; @@ -1656,11 +1797,27 @@ impl<'gc> Avm1<'gc> { Ok(()) } - fn action_with(&mut self, context: &mut ActionContext<'_, 'gc, '_>, actions: &[u8]) -> Result<(), Error> { + fn action_with( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + actions: &[u8], + ) -> Result<(), Error> { let object = self.pop()?.as_object()?; - let block = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap(); - let with_scope = Scope::new_with_scope(self.current_stack_frame().unwrap().scope_cell(), object, context.gc_context); - let new_activation = self.current_stack_frame().unwrap().to_rescope(block, with_scope); + let block = self + .current_stack_frame() + .unwrap() + .data() + .to_subslice(actions) + .unwrap(); + let with_scope = Scope::new_with_scope( + self.current_stack_frame().unwrap().scope_cell(), + object, + context.gc_context, + ); + let new_activation = self + .current_stack_frame() + .unwrap() + .to_rescope(block, with_scope); self.stack_frames.push(new_activation); Ok(()) } @@ -1674,4 +1831,4 @@ fn skip_actions(reader: &mut Reader<'_>, num_actions_to_skip: u8) -> Result<(), } Ok(()) -} \ No newline at end of file +} diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 43bbc2e3f..34d41fb92 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -1,18 +1,18 @@ //! Activation records -use std::sync::Arc; -use std::cell::{Ref, RefMut}; -use gc_arena::{GcCell, MutationContext}; -use crate::tag_utils::SwfSlice; -use crate::avm1::scope::Scope; use crate::avm1::object::Object; +use crate::avm1::scope::Scope; use crate::avm1::Value; +use crate::tag_utils::SwfSlice; +use gc_arena::{GcCell, MutationContext}; +use std::cell::{Ref, RefMut}; +use std::sync::Arc; /// Represents a single activation of a given AVM1 function or keyframe. #[derive(Clone)] pub struct Activation<'gc> { /// Represents the SWF version of a given function. - /// + /// /// Certain AVM1 operations change behavior based on the version of the SWF /// file they were defined in. For example, case sensitivity changes based /// on the SWF version. @@ -38,17 +38,17 @@ pub struct Activation<'gc> { is_function: bool, /// Local registers, if any. - /// + /// /// None indicates a function executing out of the global register set. /// Some indicates the existence of local registers, even if none exist. /// i.e. None(Vec::new()) means no registers should exist at all. - /// + /// /// Registers are numbered from 1; r0 does not exist. Therefore this vec, /// while nominally starting from zero, actually starts from r1. - /// + /// /// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the /// same register set. - local_registers: Option>>> + local_registers: Option>>>, } unsafe impl<'gc> gc_arena::Collect for Activation<'gc> { @@ -62,55 +62,71 @@ unsafe impl<'gc> gc_arena::Collect for Activation<'gc> { } impl<'gc> Activation<'gc> { - pub fn from_action(swf_version: u8, code: SwfSlice, scope: GcCell<'gc, Scope<'gc>>, this: GcCell<'gc, Object<'gc>>, arguments: Option>>) -> Activation<'gc> { + pub fn from_action( + swf_version: u8, + code: SwfSlice, + scope: GcCell<'gc, Scope<'gc>>, + this: GcCell<'gc, Object<'gc>>, + arguments: Option>>, + ) -> Activation<'gc> { Activation { - swf_version: swf_version, + swf_version, data: code, pc: 0, - scope: scope, - this: this, - arguments: arguments, + scope, + this, + arguments, is_function: false, - local_registers: None + local_registers: None, } } - pub fn from_function(swf_version: u8, code: SwfSlice, scope: GcCell<'gc, Scope<'gc>>, this: GcCell<'gc, Object<'gc>>, arguments: Option>>) -> Activation<'gc> { + pub fn from_function( + swf_version: u8, + code: SwfSlice, + scope: GcCell<'gc, Scope<'gc>>, + this: GcCell<'gc, Object<'gc>>, + arguments: Option>>, + ) -> Activation<'gc> { Activation { - swf_version: swf_version, + swf_version, data: code, pc: 0, - scope: scope, - this: this, - arguments: arguments, + scope, + this, + arguments, is_function: true, - local_registers: None + local_registers: None, } } /// Construct an empty stack frame with no code. - /// + /// /// This is primarily intended for testing purposes: the activation given /// will prevent the AVM from panicking without a current activation. /// We construct a single scope chain from a global object, and that's about /// it. - pub fn from_nothing(swf_version: u8, globals: GcCell<'gc, Object<'gc>>, mc: MutationContext<'gc, '_>) -> Activation<'gc> { + pub fn from_nothing( + swf_version: u8, + globals: GcCell<'gc, Object<'gc>>, + mc: MutationContext<'gc, '_>, + ) -> Activation<'gc> { let global_scope = GcCell::allocate(mc, Scope::from_global_object(globals)); let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc)); Activation { - swf_version: swf_version, + swf_version, data: SwfSlice { data: Arc::new(Vec::new()), start: 0, - end: 0 + end: 0, }, pc: 0, scope: child_scope, this: globals, arguments: None, is_function: false, - local_registers: None + local_registers: None, } } @@ -120,11 +136,11 @@ impl<'gc> Activation<'gc> { swf_version: self.swf_version, data: code, pc: 0, - scope: scope, + scope, this: self.this, arguments: self.arguments, is_function: false, - local_registers: self.local_registers.clone() + local_registers: self.local_registers, } } @@ -153,7 +169,6 @@ impl<'gc> Activation<'gc> { pub fn pc(&self) -> usize { self.pc } - /// Change the current PC. pub fn set_pc(&mut self, new_pc: usize) { self.pc = new_pc; @@ -171,7 +186,7 @@ impl<'gc> Activation<'gc> { /// Returns AVM local variable scope for reference. pub fn scope_cell(&self) -> GcCell<'gc, Scope<'gc>> { - self.scope.clone() + self.scope } /// Completely replace the current scope with a new one. @@ -220,7 +235,6 @@ impl<'gc> Activation<'gc> { pub fn this_cell(&self) -> GcCell<'gc, Object<'gc>> { self.this } - /// Returns true if this function was called with a local register set. pub fn has_local_registers(&self) -> bool { self.local_registers.is_some() @@ -233,7 +247,11 @@ impl<'gc> Activation<'gc> { /// Retrieve a local register. pub fn local_register(&self, id: u8) -> Value<'gc> { if let Some(local_registers) = self.local_registers { - local_registers.read().get(id as usize).map(|v| v.clone()).unwrap_or(Value::Undefined) + local_registers + .read() + .get(id as usize) + .cloned() + .unwrap_or(Value::Undefined) } else { Value::Undefined } @@ -242,7 +260,9 @@ impl<'gc> Activation<'gc> { /// Set a local register. pub fn set_local_register(&mut self, id: u8, value: Value<'gc>, mc: MutationContext<'gc, '_>) { if let Some(ref mut local_registers) = self.local_registers { - local_registers.write(mc).get_mut(id as usize).map(|r| *r = value); + if let Some(r) = local_registers.write(mc).get_mut(id as usize) { + *r = value; + } } } -} \ No newline at end of file +} diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 15444ff6f..7c7f5fac3 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -1,13 +1,13 @@ //! Code relating to executable functions + calling conventions. +use crate::avm1::activation::Activation; +use crate::avm1::object::Object; +use crate::avm1::scope::Scope; +use crate::avm1::value::Value; +use crate::avm1::{ActionContext, Avm1}; +use crate::tag_utils::SwfSlice; use gc_arena::GcCell; use swf::avm1::types::FunctionParam; -use crate::tag_utils::SwfSlice; -use crate::avm1::{Avm1, ActionContext}; -use crate::avm1::object::Object; -use crate::avm1::value::Value; -use crate::avm1::scope::Scope; -use crate::avm1::activation::Activation; pub type NativeFunction<'gc> = fn( &mut Avm1<'gc>, @@ -24,7 +24,6 @@ pub struct Avm1Function<'gc> { /// A reference to the underlying SWF data. data: SwfSlice, - /// The name of the function, if not anonymous. name: Option, @@ -32,22 +31,28 @@ pub struct Avm1Function<'gc> { params: Vec, /// The scope the function was born into. - scope: GcCell<'gc, Scope<'gc>> + scope: GcCell<'gc, Scope<'gc>>, } impl<'gc> Avm1Function<'gc> { - pub fn new(swf_version: u8, actions: SwfSlice, name: &str, params: &[&str], scope: GcCell<'gc, Scope<'gc>>) -> Self { + pub fn new( + swf_version: u8, + actions: SwfSlice, + name: &str, + params: &[&str], + scope: GcCell<'gc, Scope<'gc>>, + ) -> Self { let name = match name { "" => None, - name => Some(name.to_string()) + name => Some(name.to_string()), }; Avm1Function { - swf_version: swf_version, + swf_version, data: actions, - name: name, - params: params.into_iter().map(|s| s.to_string()).collect(), - scope: scope + name, + params: params.iter().map(|s| s.to_string()).collect(), + scope, } } @@ -60,7 +65,7 @@ impl<'gc> Avm1Function<'gc> { } pub fn scope(&self) -> GcCell<'gc, Scope<'gc>> { - self.scope.clone() + self.scope } } @@ -79,7 +84,6 @@ pub struct Avm1Function2<'gc> { /// A reference to the underlying SWF data. data: SwfSlice, - /// The name of the function, if not anonymous. name: Option, @@ -103,52 +107,46 @@ pub struct Avm1Function2<'gc> { params: Vec<(Option, String)>, /// The scope the function was born into. - scope: GcCell<'gc, Scope<'gc>> + scope: GcCell<'gc, Scope<'gc>>, } impl<'gc> Avm1Function2<'gc> { - pub fn new(swf_version: u8, + pub fn new( + swf_version: u8, actions: SwfSlice, - name: &str, - register_count: u8, - preload_parent: bool, - preload_root: bool, - supress_super: bool, - preload_super: bool, - supress_arguments: bool, - preload_arguments: bool, - supress_this: bool, - preload_this: bool, - preload_global: bool, - params: &Vec, - scope: GcCell<'gc, Scope<'gc>>) -> Self { - - let name = match name { + swf_function: &swf::avm1::types::Function, + scope: GcCell<'gc, Scope<'gc>>, + ) -> Self { + let name = match swf_function.name { "" => None, - name => Some(name.to_string()) + name => Some(name.to_string()), }; let mut owned_params = Vec::new(); - for FunctionParam{name: s, register_index: r} in params.into_iter() { - owned_params.push((r.clone(), s.to_string())) + for FunctionParam { + name: s, + register_index: r, + } in &swf_function.params + { + owned_params.push((*r, s.to_string())) } Avm1Function2 { - swf_version: swf_version, + swf_version, data: actions, - name: name, - register_count: register_count, - preload_parent: preload_parent, - preload_root: preload_root, - supress_super: supress_super, - preload_super: preload_super, - supress_arguments: supress_arguments, - preload_arguments: preload_arguments, - supress_this: supress_this, - preload_this: preload_this, - preload_global: preload_global, + name, + register_count: swf_function.params.capacity() as u8, + preload_parent: swf_function.preload_parent, + preload_root: swf_function.preload_root, + supress_super: swf_function.suppress_super, + preload_super: swf_function.preload_super, + supress_arguments: swf_function.suppress_super, + preload_arguments: swf_function.preload_arguments, + supress_this: swf_function.suppress_this, + preload_this: swf_function.preload_this, + preload_global: swf_function.preload_global, params: owned_params, - scope: scope + scope, } } @@ -161,7 +159,7 @@ impl<'gc> Avm1Function2<'gc> { } pub fn scope(&self) -> GcCell<'gc, Scope<'gc>> { - self.scope.clone() + self.scope } pub fn register_count(&self) -> u8 { @@ -180,17 +178,23 @@ pub enum Executable<'gc> { Action(Avm1Function<'gc>), /// ActionScript data defined by a previous `DefineFunction2` action. - Action2(Avm1Function2<'gc>) + Action2(Avm1Function2<'gc>), } 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> { + 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::Action(af) => { @@ -201,23 +205,38 @@ impl<'gc> Executable<'gc> { } arguments.force_set("length", Value::Number(args.len() as f64)); - let argcell = GcCell::allocate(ac.gc_context, arguments); - let child_scope = GcCell::allocate(ac.gc_context, Scope::new_local_scope(af.scope(), ac.gc_context)); + let child_scope = GcCell::allocate( + ac.gc_context, + Scope::new_local_scope(af.scope(), ac.gc_context), + ); for i in 0..args.len() { if let Some(argname) = af.params.get(i) { - child_scope.write(ac.gc_context).define(argname, args.get(i).unwrap().clone(), ac.gc_context); + child_scope.write(ac.gc_context).define( + argname, + args.get(i).unwrap().clone(), + ac.gc_context, + ); } } - let frame = Activation::from_function(af.swf_version(), af.data(), child_scope, this, Some(argcell)); + let frame = Activation::from_function( + af.swf_version(), + af.data(), + child_scope, + this, + Some(argcell), + ); avm.insert_stack_frame(frame); None - }, + } Executable::Action2(af) => { - let child_scope = GcCell::allocate(ac.gc_context, Scope::new_local_scope(af.scope(), ac.gc_context)); + let child_scope = GcCell::allocate( + ac.gc_context, + Scope::new_local_scope(af.scope(), ac.gc_context), + ); let mut arguments = Object::object(ac.gc_context); if !af.supress_arguments { for i in 0..args.len() { @@ -228,7 +247,13 @@ impl<'gc> Executable<'gc> { } let argcell = GcCell::allocate(ac.gc_context, arguments); - let mut frame = Activation::from_function(af.swf_version(), af.data(), child_scope, this, Some(argcell)); + let mut frame = Activation::from_function( + af.swf_version(), + af.data(), + child_scope, + this, + Some(argcell), + ); let mut preload_r = 1; if af.preload_this { @@ -268,16 +293,19 @@ impl<'gc> Executable<'gc> { frame.set_local_register(preload_r, avm.global_object(ac), ac.gc_context); } - //TODO: What happens if the argument registers clash with the + //TODO: What happens if the argument registers clash with the //preloaded registers? What gets done last? for i in 0..args.len() { match (args.get(i), af.params.get(i)) { - (Some(arg), Some((Some(argreg), _argname))) => frame.set_local_register(*argreg, arg.clone(), ac.gc_context), - (Some(arg), Some((None, argname))) => frame.define(argname, arg.clone(), ac.gc_context), + (Some(arg), Some((Some(argreg), _argname))) => { + frame.set_local_register(*argreg, arg.clone(), ac.gc_context) + } + (Some(arg), Some((None, argname))) => { + frame.define(argname, arg.clone(), ac.gc_context) + } _ => {} } } - frame.allocate_local_registers(af.register_count(), ac.gc_context); avm.insert_stack_frame(frame); @@ -285,4 +313,4 @@ impl<'gc> Executable<'gc> { } } } -} \ No newline at end of file +} diff --git a/core/src/avm1/scope.rs b/core/src/avm1/scope.rs index dcbd189ec..554154cc8 100644 --- a/core/src/avm1/scope.rs +++ b/core/src/avm1/scope.rs @@ -1,12 +1,11 @@ //! Represents AVM1 scope chain resolution. -use std::cell::{Ref, RefMut}; +use crate::avm1::{ActionContext, Avm1, Object, Value}; use gc_arena::{GcCell, MutationContext}; -use crate::avm1::{Avm1, ActionContext, Object, Value}; +use std::cell::{Ref, RefMut}; /// Indicates what kind of scope a scope is. -#[derive(Copy, Clone, -Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum ScopeClass { /// Scope represents global scope. Global, @@ -21,7 +20,7 @@ pub enum ScopeClass { /// Scope represents an object added to the scope chain with `with`. /// It is not inherited when closures are defined. - With + With, } /// Represents a scope chain for an AVM1 activation. @@ -29,7 +28,7 @@ pub enum ScopeClass { pub struct Scope<'gc> { parent: Option>>, class: ScopeClass, - values: GcCell<'gc, Object<'gc>> + values: GcCell<'gc, Object<'gc>>, } unsafe impl<'gc> gc_arena::Collect for Scope<'gc> { @@ -46,30 +45,33 @@ impl<'gc> Scope<'gc> { Scope { parent: None, class: ScopeClass::Global, - values: globals + values: globals, } } /// Construct a child scope of another scope. pub fn new_local_scope(parent: GcCell<'gc, Self>, mc: MutationContext<'gc, '_>) -> Scope<'gc> { Scope { - parent: Some(parent.clone()), + parent: Some(parent), class: ScopeClass::Local, - values: GcCell::allocate(mc, Object::bare_object()) + values: GcCell::allocate(mc, Object::bare_object()), } } /// Construct a closure scope to be used as the parent of all local scopes /// when invoking a function. - pub fn new_closure_scope(mut parent: GcCell<'gc, Self>, mc: MutationContext<'gc, '_>) -> GcCell<'gc, Self> { + pub fn new_closure_scope( + mut parent: GcCell<'gc, Self>, + mc: MutationContext<'gc, '_>, + ) -> GcCell<'gc, Self> { let mut closure_scope_list = Vec::new(); loop { if parent.read().class != ScopeClass::With { - closure_scope_list.push(parent.clone()); + closure_scope_list.push(parent); } - let grandparent = parent.read().parent.clone(); + let grandparent = parent.read().parent; if let Some(grandparent) = grandparent { parent = grandparent; } else { @@ -79,42 +81,52 @@ impl<'gc> Scope<'gc> { let mut parent_scope = None; for scope in closure_scope_list.iter().rev() { - parent_scope = Some(GcCell::allocate(mc, Scope { - parent: parent_scope, - class: scope.read().class, - values: scope.read().values.clone() - })); + parent_scope = Some(GcCell::allocate( + mc, + Scope { + parent: parent_scope, + class: scope.read().class, + values: scope.read().values, + }, + )); } if let Some(parent_scope) = parent_scope { parent_scope } else { - GcCell::allocate(mc, Scope { - parent: None, - class: ScopeClass::Global, - values: GcCell::allocate(mc, Object::bare_object()) - }) + GcCell::allocate( + mc, + Scope { + parent: None, + class: ScopeClass::Global, + values: GcCell::allocate(mc, Object::bare_object()), + }, + ) } } /// Construct a scope for use with `tellTarget` code where the timeline /// scope has been replaced with another given object. - pub fn new_target_scope(mut parent: GcCell<'gc, Self>, clip: GcCell<'gc, Object<'gc>>, mc: MutationContext<'gc, '_>) -> GcCell<'gc, Self> { + pub fn new_target_scope( + mut parent: GcCell<'gc, Self>, + clip: GcCell<'gc, Object<'gc>>, + mc: MutationContext<'gc, '_>, + ) -> GcCell<'gc, Self> { let mut timeline_scope_list = Vec::new(); loop { if parent.read().class != ScopeClass::Target { - timeline_scope_list.push(parent.clone()); + timeline_scope_list.push(parent); } else { let new_scope = Self { parent: None, class: ScopeClass::Target, - values: clip + values: clip, }; timeline_scope_list.push(GcCell::allocate(mc, new_scope)); } - let grandparent = parent.read().parent.clone(); + let grandparent = parent.read().parent; if let Some(grandparent) = grandparent { parent = grandparent; } else { @@ -124,53 +136,71 @@ impl<'gc> Scope<'gc> { let mut parent_scope = None; for scope in timeline_scope_list.iter().rev() { - parent_scope = Some(GcCell::allocate(mc, Scope { - parent: parent_scope, - class: scope.read().class, - values: scope.read().values.clone() - })); + parent_scope = Some(GcCell::allocate( + mc, + Scope { + parent: parent_scope, + class: scope.read().class, + values: scope.read().values, + }, + )); } if let Some(parent_scope) = parent_scope { parent_scope } else { - GcCell::allocate(mc, Scope { - parent: None, - class: ScopeClass::Global, - values: GcCell::allocate(mc, Object::bare_object()) - }) + GcCell::allocate( + mc, + Scope { + parent: None, + class: ScopeClass::Global, + values: GcCell::allocate(mc, Object::bare_object()), + }, + ) } } /// Construct a with scope to be used as the scope during a with block. - /// + /// /// A with block inserts the values of a particular object into the scope /// of currently running code, while still maintaining the same local /// scope. This requires some scope chain juggling. - pub fn new_with_scope(locals: GcCell<'gc, Self>, - with_object: GcCell<'gc, Object<'gc>>, - mc: MutationContext<'gc, '_>) -> GcCell<'gc, Self> { - let parent_scope = locals.read().parent.clone(); - let local_values = locals.read().values.clone(); - let with_scope = GcCell::allocate(mc, Scope { - parent: parent_scope, - class: ScopeClass::With, - values: with_object - }); + pub fn new_with_scope( + locals: GcCell<'gc, Self>, + with_object: GcCell<'gc, Object<'gc>>, + mc: MutationContext<'gc, '_>, + ) -> GcCell<'gc, Self> { + let parent_scope = locals.read().parent; + let local_values = locals.read().values; + let with_scope = GcCell::allocate( + mc, + Scope { + parent: parent_scope, + class: ScopeClass::With, + values: with_object, + }, + ); - GcCell::allocate(mc, Scope { - parent: Some(with_scope), - class: ScopeClass::Local, - values: local_values - }) + GcCell::allocate( + mc, + Scope { + parent: Some(with_scope), + class: ScopeClass::Local, + values: local_values, + }, + ) } /// Construct an arbitrary scope - pub fn new(parent: GcCell<'gc, Self>, class: ScopeClass, with_object: GcCell<'gc, Object<'gc>>) -> Scope<'gc> { + pub fn new( + parent: GcCell<'gc, Self>, + class: ScopeClass, + with_object: GcCell<'gc, Object<'gc>>, + ) -> Scope<'gc> { Scope { - parent: Some(parent.clone()), - class: class, - values: with_object + parent: Some(parent), + class, + values: with_object, } } @@ -188,7 +218,7 @@ impl<'gc> Scope<'gc> { pub fn parent(&self) -> Option>> { match self.parent { Some(ref p) => Some(p.read()), - None => None + None => None, } } @@ -197,7 +227,6 @@ impl<'gc> Scope<'gc> { if self.locals().has_property(name) { return self.locals().force_get(name); } - if let Some(scope) = self.parent() { return scope.resolve(name); } @@ -220,20 +249,23 @@ impl<'gc> Scope<'gc> { /// Update a particular value in the scope chain, but only if it was /// previously defined. - /// + /// /// If the value is currently already defined in this scope, then it will /// be overwritten. If it is not defined, then we traverse the scope chain /// until we find a defined value to overwrite. We do not define a property /// if it is not already defined somewhere in the scope chain, and instead /// return it so that the caller may manually define the property itself. - pub fn overwrite(&self, + pub fn overwrite( + &self, name: &str, - value: Value<'gc>, + value: Value<'gc>, avm: &mut Avm1<'gc>, context: &mut ActionContext<'_, 'gc, '_>, - this: GcCell<'gc, Object<'gc>>) -> Option> { + this: GcCell<'gc, Object<'gc>>, + ) -> Option> { if self.locals().has_property(name) { - self.locals_mut(context.gc_context).set(name, value, avm, context, this); + self.locals_mut(context.gc_context) + .set(name, value, avm, context, this); return None; } @@ -245,7 +277,7 @@ impl<'gc> Scope<'gc> { } /// Set a particular value in the locals for this scope. - /// + /// /// By convention, the locals for a given function are always defined as /// stored (e.g. not virtual) properties on the lowest object in the scope /// chain. As a result, this function always force sets a property on the @@ -264,4 +296,4 @@ impl<'gc> Scope<'gc> { return scope.delete(name, mc); } } -} \ No newline at end of file +}