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::globals::create_globals;
|
||||||
use crate::avm1::object::Object;
|
use crate::avm1::object::Object;
|
||||||
use crate::avm1::function::Avm1Function2;
|
|
||||||
use crate::backend::navigator::NavigationMethod;
|
use crate::backend::navigator::NavigationMethod;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
use rand::{rngs::SmallRng, Rng};
|
use rand::{rngs::SmallRng, Rng};
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use swf::avm1::read::Reader;
|
use swf::avm1::read::Reader;
|
||||||
use swf::avm1::types::{Action, Function};
|
use swf::avm1::types::{Action, Function};
|
||||||
|
|
||||||
use crate::tag_utils::SwfSlice;
|
use crate::tag_utils::SwfSlice;
|
||||||
|
|
||||||
|
mod activation;
|
||||||
mod fscommand;
|
mod fscommand;
|
||||||
mod function;
|
mod function;
|
||||||
mod scope;
|
|
||||||
mod activation;
|
|
||||||
mod globals;
|
mod globals;
|
||||||
pub mod movie_clip;
|
pub mod movie_clip;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
|
mod scope;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use value::Value;
|
|
||||||
use scope::Scope;
|
|
||||||
use activation::Activation;
|
use activation::Activation;
|
||||||
|
use scope::Scope;
|
||||||
|
pub use value::Value;
|
||||||
|
|
||||||
pub struct ActionContext<'a, 'gc, 'gc_context> {
|
pub struct ActionContext<'a, 'gc, 'gc_context> {
|
||||||
pub gc_context: gc_arena::MutationContext<'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
|
/// Used to enforce the restriction that no more than one mutable current
|
||||||
/// reader be active at once.
|
/// reader be active at once.
|
||||||
is_reading: bool
|
is_reading: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> gc_arena::Collect for Avm1<'gc> {
|
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)),
|
globals: GcCell::allocate(gc_context, create_globals(gc_context)),
|
||||||
stack_frames: vec![],
|
stack_frames: vec![],
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
registers: [Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined],
|
registers: [
|
||||||
is_reading: false
|
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> {
|
pub fn locals_into_form_values(&self) -> HashMap<String, String> {
|
||||||
let mut form_values = HashMap::new();
|
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());
|
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
|
/// 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, '_>) {
|
pub fn insert_stack_frame_for_action(
|
||||||
let global_scope = GcCell::allocate(action_context.gc_context, Scope::from_global_object(self.globals));
|
&mut self,
|
||||||
let clip_obj = action_context.active_clip.read().object().as_object().unwrap().to_owned();
|
swf_version: u8,
|
||||||
let child_scope = GcCell::allocate(action_context.gc_context, Scope::new(global_scope, scope::ScopeClass::Target, clip_obj));
|
code: SwfSlice,
|
||||||
self.stack_frames.push(Activation::from_action(swf_version, code, child_scope, clip_obj, None));
|
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.
|
/// 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.
|
/// 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.
|
/// Doing so will result in any changes in duplicate readers being ignored.
|
||||||
/// Always pass the borrowed reader into functions that need it.
|
/// 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 {
|
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;
|
self.is_reading = true;
|
||||||
|
|
||||||
let current_stack_id = self.stack_frames.len() - 1;
|
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);
|
let mut read = Reader::new(data.as_ref(), swf_version);
|
||||||
read.seek(pc.try_into().unwrap());
|
read.seek(pc.try_into().unwrap());
|
||||||
|
|
||||||
let r = func(self, &mut read);
|
let r = func(self, &mut read);
|
||||||
|
|
||||||
//this took an hour of fighting borrowck to figure out was necessary
|
//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 let Some(new_stack) = self.stack_frames.get_mut(current_stack_id) {
|
||||||
if new_stack.is_identical_fn(&data) {
|
if new_stack.is_identical_fn(&data) {
|
||||||
|
@ -199,16 +239,24 @@ impl<'gc> Avm1<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the AVM stack until it is exhausted.
|
/// Execute the AVM stack until it is exhausted.
|
||||||
pub fn run_stack_till_empty(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> {
|
pub fn run_stack_till_empty(
|
||||||
while self.stack_frames.len() > 0 {
|
&mut self,
|
||||||
self.with_current_reader_mut(|this, r| this.do_next_action(context, r)).unwrap()?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a single action from a given action reader.
|
/// 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();
|
let data = self.current_stack_frame().unwrap().data();
|
||||||
|
|
||||||
if reader.pos() >= (data.end - data.start) {
|
if reader.pos() >= (data.end - data.start) {
|
||||||
|
@ -234,6 +282,7 @@ impl<'gc> Avm1<'gc> {
|
||||||
Action::CallFunction => self.action_call_function(context),
|
Action::CallFunction => self.action_call_function(context),
|
||||||
Action::CallMethod => self.action_call_method(context),
|
Action::CallMethod => self.action_call_method(context),
|
||||||
Action::CharToAscii => self.action_char_to_ascii(context),
|
Action::CharToAscii => self.action_char_to_ascii(context),
|
||||||
|
Action::CloneSprite => self.action_clone_sprite(context),
|
||||||
Action::ConstantPool(constant_pool) => {
|
Action::ConstantPool(constant_pool) => {
|
||||||
self.action_constant_pool(context, &constant_pool[..])
|
self.action_constant_pool(context, &constant_pool[..])
|
||||||
}
|
}
|
||||||
|
@ -346,8 +395,8 @@ impl<'gc> Avm1<'gc> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variable_name_is_slash_path<'s>(path: &'s str) -> bool {
|
pub fn variable_name_is_slash_path(path: &str) -> bool {
|
||||||
path.contains(":") || path.contains(".")
|
path.contains(':') || path.contains('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_slash_path(
|
pub fn resolve_slash_path(
|
||||||
|
@ -414,21 +463,41 @@ impl<'gc> Avm1<'gc> {
|
||||||
/// If a given register does not exist, this function yields
|
/// If a given register does not exist, this function yields
|
||||||
/// Value::Undefined, which is also a valid register value.
|
/// Value::Undefined, which is also a valid register value.
|
||||||
pub fn current_register(&self, id: u8) -> Value<'gc> {
|
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)
|
self.current_stack_frame().unwrap().local_register(id)
|
||||||
} else {
|
} 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.
|
/// Set a register to a given value.
|
||||||
///
|
///
|
||||||
/// If a given register does not exist, this function does nothing.
|
/// 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, '_>) {
|
pub fn set_current_register(
|
||||||
if self.current_stack_frame().map(|sf| sf.has_local_registers()).unwrap_or(false) {
|
&mut self,
|
||||||
self.current_stack_frame_mut().unwrap().set_local_register(id, value, context.gc_context);
|
id: u8,
|
||||||
} else {
|
value: Value<'gc>,
|
||||||
self.registers.get_mut(id as usize).map(|v| *v = value);
|
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 a = self.pop()?;
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let result = b.into_number_v1() != 0.0 && a.into_number_v1() != 0.0;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,7 +626,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
Err("Unimplemented action: Call".into())
|
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 fn_name = self.pop()?;
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count?
|
let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count?
|
||||||
|
@ -562,7 +637,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
args.push(self.pop()?);
|
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 this = context.active_clip.read().object().as_object()?.to_owned();
|
||||||
let return_value = target_fn.call(self, context, this, &args)?;
|
let return_value = target_fn.call(self, context, this, &args)?;
|
||||||
if let Some(instant_return) = return_value {
|
if let Some(instant_return) = return_value {
|
||||||
|
@ -572,7 +650,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
Ok(())
|
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 method_name = self.pop()?;
|
||||||
let object = self.pop()?;
|
let object = self.pop()?;
|
||||||
let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count?
|
let num_args = self.pop()?.as_i64()?; // TODO(Herschel): max arg count?
|
||||||
|
@ -591,7 +672,8 @@ impl<'gc> Avm1<'gc> {
|
||||||
}
|
}
|
||||||
Value::String(name) => {
|
Value::String(name) => {
|
||||||
if name.is_empty() {
|
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 {
|
if let Some(instant_return) = return_value {
|
||||||
self.push(instant_return);
|
self.push(instant_return);
|
||||||
}
|
}
|
||||||
|
@ -607,7 +689,8 @@ impl<'gc> Avm1<'gc> {
|
||||||
return Err(format!("Object method {} is not defined", name).into());
|
return Err(format!("Object method {} is not defined", name).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = 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 {
|
if let Some(instant_return) = return_value {
|
||||||
self.push(instant_return);
|
self.push(instant_return);
|
||||||
}
|
}
|
||||||
|
@ -648,14 +731,26 @@ impl<'gc> Avm1<'gc> {
|
||||||
actions: &[u8],
|
actions: &[u8],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let swf_version = self.current_stack_frame().unwrap().swf_version();
|
let swf_version = self.current_stack_frame().unwrap().swf_version();
|
||||||
let func_data = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap();
|
let func_data = self
|
||||||
let scope = Scope::new_closure_scope(self.current_stack_frame().unwrap().scope_cell(), context.gc_context);
|
.current_stack_frame()
|
||||||
let func = Value::Object(GcCell::allocate(context.gc_context, Object::action_function(swf_version, func_data, name, params, scope)));
|
.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 == "" {
|
if name == "" {
|
||||||
self.push(func);
|
self.push(func);
|
||||||
} else {
|
} 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(())
|
Ok(())
|
||||||
|
@ -664,49 +759,61 @@ impl<'gc> Avm1<'gc> {
|
||||||
fn action_define_function_2(
|
fn action_define_function_2(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &mut ActionContext<'_, 'gc, '_>,
|
context: &mut ActionContext<'_, 'gc, '_>,
|
||||||
action_func: &Function
|
action_func: &Function,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let swf_version = self.current_stack_frame().unwrap().swf_version();
|
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 func_data = self
|
||||||
let scope = Scope::new_closure_scope(self.current_stack_frame().unwrap().scope_cell(), context.gc_context);
|
.current_stack_frame()
|
||||||
let func2 = Avm1Function2::new(
|
.unwrap()
|
||||||
swf_version,
|
.data()
|
||||||
func_data,
|
.to_subslice(action_func.actions)
|
||||||
action_func.name,
|
.unwrap();
|
||||||
action_func.params.capacity() as u8, //TODO: this needs to be refactored
|
let scope = Scope::new_closure_scope(
|
||||||
action_func.preload_parent,
|
self.current_stack_frame().unwrap().scope_cell(),
|
||||||
action_func.preload_root,
|
context.gc_context,
|
||||||
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_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 == "" {
|
if action_func.name == "" {
|
||||||
self.push(func_obj);
|
self.push(func_obj);
|
||||||
} else {
|
} 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(())
|
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 value = self.pop()?;
|
||||||
let name = 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(())
|
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()?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +825,6 @@ impl<'gc> Avm1<'gc> {
|
||||||
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
|
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
|
||||||
//a boolean based on if the delete actually deleted something.
|
//a boolean based on if the delete actually deleted something.
|
||||||
let did_exist = Value::Bool(object.read().has_property(name));
|
let did_exist = Value::Bool(object.read().has_property(name));
|
||||||
|
|
||||||
object.write(context.gc_context).delete(name);
|
object.write(context.gc_context).delete(name);
|
||||||
self.push(did_exist);
|
self.push(did_exist);
|
||||||
|
|
||||||
|
@ -733,7 +839,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
//a boolean based on if the delete actually deleted something.
|
//a boolean based on if the delete actually deleted something.
|
||||||
let did_exist = Value::Bool(self.current_stack_frame().unwrap().is_defined(name));
|
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);
|
self.push(did_exist);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -762,13 +871,12 @@ impl<'gc> Avm1<'gc> {
|
||||||
let name = self.pop()?;
|
let name = self.pop()?;
|
||||||
let name = name.as_string()?;
|
let name = name.as_string()?;
|
||||||
self.push(Value::Null); // Sentinel that indicates end of enumeration
|
self.push(Value::Null); // Sentinel that indicates end of enumeration
|
||||||
|
|
||||||
let ob = match self.current_stack_frame().unwrap().resolve(name) {
|
let ob = match self.current_stack_frame().unwrap().resolve(name) {
|
||||||
Value::Object(ob) => ob,
|
Value::Object(ob) => ob,
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Cannot enumerate properties of {}", name);
|
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 a = self.pop()?;
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let result = b.into_number_v1() == a.into_number_v1();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,7 +1017,6 @@ impl<'gc> Avm1<'gc> {
|
||||||
|
|
||||||
let is_slashpath = Self::variable_name_is_slash_path(path);
|
let is_slashpath = Self::variable_name_is_slash_path(path);
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
|
|
||||||
if is_slashpath {
|
if is_slashpath {
|
||||||
if let Some((node, var_name)) =
|
if let Some((node, var_name)) =
|
||||||
Self::resolve_slash_path_variable(context.target_clip, context.root, path)
|
Self::resolve_slash_path_variable(context.target_clip, context.root, path)
|
||||||
|
@ -1063,7 +1173,7 @@ impl<'gc> Avm1<'gc> {
|
||||||
&mut self,
|
&mut self,
|
||||||
_context: &mut ActionContext,
|
_context: &mut ActionContext,
|
||||||
jump_offset: i16,
|
jump_offset: i16,
|
||||||
reader: &mut Reader<'_>
|
reader: &mut Reader<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let val = self.pop()?;
|
let val = self.pop()?;
|
||||||
if val.as_bool() {
|
if val.as_bool() {
|
||||||
|
@ -1103,7 +1213,7 @@ impl<'gc> Avm1<'gc> {
|
||||||
&mut self,
|
&mut self,
|
||||||
_context: &mut ActionContext,
|
_context: &mut ActionContext,
|
||||||
jump_offset: i16,
|
jump_offset: i16,
|
||||||
reader: &mut Reader<'_>
|
reader: &mut Reader<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// TODO(Herschel): Handle out-of-bounds.
|
// TODO(Herschel): Handle out-of-bounds.
|
||||||
reader.seek(jump_offset.into());
|
reader.seek(jump_offset.into());
|
||||||
|
@ -1115,7 +1225,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
let a = self.pop()?;
|
let a = self.pop()?;
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let result = b.into_number_v1() < a.into_number_v1();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1186,7 +1299,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
// AS1 logical not
|
// AS1 logical not
|
||||||
let val = self.pop()?;
|
let val = self.pop()?;
|
||||||
let result = val.into_number_v1() == 0.0;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1229,7 +1345,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
let a = self.pop()?;
|
let a = self.pop()?;
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let result = b.into_number_v1() != 0.0 || a.into_number_v1() != 0.0;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1340,8 +1459,9 @@ impl<'gc> Avm1<'gc> {
|
||||||
let object = self.pop()?.as_object()?;
|
let object = self.pop()?.as_object()?;
|
||||||
let this = self.current_stack_frame().unwrap().this_cell();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1397,10 +1517,13 @@ impl<'gc> Avm1<'gc> {
|
||||||
Self::resolve_slash_path_variable(context.target_clip, context.root, var_path)
|
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() {
|
if let Some(clip) = node.write(context.gc_context).as_movie_clip_mut() {
|
||||||
clip.object()
|
clip.object().as_object()?.write(context.gc_context).set(
|
||||||
.as_object()?
|
var_name,
|
||||||
.write(context.gc_context)
|
value.clone(),
|
||||||
.set(var_name, value.clone(), self, context, this);
|
self,
|
||||||
|
context,
|
||||||
|
this,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1408,7 +1531,9 @@ impl<'gc> Avm1<'gc> {
|
||||||
let scope = self.current_stack_frame().unwrap().scope_cell();
|
let scope = self.current_stack_frame().unwrap().scope_cell();
|
||||||
let unused_value = scope.read().overwrite(var_path, value, self, context, this);
|
let unused_value = scope.read().overwrite(var_path, value, self, context, this);
|
||||||
if let Some(value) = unused_value {
|
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 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(())
|
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()?;
|
let target = self.pop()?;
|
||||||
if let Ok(target) = target.as_string() {
|
if let Ok(target) = target.as_string() {
|
||||||
self.action_set_target(context, target)?;
|
self.action_set_target(context, target)?;
|
||||||
|
@ -1508,7 +1644,6 @@ impl<'gc> Avm1<'gc> {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Does NOT pop the value from the stack.
|
// Does NOT pop the value from the stack.
|
||||||
let val = self.stack.last().ok_or("Stack underflow")?.clone();
|
let val = self.stack.last().ok_or("Stack underflow")?.clone();
|
||||||
|
|
||||||
self.set_current_register(register, val, context);
|
self.set_current_register(register, val, context);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1529,7 +1664,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
let a = self.pop()?;
|
let a = self.pop()?;
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let result = b.into_string() == a.into_string();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1566,7 +1704,10 @@ impl<'gc> Avm1<'gc> {
|
||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
// This is specifically a non-UTF8 aware comparison.
|
// This is specifically a non-UTF8 aware comparison.
|
||||||
let result = b.into_string().bytes().lt(a.into_string().bytes());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1627,7 +1768,7 @@ impl<'gc> Avm1<'gc> {
|
||||||
_context: &mut ActionContext,
|
_context: &mut ActionContext,
|
||||||
_frame: u16,
|
_frame: u16,
|
||||||
num_actions_to_skip: u8,
|
num_actions_to_skip: u8,
|
||||||
r: &mut Reader<'_>
|
r: &mut Reader<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// TODO(Herschel): Always true for now.
|
// TODO(Herschel): Always true for now.
|
||||||
let loaded = true;
|
let loaded = true;
|
||||||
|
@ -1643,7 +1784,7 @@ impl<'gc> Avm1<'gc> {
|
||||||
&mut self,
|
&mut self,
|
||||||
_context: &mut ActionContext,
|
_context: &mut ActionContext,
|
||||||
num_actions_to_skip: u8,
|
num_actions_to_skip: u8,
|
||||||
r: &mut Reader<'_>
|
r: &mut Reader<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// TODO(Herschel): Always true for now.
|
// TODO(Herschel): Always true for now.
|
||||||
let _frame_num = self.pop()?.as_f64()? as u16;
|
let _frame_num = self.pop()?.as_f64()? as u16;
|
||||||
|
@ -1656,11 +1797,27 @@ impl<'gc> Avm1<'gc> {
|
||||||
Ok(())
|
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 object = self.pop()?.as_object()?;
|
||||||
let block = self.current_stack_frame().unwrap().data().to_subslice(actions).unwrap();
|
let block = self
|
||||||
let with_scope = Scope::new_with_scope(self.current_stack_frame().unwrap().scope_cell(), object, context.gc_context);
|
.current_stack_frame()
|
||||||
let new_activation = self.current_stack_frame().unwrap().to_rescope(block, with_scope);
|
.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);
|
self.stack_frames.push(new_activation);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//! Activation records
|
//! 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::object::Object;
|
||||||
|
use crate::avm1::scope::Scope;
|
||||||
use crate::avm1::Value;
|
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.
|
/// Represents a single activation of a given AVM1 function or keyframe.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -48,7 +48,7 @@ pub struct Activation<'gc> {
|
||||||
///
|
///
|
||||||
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
|
/// Registers are stored in a `GcCell` so that rescopes (e.g. with) use the
|
||||||
/// same register set.
|
/// 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> {
|
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> {
|
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 {
|
Activation {
|
||||||
swf_version: swf_version,
|
swf_version,
|
||||||
data: code,
|
data: code,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
scope: scope,
|
scope,
|
||||||
this: this,
|
this,
|
||||||
arguments: arguments,
|
arguments,
|
||||||
is_function: false,
|
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 {
|
Activation {
|
||||||
swf_version: swf_version,
|
swf_version,
|
||||||
data: code,
|
data: code,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
scope: scope,
|
scope,
|
||||||
this: this,
|
this,
|
||||||
arguments: arguments,
|
arguments,
|
||||||
is_function: true,
|
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.
|
/// will prevent the AVM from panicking without a current activation.
|
||||||
/// We construct a single scope chain from a global object, and that's about
|
/// We construct a single scope chain from a global object, and that's about
|
||||||
/// it.
|
/// 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 global_scope = GcCell::allocate(mc, Scope::from_global_object(globals));
|
||||||
let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc));
|
let child_scope = GcCell::allocate(mc, Scope::new_local_scope(global_scope, mc));
|
||||||
|
|
||||||
Activation {
|
Activation {
|
||||||
swf_version: swf_version,
|
swf_version,
|
||||||
data: SwfSlice {
|
data: SwfSlice {
|
||||||
data: Arc::new(Vec::new()),
|
data: Arc::new(Vec::new()),
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0
|
end: 0,
|
||||||
},
|
},
|
||||||
pc: 0,
|
pc: 0,
|
||||||
scope: child_scope,
|
scope: child_scope,
|
||||||
this: globals,
|
this: globals,
|
||||||
arguments: None,
|
arguments: None,
|
||||||
is_function: false,
|
is_function: false,
|
||||||
local_registers: None
|
local_registers: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +136,11 @@ impl<'gc> Activation<'gc> {
|
||||||
swf_version: self.swf_version,
|
swf_version: self.swf_version,
|
||||||
data: code,
|
data: code,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
scope: scope,
|
scope,
|
||||||
this: self.this,
|
this: self.this,
|
||||||
arguments: self.arguments,
|
arguments: self.arguments,
|
||||||
is_function: false,
|
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 {
|
pub fn pc(&self) -> usize {
|
||||||
self.pc
|
self.pc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the current PC.
|
/// Change the current PC.
|
||||||
pub fn set_pc(&mut self, new_pc: usize) {
|
pub fn set_pc(&mut self, new_pc: usize) {
|
||||||
self.pc = new_pc;
|
self.pc = new_pc;
|
||||||
|
@ -171,7 +186,7 @@ impl<'gc> Activation<'gc> {
|
||||||
|
|
||||||
/// Returns AVM local variable scope for reference.
|
/// Returns AVM local variable scope for reference.
|
||||||
pub fn scope_cell(&self) -> GcCell<'gc, Scope<'gc>> {
|
pub fn scope_cell(&self) -> GcCell<'gc, Scope<'gc>> {
|
||||||
self.scope.clone()
|
self.scope
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Completely replace the current scope with a new one.
|
/// 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>> {
|
pub fn this_cell(&self) -> GcCell<'gc, Object<'gc>> {
|
||||||
self.this
|
self.this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this function was called with a local register set.
|
/// Returns true if this function was called with a local register set.
|
||||||
pub fn has_local_registers(&self) -> bool {
|
pub fn has_local_registers(&self) -> bool {
|
||||||
self.local_registers.is_some()
|
self.local_registers.is_some()
|
||||||
|
@ -233,7 +247,11 @@ impl<'gc> Activation<'gc> {
|
||||||
/// Retrieve a local register.
|
/// Retrieve a local register.
|
||||||
pub fn local_register(&self, id: u8) -> Value<'gc> {
|
pub fn local_register(&self, id: u8) -> Value<'gc> {
|
||||||
if let Some(local_registers) = self.local_registers {
|
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 {
|
} else {
|
||||||
Value::Undefined
|
Value::Undefined
|
||||||
}
|
}
|
||||||
|
@ -242,7 +260,9 @@ impl<'gc> Activation<'gc> {
|
||||||
/// Set a local register.
|
/// Set a local register.
|
||||||
pub fn set_local_register(&mut self, id: u8, value: Value<'gc>, mc: MutationContext<'gc, '_>) {
|
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 {
|
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.
|
//! 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 gc_arena::GcCell;
|
||||||
use swf::avm1::types::FunctionParam;
|
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(
|
pub type NativeFunction<'gc> = fn(
|
||||||
&mut Avm1<'gc>,
|
&mut Avm1<'gc>,
|
||||||
|
@ -24,7 +24,6 @@ pub struct Avm1Function<'gc> {
|
||||||
|
|
||||||
/// A reference to the underlying SWF data.
|
/// A reference to the underlying SWF data.
|
||||||
data: SwfSlice,
|
data: SwfSlice,
|
||||||
|
|
||||||
/// The name of the function, if not anonymous.
|
/// The name of the function, if not anonymous.
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
|
@ -32,22 +31,28 @@ pub struct Avm1Function<'gc> {
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
|
|
||||||
/// The scope the function was born into.
|
/// The scope the function was born into.
|
||||||
scope: GcCell<'gc, Scope<'gc>>
|
scope: GcCell<'gc, Scope<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Avm1Function<'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 {
|
let name = match name {
|
||||||
"" => None,
|
"" => None,
|
||||||
name => Some(name.to_string())
|
name => Some(name.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Avm1Function {
|
Avm1Function {
|
||||||
swf_version: swf_version,
|
swf_version,
|
||||||
data: actions,
|
data: actions,
|
||||||
name: name,
|
name,
|
||||||
params: params.into_iter().map(|s| s.to_string()).collect(),
|
params: params.iter().map(|s| s.to_string()).collect(),
|
||||||
scope: scope
|
scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +65,7 @@ impl<'gc> Avm1Function<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope(&self) -> GcCell<'gc, Scope<'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.
|
/// A reference to the underlying SWF data.
|
||||||
data: SwfSlice,
|
data: SwfSlice,
|
||||||
|
|
||||||
/// The name of the function, if not anonymous.
|
/// The name of the function, if not anonymous.
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
|
@ -103,52 +107,46 @@ pub struct Avm1Function2<'gc> {
|
||||||
params: Vec<(Option<u8>, String)>,
|
params: Vec<(Option<u8>, String)>,
|
||||||
|
|
||||||
/// The scope the function was born into.
|
/// The scope the function was born into.
|
||||||
scope: GcCell<'gc, Scope<'gc>>
|
scope: GcCell<'gc, Scope<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Avm1Function2<'gc> {
|
impl<'gc> Avm1Function2<'gc> {
|
||||||
pub fn new(swf_version: u8,
|
pub fn new(
|
||||||
|
swf_version: u8,
|
||||||
actions: SwfSlice,
|
actions: SwfSlice,
|
||||||
name: &str,
|
swf_function: &swf::avm1::types::Function,
|
||||||
register_count: u8,
|
scope: GcCell<'gc, Scope<'gc>>,
|
||||||
preload_parent: bool,
|
) -> Self {
|
||||||
preload_root: bool,
|
let name = match swf_function.name {
|
||||||
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 {
|
|
||||||
"" => None,
|
"" => None,
|
||||||
name => Some(name.to_string())
|
name => Some(name.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut owned_params = Vec::new();
|
let mut owned_params = Vec::new();
|
||||||
for FunctionParam{name: s, register_index: r} in params.into_iter() {
|
for FunctionParam {
|
||||||
owned_params.push((r.clone(), s.to_string()))
|
name: s,
|
||||||
|
register_index: r,
|
||||||
|
} in &swf_function.params
|
||||||
|
{
|
||||||
|
owned_params.push((*r, s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
Avm1Function2 {
|
Avm1Function2 {
|
||||||
swf_version: swf_version,
|
swf_version,
|
||||||
data: actions,
|
data: actions,
|
||||||
name: name,
|
name,
|
||||||
register_count: register_count,
|
register_count: swf_function.params.capacity() as u8,
|
||||||
preload_parent: preload_parent,
|
preload_parent: swf_function.preload_parent,
|
||||||
preload_root: preload_root,
|
preload_root: swf_function.preload_root,
|
||||||
supress_super: supress_super,
|
supress_super: swf_function.suppress_super,
|
||||||
preload_super: preload_super,
|
preload_super: swf_function.preload_super,
|
||||||
supress_arguments: supress_arguments,
|
supress_arguments: swf_function.suppress_super,
|
||||||
preload_arguments: preload_arguments,
|
preload_arguments: swf_function.preload_arguments,
|
||||||
supress_this: supress_this,
|
supress_this: swf_function.suppress_this,
|
||||||
preload_this: preload_this,
|
preload_this: swf_function.preload_this,
|
||||||
preload_global: preload_global,
|
preload_global: swf_function.preload_global,
|
||||||
params: owned_params,
|
params: owned_params,
|
||||||
scope: scope
|
scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +159,7 @@ impl<'gc> Avm1Function2<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope(&self) -> GcCell<'gc, Scope<'gc>> {
|
pub fn scope(&self) -> GcCell<'gc, Scope<'gc>> {
|
||||||
self.scope.clone()
|
self.scope
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_count(&self) -> u8 {
|
pub fn register_count(&self) -> u8 {
|
||||||
|
@ -180,7 +178,7 @@ pub enum Executable<'gc> {
|
||||||
Action(Avm1Function<'gc>),
|
Action(Avm1Function<'gc>),
|
||||||
|
|
||||||
/// ActionScript data defined by a previous `DefineFunction2` action.
|
/// ActionScript data defined by a previous `DefineFunction2` action.
|
||||||
Action2(Avm1Function2<'gc>)
|
Action2(Avm1Function2<'gc>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Executable<'gc> {
|
impl<'gc> Executable<'gc> {
|
||||||
|
@ -190,7 +188,13 @@ impl<'gc> Executable<'gc> {
|
||||||
/// returns. If on-stack execution is possible, then this function returns
|
/// returns. If on-stack execution is possible, then this function returns
|
||||||
/// a return value you must push onto the stack. Otherwise, you must
|
/// a return value you must push onto the stack. Otherwise, you must
|
||||||
/// create a new stack frame and execute the action data yourself.
|
/// 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 {
|
match self {
|
||||||
Executable::Native(nf) => Some(nf(avm, ac, this, args)),
|
Executable::Native(nf) => Some(nf(avm, ac, this, args)),
|
||||||
Executable::Action(af) => {
|
Executable::Action(af) => {
|
||||||
|
@ -201,23 +205,38 @@ impl<'gc> Executable<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments.force_set("length", Value::Number(args.len() as f64));
|
arguments.force_set("length", Value::Number(args.len() as f64));
|
||||||
|
|
||||||
let argcell = GcCell::allocate(ac.gc_context, arguments);
|
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() {
|
for i in 0..args.len() {
|
||||||
if let Some(argname) = af.params.get(i) {
|
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);
|
avm.insert_stack_frame(frame);
|
||||||
|
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
Executable::Action2(af) => {
|
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);
|
let mut arguments = Object::object(ac.gc_context);
|
||||||
if !af.supress_arguments {
|
if !af.supress_arguments {
|
||||||
for i in 0..args.len() {
|
for i in 0..args.len() {
|
||||||
|
@ -228,7 +247,13 @@ impl<'gc> Executable<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let argcell = GcCell::allocate(ac.gc_context, arguments);
|
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;
|
let mut preload_r = 1;
|
||||||
|
|
||||||
if af.preload_this {
|
if af.preload_this {
|
||||||
|
@ -272,12 +297,15 @@ impl<'gc> Executable<'gc> {
|
||||||
//preloaded registers? What gets done last?
|
//preloaded registers? What gets done last?
|
||||||
for i in 0..args.len() {
|
for i in 0..args.len() {
|
||||||
match (args.get(i), af.params.get(i)) {
|
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((Some(argreg), _argname))) => {
|
||||||
(Some(arg), Some((None, argname))) => frame.define(argname, arg.clone(), ac.gc_context),
|
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);
|
frame.allocate_local_registers(af.register_count(), ac.gc_context);
|
||||||
avm.insert_stack_frame(frame);
|
avm.insert_stack_frame(frame);
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
//! Represents AVM1 scope chain resolution.
|
//! Represents AVM1 scope chain resolution.
|
||||||
|
|
||||||
use std::cell::{Ref, RefMut};
|
use crate::avm1::{ActionContext, Avm1, Object, Value};
|
||||||
use gc_arena::{GcCell, MutationContext};
|
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.
|
/// Indicates what kind of scope a scope is.
|
||||||
#[derive(Copy, Clone,
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
Debug, PartialEq)]
|
|
||||||
pub enum ScopeClass {
|
pub enum ScopeClass {
|
||||||
/// Scope represents global scope.
|
/// Scope represents global scope.
|
||||||
Global,
|
Global,
|
||||||
|
@ -21,7 +20,7 @@ pub enum ScopeClass {
|
||||||
|
|
||||||
/// Scope represents an object added to the scope chain with `with`.
|
/// Scope represents an object added to the scope chain with `with`.
|
||||||
/// It is not inherited when closures are defined.
|
/// It is not inherited when closures are defined.
|
||||||
With
|
With,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a scope chain for an AVM1 activation.
|
/// Represents a scope chain for an AVM1 activation.
|
||||||
|
@ -29,7 +28,7 @@ pub enum ScopeClass {
|
||||||
pub struct Scope<'gc> {
|
pub struct Scope<'gc> {
|
||||||
parent: Option<GcCell<'gc, Scope<'gc>>>,
|
parent: Option<GcCell<'gc, Scope<'gc>>>,
|
||||||
class: ScopeClass,
|
class: ScopeClass,
|
||||||
values: GcCell<'gc, Object<'gc>>
|
values: GcCell<'gc, Object<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> gc_arena::Collect for Scope<'gc> {
|
unsafe impl<'gc> gc_arena::Collect for Scope<'gc> {
|
||||||
|
@ -46,30 +45,33 @@ impl<'gc> Scope<'gc> {
|
||||||
Scope {
|
Scope {
|
||||||
parent: None,
|
parent: None,
|
||||||
class: ScopeClass::Global,
|
class: ScopeClass::Global,
|
||||||
values: globals
|
values: globals,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a child scope of another scope.
|
/// Construct a child scope of another scope.
|
||||||
pub fn new_local_scope(parent: GcCell<'gc, Self>, mc: MutationContext<'gc, '_>) -> Scope<'gc> {
|
pub fn new_local_scope(parent: GcCell<'gc, Self>, mc: MutationContext<'gc, '_>) -> Scope<'gc> {
|
||||||
Scope {
|
Scope {
|
||||||
parent: Some(parent.clone()),
|
parent: Some(parent),
|
||||||
class: ScopeClass::Local,
|
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
|
/// Construct a closure scope to be used as the parent of all local scopes
|
||||||
/// when invoking a function.
|
/// 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();
|
let mut closure_scope_list = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if parent.read().class != ScopeClass::With {
|
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 {
|
if let Some(grandparent) = grandparent {
|
||||||
parent = grandparent;
|
parent = grandparent;
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,42 +81,52 @@ impl<'gc> Scope<'gc> {
|
||||||
|
|
||||||
let mut parent_scope = None;
|
let mut parent_scope = None;
|
||||||
for scope in closure_scope_list.iter().rev() {
|
for scope in closure_scope_list.iter().rev() {
|
||||||
parent_scope = Some(GcCell::allocate(mc, Scope {
|
parent_scope = Some(GcCell::allocate(
|
||||||
parent: parent_scope,
|
mc,
|
||||||
class: scope.read().class,
|
Scope {
|
||||||
values: scope.read().values.clone()
|
parent: parent_scope,
|
||||||
}));
|
class: scope.read().class,
|
||||||
|
values: scope.read().values,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent_scope) = parent_scope {
|
if let Some(parent_scope) = parent_scope {
|
||||||
parent_scope
|
parent_scope
|
||||||
} else {
|
} else {
|
||||||
GcCell::allocate(mc, Scope {
|
GcCell::allocate(
|
||||||
parent: None,
|
mc,
|
||||||
class: ScopeClass::Global,
|
Scope {
|
||||||
values: GcCell::allocate(mc, Object::bare_object())
|
parent: None,
|
||||||
})
|
class: ScopeClass::Global,
|
||||||
|
values: GcCell::allocate(mc, Object::bare_object()),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a scope for use with `tellTarget` code where the timeline
|
/// Construct a scope for use with `tellTarget` code where the timeline
|
||||||
/// scope has been replaced with another given object.
|
/// 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();
|
let mut timeline_scope_list = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if parent.read().class != ScopeClass::Target {
|
if parent.read().class != ScopeClass::Target {
|
||||||
timeline_scope_list.push(parent.clone());
|
timeline_scope_list.push(parent);
|
||||||
} else {
|
} else {
|
||||||
let new_scope = Self {
|
let new_scope = Self {
|
||||||
parent: None,
|
parent: None,
|
||||||
class: ScopeClass::Target,
|
class: ScopeClass::Target,
|
||||||
values: clip
|
values: clip,
|
||||||
};
|
};
|
||||||
timeline_scope_list.push(GcCell::allocate(mc, new_scope));
|
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 {
|
if let Some(grandparent) = grandparent {
|
||||||
parent = grandparent;
|
parent = grandparent;
|
||||||
} else {
|
} else {
|
||||||
|
@ -124,21 +136,27 @@ impl<'gc> Scope<'gc> {
|
||||||
|
|
||||||
let mut parent_scope = None;
|
let mut parent_scope = None;
|
||||||
for scope in timeline_scope_list.iter().rev() {
|
for scope in timeline_scope_list.iter().rev() {
|
||||||
parent_scope = Some(GcCell::allocate(mc, Scope {
|
parent_scope = Some(GcCell::allocate(
|
||||||
parent: parent_scope,
|
mc,
|
||||||
class: scope.read().class,
|
Scope {
|
||||||
values: scope.read().values.clone()
|
parent: parent_scope,
|
||||||
}));
|
class: scope.read().class,
|
||||||
|
values: scope.read().values,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent_scope) = parent_scope {
|
if let Some(parent_scope) = parent_scope {
|
||||||
parent_scope
|
parent_scope
|
||||||
} else {
|
} else {
|
||||||
GcCell::allocate(mc, Scope {
|
GcCell::allocate(
|
||||||
parent: None,
|
mc,
|
||||||
class: ScopeClass::Global,
|
Scope {
|
||||||
values: GcCell::allocate(mc, Object::bare_object())
|
parent: None,
|
||||||
})
|
class: ScopeClass::Global,
|
||||||
|
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
|
/// A with block inserts the values of a particular object into the scope
|
||||||
/// of currently running code, while still maintaining the same local
|
/// of currently running code, while still maintaining the same local
|
||||||
/// scope. This requires some scope chain juggling.
|
/// scope. This requires some scope chain juggling.
|
||||||
pub fn new_with_scope(locals: GcCell<'gc, Self>,
|
pub fn new_with_scope(
|
||||||
with_object: GcCell<'gc, Object<'gc>>,
|
locals: GcCell<'gc, Self>,
|
||||||
mc: MutationContext<'gc, '_>) -> GcCell<'gc, Self> {
|
with_object: GcCell<'gc, Object<'gc>>,
|
||||||
let parent_scope = locals.read().parent.clone();
|
mc: MutationContext<'gc, '_>,
|
||||||
let local_values = locals.read().values.clone();
|
) -> GcCell<'gc, Self> {
|
||||||
let with_scope = GcCell::allocate(mc, Scope {
|
let parent_scope = locals.read().parent;
|
||||||
parent: parent_scope,
|
let local_values = locals.read().values;
|
||||||
class: ScopeClass::With,
|
let with_scope = GcCell::allocate(
|
||||||
values: with_object
|
mc,
|
||||||
});
|
Scope {
|
||||||
|
parent: parent_scope,
|
||||||
|
class: ScopeClass::With,
|
||||||
|
values: with_object,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
GcCell::allocate(mc, Scope {
|
GcCell::allocate(
|
||||||
parent: Some(with_scope),
|
mc,
|
||||||
class: ScopeClass::Local,
|
Scope {
|
||||||
values: local_values
|
parent: Some(with_scope),
|
||||||
})
|
class: ScopeClass::Local,
|
||||||
|
values: local_values,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct an arbitrary scope
|
/// 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 {
|
Scope {
|
||||||
parent: Some(parent.clone()),
|
parent: Some(parent),
|
||||||
class: class,
|
class,
|
||||||
values: with_object
|
values: with_object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +218,7 @@ impl<'gc> Scope<'gc> {
|
||||||
pub fn parent(&self) -> Option<Ref<Scope<'gc>>> {
|
pub fn parent(&self) -> Option<Ref<Scope<'gc>>> {
|
||||||
match self.parent {
|
match self.parent {
|
||||||
Some(ref p) => Some(p.read()),
|
Some(ref p) => Some(p.read()),
|
||||||
None => None
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +227,6 @@ impl<'gc> Scope<'gc> {
|
||||||
if self.locals().has_property(name) {
|
if self.locals().has_property(name) {
|
||||||
return self.locals().force_get(name);
|
return self.locals().force_get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.resolve(name);
|
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
|
/// 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
|
/// 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.
|
/// return it so that the caller may manually define the property itself.
|
||||||
pub fn overwrite(&self,
|
pub fn overwrite(
|
||||||
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm1<'gc>,
|
avm: &mut Avm1<'gc>,
|
||||||
context: &mut ActionContext<'_, '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) {
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue