avm2: Add `StageObject` object implementation for holding display objects in AVM2.
This commit is contained in:
parent
5f6e3ca3d4
commit
63f411d231
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue