Fix vtable property amf serialization
This commit is contained in:
parent
10d6157755
commit
bab2a80d64
|
@ -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<Element>,
|
||||
static_properties: &mut Vec<String>,
|
||||
static_properties: Option<&mut Vec<String>>,
|
||||
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
|
||||
|
|
|
@ -17,13 +17,7 @@ fn new_lso<'gc>(
|
|||
data: Object<'gc>,
|
||||
) -> Result<Lso, Error<'gc>> {
|
||||
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('/')
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue