diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 065807d05..cb230d40f 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -43,6 +43,7 @@ mod value_object; pub mod xml_attributes_object; pub mod xml_idmap_object; pub mod xml_object; +pub mod shared_object; #[cfg(test)] mod tests; diff --git a/core/src/avm1/globals/shared_object.rs b/core/src/avm1/globals/shared_object.rs index 535e79c00..c01134bc3 100644 --- a/core/src/avm1/globals/shared_object.rs +++ b/core/src/avm1/globals/shared_object.rs @@ -7,295 +7,11 @@ use gc_arena::{GcCell, MutationContext, Collect}; use crate::avm1::property::Attribute; use crate::display_object::DisplayObject; use crate::avm1::sound_object::SoundObject; +use crate::avm1::shared_object::SharedObject; use json::JsonValue; use std::fmt; -/// A SharedObject -#[derive(Clone, Copy, Collect)] -#[collect(no_drop)] -pub struct SharedObject<'gc>(GcCell<'gc, SharedObjectData<'gc>>); - -#[derive(Clone, Collect)] -#[collect(no_drop)] -pub struct SharedObjectData<'gc> { - /// The underlying script object. - base: ScriptObject<'gc>, - - /// The local name of this shared object - name: Option, -} - -impl fmt::Debug for SharedObject<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let this = self.0.read(); - f.debug_struct("SharedObject") - .field("name", &this.name) - .finish() - } -} - -impl<'gc> SharedObject<'gc> { - fn empty_shared_obj( - gc_context: MutationContext<'gc, '_>, - proto: Option>, - ) -> Self { - SharedObject(GcCell::allocate( - gc_context, - SharedObjectData { - base: ScriptObject::object(gc_context, proto), - name: None, - }, - )) - } - //TODO: any need for these - - //TODO: use enum Remote(url), Local(name) - - fn set_name(&self, gc_context: MutationContext<'gc, '_>, name: String) { - self.0.write(gc_context).name = Some(name); - } - - fn get_name(&self) -> String { - self.0.read().name.as_ref().cloned().unwrap_or("".to_owned()) - } - - fn base(self) -> ScriptObject<'gc> { - self.0.read().base - } -} - -impl<'gc> TObject<'gc> for SharedObject<'gc> { - fn get_local( - &self, - name: &str, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - this: Object<'gc>, - ) -> Result, Error> { - self.base().get_local(name, avm, context, this) - } - - fn set( - &self, - name: &str, - value: Value<'gc>, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - ) -> Result<(), Error> { - self.base().set(name, value, avm, context) - } - - fn call( - &self, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - this: Object<'gc>, - base_proto: Option>, - args: &[Value<'gc>], - ) -> Result, Error> { - self.base().call(avm, context, this, base_proto, args) - } - - fn call_setter( - &self, - name: &str, - value: Value<'gc>, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - this: Object<'gc>, - ) -> Result, Error> { - self.base().call_setter(name, value, avm, context, this) - } - - #[allow(clippy::new_ret_no_self)] - fn new( - &self, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - _this: Object<'gc>, - _args: &[Value<'gc>], - ) -> Result, Error> { - Ok(SharedObject::empty_shared_obj(context.gc_context, Some(avm.prototypes.shared_object)).into()) - } - - fn delete( - &self, - avm: &mut Avm1<'gc>, - gc_context: MutationContext<'gc, '_>, - name: &str, - ) -> bool { - self.base().delete(avm, gc_context, name) - } - - fn proto(&self) -> Option> { - self.base().proto() - } - - fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option>) { - self.base().set_proto(gc_context, prototype); - } - - fn define_value( - &self, - gc_context: MutationContext<'gc, '_>, - name: &str, - value: Value<'gc>, - attributes: EnumSet, - ) { - self.base() - .define_value(gc_context, name, value, attributes) - } - - fn set_attributes( - &mut self, - gc_context: MutationContext<'gc, '_>, - name: Option<&str>, - set_attributes: EnumSet, - clear_attributes: EnumSet, - ) { - self.base() - .set_attributes(gc_context, name, set_attributes, clear_attributes) - } - - fn add_property( - &self, - gc_context: MutationContext<'gc, '_>, - name: &str, - get: Executable<'gc>, - set: Option>, - attributes: EnumSet, - ) { - self.base() - .add_property(gc_context, name, get, set, attributes) - } - - fn add_property_with_case( - &self, - avm: &mut Avm1<'gc>, - gc_context: MutationContext<'gc, '_>, - name: &str, - get: Executable<'gc>, - set: Option>, - attributes: EnumSet, - ) { - self.base() - .add_property_with_case(avm, gc_context, name, get, set, attributes) - } - - fn has_property( - &self, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - name: &str, - ) -> bool { - self.base().has_property(avm, context, name) - } - - fn has_own_property( - &self, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - name: &str, - ) -> bool { - self.base().has_own_property(avm, context, name) - } - - fn has_own_virtual( - &self, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, - name: &str, - ) -> bool { - self.base().has_own_virtual(avm, context, name) - } - - fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { - self.base().is_property_overwritable(avm, name) - } - - fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { - self.base().is_property_enumerable(avm, name) - } - - fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec { - self.base().get_keys(avm) - } - - fn as_string(&self) -> String { - self.base().as_string() - } - - fn type_of(&self) -> &'static str { - self.base().type_of() - } - - fn interfaces(&self) -> Vec> { - self.base().interfaces() - } - - fn set_interfaces( - &mut self, - gc_context: MutationContext<'gc, '_>, - iface_list: Vec>, - ) { - self.base().set_interfaces(gc_context, iface_list) - } - - fn as_script_object(&self) -> Option> { - Some(self.base()) - } - - fn as_display_object(&self) -> Option> { - None - } - - fn as_executable(&self) -> Option> { - None - } - - fn as_sound_object(&self) -> Option> { - None - } - - fn as_shared_object(&self) -> Option> { - Some(*self) - } - - fn as_ptr(&self) -> *const ObjectPtr { - self.0.as_ptr() as *const ObjectPtr - } - - fn length(&self) -> usize { - self.base().length() - } - - fn array(&self) -> Vec> { - self.base().array() - } - - fn set_length(&self, gc_context: MutationContext<'gc, '_>, length: usize) { - self.base().set_length(gc_context, length) - } - - fn array_element(&self, index: usize) -> Value<'gc> { - self.base().array_element(index) - } - - fn set_array_element( - &self, - index: usize, - value: Value<'gc>, - gc_context: MutationContext<'gc, '_>, - ) -> usize { - self.base().set_array_element(index, value, gc_context) - } - - fn delete_array_element(&self, index: usize, gc_context: MutationContext<'gc, '_>) { - self.base().delete_array_element(index, gc_context) - } -} - pub fn delete_all<'gc>( _avm: &mut Avm1<'gc>, _action_context: &mut UpdateContext<'_, 'gc, '_>, diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 8a31e1c57..9b439070b 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -6,7 +6,7 @@ use crate::avm1::property::Attribute; use crate::avm1::return_value::ReturnValue; use crate::avm1::super_object::SuperObject; use crate::avm1::value_object::ValueObject; -use crate::avm1::globals::shared_object::SharedObject; +use crate::avm1::shared_object::SharedObject; use crate::avm1::xml_attributes_object::XMLAttributesObject; use crate::avm1::xml_idmap_object::XMLIDMapObject; diff --git a/core/src/avm1/shared_object.rs b/core/src/avm1/shared_object.rs new file mode 100644 index 000000000..988b39497 --- /dev/null +++ b/core/src/avm1/shared_object.rs @@ -0,0 +1,297 @@ +use crate::avm1::function::{Executable, FunctionObject}; +use crate::avm1::return_value::ReturnValue; +use crate::avm1::{Avm1, Error, Object, ObjectPtr, ScriptObject, TObject, Value}; +use crate::context::UpdateContext; +use enumset::EnumSet; +use gc_arena::{GcCell, MutationContext, Collect}; +use crate::avm1::property::Attribute; +use crate::display_object::DisplayObject; +use crate::avm1::sound_object::SoundObject; + +use json::JsonValue; +use std::fmt; + +/// A SharedObject +#[derive(Clone, Copy, Collect)] +#[collect(no_drop)] +pub struct SharedObject<'gc>(GcCell<'gc, SharedObjectData<'gc>>); + +#[derive(Clone, Collect)] +#[collect(no_drop)] +pub struct SharedObjectData<'gc> { + /// The underlying script object. + base: ScriptObject<'gc>, + + /// The local name of this shared object + name: Option, +} + +impl fmt::Debug for SharedObject<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let this = self.0.read(); + f.debug_struct("SharedObject") + .field("name", &this.name) + .finish() + } +} + +impl<'gc> SharedObject<'gc> { + pub fn empty_shared_obj( + gc_context: MutationContext<'gc, '_>, + proto: Option>, + ) -> Self { + SharedObject(GcCell::allocate( + gc_context, + SharedObjectData { + base: ScriptObject::object(gc_context, proto), + name: None, + }, + )) + } + //TODO: any need for these + + //TODO: use enum Remote(url), Local(name) + + pub fn set_name(&self, gc_context: MutationContext<'gc, '_>, name: String) { + self.0.write(gc_context).name = Some(name); + } + + pub fn get_name(&self) -> String { + self.0.read().name.as_ref().cloned().unwrap_or("".to_owned()) + } + + fn base(self) -> ScriptObject<'gc> { + self.0.read().base + } +} + +impl<'gc> TObject<'gc> for SharedObject<'gc> { + fn get_local( + &self, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + ) -> Result, Error> { + self.base().get_local(name, avm, context, this) + } + + fn set( + &self, + name: &str, + value: Value<'gc>, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result<(), Error> { + self.base().set(name, value, avm, context) + } + + fn call( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + base_proto: Option>, + args: &[Value<'gc>], + ) -> Result, Error> { + self.base().call(avm, context, this, base_proto, args) + } + + fn call_setter( + &self, + name: &str, + value: Value<'gc>, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + ) -> Result, Error> { + self.base().call_setter(name, value, avm, context, this) + } + + #[allow(clippy::new_ret_no_self)] + fn new( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + _this: Object<'gc>, + _args: &[Value<'gc>], + ) -> Result, Error> { + Ok(SharedObject::empty_shared_obj(context.gc_context, Some(avm.prototypes.shared_object)).into()) + } + + fn delete( + &self, + avm: &mut Avm1<'gc>, + gc_context: MutationContext<'gc, '_>, + name: &str, + ) -> bool { + self.base().delete(avm, gc_context, name) + } + + fn proto(&self) -> Option> { + self.base().proto() + } + + fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option>) { + self.base().set_proto(gc_context, prototype); + } + + fn define_value( + &self, + gc_context: MutationContext<'gc, '_>, + name: &str, + value: Value<'gc>, + attributes: EnumSet, + ) { + self.base() + .define_value(gc_context, name, value, attributes) + } + + fn set_attributes( + &mut self, + gc_context: MutationContext<'gc, '_>, + name: Option<&str>, + set_attributes: EnumSet, + clear_attributes: EnumSet, + ) { + self.base() + .set_attributes(gc_context, name, set_attributes, clear_attributes) + } + + fn add_property( + &self, + gc_context: MutationContext<'gc, '_>, + name: &str, + get: Executable<'gc>, + set: Option>, + attributes: EnumSet, + ) { + self.base() + .add_property(gc_context, name, get, set, attributes) + } + + fn add_property_with_case( + &self, + avm: &mut Avm1<'gc>, + gc_context: MutationContext<'gc, '_>, + name: &str, + get: Executable<'gc>, + set: Option>, + attributes: EnumSet, + ) { + self.base() + .add_property_with_case(avm, gc_context, name, get, set, attributes) + } + + fn has_property( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_property(avm, context, name) + } + + fn has_own_property( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_own_property(avm, context, name) + } + + fn has_own_virtual( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_own_virtual(avm, context, name) + } + + fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { + self.base().is_property_overwritable(avm, name) + } + + fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { + self.base().is_property_enumerable(avm, name) + } + + fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec { + self.base().get_keys(avm) + } + + fn as_string(&self) -> String { + self.base().as_string() + } + + fn type_of(&self) -> &'static str { + self.base().type_of() + } + + fn interfaces(&self) -> Vec> { + self.base().interfaces() + } + + fn set_interfaces( + &mut self, + gc_context: MutationContext<'gc, '_>, + iface_list: Vec>, + ) { + self.base().set_interfaces(gc_context, iface_list) + } + + fn as_script_object(&self) -> Option> { + Some(self.base()) + } + + fn as_display_object(&self) -> Option> { + None + } + + fn as_executable(&self) -> Option> { + None + } + + fn as_sound_object(&self) -> Option> { + None + } + + fn as_shared_object(&self) -> Option> { + Some(*self) + } + + fn as_ptr(&self) -> *const ObjectPtr { + self.0.as_ptr() as *const ObjectPtr + } + + fn length(&self) -> usize { + self.base().length() + } + + fn array(&self) -> Vec> { + self.base().array() + } + + fn set_length(&self, gc_context: MutationContext<'gc, '_>, length: usize) { + self.base().set_length(gc_context, length) + } + + fn array_element(&self, index: usize) -> Value<'gc> { + self.base().array_element(index) + } + + fn set_array_element( + &self, + index: usize, + value: Value<'gc>, + gc_context: MutationContext<'gc, '_>, + ) -> usize { + self.base().set_array_element(index, value, gc_context) + } + + fn delete_array_element(&self, index: usize, gc_context: MutationContext<'gc, '_>) { + self.base().delete_array_element(index, gc_context) + } +}