avm1: Add impl_custom_object!() macro to simplify custom object types

This commit is contained in:
Nathan Adams 2020-07-11 01:05:09 +02:00 committed by Mike Welsh
parent eb94cc54b2
commit 180891c58e
7 changed files with 335 additions and 1192 deletions

View File

@ -22,6 +22,7 @@ use std::borrow::Cow;
use std::fmt::Debug;
pub mod color_transform_object;
mod custom_object;
pub mod script_object;
pub mod shared_object;
pub mod sound_object;

View File

@ -1,13 +1,10 @@
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::property::Attribute;
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use enumset::EnumSet;
use crate::impl_custom_object_without_set;
use gc_arena::{Collect, GcCell, MutationContext};
use crate::avm1::activation::Activation;
use std::borrow::Cow;
use std::fmt;
/// A ColorTransform
@ -93,22 +90,10 @@ impl<'gc> ColorTransformObject<'gc> {
[set_blue_offset, get_blue_offset, blue_offset],
[set_alpha_offset, get_alpha_offset, alpha_offset],
);
fn base(self) -> ScriptObject<'gc> {
self.0.read().base
}
}
impl<'gc> TObject<'gc> for ColorTransformObject<'gc> {
fn get_local(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, activation, context, this)
}
impl_custom_object_without_set!(base);
fn set(
&self,
@ -117,7 +102,8 @@ impl<'gc> TObject<'gc> for ColorTransformObject<'gc> {
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base().internal_set(
let base = self.0.read().base;
base.internal_set(
name,
value,
activation,
@ -127,27 +113,8 @@ impl<'gc> TObject<'gc> for ColorTransformObject<'gc> {
)
}
fn call(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
self.base()
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Option<Executable<'gc>> {
self.base().call_setter(name, value, activation, context)
fn as_color_transform_object(&self) -> Option<ColorTransformObject<'gc>> {
Some(*self)
}
#[allow(clippy::new_ret_no_self)]
@ -164,185 +131,4 @@ impl<'gc> TObject<'gc> for ColorTransformObject<'gc> {
)
.into())
}
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base().delete(activation, gc_context, name)
}
fn proto(&self) -> Option<Object<'gc>> {
self.base().proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.base().set_proto(gc_context, prototype);
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.base()
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
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<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn set_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
callback: Executable<'gc>,
user_data: Value<'gc>,
) {
self.base()
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
) -> bool {
self.base().remove_watcher(activation, gc_context, name)
}
fn has_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_virtual(activation, context, name)
}
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(activation)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.base().as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.base().type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base().interfaces()
}
fn set_interfaces(
&mut self,
gc_context: MutationContext<'gc, '_>,
iface_list: Vec<Object<'gc>>,
) {
self.base().set_interfaces(gc_context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.base())
}
fn as_color_transform_object(&self) -> Option<ColorTransformObject<'gc>> {
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<Value<'gc>> {
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)
}
}

View File

