diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 5daa6c4a8..88875c80b 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -69,6 +69,7 @@ pub struct SystemPrototypes<'gc> { pub framelabel: Object<'gc>, pub scene: Object<'gc>, pub application_domain: Object<'gc>, + pub event: Object<'gc>, } impl<'gc> SystemPrototypes<'gc> { @@ -101,6 +102,7 @@ impl<'gc> SystemPrototypes<'gc> { framelabel: empty, scene: empty, application_domain: empty, + event: empty, } } } @@ -428,7 +430,13 @@ pub fn load_player_globals<'gc>( )?; // package `flash.events` - class( + activation + .context + .avm2 + .system_prototypes + .as_mut() + .unwrap() + .event = class( activation, flash::events::event::create_class(mc), flash::events::event::event_deriver, diff --git a/core/src/avm2/globals/flash/events/event.rs b/core/src/avm2/globals/flash/events/event.rs index 302919810..d6260a003 100644 --- a/core/src/avm2/globals/flash/events/event.rs +++ b/core/src/avm2/globals/flash/events/event.rs @@ -132,6 +132,26 @@ pub fn event_phase<'gc>( Ok(Value::Undefined) } +/// Implements `clone` +pub fn clone<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(evt) = this.unwrap().as_event() { + let evt_proto = activation.avm2().system_prototypes.as_ref().unwrap().event; + + return Ok(EventObject::from_event( + activation.context.gc_context, + Some(evt_proto), + evt.clone(), + ) + .into()); + } + + Ok(Value::Undefined) +} + /// Construct `Event`'s class. pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { let class = Class::new( @@ -168,6 +188,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> QName::new(Namespace::public_namespace(), "eventPhase"), Method::from_builtin(event_phase), )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::public_namespace(), "clone"), + Method::from_builtin(clone), + )); class } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index ff232c3d9..b94126e36 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -449,6 +449,7 @@ swf_tests! { (as3_event_bubbles, "avm2/event_bubbles", 1), (as3_event_cancelable, "avm2/event_cancelable", 1), (as3_event_type, "avm2/event_type", 1), + (as3_event_clone, "avm2/event_clone", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/core/tests/swfs/avm2/event_clone/Test.as b/core/tests/swfs/avm2/event_clone/Test.as new file mode 100644 index 000000000..d04d72e09 --- /dev/null +++ b/core/tests/swfs/avm2/event_clone/Test.as @@ -0,0 +1,46 @@ +package { + public class Test { + } +} + +import flash.events.Event; + +trace("///(DynEvent is a dynamic subclass of Event that fails to override clone)"); +dynamic class DynEvent extends Event { + function DynEvent(type: String, bubbles: Boolean = false, cancelable: Boolean = false) { + super(type, bubbles, cancelable); + } +} + +trace("///var e = new DynEvent(\"test_event\", false, true);"); +var e = new DynEvent("test_event", false, true); + +trace("///e.expando = \"Original expando property!\""); +e.expando = "Original expando property!"; + +trace("///e.expando;"); +trace(e.expando); + +trace("///var e2 = e.clone();"); +var e2 = e.clone(); + +trace("///e2 === e;"); +trace(e2 === e); + +trace("///e2.hasOwnProperty('expando');"); +trace(e2.hasOwnProperty('expando')); + +trace("///e2.type"); +trace(e2.type); + +trace("///e2.bubbles"); +trace(e2.bubbles); + +trace("///e2.cancelable"); +trace(e2.cancelable); + +trace("///e2 instanceof Event"); +trace(e2 instanceof Event); + +trace("///e2 instanceof DynEvent"); +trace(e2 instanceof DynEvent); \ No newline at end of file diff --git a/core/tests/swfs/avm2/event_clone/output.txt b/core/tests/swfs/avm2/event_clone/output.txt new file mode 100644 index 000000000..b34a8d543 --- /dev/null +++ b/core/tests/swfs/avm2/event_clone/output.txt @@ -0,0 +1,20 @@ +///(DynEvent is a dynamic subclass of Event that fails to override clone) +///var e = new DynEvent("test_event", false, true); +///e.expando = "Original expando property!" +///e.expando; +Original expando property! +///var e2 = e.clone(); +///e2 === e; +false +///e2.hasOwnProperty('expando'); +false +///e2.type +test_event +///e2.bubbles +false +///e2.cancelable +true +///e2 instanceof Event +true +///e2 instanceof DynEvent +false diff --git a/core/tests/swfs/avm2/event_clone/test.fla b/core/tests/swfs/avm2/event_clone/test.fla new file mode 100644 index 000000000..484f64360 Binary files /dev/null and b/core/tests/swfs/avm2/event_clone/test.fla differ diff --git a/core/tests/swfs/avm2/event_clone/test.swf b/core/tests/swfs/avm2/event_clone/test.swf new file mode 100644 index 000000000..94ee18a1f Binary files /dev/null and b/core/tests/swfs/avm2/event_clone/test.swf differ