From 63f411d231845ff086d805b0645240ca1073ec2e Mon Sep 17 00:00:00 2001 From: David Wendt Date: Mon, 20 Jul 2020 21:32:53 -0400 Subject: [PATCH] avm2: Add `StageObject` object implementation for holding display objects in AVM2. --- core/src/avm2/object.rs | 3 + core/src/avm2/object/stage_object.rs | 346 +++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 core/src/avm2/object/stage_object.rs diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 1fe57d73a..d30069db0 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -21,12 +21,14 @@ mod function_object; mod namespace_object; mod primitive_object; mod script_object; +mod stage_object; pub use crate::avm2::object::array_object::ArrayObject; pub use crate::avm2::object::function_object::{implicit_deriver, FunctionObject}; pub use crate::avm2::object::namespace_object::NamespaceObject; pub use crate::avm2::object::primitive_object::PrimitiveObject; pub use crate::avm2::object::script_object::ScriptObject; +pub use crate::avm2::object::stage_object::StageObject; /// Represents an object that can be directly interacted with by the AVM2 /// runtime. @@ -39,6 +41,7 @@ pub use crate::avm2::object::script_object::ScriptObject; PrimitiveObject(PrimitiveObject<'gc>), NamespaceObject(NamespaceObject<'gc>), ArrayObject(ArrayObject<'gc>), + StageObject(StageObject<'gc>), } )] pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy { diff --git a/core/src/avm2/object/stage_object.rs b/core/src/avm2/object/stage_object.rs new file mode 100644 index 000000000..4e8added6 --- /dev/null +++ b/core/src/avm2/object/stage_object.rs @@ -0,0 +1,346 @@ +//! AVM2 object impl for the display hierarchy. + +use crate::avm2::activation::Activation; +use crate::avm2::class::Class; +use crate::avm2::function::Executable; +use crate::avm2::names::{Namespace, QName}; +use crate::avm2::object::script_object::{ScriptObjectClass, ScriptObjectData}; +use crate::avm2::object::{Object, ObjectPtr, TObject}; +use crate::avm2::scope::Scope; +use crate::avm2::string::AvmString; +use crate::avm2::traits::Trait; +use crate::avm2::value::Value; +use crate::avm2::Error; +use crate::display_object::DisplayObject; +use gc_arena::{Collect, GcCell, MutationContext}; + +#[derive(Clone, Collect, Debug, Copy)] +#[collect(no_drop)] +pub struct StageObject<'gc>(GcCell<'gc, StageObjectData<'gc>>); + +#[derive(Clone, Collect, Debug)] +#[collect(no_drop)] +pub struct StageObjectData<'gc> { + /// The base data common to all AVM2 objects. + base: ScriptObjectData<'gc>, + + /// The associated display object, if one exists. + display_object: Option>, +} + +impl<'gc> TObject<'gc> for StageObject<'gc> { + fn get_property_local( + self, + reciever: Object<'gc>, + name: &QName<'gc>, + activation: &mut Activation<'_, 'gc, '_>, + ) -> Result, Error> { + let read = self.0.read(); + let rv = read.base.get_property_local(reciever, name, activation)?; + + drop(read); + + rv.resolve(activation) + } + + fn set_property_local( + self, + reciever: Object<'gc>, + name: &QName<'gc>, + value: Value<'gc>, + activation: &mut Activation<'_, 'gc, '_>, + ) -> Result<(), Error> { + let mut write = self.0.write(activation.context.gc_context); + let rv = write + .base + .set_property_local(reciever, name, value, activation)?; + + drop(write); + + rv.resolve(activation)?; + + Ok(()) + } + + fn init_property_local( + self, + reciever: Object<'gc>, + name: &QName<'gc>, + value: Value<'gc>, + activation: &mut Activation<'_, 'gc, '_>, + ) -> Result<(), Error> { + let mut write = self.0.write(activation.context.gc_context); + let rv = write + .base + .init_property_local(reciever, name, value, activation)?; + + drop(write); + + rv.resolve(activation)?; + + Ok(()) + } + + fn is_property_overwritable( + self, + gc_context: MutationContext<'gc, '_>, + name: &QName<'gc>, + ) -> bool { + self.0.write(gc_context).base.is_property_overwritable(name) + } + + fn delete_property( + &self, + gc_context: MutationContext<'gc, '_>, + multiname: &QName<'gc>, + ) -> bool { + self.0.write(gc_context).base.delete_property(multiname) + } + + fn get_slot(self, id: u32) -> Result, Error> { + self.0.read().base.get_slot(id) + } + + fn set_slot( + self, + id: u32, + value: Value<'gc>, + mc: MutationContext<'gc, '_>, + ) -> Result<(), Error> { + self.0.write(mc).base.set_slot(id, value, mc) + } + + fn init_slot( + self, + id: u32, + value: Value<'gc>, + mc: MutationContext<'gc, '_>, + ) -> Result<(), Error> { + self.0.write(mc).base.init_slot(id, value, mc) + } + + fn get_method(self, id: u32) -> Option> { + self.0.read().base.get_method(id) + } + + fn get_trait(self, name: &QName<'gc>) -> Result>, Error> { + self.0.read().base.get_trait(name) + } + + fn get_provided_trait( + &self, + name: &QName<'gc>, + known_traits: &mut Vec>, + ) -> Result<(), Error> { + self.0.read().base.get_provided_trait(name, known_traits) + } + + fn get_scope(self) -> Option>> { + self.0.read().base.get_scope() + } + + fn resolve_any(self, local_name: AvmString<'gc>) -> Result>, Error> { + self.0.read().base.resolve_any(local_name) + } + + fn resolve_any_trait( + self, + local_name: AvmString<'gc>, + ) -> Result>, Error> { + self.0.read().base.resolve_any_trait(local_name) + } + + fn has_own_property(self, name: &QName<'gc>) -> Result { + self.0.read().base.has_own_property(name) + } + + fn has_trait(self, name: &QName<'gc>) -> Result { + self.0.read().base.has_trait(name) + } + + fn provides_trait(self, name: &QName<'gc>) -> Result { + self.0.read().base.provides_trait(name) + } + + fn has_instantiated_property(self, name: &QName<'gc>) -> bool { + self.0.read().base.has_instantiated_property(name) + } + + fn has_own_virtual_getter(self, name: &QName<'gc>) -> bool { + self.0.read().base.has_own_virtual_getter(name) + } + + fn has_own_virtual_setter(self, name: &QName<'gc>) -> bool { + self.0.read().base.has_own_virtual_setter(name) + } + + fn proto(&self) -> Option> { + self.0.read().base.proto() + } + + fn get_enumerant_name(&self, index: u32) -> Option> { + self.0.read().base.get_enumerant_name(index) + } + + fn property_is_enumerable(&self, name: &QName<'gc>) -> bool { + self.0.read().base.property_is_enumerable(name) + } + + fn set_local_property_is_enumerable( + &self, + mc: MutationContext<'gc, '_>, + name: &QName<'gc>, + is_enumerable: bool, + ) -> Result<(), Error> { + self.0 + .write(mc) + .base + .set_local_property_is_enumerable(name, is_enumerable) + } + + fn as_ptr(&self) -> *const ObjectPtr { + self.0.as_ptr() as *const ObjectPtr + } + + fn as_executable(&self) -> Option> { + None + } + + fn as_class(&self) -> Option>> { + self.0.read().base.as_class() + } + + fn call( + self, + _reciever: Option>, + _arguments: &[Value<'gc>], + _activation: &mut Activation<'_, 'gc, '_>, + _base_proto: Option>, + ) -> Result, Error> { + Err("Not a callable function!".into()) + } + + fn construct( + &self, + activation: &mut Activation<'_, 'gc, '_>, + _args: &[Value<'gc>], + ) -> Result, Error> { + let this: Object<'gc> = Object::StageObject(*self); + let base = ScriptObjectData::base_new(Some(this), ScriptObjectClass::NoClass); + + Ok(StageObject(GcCell::allocate( + activation.context.gc_context, + StageObjectData { + base, + display_object: None, + }, + )) + .into()) + } + + fn derive( + &self, + activation: &mut Activation<'_, 'gc, '_>, + class: GcCell<'gc, Class<'gc>>, + scope: Option>>, + ) -> Result, Error> { + let this: Object<'gc> = Object::StageObject(*self); + let base = ScriptObjectData::base_new( + Some(this), + ScriptObjectClass::InstancePrototype(class, scope), + ); + + Ok(StageObject(GcCell::allocate( + activation.context.gc_context, + StageObjectData { + base, + display_object: None, + }, + )) + .into()) + } + + fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result, Error> { + Ok("todo: movieclip values".into()) + } + + fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result, Error> { + Ok(Value::Object(Object::from(*self))) + } + + fn install_method( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + disp_id: u32, + function: Object<'gc>, + ) { + self.0 + .write(mc) + .base + .install_method(name, disp_id, function) + } + + fn install_getter( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + disp_id: u32, + function: Object<'gc>, + ) -> Result<(), Error> { + self.0 + .write(mc) + .base + .install_getter(name, disp_id, function) + } + + fn install_setter( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + disp_id: u32, + function: Object<'gc>, + ) -> Result<(), Error> { + self.0 + .write(mc) + .base + .install_setter(name, disp_id, function) + } + + fn install_dynamic_property( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + value: Value<'gc>, + ) -> Result<(), Error> { + self.0.write(mc).base.install_dynamic_property(name, value) + } + + fn install_slot( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + id: u32, + value: Value<'gc>, + ) { + self.0.write(mc).base.install_slot(name, id, value) + } + + fn install_const( + &mut self, + mc: MutationContext<'gc, '_>, + name: QName<'gc>, + id: u32, + value: Value<'gc>, + ) { + self.0.write(mc).base.install_const(name, id, value) + } + + fn interfaces(&self) -> Vec> { + self.0.read().base.interfaces() + } + + fn set_interfaces(&self, context: MutationContext<'gc, '_>, iface_list: Vec>) { + self.0.write(context).base.set_interfaces(iface_list) + } +}