@ -0,0 +1,281 @@
#[macro_export]
macro_rules! impl_custom_object_without_set {
($field:ident) => {
fn get_local(
&self,
name: &str,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
this: crate::avm1::Object<'gc>,
) -> Result<crate::avm1::Value<'gc>, crate::avm1::Error<'gc>> {
self.0
.read()
.$field
.get_local(name, activation, context, this)
}
fn call(
&self,
name: &str,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
this: crate::avm1::Object<'gc>,
base_proto: Option<crate::avm1::Object<'gc>>,
args: &[crate::avm1::Value<'gc>],
) -> Result<crate::avm1::Value<'gc>, crate::avm1::Error<'gc>> {
self.0
.read()
.$field
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: crate::avm1::Value<'gc>,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
) -> Option<crate::avm1::function::Executable<'gc>> {
self.0
.read()
.$field
.call_setter(name, value, activation, context)
}
fn delete(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.0.read().$field.delete(activation, gc_context, name)
}
fn proto(&self) -> Option<crate::avm1::Object<'gc>> {
self.0.read().$field.proto()
}
fn set_proto(
&self,
gc_context: gc_arena::MutationContext<'gc, '_>,
prototype: Option<crate::avm1::Object<'gc>>,
) {
self.0.read().$field.set_proto(gc_context, prototype);
}
fn define_value(
&self,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: &str,
value: crate::avm1::Value<'gc>,
attributes: enumset::EnumSet<crate::avm1::property::Attribute>,
) {
self.0
.read()
.$field
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: enumset::EnumSet<crate::avm1::property::Attribute>,
clear_attributes: enumset::EnumSet<crate::avm1::property::Attribute>,
) {
self.0.write(gc_context).$field.set_attributes(
gc_context,
name,
set_attributes,
clear_attributes,
)
}
fn add_property(
&self,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: &str,
get: crate::avm1::function::Executable<'gc>,
set: Option<crate::avm1::function::Executable<'gc>>,
attributes: enumset::EnumSet<crate::avm1::property::Attribute>,
) {
self.0
.read()
.$field
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: &str,
get: crate::avm1::function::Executable<'gc>,
set: Option<crate::avm1::function::Executable<'gc>>,
attributes: enumset::EnumSet<crate::avm1::property::Attribute>,
) {
self.0
.read()
.$field
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn has_property(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0.read().$field.has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0
.read()
.$field
.has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0
.read()
.$field
.has_own_virtual(activation, context, name)
}
fn is_property_enumerable(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
name: &str,
) -> bool {
self.0
.read()
.$field
.is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut crate::avm1::Activation<'_, 'gc>) -> Vec<String> {
self.0.read().$field.get_keys(activation)
}
fn as_string(&self) -> std::borrow::Cow<str> {
std::borrow::Cow::Owned(self.0.read().$field.as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.0.read().$field.type_of()
}
fn interfaces(&self) -> Vec<crate::avm1::Object<'gc>> {
self.0.read().$field.interfaces()
}
fn set_interfaces(
&mut self,
gc_context: gc_arena::MutationContext<'gc, '_>,
iface_list: Vec<crate::avm1::Object<'gc>>,
) {
self.0
.write(gc_context)
.$field
.set_interfaces(gc_context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.0.read().$field)
}
fn as_ptr(&self) -> *const crate::avm1::ObjectPtr {
self.0.as_ptr() as *const crate::avm1::ObjectPtr
}
fn length(&self) -> usize {
self.0.read().$field.length()
}
fn array(&self) -> Vec<crate::avm1::Value<'gc>> {
self.0.read().$field.array()
}
fn set_length(&self, gc_context: gc_arena::MutationContext<'gc, '_>, length: usize) {
self.0.read().$field.set_length(gc_context, length)
}
fn array_element(&self, index: usize) -> crate::avm1::Value<'gc> {
self.0.read().$field.array_element(index)
}
fn set_array_element(
&self,
index: usize,
value: crate::avm1::Value<'gc>,
gc_context: gc_arena::MutationContext<'gc, '_>,
) -> usize {
self.0
.read()
.$field
.set_array_element(index, value, gc_context)
}
fn delete_array_element(
&self,
index: usize,
gc_context: gc_arena::MutationContext<'gc, '_>,
) {
self.0.read().$field.delete_array_element(index, gc_context)
}
fn set_watcher(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: std::borrow::Cow<str>,
callback: crate::avm1::function::Executable<'gc>,
user_data: crate::avm1::Value<'gc>,
) {
self.0
.read()
.$field
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut crate::avm1::Activation<'_, 'gc>,
gc_context: gc_arena::MutationContext<'gc, '_>,
name: std::borrow::Cow<str>,
) -> bool {
self.0
.read()
.$field
.remove_watcher(activation, gc_context, name)
}
};
}
#[macro_export]
macro_rules! impl_custom_object {
($field:ident) => {
crate::impl_custom_object_without_set!($field);
fn set(
&self,
name: &str,
value: crate::avm1::Value<'gc>,
activation: &mut crate::avm1::Activation<'_, 'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
) -> Result<(), crate::avm1::Error<'gc>> {
self.0.read().$field.set(name, value, activation, context)
}
};
}

View File

@ -1,15 +1,10 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::sound_object::SoundObject;
use crate::avm1::property::Attribute;
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use crate::display_object::DisplayObject;
use enumset::EnumSet;
use crate::impl_custom_object;
use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow;
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use std::fmt;
/// A SharedObject
@ -63,54 +58,10 @@ impl<'gc> SharedObject<'gc> {
.cloned()
.unwrap_or_else(|| "".to_string())
}
fn base(self) -> ScriptObject<'gc> {
self.0.read().base
}
}
impl<'gc> TObject<'gc> for SharedObject<'gc> {
fn get_local(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context)
}
fn call(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
self.base()
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Option<Executable<'gc>> {
self.base().call_setter(name, value, activation, context)
}
impl_custom_object!(base);
#[allow(clippy::new_ret_no_self)]
fn new(
@ -127,196 +78,7 @@ impl<'gc> TObject<'gc> for SharedObject<'gc> {
.into())
}
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base().delete(activation, gc_context, name)
}
fn proto(&self) -> Option<Object<'gc>> {
self.base().proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.base().set_proto(gc_context, prototype);
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.base()
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
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<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn set_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
callback: Executable<'gc>,
user_data: Value<'gc>,
) {
self.base()
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
) -> bool {
self.base().remove_watcher(activation, gc_context, name)
}
fn has_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_virtual(activation, context, name)
}
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(activation)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.base().as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.base().type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base().interfaces()
}
fn set_interfaces(
&mut self,
gc_context: MutationContext<'gc, '_>,
iface_list: Vec<Object<'gc>>,
) {
self.base().set_interfaces(gc_context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.base())
}
fn as_display_object(&self) -> Option<DisplayObject<'gc>> {
None
}
fn as_executable(&self) -> Option<Executable<'gc>> {
None
}
fn as_sound_object(&self) -> Option<SoundObject<'gc>> {
None
}
fn as_shared_object(&self) -> Option<SharedObject<'gc>> {
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<Value<'gc>> {
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)
}
}

View File

@ -2,15 +2,12 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::property::Attribute;
use crate::avm1::{Object, ObjectPtr, ScriptObject, TObject, Value};
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
use crate::context::UpdateContext;
use crate::display_object::DisplayObject;
use enumset::EnumSet;
use crate::impl_custom_object;
use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow;
use std::fmt;
/// A SounObject that is tied to a sound from the AudioBackend.
@ -124,54 +121,10 @@ impl<'gc> SoundObject<'gc> {
pub fn set_position(self, gc_context: MutationContext<'gc, '_>, position: u32) {
self.0.write(gc_context).position = position;
}
fn base(self) -> ScriptObject<'gc> {
self.0.read().base
}
}
impl<'gc> TObject<'gc> for SoundObject<'gc> {
fn get_local(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context)
}
fn call(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
self.base()
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Option<Executable<'gc>> {
self.base().call_setter(name, value, activation, context)
}
impl_custom_object!(base);
#[allow(clippy::new_ret_no_self)]
fn new(
@ -187,192 +140,7 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
)
}
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base().delete(activation, gc_context, name)
}
fn proto(&self) -> Option<Object<'gc>> {
self.base().proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.base().set_proto(gc_context, prototype);
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.base()
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
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<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn set_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
callback: Executable<'gc>,
user_data: Value<'gc>,
) {
self.base()
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
) -> bool {
self.base().remove_watcher(activation, gc_context, name)
}
fn has_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_virtual(activation, context, name)
}
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(activation)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.base().as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.base().type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base().interfaces()
}
fn set_interfaces(
&mut self,
gc_context: MutationContext<'gc, '_>,
iface_list: Vec<Object<'gc>>,
) {
self.base().set_interfaces(gc_context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.base())
}
fn as_display_object(&self) -> Option<DisplayObject<'gc>> {
None
}
fn as_executable(&self) -> Option<Executable<'gc>> {
None
}
fn as_sound_object(&self) -> Option<SoundObject<'gc>> {
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<Value<'gc>> {
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)
}
}

View File

