diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index d0bab7ffc..210be8bcc 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -11,7 +11,7 @@ use crate::avm2::scope::Scope; use crate::avm2::script::Script; use crate::avm2::string::AvmString; use crate::avm2::value::Value; -use crate::avm2::{value, Avm2, Error}; +use crate::avm2::{value, Avm2, Error, Domain}; use crate::context::UpdateContext; use crate::swf::extensions::ReadSwfExt; use gc_arena::{Gc, GcCell, MutationContext}; @@ -21,6 +21,7 @@ use swf::avm2::types::{ Class as AbcClass, Index, Method as AbcMethod, Multiname as AbcMultiname, Namespace as AbcNamespace, Op, }; +use crate::avm2::bytearray::ByteArrayStorage; /// Represents a particular register set. /// @@ -2470,34 +2471,50 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { } /// Implements `Op::Coerce` - fn op_coerce(&mut self, _method: Gc<'gc, BytecodeMethod<'gc>>, _index: Index) -> Result, Error> { + fn op_coerce( + &mut self, + _method: Gc<'gc, BytecodeMethod<'gc>>, + _index: Index, + ) -> Result, Error> { //TODO: should check if x is a subclass of the given type when object and typeerror if not, see "instr.cpp::coerceImpl (287)" //TODO: update tests with typeof + description let val = self.context.avm2.pop(); let x = match val { - Value::Null | Value::Undefined => { - Value::Null - }, - Value::Number(_) | Value::String(_) | Value::Unsigned(_) | Value::Integer(_) | Value::Bool(_) => Value::Object(val.coerce_to_object(self)?), - Value::Object(_) => { - val - } + Value::Null | Value::Undefined => Value::Null, + Value::Number(_) + | Value::String(_) + | Value::Unsigned(_) + | Value::Integer(_) + | Value::Bool(_) => Value::Object(val.coerce_to_object(self)?), + Value::Object(_) => val, }; self.context.avm2.push(x); Ok(FrameControl::Continue) } + fn domain_memory(&self) -> Result, Error> { + self.scope().map(|s| s.read().globals()).and_then(|g| g.as_application_domain()).map(|d| d.domain_memory()) + .ok_or_else(|| "No domain memory assigned".into()) + } + /// Implements `Op::Si8` fn op_si8(&mut self) -> Result, Error> { - let address = self.context.avm2.pop(); - let val = self.context.avm2.pop(); + let address = self.context.avm2.pop().coerce_to_i32(self)?; + let val = self.context.avm2.pop().coerce_to_i32(self)?; - let dm = self.scope().unwrap().read().globals().as_application_domain().unwrap().domain_memory().expect("Not domain memory?"); - let mut ba = dm.as_bytearray_mut(self.context.gc_context).unwrap(); - ba.write_bytes_at(&[val.coerce_to_i32(self)? as u8], address.coerce_to_i32(self)? as usize); + let dm = self.domain_memory().expect("Not domain memory?"); + + if address < 0 || address as usize >= dm.read().len() { + return Err("RangeError: The specified range is invalid".into()); + } + + dm.write(self.context.gc_context).write_bytes_at( + &[(val & 0xFF) as u8], + address as usize, + ); Ok(FrameControl::Continue) } @@ -2506,11 +2523,15 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> { fn op_li8(&mut self) -> Result, Error> { let address = self.context.avm2.pop().coerce_to_u32(self)? as usize; - let dm = self.scope().unwrap().read().globals().as_application_domain().unwrap().domain_memory().expect("Not domain memory?"); - let mut ba = dm.as_bytearray_mut(self.context.gc_context).unwrap(); - let x = ba.bytes().iter().nth(address).copied().unwrap(); + let dm = self.domain_memory().expect("Not domain memory?"); + let val = dm.read().get(address); + drop(dm); - self.context.avm2.push(Value::Integer(x as i32)); + if let Some(val) = val { + self.context.avm2.push(Value::Integer(val as i32)); + } else { + return Err("RangeError: The specified range is invalid".into()); + } Ok(FrameControl::Continue) } diff --git a/core/src/avm2/bytearray.rs b/core/src/avm2/bytearray.rs index 497a75ad7..35fe1f9b9 100644 --- a/core/src/avm2/bytearray.rs +++ b/core/src/avm2/bytearray.rs @@ -37,6 +37,15 @@ impl ByteArrayStorage { } } + /// Create a new ByteArrayStorage with given initial data and endianness + pub fn with_initial(bytes: Vec, endian: Endian) -> Self { + Self { + bytes, + position: 0, + endian + } + } + /// Write a byte at next position in the bytearray pub fn write_byte(&mut self, byte: u8) { let bytes_len = self.bytes.len(); @@ -329,6 +338,10 @@ impl ByteArrayStorage { pub fn set_endian(&mut self, new_endian: Endian) { self.endian = new_endian; } + + pub fn len(&self) -> usize { + self.bytes.len() + } } impl Write for ByteArrayStorage { diff --git a/core/src/avm2/domain.rs b/core/src/avm2/domain.rs index b462714cb..ebbd424ad 100644 --- a/core/src/avm2/domain.rs +++ b/core/src/avm2/domain.rs @@ -2,13 +2,14 @@ use crate::avm2::activation::Activation; use crate::avm2::names::{Multiname, QName}; -use crate::avm2::object::TObject; use crate::avm2::object::Object; +use crate::avm2::object::TObject; use crate::avm2::script::Script; use crate::avm2::value::Value; use crate::avm2::Error; use gc_arena::{Collect, GcCell, MutationContext}; use std::collections::HashMap; +use crate::avm2::bytearray::{ByteArrayStorage, Endian}; /// Represents a set of scripts and movies that share traits across different /// script-global scopes. @@ -26,7 +27,7 @@ struct DomainData<'gc> { parent: Option>, /// The bytearray used for storing domain memory - pub domain_memory: Option> + pub domain_memory: GcCell<'gc, ByteArrayStorage>, } impl<'gc> Domain<'gc> { @@ -40,7 +41,7 @@ impl<'gc> Domain<'gc> { DomainData { defs: HashMap::new(), parent: None, - domain_memory: None, + domain_memory: GcCell::allocate(mc, ByteArrayStorage::with_initial(vec![0; 100], Endian::Little)), }, )) } @@ -52,7 +53,7 @@ impl<'gc> Domain<'gc> { DomainData { defs: HashMap::new(), parent: Some(parent), - domain_memory: None + domain_memory: GcCell::allocate(mc, ByteArrayStorage::with_initial(vec![0; 100], Endian::Little)), }, )) } @@ -153,11 +154,11 @@ impl<'gc> Domain<'gc> { Ok(()) } - pub fn domain_memory(&self) -> Option> { + pub fn domain_memory(&self) -> GcCell<'gc, ByteArrayStorage> { self.0.read().domain_memory } - pub fn set_domain_memory(&self, mc: MutationContext<'gc, '_>, domain_memory: Object<'gc>) { - self.0.write(mc).domain_memory = Some(domain_memory) + pub fn set_domain_memory(&self, mc: MutationContext<'gc, '_>, domain_memory: GcCell<'gc, ByteArrayStorage>) { + self.0.write(mc).domain_memory = domain_memory } } diff --git a/core/src/avm2/globals/flash/system/application_domain.rs b/core/src/avm2/globals/flash/system/application_domain.rs index 5a60f76b6..fa4af6db7 100644 --- a/core/src/avm2/globals/flash/system/application_domain.rs +++ b/core/src/avm2/globals/flash/system/application_domain.rs @@ -125,10 +125,9 @@ pub fn set_domain_memory<'gc>( args: &[Value<'gc>], ) -> Result, Error> { if let Some(d) = args.get(0) { - let o = d.coerce_to_object(activation)?; - //TODO: same domain as the one in avm2? if let Some(appdomain) = this.and_then(|this| this.as_application_domain()) { - appdomain.set_domain_memory(activation.context.gc_context, o); + // let o = d.coerce_to_object(activation)?.as; + // appdomain.set_domain_memory(activation.context.gc_context, o); } } @@ -141,11 +140,11 @@ pub fn domain_memory<'gc>( this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { - if let Some(appdomain) = this.and_then(|this| this.as_application_domain()) { - if let Some(o) = appdomain.domain_memory() { - return Ok(o.into()); - } - } + // if let Some(appdomain) = this.and_then(|this| this.as_application_domain()) { + // if let Some(o) = appdomain.domain_memory() { + // return Ok(o.into()); + // } + // } Ok(Value::Undefined) } diff --git a/core/tests/swfs/avm2/domain_memory/output.txt b/core/tests/swfs/avm2/domain_memory/output.txt index 135db2342..04f9dfd19 100644 --- a/core/tests/swfs/avm2/domain_memory/output.txt +++ b/core/tests/swfs/avm2/domain_memory/output.txt @@ -1,2 +1,6 @@ -// li8() -10 +// li8(0) after si8(65, 0) +65 +// domain +// li8(0) after si8(255, 0) +255 +// domain diff --git a/core/tests/swfs/avm2/domain_memory/test.swf b/core/tests/swfs/avm2/domain_memory/test.swf index d44316ce1..27caf776d 100644 Binary files a/core/tests/swfs/avm2/domain_memory/test.swf and b/core/tests/swfs/avm2/domain_memory/test.swf differ