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:
parent
2d365856a7
commit
1c3e4406b3
357
core/src/avm1.rs
357
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<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(())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue