diff --git a/core/src/avm2/amf.rs b/core/src/avm2/amf.rs index 733a8d37b..7dc35a697 100644 --- a/core/src/avm2/amf.rs +++ b/core/src/avm2/amf.rs @@ -10,6 +10,7 @@ use enumset::EnumSet; use flash_lso::types::{AMFVersion, Element, Lso}; use flash_lso::types::{Attribute, ClassDefinition, Value as AmfValue}; +use super::property::Property; use super::{Namespace, QName}; /// Serialize a Value to an AmfValue @@ -45,7 +46,8 @@ pub fn serialize_value<'gc>( Some(AmfValue::Undefined) } else if o.as_array_storage().is_some() { let mut values = Vec::new(); - recursive_serialize(activation, o, &mut values, &mut vec![], amf_version).unwrap(); + // Don't serialize properties from the vtable (we don't want a 'length' field) + recursive_serialize(activation, o, &mut values, None, amf_version).unwrap(); let mut dense = vec![]; let mut sparse = vec![]; @@ -155,7 +157,7 @@ pub fn serialize_value<'gc>( activation, o, &mut object_body, - &mut static_properties, + Some(&mut static_properties), amf_version, ) .unwrap(); @@ -182,23 +184,31 @@ pub fn recursive_serialize<'gc>( activation: &mut Activation<'_, 'gc>, obj: Object<'gc>, elements: &mut Vec, - static_properties: &mut Vec, + static_properties: Option<&mut Vec>, amf_version: AMFVersion, ) -> Result<(), Error<'gc>> { - if let Some(vtable) = obj.vtable() { - let mut props = vtable.public_properties(); - // Flash appears to use vtable iteration order, but we sort ours - // to make our test output consistent. - props.sort_by_key(|(name, _)| name.to_utf8_lossy().to_string()); - for (name, _) in props { - let value = obj.get_public_property(name, activation)?; - if let Some(value) = serialize_value(activation, value, amf_version) { - let name = name.to_utf8_lossy().to_string(); - elements.push(Element::new(name.clone(), value)); - static_properties.push(name); + if let Some(static_properties) = static_properties { + if let Some(vtable) = obj.vtable() { + let mut props = vtable.public_properties(); + // Flash appears to use vtable iteration order, but we sort ours + // to make our test output consistent. + props.sort_by_key(|(name, _)| name.to_utf8_lossy().to_string()); + for (name, prop) in props { + if let Property::Virtual { get, set } = prop { + if !(get.is_some() && set.is_some()) { + continue; + } + } + let value = obj.get_public_property(name, activation)?; + if let Some(value) = serialize_value(activation, value, amf_version) { + let name = name.to_utf8_lossy().to_string(); + elements.push(Element::new(name.clone(), value)); + static_properties.push(name); + } } } } + let mut last_index = obj.get_next_enumerant(0, activation)?; while let Some(index) = last_index { let name = obj diff --git a/core/src/avm2/globals/flash/net/shared_object.rs b/core/src/avm2/globals/flash/net/shared_object.rs index 88ef99699..ad47970e1 100644 --- a/core/src/avm2/globals/flash/net/shared_object.rs +++ b/core/src/avm2/globals/flash/net/shared_object.rs @@ -17,13 +17,7 @@ fn new_lso<'gc>( data: Object<'gc>, ) -> Result> { let mut elements = Vec::new(); - crate::avm2::amf::recursive_serialize( - activation, - data, - &mut elements, - &mut vec![], - AMFVersion::AMF3, - )?; + crate::avm2::amf::recursive_serialize(activation, data, &mut elements, None, AMFVersion::AMF3)?; Ok(Lso::new( elements, name.split('/') diff --git a/core/src/avm2/globals/flash/utils/byte_array.rs b/core/src/avm2/globals/flash/utils/byte_array.rs index 07948cb83..4a1cb14c1 100644 --- a/core/src/avm2/globals/flash/utils/byte_array.rs +++ b/core/src/avm2/globals/flash/utils/byte_array.rs @@ -761,7 +761,7 @@ pub fn read_object<'gc>( let mut decoder = AMF0Decoder::default(); let (extra, amf) = decoder .parse_single_element(bytes) - .map_err(|e| format!("Error: Invalid AMF0 object: {e:?}"))?; + .map_err(|_| "Error: Invalid object")?; ( extra.len(), crate::avm2::amf::deserialize_value(activation, &amf)?, @@ -771,7 +771,7 @@ pub fn read_object<'gc>( let mut decoder = AMF3Decoder::default(); let (extra, amf) = decoder .parse_single_element(bytes) - .map_err(|e| format!("Error: Invalid AMF3 object: {e:?}"))?; + .map_err(|_| "Error: Invalid object")?; ( extra.len(), crate::avm2::amf::deserialize_value(activation, &amf)?, @@ -782,6 +782,7 @@ pub fn read_object<'gc>( bytearray.set_position(bytearray.len() - bytes_left); return Ok(value); } + Ok(Value::Undefined) } diff --git a/tests/tests/swfs/avm2/amf_custom_obj/Test.as b/tests/tests/swfs/avm2/amf_custom_obj/Test.as index bec012b53..bd45169f1 100755 --- a/tests/tests/swfs/avm2/amf_custom_obj/Test.as +++ b/tests/tests/swfs/avm2/amf_custom_obj/Test.as @@ -9,17 +9,43 @@ import flash.utils.ByteArray; import flash.net.registerClassAlias; class MyClass { - public var secondProp: Object; public var firstProp: String; - public var thirdProp: Number; private var privProp: String = "Default Private prop"; - + + public function MyClass(priv:String = "Constructor private prop") { this.privProp = priv; } public function toString() { - trace("MyClass(firstProp= " + this.firstProp + " secondProp=" + this.secondProp + " thirdProp=" + this.thirdProp + " privProp=" + this.privProp); + trace("MyClass(firstProp= " + this.firstProp + " privProp=" + this.privProp); + } +} + +class GetterSetterClass { + public function get getAndSet(): String { + trace("Called getAndSet getter"); + return "getAndSet getter value"; + } + + public function set getAndSet(val: String):void { + trace("Called getAndSet setter: " + val); + } + + + public function get getterOnly(): String { + trace("Called getterOnly"); + return "getterOnly value"; + } + + public function set setterOnly(val: String): void { + trace("Called setterOnly: " + val); + } + + AS3 var myAS3Var: String = "AS3 string"; + + public function toString():String { + return "GetterSetterClass(myAS3Var=" + this.myAS3Var + ")"; } } @@ -32,8 +58,6 @@ registerClassAlias("MyClassAlias", MyClass); var mycls = new MyClass("Overwritten private prop"); mycls.firstProp = "Hello"; -mycls.secondProp = null; -mycls.thirdProp = -5.1; // Note - Flash player appears to serialize properties in // vtable order, which cannot in general reproduce. Our raw // bytes match for this particular class definition, but all @@ -41,6 +65,10 @@ mycls.thirdProp = -5.1; // in order to make it easier to match the exact bytes from Flash Player roundtrip(mycls); +var getterSetter = new GetterSetterClass(); +getterSetter.myAS3Var = "Overwritten as3 str"; +roundtrip(getterSetter); + function dump(obj: *) { var keys = []; for (var key in obj) { @@ -54,7 +82,7 @@ function dump(obj: *) { trace(out); } -function roundtrip(obj: Object) { +function roundtrip(obj: Object): Object { trace("Original: [" + obj + "] class: " + getQualifiedClassName(obj)); dump(obj); var out = new ByteArray(); @@ -71,4 +99,5 @@ function roundtrip(obj: Object) { trace("Deserialized: [" + readBack + "] class: " + getQualifiedClassName(readBack)); dump(readBack); trace() + return readBack; } \ No newline at end of file diff --git a/tests/tests/swfs/avm2/amf_custom_obj/output.txt b/tests/tests/swfs/avm2/amf_custom_obj/output.txt index dcdb0a178..95544c3f4 100644 --- a/tests/tests/swfs/avm2/amf_custom_obj/output.txt +++ b/tests/tests/swfs/avm2/amf_custom_obj/output.txt @@ -10,11 +10,18 @@ Serialized: 10,11,1,11,102,105,114,115,116,6,11,72,101,108,108,111,1 Deserialized: [[object Object]] class: Object first=Hello, -MyClass(firstProp= Hello secondProp=null thirdProp=-5.1 privProp=Overwritten private prop +MyClass(firstProp= Hello privProp=Overwritten private prop Original: [undefined] class: Test.as$38::MyClass -Serialized: 10,51,25,77,121,67,108,97,115,115,65,108,105,97,115,19,102,105,114,115,116,80,114,111,112,21,115,101,99,111,110,100,80,114,111,112,19,116,104,105,114,100,80,114,111,112,6,11,72,101,108,108,111,1,5,192,20,102,102,102,102,102,102 -MyClass(firstProp= Hello secondProp=null thirdProp=-5.1 privProp=Constructor private prop +Serialized: 10,19,25,77,121,67,108,97,115,115,65,108,105,97,115,19,102,105,114,115,116,80,114,111,112,6,11,72,101,108,108,111 +MyClass(firstProp= Hello privProp=Constructor private prop Deserialized: [undefined] class: Test.as$38::MyClass +Original: [GetterSetterClass(myAS3Var=Overwritten as3 str)] class: Test.as$38::GetterSetterClass + +Called getAndSet getter +Serialized: 10,19,1,19,103,101,116,65,110,100,83,101,116,6,45,103,101,116,65,110,100,83,101,116,32,103,101,116,116,101,114,32,118,97,108,117,101 +Deserialized: [[object Object]] class: Object +getAndSet=getAndSet getter value, + diff --git a/tests/tests/swfs/avm2/amf_custom_obj/test.swf b/tests/tests/swfs/avm2/amf_custom_obj/test.swf index 26aa6063e..f7548f246 100755 Binary files a/tests/tests/swfs/avm2/amf_custom_obj/test.swf and b/tests/tests/swfs/avm2/amf_custom_obj/test.swf differ