diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 60cc62d6a..392084c8a 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -101,6 +101,7 @@ pub struct SystemPrototypes<'gc> { pub xml: Object<'gc>, pub xml_list: Object<'gc>, pub display_object: Object<'gc>, + pub shape: Object<'gc>, } impl<'gc> SystemPrototypes<'gc> { @@ -138,6 +139,7 @@ impl<'gc> SystemPrototypes<'gc> { xml: empty, xml_list: empty, display_object: empty, + shape: empty, } } } @@ -553,6 +555,19 @@ pub fn load_player_globals<'gc>( domain, script, )?; + activation + .context + .avm2 + .system_prototypes + .as_mut() + .unwrap() + .shape = class( + activation, + flash::display::shape::create_class(mc), + implicit_deriver, + domain, + script, + )?; class( activation, flash::display::interactiveobject::create_class(mc), diff --git a/core/src/avm2/globals/flash/display.rs b/core/src/avm2/globals/flash/display.rs index 3ff2e0d5d..66c00c152 100644 --- a/core/src/avm2/globals/flash/display.rs +++ b/core/src/avm2/globals/flash/display.rs @@ -6,4 +6,5 @@ pub mod framelabel; pub mod interactiveobject; pub mod movieclip; pub mod scene; +pub mod shape; pub mod sprite; diff --git a/core/src/avm2/globals/flash/display/shape.rs b/core/src/avm2/globals/flash/display/shape.rs new file mode 100644 index 000000000..875504377 --- /dev/null +++ b/core/src/avm2/globals/flash/display/shape.rs @@ -0,0 +1,43 @@ +//! `flash.display.Shape` builtin/prototype + +use crate::avm2::activation::Activation; +use crate::avm2::class::Class; +use crate::avm2::method::Method; +use crate::avm2::names::{Namespace, QName}; +use crate::avm2::object::Object; +use crate::avm2::value::Value; +use crate::avm2::Error; +use gc_arena::{GcCell, MutationContext}; + +/// Implements `flash.display.Shape`'s instance constructor. +pub fn instance_init<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(this) = this { + activation.super_init(this, &[])?; + } + + Ok(Value::Undefined) +} + +/// Implements `flash.display.Shape`'s class constructor. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + Ok(Value::Undefined) +} + +/// Construct `Shape`'s class. +pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { + Class::new( + QName::new(Namespace::package("flash.display"), "Shape"), + Some(QName::new(Namespace::package("flash.display"), "DisplayObject").into()), + Method::from_builtin(instance_init), + Method::from_builtin(class_init), + mc, + ) +} diff --git a/core/src/display_object/graphic.rs b/core/src/display_object/graphic.rs index 80eda1789..77eb32cbd 100644 --- a/core/src/display_object/graphic.rs +++ b/core/src/display_object/graphic.rs @@ -1,9 +1,16 @@ +use crate::avm1::Object as Avm1Object; +use crate::avm2::{ + Activation as Avm2Activation, Error as Avm2Error, Namespace as Avm2Namespace, + Object as Avm2Object, QName as Avm2QName, StageObject as Avm2StageObject, + TObject as Avm2TObject, +}; use crate::backend::render::ShapeHandle; use crate::context::{RenderContext, UpdateContext}; use crate::display_object::{DisplayObjectBase, TDisplayObject}; use crate::prelude::*; use crate::tag_utils::SwfMovie; use crate::types::{Degrees, Percent}; +use crate::vminterface::{AvmType, Instantiator}; use gc_arena::{Collect, GcCell}; use std::sync::Arc; @@ -16,6 +23,7 @@ pub struct Graphic<'gc>(GcCell<'gc, GraphicData<'gc>>); pub struct GraphicData<'gc> { base: DisplayObjectBase<'gc>, static_data: gc_arena::Gc<'gc, GraphicStatic>, + avm2_object: Option>, } impl<'gc> Graphic<'gc> { @@ -38,6 +46,7 @@ impl<'gc> Graphic<'gc> { GraphicData { base: Default::default(), static_data: gc_arena::Gc::allocate(context.gc_context, static_data), + avm2_object: None, }, )) } @@ -96,6 +105,57 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> { false } } + + fn post_instantiation( + &self, + context: &mut UpdateContext<'_, 'gc, '_>, + display_object: DisplayObject<'gc>, + _init_object: Option>, + _instantiated_by: Instantiator, + run_frame: bool, + ) { + if self.vm_type(context) == AvmType::Avm2 { + let mut allocator = || { + let mut activation = Avm2Activation::from_nothing(context.reborrow()); + let mut proto = activation.context.avm2.prototypes().shape; + let constr = proto + .get_property( + proto, + &Avm2QName::new(Avm2Namespace::public(), "constructor"), + &mut activation, + )? + .coerce_to_object(&mut activation)?; + + let object = Avm2StageObject::for_display_object( + activation.context.gc_context, + display_object, + proto, + ) + .into(); + constr.call(Some(object), &[], &mut activation, Some(proto))?; + + Ok(object) + }; + let result: Result, Avm2Error> = allocator(); + + match result { + Ok(object) => self.0.write(context.gc_context).avm2_object = Some(object), + Err(e) => log::error!("Got {} when constructing AVM2 side of display object", e), + } + } + + if run_frame { + self.run_frame(context); + } + } + + fn object2(&self) -> Avm2Value<'gc> { + self.0 + .read() + .avm2_object + .map(Avm2Value::from) + .unwrap_or(Avm2Value::Undefined) + } } /// Static data shared between all instances of a graphic.