@ -2,13 +2,10 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute;
use crate::avm1::object::TObject;
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use enumset::EnumSet;
use crate::impl_custom_object;
use gc_arena::{Collect, GcCell, MutationContext};
use std::borrow::Cow;
use std::fmt;
/// An Object that serves as a box for a primitive value.
@ -132,55 +129,7 @@ impl fmt::Debug for ValueObject<'_> {
}
impl<'gc> TObject<'gc> for ValueObject<'gc> {
fn get_local(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.0
.read()
.base
.get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.0.read().base.set(name, value, activation, context)
}
fn call(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
self.0
.read()
.base
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Option<Executable<'gc>> {
self.0
.read()
.base
.call_setter(name, value, activation, context)
}
impl_custom_object!(base);
#[allow(clippy::new_ret_no_self)]
fn new(
@ -193,210 +142,7 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
Ok(ValueObject::empty_box(context.gc_context, Some(this)))
}
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.0.read().base.delete(activation, gc_context, name)
}
fn add_property(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.0
.read()
.base
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.0
.read()
.base
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn set_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
callback: Executable<'gc>,
user_data: Value<'gc>,
) {
self.0
.read()
.base
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
) -> bool {
self.0
.write(gc_context)
.base
.remove_watcher(activation, gc_context, name)
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.0
.read()
.base
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
self.0.write(gc_context).base.set_attributes(
gc_context,
name,
set_attributes,
clear_attributes,
)
}
fn proto(&self) -> Option<Object<'gc>> {
self.0.read().base.proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.0
.write(gc_context)
.base
.set_proto(gc_context, prototype);
}
fn has_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0.read().base.has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0
.read()
.base
.has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.0
.read()
.base
.has_own_virtual(activation, context, name)
}
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.0.read().base.is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.0.read().base.get_keys(activation)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.0.read().base.as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.0.read().base.type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.0.read().base.interfaces()
}
fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.0
.write(context)
.base
.set_interfaces(context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.0.read().base)
}
fn as_value_object(&self) -> Option<ValueObject<'gc>> {
Some(*self)
}
fn as_ptr(&self) -> *const ObjectPtr {
self.0.read().base.as_ptr() as *const ObjectPtr
}
fn length(&self) -> usize {
self.0.read().base.length()
}
fn array(&self) -> Vec<Value<'gc>> {
self.0.read().base.array()
}
fn set_length(&self, gc_context: MutationContext<'gc, '_>, length: usize) {
self.0.read().base.set_length(gc_context, length)
}
fn array_element(&self, index: usize) -> Value<'gc> {
self.0.read().base.array_element(index)
}
fn set_array_element(
&self,
index: usize,
value: Value<'gc>,
gc_context: MutationContext<'gc, '_>,
) -> usize {
self.0
.read()
.base
.set_array_element(index, value, gc_context)
}
fn delete_array_element(&self, index: usize, gc_context: MutationContext<'gc, '_>) {
self.0.read().base.delete_array_element(index, gc_context)
}
}

View File

