avm2: Add `StageObject` object implementation for holding display objects in AVM2.

This commit is contained in:
David Wendt 2020-07-20 21:32:53 -04:00 committed by Mike Welsh
parent 5f6e3ca3d4
commit 63f411d231
2 changed files with 349 additions and 0 deletions

View File

@ -21,12 +21,14 @@ mod function_object;
mod namespace_object; mod namespace_object;
mod primitive_object; mod primitive_object;
mod script_object; mod script_object;
mod stage_object;
pub use crate::avm2::object::array_object::ArrayObject; pub use crate::avm2::object::array_object::ArrayObject;
pub use crate::avm2::object::function_object::{implicit_deriver, FunctionObject}; pub use crate::avm2::object::function_object::{implicit_deriver, FunctionObject};
pub use crate::avm2::object::namespace_object::NamespaceObject; pub use crate::avm2::object::namespace_object::NamespaceObject;
pub use crate::avm2::object::primitive_object::PrimitiveObject; pub use crate::avm2::object::primitive_object::PrimitiveObject;
pub use crate::avm2::object::script_object::ScriptObject; 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 /// Represents an object that can be directly interacted with by the AVM2
/// runtime. /// runtime.
@ -39,6 +41,7 @@ pub use crate::avm2::object::script_object::ScriptObject;
PrimitiveObject(PrimitiveObject<'gc>), PrimitiveObject(PrimitiveObject<'gc>),
NamespaceObject(NamespaceObject<'gc>), NamespaceObject(NamespaceObject<'gc>),
ArrayObject(ArrayObject<'gc>), ArrayObject(ArrayObject<'gc>),
StageObject(StageObject<'gc>),
} }
)] )]
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy { pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {

View File

@ -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<DisplayObject<'gc>>,
}
impl<'gc> TObject<'gc> for StageObject<'gc> {
fn get_property_local(
self,
reciever: Object<'gc>,
name: &QName<'gc>,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, 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<Value<'gc>, 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<Object<'gc>> {
self.0.read().base.get_method(id)
}
fn get_trait(self, name: &QName<'gc>) -> Result<Vec<Trait<'gc>>, Error> {
self.0.read().base.get_trait(name)
}
fn get_provided_trait(
&self,
name: &QName<'gc>,
known_traits: &mut Vec<Trait<'gc>>,
) -> Result<(), Error> {
self.0.read().base.get_provided_trait(name, known_traits)
}
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
self.0.read().base.get_scope()
}
fn resolve_any(self, local_name: AvmString<'gc>) -> Result<Option<Namespace<'gc>>, Error> {
self.0.read().base.resolve_any(local_name)
}
fn resolve_any_trait(
self,
local_name: AvmString<'gc>,
) -> Result<Option<Namespace<'gc>>, Error> {
self.0.read().base.resolve_any_trait(local_name)
}
fn has_own_property(self, name: &QName<'gc>) -> Result<bool, Error> {
self.0.read().base.has_own_property(name)
}
fn has_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
self.0.read().base.has_trait(name)
}
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
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<Object<'gc>> {
self.0.read().base.proto()
}
fn get_enumerant_name(&self, index: u32) -> Option<QName<'gc>> {
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<Executable<'gc>> {
None
}
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
self.0.read().base.as_class()
}
fn call(
self,
_reciever: Option<Object<'gc>>,
_arguments: &[Value<'gc>],
_activation: &mut Activation<'_, 'gc, '_>,
_base_proto: Option<Object<'gc>>,
) -> Result<Value<'gc>, Error> {
Err("Not a callable function!".into())
}
fn construct(
&self,
activation: &mut Activation<'_, 'gc, '_>,
_args: &[Value<'gc>],
) -> Result<Object<'gc>, 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<GcCell<'gc, Scope<'gc>>>,
) -> Result<Object<'gc>, 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<Value<'gc>, Error> {
Ok("todo: movieclip values".into())
}
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, 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<Object<'gc>> {
self.0.read().base.interfaces()
}
fn set_interfaces(&self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.0.write(context).base.set_interfaces(iface_list)
}
}