From f96a41403697d362aa051d0977873489269300b3 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Wed, 24 Mar 2021 12:13:19 -0700 Subject: [PATCH] avm2: Lazily initialize Graphics object Initialize a Sprite/Shape's Graphics object on the first access, so that `shape.graphics == shape.graphics` is `true`. --- core/src/avm2/globals/flash/display/shape.rs | 44 +++++++++++++++---- core/src/avm2/globals/flash/display/sprite.rs | 43 ++++++++++++++---- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/core/src/avm2/globals/flash/display/shape.rs b/core/src/avm2/globals/flash/display/shape.rs index 0830f1af9..10a7120dd 100644 --- a/core/src/avm2/globals/flash/display/shape.rs +++ b/core/src/avm2/globals/flash/display/shape.rs @@ -2,6 +2,7 @@ use crate::avm2::activation::Activation; use crate::avm2::class::Class; +use crate::avm2::globals::NS_RUFFLE_INTERNAL; use crate::avm2::method::Method; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, StageObject, TObject}; @@ -52,15 +53,33 @@ pub fn graphics<'gc>( this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { - if let Some(dobj) = this.and_then(|o| o.as_display_object()) { - let graphics_proto = activation.context.avm2.prototypes().graphics; - - return Ok(StageObject::for_display_object( - activation.context.gc_context, - dobj, - graphics_proto, - ) - .into()); + if let Some(mut this) = this { + if let Some(dobj) = this.as_display_object() { + // Lazily initialize the `Graphics` object in a hidden property. + let graphics = match this.get_property( + this, + &QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + activation, + )? { + Value::Undefined | Value::Null => { + let graphics_proto = activation.context.avm2.prototypes().graphics; + let graphics = Value::from(StageObject::for_display_object( + activation.context.gc_context, + dobj, + graphics_proto, + )); + this.set_property( + this, + &QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + graphics.clone(), + activation, + )?; + graphics + } + graphics => graphics, + }; + return Ok(graphics); + } } Ok(Value::Undefined) @@ -83,5 +102,12 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> Method::from_builtin(graphics), )); + // Slot for lazy-initialized Graphics object. + write.define_instance_trait(Trait::from_slot( + QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + QName::new(Namespace::package("flash.display"), "Graphics").into(), + None, + )); + class } diff --git a/core/src/avm2/globals/flash/display/sprite.rs b/core/src/avm2/globals/flash/display/sprite.rs index b63ea8886..e261cd6e0 100644 --- a/core/src/avm2/globals/flash/display/sprite.rs +++ b/core/src/avm2/globals/flash/display/sprite.rs @@ -2,6 +2,7 @@ use crate::avm2::activation::Activation; use crate::avm2::class::{Class, ClassAttributes}; +use crate::avm2::globals::NS_RUFFLE_INTERNAL; use crate::avm2::method::Method; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, StageObject, TObject}; @@ -38,15 +39,33 @@ pub fn graphics<'gc>( this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { - if let Some(dobj) = this.and_then(|o| o.as_display_object()) { - let graphics_proto = activation.context.avm2.prototypes().graphics; - - return Ok(StageObject::for_display_object( - activation.context.gc_context, - dobj, - graphics_proto, - ) - .into()); + if let Some(mut this) = this { + if let Some(dobj) = this.as_display_object() { + // Lazily initialize the `Graphics` object in a hidden property. + let graphics = match this.get_property( + this, + &QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + activation, + )? { + Value::Undefined | Value::Null => { + let graphics_proto = activation.context.avm2.prototypes().graphics; + let graphics = Value::from(StageObject::for_display_object( + activation.context.gc_context, + dobj, + graphics_proto, + )); + this.set_property( + this, + &QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + graphics.clone(), + activation, + )?; + graphics + } + graphics => graphics, + }; + return Ok(graphics); + } } Ok(Value::Undefined) @@ -77,5 +96,11 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> Method::from_builtin(graphics), )); + write.define_instance_trait(Trait::from_slot( + QName::new(Namespace::private(NS_RUFFLE_INTERNAL), "graphics"), + QName::new(Namespace::package("flash.display"), "Graphics").into(), + None, + )); + class }