Skip to content

Commit

Permalink
avm2: Ignore errors when setting properties in amf deserializer
Browse files Browse the repository at this point in the history
Flash Player *traces* any errors that occur during setting properties
on the newly-constructed object durting AMf deserialization, and
continues deserializing.
  • Loading branch information
Aaron1011 committed Jul 3, 2024
1 parent e9ec441 commit 2ece190
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 3 deletions.
21 changes: 18 additions & 3 deletions core/src/avm2/amf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,27 @@ pub fn deserialize_value<'gc>(
let obj = target_class.construct(activation, &[])?;

for entry in elements {
let name = entry.name();
let value = deserialize_value(activation, entry.value())?;
obj.set_public_property(
AvmString::new_utf8(activation.context.gc_context, entry.name()),
// Flash player logs the error and continues deserializing the rest of the object,
// even when calling a customer setter
if let Err(e) = obj.set_public_property(
AvmString::new_utf8(activation.context.gc_context, name),
value,
activation,
)?;
) {
tracing::warn!(
"Ignoring error deserializing AMF property for field {name:?}: {e:?}"
);
if let Error::AvmError(e) = e {
if let Some(e) = e.as_object().and_then(|o| o.as_error_object()) {
// Flash player *traces* the error (without a stacktrace)
activation.context.avm_trace(
&e.display().expect("Failed to display error").to_string(),
);
}
}
}
}
obj.into()
}
Expand Down
61 changes: 61 additions & 0 deletions tests/tests/swfs/avm2/amf_missing_prop/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package {

import flash.display.MovieClip;


public class Test extends MovieClip {


public function Test() {
}
}

}

import flash.utils.ByteArray;
import flash.net.registerClassAlias;
import flash.net.getClassByAlias;
import flash.net.ObjectEncoding;


class MyFirstClass {
public var myProp: String;
public var myOtherProp: String;
public var oneGoodProp: String;
public function MyFirstClass(val: String) {
this.myProp = val;
this.myOtherProp = "My other val";
this.oneGoodProp = "The one good prop";
}
}

class MySecondClass {
public function set myOtherProp(newVal: String) {
trace("Called setter with: " + newVal);
throw new Error("Called myOtherProp setter with " + newVal);
}

public var oneGoodProp:String;

public function toString(): String {
return "MySecondClass(oneGoodProp = " + this.oneGoodProp + ")";
}
}

function doRoundTrip(version: int) {
trace("Roundtrip with AMF version: " + version);
var bytes = new ByteArray();
bytes.objectEncoding = version;
registerClassAlias("MyClass", MyFirstClass);
bytes.writeObject(new MyFirstClass("My value"));
bytes.position = 0;
registerClassAlias("MyClass", MySecondClass);
trace("Current alias: " + getClassByAlias("MyClass"));
var roundtrip = bytes.readObject();
trace("Deserialized: " + roundtrip);
}

// FIXME - Ruffle AMF0 class serialization is broken
//doRoundTrip(ObjectEncoding.AMF0);
trace();
doRoundTrip(ObjectEncoding.AMF3);
7 changes: 7 additions & 0 deletions tests/tests/swfs/avm2/amf_missing_prop/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

Roundtrip with AMF version: 3
Current alias: [class MySecondClass]
Called setter with: My other val
Error: Called myOtherProp setter with My other val
ReferenceError: Error #1056: Cannot create property myProp on Test.as$0.MySecondClass.
Deserialized: MySecondClass(oneGoodProp = The one good prop)
Binary file added tests/tests/swfs/avm2/amf_missing_prop/test.fla
Binary file not shown.
Binary file added tests/tests/swfs/avm2/amf_missing_prop/test.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/avm2/amf_missing_prop/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_ticks = 1

0 comments on commit 2ece190

Please sign in to comment.