2020-02-06 04:15:03 +00:00
|
|
|
//! AVM2 executables.
|
|
|
|
|
2020-02-08 03:42:04 +00:00
|
|
|
use crate::avm2::activation::Activation;
|
2020-02-19 03:26:08 +00:00
|
|
|
use crate::avm2::names::{Namespace, QName};
|
2020-02-06 04:15:03 +00:00
|
|
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
|
|
|
use crate::avm2::return_value::ReturnValue;
|
2020-02-12 23:52:00 +00:00
|
|
|
use crate::avm2::scope::Scope;
|
2020-02-06 04:15:03 +00:00
|
|
|
use crate::avm2::script_object::ScriptObjectData;
|
|
|
|
use crate::avm2::value::Value;
|
|
|
|
use crate::avm2::{Avm2, Error};
|
|
|
|
use crate::context::UpdateContext;
|
2020-02-14 04:33:24 +00:00
|
|
|
use gc_arena::{Collect, CollectionContext, GcCell, MutationContext};
|
2020-02-06 04:15:03 +00:00
|
|
|
use std::fmt;
|
|
|
|
use std::rc::Rc;
|
2020-02-14 04:33:24 +00:00
|
|
|
use swf::avm2::types::{
|
|
|
|
AbcFile, Class as AbcClass, Index, Instance as AbcInstance, Method as AbcMethod,
|
2020-02-20 04:10:21 +00:00
|
|
|
MethodBody as AbcMethodBody,
|
2020-02-14 04:33:24 +00:00
|
|
|
};
|
2020-02-06 04:15:03 +00:00
|
|
|
|
|
|
|
/// Represents a function defined in Ruffle's code.
|
|
|
|
///
|
|
|
|
/// Parameters are as follows:
|
|
|
|
///
|
|
|
|
/// * The AVM2 runtime
|
|
|
|
/// * The action context
|
|
|
|
/// * The current `this` object
|
|
|
|
/// * The arguments this function was called with
|
|
|
|
///
|
|
|
|
/// Native functions are allowed to return a value or `None`. `None` indicates
|
|
|
|
/// that the given value will not be returned on the stack and instead will
|
|
|
|
/// resolve on the AVM stack, as if you had called a non-native function. If
|
|
|
|
/// your function yields `None`, you must ensure that the top-most activation
|
|
|
|
/// in the AVM1 runtime will return with the value of this function.
|
|
|
|
pub type NativeFunction<'gc> = fn(
|
|
|
|
&mut Avm2<'gc>,
|
|
|
|
&mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
Object<'gc>,
|
|
|
|
&[Value<'gc>],
|
|
|
|
) -> Result<ReturnValue<'gc>, Error>;
|
|
|
|
|
2020-02-12 23:52:00 +00:00
|
|
|
/// Represents a reference to an AVM2 method and body.
|
2020-02-06 04:15:03 +00:00
|
|
|
#[derive(Collect, Clone, Debug)]
|
|
|
|
#[collect(require_static)]
|
2020-02-12 23:52:00 +00:00
|
|
|
pub struct Avm2MethodEntry {
|
2020-02-06 04:15:03 +00:00
|
|
|
/// The ABC file this function was defined in.
|
|
|
|
pub abc: Rc<AbcFile>,
|
|
|
|
|
|
|
|
/// The ABC method this function uses.
|
|
|
|
pub abc_method: u32,
|
|
|
|
|
|
|
|
/// The ABC method body this function uses.
|
|
|
|
pub abc_method_body: u32,
|
|
|
|
}
|
|
|
|
|
2020-02-12 23:52:00 +00:00
|
|
|
impl Avm2MethodEntry {
|
2020-02-14 04:33:24 +00:00
|
|
|
/// Construct an `Avm2MethodEntry` from an `AbcFile` and method index.
|
|
|
|
///
|
|
|
|
/// The method body index will be determined by searching through the ABC
|
|
|
|
/// for a matching method. If none exists, this function returns `None`.
|
|
|
|
pub fn from_method_index(abc: Rc<AbcFile>, abc_method: Index<AbcMethod>) -> Option<Self> {
|
|
|
|
if abc.methods.get(abc_method.0 as usize).is_some() {
|
|
|
|
for (index, method_body) in abc.method_bodies.iter().enumerate() {
|
|
|
|
if method_body.method.0 == abc_method.0 {
|
|
|
|
return Some(Self {
|
|
|
|
abc,
|
|
|
|
abc_method: abc_method.0,
|
|
|
|
abc_method_body: index as u32,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the underlying ABC file.
|
2020-02-20 04:10:21 +00:00
|
|
|
#[allow(dead_code)]
|
2020-02-14 04:33:24 +00:00
|
|
|
pub fn abc(&self) -> Rc<AbcFile> {
|
|
|
|
self.abc.clone()
|
|
|
|
}
|
|
|
|
|
2020-02-12 23:52:00 +00:00
|
|
|
/// Get a reference to the ABC method entry this refers to.
|
2020-02-20 04:10:21 +00:00
|
|
|
#[allow(dead_code)]
|
2020-02-12 23:52:00 +00:00
|
|
|
pub fn method(&self) -> &AbcMethod {
|
|
|
|
self.abc.methods.get(self.abc_method as usize).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the ABC method body entry this refers to.
|
|
|
|
pub fn body(&self) -> &AbcMethodBody {
|
|
|
|
self.abc
|
|
|
|
.method_bodies
|
|
|
|
.get(self.abc_method_body as usize)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents an AVM2 function.
|
|
|
|
#[derive(Collect, Clone, Debug)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct Avm2Function<'gc> {
|
|
|
|
/// The AVM method entry used to create this function.
|
|
|
|
pub method: Avm2MethodEntry,
|
|
|
|
|
|
|
|
/// Closure scope stack at time of creation
|
|
|
|
pub scope: Option<GcCell<'gc, Scope<'gc>>>,
|
|
|
|
}
|
|
|
|
|
2020-02-14 04:33:24 +00:00
|
|
|
impl<'gc> Avm2Function<'gc> {
|
|
|
|
pub fn from_method(method: Avm2MethodEntry, scope: Option<GcCell<'gc, Scope<'gc>>>) -> Self {
|
|
|
|
Self { method, scope }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
/// Represents code that can be executed by some means.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum Executable<'gc> {
|
|
|
|
Native(NativeFunction<'gc>),
|
2020-02-12 23:52:00 +00:00
|
|
|
Action(Avm2Function<'gc>),
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> Collect for Executable<'gc> {
|
|
|
|
fn trace(&self, cc: CollectionContext) {
|
|
|
|
match self {
|
|
|
|
Self::Action(a2f) => a2f.trace(cc),
|
|
|
|
Self::Native(_nf) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 01:30:19 +00:00
|
|
|
impl<'gc> Executable<'gc> {
|
|
|
|
pub fn exec(
|
|
|
|
&self,
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
reciever: Object<'gc>,
|
|
|
|
arguments: &[Value<'gc>],
|
|
|
|
) -> Result<ReturnValue<'gc>, Error> {
|
|
|
|
match self {
|
|
|
|
Executable::Native(nf) => nf(avm, context, reciever, arguments),
|
|
|
|
Executable::Action(a2f) => {
|
|
|
|
let activation = GcCell::allocate(
|
|
|
|
context.gc_context,
|
|
|
|
Activation::from_action(context, &a2f, reciever, None)?,
|
|
|
|
);
|
|
|
|
|
|
|
|
avm.insert_stack_frame(activation);
|
|
|
|
Ok(activation.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
impl<'gc> fmt::Debug for Executable<'gc> {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Action(a2f) => fmt.debug_tuple("Executable::Action").field(a2f).finish(),
|
|
|
|
Self::Native(nf) => fmt
|
|
|
|
.debug_tuple("Executable::Native")
|
|
|
|
.field(&format!("{:p}", nf))
|
|
|
|
.finish(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 04:33:24 +00:00
|
|
|
impl<'gc> From<NativeFunction<'gc>> for Executable<'gc> {
|
|
|
|
fn from(nf: NativeFunction<'gc>) -> Self {
|
|
|
|
Self::Native(nf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> From<Avm2Function<'gc>> for Executable<'gc> {
|
|
|
|
fn from(a2f: Avm2Function<'gc>) -> Self {
|
|
|
|
Self::Action(a2f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents a reference to an AVM2 class.
|
|
|
|
///
|
|
|
|
/// For some reason, this comes in two parts, one for static properties (called
|
|
|
|
/// the "class") and one for dynamic properties (called the "instance", even
|
|
|
|
/// though it really defines what ES3/AS2 would call a prototype)
|
|
|
|
#[derive(Collect, Clone, Debug)]
|
|
|
|
#[collect(require_static)]
|
|
|
|
pub struct Avm2ClassEntry {
|
|
|
|
/// The ABC file this function was defined in.
|
|
|
|
pub abc: Rc<AbcFile>,
|
|
|
|
|
2020-02-20 04:10:21 +00:00
|
|
|
/// The ABC class (used to define static properties).
|
|
|
|
///
|
|
|
|
/// This is also the index of the ABC instance, which holds instance
|
|
|
|
/// properties.
|
2020-02-14 04:33:24 +00:00
|
|
|
pub abc_class: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Avm2ClassEntry {
|
2020-02-20 04:10:21 +00:00
|
|
|
/// Construct an `Avm2MethodEntry` from an `AbcFile` and method index.
|
|
|
|
///
|
|
|
|
/// This function returns `None` if the given class index does not resolve
|
|
|
|
/// to a valid ABC class, or a valid ABC instance. As mentioned in the type
|
|
|
|
/// documentation, ABC classes and instances are intended to be paired.
|
|
|
|
pub fn from_class_index(abc: Rc<AbcFile>, abc_class: Index<AbcClass>) -> Option<Self> {
|
|
|
|
if abc.classes.get(abc_class.0 as usize).is_some()
|
|
|
|
&& abc.instances.get(abc_class.0 as usize).is_some()
|
|
|
|
{
|
|
|
|
return Some(Self {
|
|
|
|
abc,
|
|
|
|
abc_class: abc_class.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-02-14 04:33:24 +00:00
|
|
|
/// Get the underlying ABC file.
|
|
|
|
pub fn abc(&self) -> Rc<AbcFile> {
|
|
|
|
self.abc.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the ABC class entry this refers to.
|
|
|
|
pub fn class(&self) -> &AbcClass {
|
|
|
|
self.abc.classes.get(self.abc_class as usize).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the ABC class instance entry this refers to.
|
|
|
|
pub fn instance(&self) -> &AbcInstance {
|
2020-02-20 04:10:21 +00:00
|
|
|
self.abc.instances.get(self.abc_class as usize).unwrap()
|
2020-02-14 04:33:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
/// An Object which can be called to execute it's function code.
|
|
|
|
#[derive(Collect, Debug, Clone, Copy)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct FunctionObject<'gc>(GcCell<'gc, FunctionObjectData<'gc>>);
|
|
|
|
|
|
|
|
#[derive(Collect, Debug, Clone)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct FunctionObjectData<'gc> {
|
|
|
|
/// Base script object
|
|
|
|
base: ScriptObjectData<'gc>,
|
|
|
|
|
|
|
|
/// Executable code
|
2020-02-19 23:53:21 +00:00
|
|
|
exec: Option<Executable<'gc>>,
|
2020-02-14 04:33:24 +00:00
|
|
|
|
|
|
|
/// The class that defined this function object, if any.
|
|
|
|
class: Option<Avm2ClassEntry>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> FunctionObject<'gc> {
|
|
|
|
/// Construct a class from an ABC class/instance pair.
|
|
|
|
///
|
|
|
|
/// If the initializer method cannot be found, this function returns None.
|
|
|
|
pub fn from_abc_class(
|
2020-02-20 04:10:21 +00:00
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
2020-02-14 04:33:24 +00:00
|
|
|
class: Avm2ClassEntry,
|
2020-02-20 04:10:21 +00:00
|
|
|
proto: Object<'gc>,
|
|
|
|
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
|
|
|
fn_proto: Object<'gc>,
|
|
|
|
) -> Result<Object<'gc>, Error> {
|
2020-02-14 04:33:24 +00:00
|
|
|
let initializer_index = class.class().init_method.clone();
|
2020-02-20 04:10:21 +00:00
|
|
|
let initializer: Result<Avm2MethodEntry, Error> =
|
|
|
|
Avm2MethodEntry::from_method_index(class.abc(), initializer_index.clone()).ok_or_else(
|
|
|
|
|| {
|
|
|
|
format!(
|
|
|
|
"Class initializer method index {} does not exist",
|
|
|
|
initializer_index.0
|
|
|
|
)
|
|
|
|
.into()
|
2020-02-14 04:33:24 +00:00
|
|
|
},
|
2020-02-20 04:10:21 +00:00
|
|
|
);
|
|
|
|
let mut constr: Object<'gc> = FunctionObject(GcCell::allocate(
|
|
|
|
context.gc_context,
|
|
|
|
FunctionObjectData {
|
|
|
|
base: ScriptObjectData::base_new(None),
|
|
|
|
exec: Some(Avm2Function::from_method(initializer?, None).into()),
|
|
|
|
class: Some(class.clone()),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.into();
|
|
|
|
|
|
|
|
for trait_entry in class.class().traits.iter() {
|
|
|
|
constr.install_trait(avm, context, class.abc(), trait_entry, scope, fn_proto)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
constr.install_method(
|
|
|
|
context.gc_context,
|
|
|
|
QName::new(Namespace::public_namespace(), "prototype"),
|
|
|
|
proto,
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(constr)
|
2020-02-14 04:33:24 +00:00
|
|
|
}
|
2020-02-18 00:43:23 +00:00
|
|
|
|
|
|
|
/// Construct a function from an ABC method and the current closure scope.
|
|
|
|
pub fn from_abc_method(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
method: Avm2MethodEntry,
|
|
|
|
scope: Option<GcCell<'gc, Scope<'gc>>>,
|
|
|
|
fn_proto: Object<'gc>,
|
|
|
|
) -> Object<'gc> {
|
2020-02-19 23:53:21 +00:00
|
|
|
let exec = Some(Avm2Function::from_method(method, scope).into());
|
2020-02-18 00:43:23 +00:00
|
|
|
|
|
|
|
FunctionObject(GcCell::allocate(
|
|
|
|
mc,
|
|
|
|
FunctionObjectData {
|
|
|
|
base: ScriptObjectData::base_new(Some(fn_proto)),
|
|
|
|
exec,
|
|
|
|
class: None,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.into()
|
|
|
|
}
|
2020-02-19 03:26:08 +00:00
|
|
|
|
|
|
|
/// Construct a builtin function object from a Rust function.
|
|
|
|
pub fn from_builtin(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
nf: NativeFunction<'gc>,
|
|
|
|
fn_proto: Object<'gc>,
|
|
|
|
) -> Object<'gc> {
|
|
|
|
FunctionObject(GcCell::allocate(
|
|
|
|
mc,
|
|
|
|
FunctionObjectData {
|
|
|
|
base: ScriptObjectData::base_new(Some(fn_proto)),
|
2020-02-19 23:53:21 +00:00
|
|
|
exec: Some(nf.into()),
|
2020-02-19 03:26:08 +00:00
|
|
|
class: None,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct a builtin type from a Rust constructor and prototype.
|
|
|
|
pub fn from_builtin_constr(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
constr: NativeFunction<'gc>,
|
|
|
|
prototype: Object<'gc>,
|
|
|
|
fn_proto: Object<'gc>,
|
|
|
|
) -> Result<Object<'gc>, Error> {
|
|
|
|
let mut base = ScriptObjectData::base_new(Some(fn_proto));
|
|
|
|
|
|
|
|
base.install_dynamic_property(
|
|
|
|
QName::new(Namespace::public_namespace(), "prototype"),
|
|
|
|
prototype.into(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(FunctionObject(GcCell::allocate(
|
|
|
|
mc,
|
|
|
|
FunctionObjectData {
|
|
|
|
base,
|
2020-02-19 23:53:21 +00:00
|
|
|
exec: Some(constr.into()),
|
2020-02-19 03:26:08 +00:00
|
|
|
class: None,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.into())
|
|
|
|
}
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
2020-02-12 00:28:42 +00:00
|
|
|
fn get_property(
|
|
|
|
self,
|
|
|
|
name: &QName,
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<ReturnValue<'gc>, Error> {
|
2020-02-15 01:30:19 +00:00
|
|
|
self.0
|
|
|
|
.read()
|
|
|
|
.base
|
|
|
|
.get_property(name, avm, context, self.into())
|
2020-02-12 00:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn set_property(
|
|
|
|
self,
|
|
|
|
name: &QName,
|
|
|
|
value: Value<'gc>,
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.0
|
|
|
|
.write(context.gc_context)
|
|
|
|
.base
|
2020-02-15 01:30:19 +00:00
|
|
|
.set_property(name, value, avm, context, self.into())
|
2020-02-12 00:28:42 +00:00
|
|
|
}
|
|
|
|
|
2020-02-19 19:17:33 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-02-12 00:42:47 +00:00
|
|
|
fn has_property(self, name: &QName) -> bool {
|
|
|
|
self.0.read().base.has_property(name)
|
|
|
|
}
|
|
|
|
|
2020-02-13 03:47:56 +00:00
|
|
|
fn proto(&self) -> Option<Object<'gc>> {
|
|
|
|
self.0.read().base.proto()
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
fn as_ptr(&self) -> *const ObjectPtr {
|
|
|
|
self.0.as_ptr() as *const ObjectPtr
|
|
|
|
}
|
2020-02-08 03:42:04 +00:00
|
|
|
|
|
|
|
fn call(
|
|
|
|
self,
|
|
|
|
reciever: Object<'gc>,
|
|
|
|
arguments: &[Value<'gc>],
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
) -> Result<ReturnValue<'gc>, Error> {
|
2020-02-19 23:53:21 +00:00
|
|
|
if let Some(exec) = &self.0.read().exec {
|
|
|
|
exec.exec(avm, context, reciever, arguments)
|
|
|
|
} else {
|
|
|
|
Err("Not a callable function!".into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn construct(
|
|
|
|
&self,
|
|
|
|
_avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
_args: &[Value<'gc>],
|
|
|
|
) -> Result<Object<'gc>, Error> {
|
|
|
|
let this: Object<'gc> = Object::FunctionObject(*self);
|
|
|
|
let base = ScriptObjectData::base_new(Some(this));
|
|
|
|
|
|
|
|
Ok(FunctionObject(GcCell::allocate(
|
|
|
|
context.gc_context,
|
|
|
|
FunctionObjectData {
|
|
|
|
base,
|
|
|
|
exec: None,
|
|
|
|
class: None,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.into())
|
2020-02-08 03:42:04 +00:00
|
|
|
}
|
2020-02-19 01:08:13 +00:00
|
|
|
|
2020-02-20 04:10:21 +00:00
|
|
|
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
|
|
|
self.0.write(mc).base.install_method(name, function)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn install_getter(
|
2020-02-19 01:08:13 +00:00
|
|
|
&mut self,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
2020-02-20 04:10:21 +00:00
|
|
|
name: QName,
|
|
|
|
function: Executable<'gc>,
|
2020-02-19 01:08:13 +00:00
|
|
|
) -> Result<(), Error> {
|
2020-02-20 04:10:21 +00:00
|
|
|
self.0.write(mc).base.install_getter(name, function)
|
2020-02-19 01:08:13 +00:00
|
|
|
}
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-02-20 04:10:21 +00:00
|
|
|
fn install_setter(
|
|
|
|
&mut self,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
name: QName,
|
|
|
|
function: Executable<'gc>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.0.write(mc).base.install_setter(name, function)
|
2020-02-19 03:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn install_dynamic_property(
|
|
|
|
&mut self,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
name: QName,
|
|
|
|
value: Value<'gc>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.0.write(mc).base.install_dynamic_property(name, value)
|
|
|
|
}
|
2020-02-21 02:02:49 +00:00
|
|
|
|
|
|
|
fn install_slot(
|
|
|
|
&mut self,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
name: QName,
|
|
|
|
id: u32,
|
|
|
|
value: Value<'gc>,
|
|
|
|
) {
|
|
|
|
self.0.write(mc).base.install_slot(name, id, value)
|
|
|
|
}
|
2020-02-21 04:20:46 +00:00
|
|
|
|
|
|
|
fn install_const(
|
|
|
|
&mut self,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
name: QName,
|
|
|
|
id: u32,
|
|
|
|
value: Value<'gc>,
|
|
|
|
) {
|
|
|
|
self.0.write(mc).base.install_const(name, id, value)
|
|
|
|
}
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|