diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 6d80203d1..e5733fd96 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -72,7 +72,7 @@ pub struct Avm1<'gc> { globals: GcCell<'gc, Object<'gc>>, /// All activation records for the current execution context. - stack_frames: Vec>, + stack_frames: Vec>>, /// The operand stack (shared across functions). stack: Vec>, @@ -124,9 +124,10 @@ impl<'gc> Avm1<'gc> { ) -> HashMap { let mut form_values = HashMap::new(); let locals = self - .current_stack_frame_mut() + .current_stack_frame() .unwrap() - .scope_mut(context.gc_context) + .read() + .scope() .locals_cell(); for k in locals.read().get_keys() { @@ -161,37 +162,32 @@ impl<'gc> Avm1<'gc> { 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, + self.stack_frames.push(GcCell::allocate( + action_context.gc_context, + Activation::from_action(swf_version, code, child_scope, clip_obj, None), )); } /// Add a stack frame for any arbitrary code. - pub fn insert_stack_frame(&mut self, frame: Activation<'gc>) { - self.stack_frames.push(frame); + pub fn insert_stack_frame( + &mut self, + frame: Activation<'gc>, + context: &mut ActionContext<'_, 'gc, '_>, + ) { + self.stack_frames + .push(GcCell::allocate(context.gc_context, frame)); } /// Retrieve the current AVM execution frame. /// /// Yields None if there is no stack frame. - pub fn current_stack_frame(&self) -> Option<&Activation<'gc>> { - self.stack_frames.last() - } - - /// Retrieve the current AVM execution frame for mutation. - /// - /// Yields None if there is no stack frame. - pub fn current_stack_frame_mut(&mut self) -> Option<&mut Activation<'gc>> { - self.stack_frames.last_mut() + pub fn current_stack_frame(&self) -> Option>> { + self.stack_frames.last().map(|ac| ac.clone()) } /// Get the currently executing SWF version, if there is one. fn current_swf_version(&self) -> Option { - self.current_stack_frame().map(|sf| sf.swf_version()) + self.current_stack_frame().map(|sf| sf.read().swf_version()) } /// Perform some action with the current stack frame's reader. @@ -209,9 +205,13 @@ 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(&mut self, func: F) -> Option + fn with_current_reader_mut( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + func: F, + ) -> Option where - F: FnOnce(&mut Self, &mut Reader<'_>) -> R, + F: FnOnce(&mut Self, &mut Reader<'_>, &mut ActionContext<'_, 'gc, '_>) -> R, { if self.is_reading { log::error!( @@ -220,21 +220,19 @@ impl<'gc> Avm1<'gc> { } 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 (frame_cell, swf_version, data, pc) = self.stack_frames.last().map(|frame| { + ( + frame.clone(), + frame.read().swf_version(), + frame.read().data(), + frame.read().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) { - new_stack.set_pc(read.pos()); - } - } + let r = func(self, &mut read, context); + frame_cell.write(context.gc_context).set_pc(read.pos()); self.is_reading = false; @@ -252,8 +250,10 @@ impl<'gc> Avm1<'gc> { 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()?; + self.with_current_reader_mut(context, |this, r, context| { + this.do_next_action(context, r) + }) + .unwrap()?; } Ok(()) @@ -265,11 +265,16 @@ impl<'gc> Avm1<'gc> { context: &mut ActionContext<'_, 'gc, '_>, reader: &mut Reader<'_>, ) -> Result<(), Error> { - let data = self.current_stack_frame().unwrap().data(); + let data = self.current_stack_frame().unwrap().read().data(); if reader.pos() >= (data.end - data.start) { //Executing beyond the end of a function constitutes an implicit return. - if self.current_stack_frame().unwrap().can_implicit_return() { + if self + .current_stack_frame() + .unwrap() + .read() + .can_implicit_return() + { self.push(Value::Undefined); } @@ -396,7 +401,12 @@ impl<'gc> Avm1<'gc> { } } else { //The explicit end opcode was encountered so return here - if self.current_stack_frame().unwrap().can_implicit_return() { + if self + .current_stack_frame() + .unwrap() + .read() + .can_implicit_return() + { self.push(Value::Undefined); } @@ -476,10 +486,13 @@ impl<'gc> Avm1<'gc> { pub fn current_register(&self, id: u8) -> Value<'gc> { if self .current_stack_frame() - .map(|sf| sf.has_local_registers()) + .map(|sf| sf.read().has_local_registers()) .unwrap_or(false) { - self.current_stack_frame().unwrap().local_register(id) + self.current_stack_frame() + .unwrap() + .read() + .local_register(id) } else { self.registers .get(id as usize) @@ -499,14 +512,13 @@ impl<'gc> Avm1<'gc> { ) { if self .current_stack_frame() - .map(|sf| sf.has_local_registers()) + .map(|sf| sf.read().has_local_registers()) .unwrap_or(false) { - self.current_stack_frame_mut().unwrap().set_local_register( - id, - value, - context.gc_context, - ); + self.current_stack_frame() + .unwrap() + .write(context.gc_context) + .set_local_register(id, value, context.gc_context); } else if let Some(v) = self.registers.get_mut(id as usize) { *v = value; } @@ -648,10 +660,11 @@ 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.stack_frames.last().unwrap().clone().read().resolve( + fn_name.as_string()?, + self, + context, + ); 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 { @@ -741,15 +754,16 @@ impl<'gc> Avm1<'gc> { params: &[&str], actions: &[u8], ) -> Result<(), Error> { - let swf_version = self.current_stack_frame().unwrap().swf_version(); + let swf_version = self.current_stack_frame().unwrap().read().swf_version(); let func_data = self .current_stack_frame() .unwrap() + .read() .data() .to_subslice(actions) .unwrap(); let scope = Scope::new_closure_scope( - self.current_stack_frame().unwrap().scope_cell(), + self.current_stack_frame().unwrap().read().scope_cell(), context.gc_context, ); let func = Avm1Function::from_df1(swf_version, func_data, name, params, scope); @@ -760,8 +774,9 @@ impl<'gc> Avm1<'gc> { if name == "" { self.push(func_obj); } else { - self.current_stack_frame_mut() + self.current_stack_frame() .unwrap() + .read() .define(name, func_obj, context.gc_context); } @@ -773,15 +788,16 @@ impl<'gc> Avm1<'gc> { context: &mut ActionContext<'_, 'gc, '_>, action_func: &Function, ) -> Result<(), Error> { - let swf_version = self.current_stack_frame().unwrap().swf_version(); + let swf_version = self.current_stack_frame().unwrap().read().swf_version(); let func_data = self .current_stack_frame() .unwrap() + .read() .data() .to_subslice(action_func.actions) .unwrap(); let scope = Scope::new_closure_scope( - self.current_stack_frame().unwrap().scope_cell(), + self.current_stack_frame().unwrap().read().scope_cell(), context.gc_context, ); let func = Avm1Function::from_df2(swf_version, func_data, action_func, scope); @@ -792,7 +808,7 @@ impl<'gc> Avm1<'gc> { if action_func.name == "" { self.push(func_obj); } else { - self.current_stack_frame_mut().unwrap().define( + self.current_stack_frame().unwrap().read().define( action_func.name, func_obj, context.gc_context, @@ -808,7 +824,7 @@ impl<'gc> Avm1<'gc> { ) -> Result<(), Error> { let value = self.pop()?; let name = self.pop()?; - self.current_stack_frame_mut().unwrap().define( + self.current_stack_frame().unwrap().read().define( name.as_string()?, value, context.gc_context, @@ -821,7 +837,7 @@ impl<'gc> Avm1<'gc> { context: &mut ActionContext<'_, 'gc, '_>, ) -> Result<(), Error> { let name = self.pop()?; - self.current_stack_frame_mut().unwrap().define( + self.current_stack_frame().unwrap().read().define( name.as_string()?, Value::Undefined, context.gc_context, @@ -846,10 +862,11 @@ 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(self.current_stack_frame().unwrap().is_defined(name)); + let did_exist = Value::Bool(self.current_stack_frame().unwrap().read().is_defined(name)); self.current_stack_frame() .unwrap() + .read() .scope() .delete(name, context.gc_context); self.push(did_exist); @@ -876,11 +893,16 @@ impl<'gc> Avm1<'gc> { Ok(()) } - fn action_enumerate(&mut self, _context: &mut ActionContext) -> Result<(), Error> { + fn action_enumerate(&mut self, context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { 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) { + let ob = match self + .current_stack_frame() + .unwrap() + .read() + .resolve(name, self, context) + { Value::Object(ob) => ob, _ => { log::error!("Cannot enumerate properties of {}", name); @@ -936,7 +958,7 @@ impl<'gc> Avm1<'gc> { let name_val = self.pop()?; let name = name_val.into_string(); let object = self.pop()?.as_object()?; - let this = self.current_stack_frame().unwrap().this_cell(); + let this = self.current_stack_frame().unwrap().read().this_cell(); let value = object.read().get(&name, self, context, this); self.push(value); @@ -1030,8 +1052,13 @@ impl<'gc> Avm1<'gc> { } } } - } else if self.current_stack_frame().unwrap().is_defined(path) { - result = Some(self.current_stack_frame().unwrap().resolve(path)); + } else if self.current_stack_frame().unwrap().read().is_defined(path) { + result = Some( + self.current_stack_frame() + .unwrap() + .read() + .resolve(path, self, context), + ); } self.push(result.unwrap_or(Value::Undefined)); @@ -1455,7 +1482,7 @@ impl<'gc> Avm1<'gc> { Ok(()) } - fn action_return(&mut self, _context: &mut ActionContext) -> Result<(), Error> { + fn action_return(&mut self, _context: &mut ActionContext<'_, 'gc, '_>) -> Result<(), Error> { let result = self.pop()?; if self.stack_frames.len() > 1 { @@ -1471,7 +1498,7 @@ impl<'gc> Avm1<'gc> { let name_val = self.pop()?; let name = name_val.as_string()?; let object = self.pop()?.as_object()?; - let this = self.current_stack_frame().unwrap().this_cell(); + let this = self.current_stack_frame().unwrap().read().this_cell(); object .write(context.gc_context) @@ -1533,7 +1560,7 @@ impl<'gc> Avm1<'gc> { var_path: &str, value: Value<'gc>, ) -> Result<(), Error> { - let this = self.current_stack_frame().unwrap().this_cell(); + let this = self.current_stack_frame().unwrap().read().this_cell(); let is_slashpath = Self::variable_name_is_slash_path(var_path); if is_slashpath { @@ -1551,13 +1578,15 @@ impl<'gc> Avm1<'gc> { } } } else { - let this = self.current_stack_frame().unwrap().this_cell(); - let scope = self.current_stack_frame().unwrap().scope_cell(); + let this = self.current_stack_frame().unwrap().read().this_cell(); + let scope = self.current_stack_frame().unwrap().read().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().read().define( + var_path, + value, + context.gc_context, + ); } } @@ -1592,7 +1621,7 @@ impl<'gc> Avm1<'gc> { context.target_path = Value::Undefined; } - let scope = self.current_stack_frame().unwrap().scope_cell(); + let scope = self.current_stack_frame().unwrap().read().scope_cell(); let clip_obj = context .active_clip .read() @@ -1601,8 +1630,9 @@ impl<'gc> Avm1<'gc> { .unwrap() .to_owned(); - self.current_stack_frame_mut() + self.current_stack_frame() .unwrap() + .write(context.gc_context) .set_scope(Scope::new_target_scope(scope, clip_obj, context.gc_context)); Ok(()) } @@ -1843,19 +1873,22 @@ impl<'gc> Avm1<'gc> { let block = self .current_stack_frame() .unwrap() + .read() .data() .to_subslice(actions) .unwrap(); let with_scope = Scope::new_with_scope( - self.current_stack_frame().unwrap().scope_cell(), + self.current_stack_frame().unwrap().read().scope_cell(), object, context.gc_context, ); let new_activation = self .current_stack_frame() .unwrap() + .read() .to_rescope(block, with_scope); - self.stack_frames.push(new_activation); + self.stack_frames + .push(GcCell::allocate(context.gc_context, new_activation)); Ok(()) } } diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 34d41fb92..f0540779d 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -2,7 +2,7 @@ use crate::avm1::object::Object; use crate::avm1::scope::Scope; -use crate::avm1::Value; +use crate::avm1::{ActionContext, Avm1, Value}; use crate::tag_utils::SwfSlice; use gc_arena::{GcCell, MutationContext}; use std::cell::{Ref, RefMut}; @@ -201,7 +201,12 @@ impl<'gc> Activation<'gc> { } /// Resolve a particular named local variable within this activation. - pub fn resolve(&self, name: &str) -> Value<'gc> { + pub fn resolve( + &self, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> Value<'gc> { if name == "this" { return Value::Object(self.this); } @@ -210,7 +215,7 @@ impl<'gc> Activation<'gc> { return Value::Object(self.arguments.unwrap()); } - self.scope().resolve(name) + self.scope().resolve(name, avm, context, self.this) } /// Check if a particular property in the scope chain is defined. diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 8a9cf5383..b1b058c8a 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -243,7 +243,7 @@ impl<'gc> Executable<'gc> { if af.preload_parent { frame.set_local_register( preload_r, - child_scope.read().resolve("_parent"), + child_scope.read().resolve("_parent", avm, ac, this), ac.gc_context, ); preload_r += 1; @@ -267,7 +267,7 @@ impl<'gc> Executable<'gc> { _ => {} } } - avm.insert_stack_frame(frame); + avm.insert_stack_frame(frame, ac); None } diff --git a/core/src/avm1/globals/math.rs b/core/src/avm1/globals/math.rs index 7c96b0c8e..39edef82b 100644 --- a/core/src/avm1/globals/math.rs +++ b/core/src/avm1/globals/math.rs @@ -172,7 +172,10 @@ mod tests { }; let globals = avm.global_object_cell(); - avm.insert_stack_frame(Activation::from_nothing(swf_version, globals, gc_context)); + avm.insert_stack_frame( + Activation::from_nothing(swf_version, globals, gc_context), + &mut context, + ); test(&mut avm, &mut context) }) diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 0a4b52381..6e12bcec1 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -417,7 +417,10 @@ mod tests { let object = GcCell::allocate(gc_context, Object::object(gc_context)); let globals = avm.global_object_cell(); - avm.insert_stack_frame(Activation::from_nothing(swf_version, globals, gc_context)); + avm.insert_stack_frame( + Activation::from_nothing(swf_version, globals, gc_context), + &mut context, + ); test(&mut avm, &mut context, object) }) diff --git a/core/src/avm1/scope.rs b/core/src/avm1/scope.rs index 455424519..6b86c7b86 100644 --- a/core/src/avm1/scope.rs +++ b/core/src/avm1/scope.rs @@ -236,12 +236,18 @@ impl<'gc> Scope<'gc> { } /// Resolve a particular value in the scope chain. - pub fn resolve(&self, name: &str) -> Value<'gc> { + pub fn resolve( + &self, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut ActionContext<'_, 'gc, '_>, + this: GcCell<'gc, Object<'gc>>, + ) -> Value<'gc> { if self.locals().has_property(name) { - return self.locals().force_get(name); + return self.locals().get(name, avm, context, this); } if let Some(scope) = self.parent() { - return scope.resolve(name); + return scope.resolve(name, avm, context, this); } Value::Undefined