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
This commit is contained in:
Mike Welsh 2019-10-06 14:45:14 -07:00
parent 2d365856a7
commit 1c3e4406b3
4 changed files with 512 additions and 275 deletions

View File

@ -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<String, String> {
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.
@ -167,20 +201,26 @@ impl<'gc> Avm1<'gc> {
/// 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<F, R>(&mut self, func: F) -> Option<R> where F: FnOnce(&mut Self, &mut Reader<'_>) -> R {
fn with_current_reader_mut<F, R>(&mut self, func: F) -> Option<R>
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(
@ -414,21 +463,41 @@ impl<'gc> Avm1<'gc> {
/// 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,7 +626,10 @@ 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?
@ -562,7 +637,10 @@ impl<'gc> Avm1<'gc> {
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(())
}
@ -1340,8 +1459,9 @@ impl<'gc> Avm1<'gc> {
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(())
}

View File

@ -1,12 +1,12 @@
//! 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)]
@ -48,7 +48,7 @@ pub struct Activation<'gc> {
///
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
/// same register set.
local_registers: Option<GcCell<'gc, Vec<Value<'gc>>>>
local_registers: Option<GcCell<'gc, Vec<Value<'gc>>>>,
}
unsafe impl<'gc> gc_arena::Collect for Activation<'gc> {
@ -62,29 +62,41 @@ 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<GcCell<'gc, Object<'gc>>>) -> Activation<'gc> {
pub fn from_action(
swf_version: u8,
code: SwfSlice,
scope: GcCell<'gc, Scope<'gc>>,
this: GcCell<'gc, Object<'gc>>,
arguments: Option<GcCell<'gc, Object<'gc>>>,
) -> 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<GcCell<'gc, Object<'gc>>>) -> Activation<'gc> {
pub fn from_function(
swf_version: u8,
code: SwfSlice,
scope: GcCell<'gc, Scope<'gc>>,
this: GcCell<'gc, Object<'gc>>,
arguments: Option<GcCell<'gc, Object<'gc>>>,
) -> 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,
}
}
@ -94,23 +106,27 @@ impl<'gc> Activation<'gc> {
/// 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;
}
}
}
}

View File

@ -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<String>,
@ -32,22 +31,28 @@ pub struct Avm1Function<'gc> {
params: Vec<String>,
/// 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<String>,
@ -103,52 +107,46 @@ pub struct Avm1Function2<'gc> {
params: Vec<(Option<u8>, 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<FunctionParam>,
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,7 +178,7 @@ 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> {
@ -190,7 +188,13 @@ impl<'gc> Executable<'gc> {
/// 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>> {
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::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 {
@ -272,12 +297,15 @@ impl<'gc> Executable<'gc> {
//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);

View File

@ -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<GcCell<'gc, Scope<'gc>>>,
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_scope = Some(GcCell::allocate(
mc,
Scope {
parent: parent_scope,
class: scope.read().class,
values: scope.read().values.clone()
}));
values: scope.read().values,
},
));
}
if let Some(parent_scope) = parent_scope {
parent_scope
} else {
GcCell::allocate(mc, Scope {
GcCell::allocate(
mc,
Scope {
parent: None,
class: ScopeClass::Global,
values: GcCell::allocate(mc, Object::bare_object())
})
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,21 +136,27 @@ 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_scope = Some(GcCell::allocate(
mc,
Scope {
parent: parent_scope,
class: scope.read().class,
values: scope.read().values.clone()
}));
values: scope.read().values,
},
));
}
if let Some(parent_scope) = parent_scope {
parent_scope
} else {
GcCell::allocate(mc, Scope {
GcCell::allocate(
mc,
Scope {
parent: None,
class: ScopeClass::Global,
values: GcCell::allocate(mc, Object::bare_object())
})
values: GcCell::allocate(mc, Object::bare_object()),
},
)
}
}
@ -147,30 +165,42 @@ impl<'gc> Scope<'gc> {
/// 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>,
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 {
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
});
values: with_object,
},
);
GcCell::allocate(mc, Scope {
GcCell::allocate(
mc,
Scope {
parent: Some(with_scope),
class: ScopeClass::Local,
values: local_values
})
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<Ref<Scope<'gc>>> {
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);
}
@ -226,14 +255,17 @@ impl<'gc> Scope<'gc> {
/// 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>,
avm: &mut Avm1<'gc>,
context: &mut ActionContext<'_, 'gc, '_>,
this: GcCell<'gc, Object<'gc>>) -> Option<Value<'gc>> {
this: GcCell<'gc, Object<'gc>>,
) -> Option<Value<'gc>> {
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;
}