@ -2,20 +2,24 @@
use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::Executable;
use crate::avm1::object::{ObjectPtr, TObject};
use crate::avm1::property::Attribute;
use crate::avm1::object::TObject;
use crate::avm1::{Object, ScriptObject, UpdateContext, Value};
use crate::impl_custom_object;
use crate::xml::{XMLDocument, XMLNode};
use enumset::EnumSet;
use gc_arena::{Collect, MutationContext};
use std::borrow::Cow;
use gc_arena::{Collect, GcCell, MutationContext};
use std::fmt;
/// A ScriptObject that is inherently tied to an XML node.
#[derive(Clone, Copy, Collect)]
#[collect(no_drop)]
pub struct XMLObject<'gc>(ScriptObject<'gc>, XMLNode<'gc>);
pub struct XMLObject<'gc>(GcCell<'gc, XMLObjectData<'gc>>);
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct XMLObjectData<'gc> {
base: ScriptObject<'gc>,
node: XMLNode<'gc>,
}
impl<'gc> XMLObject<'gc> {
/// Construct a new XML node and object pair.
@ -26,7 +30,14 @@ impl<'gc> XMLObject<'gc> {
let empty_document = XMLDocument::new(gc_context);
let mut xml_node = XMLNode::new_text(gc_context, "", empty_document);
let base_object = ScriptObject::object(gc_context, proto);
let object = XMLObject(base_object, xml_node).into();
let object = XMLObject(GcCell::allocate(
gc_context,
XMLObjectData {
base: base_object,
node: xml_node,
},
))
.into();
xml_node.introduce_script_object(gc_context, object);
@ -39,66 +50,29 @@ impl<'gc> XMLObject<'gc> {
xml_node: XMLNode<'gc>,
proto: Option<Object<'gc>>,
) -> Object<'gc> {
XMLObject(ScriptObject::object(gc_context, proto), xml_node).into()
}
fn base(&self) -> ScriptObject<'gc> {
match self {
XMLObject(base, ..) => *base,
}
XMLObject(GcCell::allocate(
gc_context,
XMLObjectData {
base: ScriptObject::object(gc_context, proto),
node: xml_node,
},
))
.into()
}
}
impl fmt::Debug for XMLObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
XMLObject(base, node) => f.debug_tuple("XMLObject").field(base).field(node).finish(),
}
let this = self.0.read();
f.debug_struct("XMLObject")
.field("base", &this.base)
.field("node", &this.node)
.finish()
}
}
impl<'gc> TObject<'gc> for XMLObject<'gc> {
fn get_local(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
self.base().get_local(name, activation, context, this)
}
fn set(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error<'gc>> {
self.base().set(name, value, activation, context)
}
fn call(
&self,
name: &str,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
self.base()
.call(name, activation, context, this, base_proto, args)
}
fn call_setter(
&self,
name: &str,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Option<Executable<'gc>> {
self.base().call_setter(name, value, activation, context)
}
impl_custom_object!(base);
#[allow(clippy::new_ret_no_self)]
fn new(
@ -111,182 +85,7 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
Ok(XMLObject::empty_node(context.gc_context, Some(this)))
}
fn delete(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
) -> bool {
self.base().delete(activation, gc_context, name)
}
fn add_property(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property(gc_context, name, get, set, attributes)
}
fn add_property_with_case(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: &str,
get: Executable<'gc>,
set: Option<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
self.base()
.add_property_with_case(activation, gc_context, name, get, set, attributes)
}
fn set_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
callback: Executable<'gc>,
user_data: Value<'gc>,
) {
self.base()
.set_watcher(activation, gc_context, name, callback, user_data);
}
fn remove_watcher(
&self,
activation: &mut Activation<'_, 'gc>,
gc_context: MutationContext<'gc, '_>,
name: Cow<str>,
) -> bool {
self.base().remove_watcher(activation, gc_context, name)
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.base()
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
self.base()
.set_attributes(gc_context, name, set_attributes, clear_attributes)
}
fn proto(&self) -> Option<Object<'gc>> {
self.base().proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.base().set_proto(gc_context, prototype);
}
fn has_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_property(activation, context, name)
}
fn has_own_property(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_property(activation, context, name)
}
fn has_own_virtual(
&self,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
name: &str,
) -> bool {
self.base().has_own_virtual(activation, context, name)
}
fn is_property_enumerable(&self, activation: &mut Activation<'_, 'gc>, name: &str) -> bool {
self.base().is_property_enumerable(activation, name)
}
fn get_keys(&self, activation: &mut Activation<'_, 'gc>) -> Vec<String> {
self.base().get_keys(activation)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.base().as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.base().type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base().interfaces()
}
fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.base().set_interfaces(context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.base())
}
fn as_xml_node(&self) -> Option<XMLNode<'gc>> {
match self {
XMLObject(_base, node) => Some(*node),
}
}
fn as_ptr(&self) -> *const ObjectPtr {
self.base().as_ptr() as *const ObjectPtr
}
fn length(&self) -> usize {
self.base().length()
}
fn array(&self) -> Vec<Value<'gc>> {
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)
Some(self.0.read().node)
}
}