2020-02-05 18:52:11 +00:00
|
|
|
//! Default AVM2 object impl
|
|
|
|
|
2020-02-20 04:10:21 +00:00
|
|
|
use crate::avm2::function::Executable;
|
2020-02-05 18:52:11 +00:00
|
|
|
use crate::avm2::names::QName;
|
2020-02-10 19:54:55 +00:00
|
|
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
2020-02-15 01:30:19 +00:00
|
|
|
use crate::avm2::property::Property;
|
2020-02-12 00:28:42 +00:00
|
|
|
use crate::avm2::return_value::ReturnValue;
|
2020-02-05 18:52:11 +00:00
|
|
|
use crate::avm2::value::Value;
|
2020-02-12 00:28:42 +00:00
|
|
|
use crate::avm2::{Avm2, Error};
|
|
|
|
use crate::context::UpdateContext;
|
2020-02-10 19:54:55 +00:00
|
|
|
use gc_arena::{Collect, GcCell, MutationContext};
|
2020-02-05 18:52:11 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::fmt::Debug;
|
|
|
|
|
|
|
|
/// Default implementation of `avm2::Object`.
|
|
|
|
#[derive(Clone, Collect, Debug, Copy)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct ScriptObject<'gc>(GcCell<'gc, ScriptObjectData<'gc>>);
|
|
|
|
|
|
|
|
#[derive(Clone, Collect, Debug)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct ScriptObjectData<'gc> {
|
|
|
|
/// Properties stored on this object.
|
2020-02-15 01:30:19 +00:00
|
|
|
values: HashMap<QName, Property<'gc>>,
|
2020-02-13 03:47:56 +00:00
|
|
|
|
2020-02-19 19:17:33 +00:00
|
|
|
/// Slots stored on this object.
|
|
|
|
slots: Vec<Value<'gc>>,
|
|
|
|
|
2020-02-13 03:47:56 +00:00
|
|
|
/// Implicit prototype (or declared base class) of this script object.
|
|
|
|
proto: Option<Object<'gc>>,
|
2020-02-05 18:52:11 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
impl<'gc> TObject<'gc> for ScriptObject<'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().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)
|
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().get_slot(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_slot(
|
|
|
|
self,
|
|
|
|
id: u32,
|
|
|
|
value: Value<'gc>,
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.0.write(mc).set_slot(id, value, mc)
|
|
|
|
}
|
|
|
|
|
2020-02-12 00:42:47 +00:00
|
|
|
fn has_property(self, name: &QName) -> bool {
|
|
|
|
self.0.read().has_property(name)
|
|
|
|
}
|
|
|
|
|
2020-02-13 03:47:56 +00:00
|
|
|
fn proto(&self) -> Option<Object<'gc>> {
|
|
|
|
self.0.read().proto
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
fn as_ptr(&self) -> *const ObjectPtr {
|
|
|
|
self.0.as_ptr() as *const ObjectPtr
|
|
|
|
}
|
2020-02-19 01:08:13 +00:00
|
|
|
|
2020-02-19 23:53:21 +00:00
|
|
|
fn construct(
|
|
|
|
&self,
|
|
|
|
_avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
|
|
_args: &[Value<'gc>],
|
|
|
|
) -> Result<Object<'gc>, Error> {
|
|
|
|
let this: Object<'gc> = Object::ScriptObject(*self);
|
|
|
|
Ok(ScriptObject::object(context.gc_context, this))
|
|
|
|
}
|
|
|
|
|
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).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).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).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).install_dynamic_property(name, value)
|
|
|
|
}
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
2020-02-10 19:54:55 +00:00
|
|
|
|
|
|
|
impl<'gc> ScriptObject<'gc> {
|
2020-02-13 03:47:56 +00:00
|
|
|
/// Construct a bare object with no base class.
|
|
|
|
///
|
|
|
|
/// This is *not* the same thing as an object literal, which actually does
|
|
|
|
/// have a base class: `Object`.
|
2020-02-10 19:54:55 +00:00
|
|
|
pub fn bare_object(mc: MutationContext<'gc, '_>) -> Object<'gc> {
|
2020-02-14 04:33:24 +00:00
|
|
|
ScriptObject(GcCell::allocate(mc, ScriptObjectData::base_new(None))).into()
|
2020-02-10 19:54:55 +00:00
|
|
|
}
|
2020-02-18 00:43:23 +00:00
|
|
|
|
2020-02-19 03:26:08 +00:00
|
|
|
/// Construct an object with a base class.
|
|
|
|
pub fn object(mc: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> {
|
|
|
|
ScriptObject(GcCell::allocate(
|
|
|
|
mc,
|
|
|
|
ScriptObjectData::base_new(Some(proto)),
|
|
|
|
))
|
|
|
|
.into()
|
|
|
|
}
|
2020-02-10 19:54:55 +00:00
|
|
|
}
|
2020-02-12 00:28:42 +00:00
|
|
|
|
|
|
|
impl<'gc> ScriptObjectData<'gc> {
|
2020-02-14 04:33:24 +00:00
|
|
|
pub fn base_new(proto: Option<Object<'gc>>) -> Self {
|
|
|
|
ScriptObjectData {
|
|
|
|
values: HashMap::new(),
|
2020-02-19 19:17:33 +00:00
|
|
|
slots: Vec::new(),
|
2020-02-14 04:33:24 +00:00
|
|
|
proto,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 00:28:42 +00:00
|
|
|
pub fn get_property(
|
|
|
|
&self,
|
|
|
|
name: &QName,
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
2020-02-15 01:30:19 +00:00
|
|
|
this: Object<'gc>,
|
2020-02-12 00:28:42 +00:00
|
|
|
) -> Result<ReturnValue<'gc>, Error> {
|
2020-02-15 01:30:19 +00:00
|
|
|
let prop = self.values.get(name);
|
|
|
|
|
|
|
|
if let Some(prop) = prop {
|
|
|
|
prop.get(avm, context, this)
|
|
|
|
} else {
|
|
|
|
Ok(Value::Undefined.into())
|
|
|
|
}
|
2020-02-12 00:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_property(
|
|
|
|
&mut self,
|
|
|
|
name: &QName,
|
|
|
|
value: Value<'gc>,
|
|
|
|
avm: &mut Avm2<'gc>,
|
|
|
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
2020-02-15 01:30:19 +00:00
|
|
|
this: Object<'gc>,
|
2020-02-12 00:28:42 +00:00
|
|
|
) -> Result<(), Error> {
|
2020-02-15 01:30:19 +00:00
|
|
|
if let Some(prop) = self.values.get_mut(name) {
|
|
|
|
prop.set(avm, context, this, value)?;
|
2020-02-12 00:28:42 +00:00
|
|
|
} else {
|
2020-02-15 01:30:19 +00:00
|
|
|
//TODO: Not all classes are dynamic like this
|
|
|
|
self.values
|
|
|
|
.insert(name.clone(), Property::new_dynamic_property(value));
|
2020-02-12 00:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-12 00:42:47 +00:00
|
|
|
|
2020-02-19 19:17:33 +00:00
|
|
|
pub fn get_slot(&self, id: u32) -> Result<Value<'gc>, Error> {
|
|
|
|
self.slots
|
|
|
|
.get(id as usize)
|
|
|
|
.cloned()
|
|
|
|
.ok_or_else(|| format!("Slot index {} out of bounds!", id).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set a slot by it's index.
|
|
|
|
pub fn set_slot(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
value: Value<'gc>,
|
2020-02-20 04:10:21 +00:00
|
|
|
_mc: MutationContext<'gc, '_>,
|
2020-02-19 19:17:33 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
if let Some(slot) = self.slots.get_mut(id as usize) {
|
|
|
|
*slot = value;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(format!("Slot index {} out of bounds!", id).into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-12 00:42:47 +00:00
|
|
|
pub fn has_property(&self, name: &QName) -> bool {
|
|
|
|
self.values.get(name).is_some()
|
|
|
|
}
|
2020-02-13 03:47:56 +00:00
|
|
|
|
|
|
|
pub fn proto(&self) -> Option<Object<'gc>> {
|
|
|
|
self.proto
|
|
|
|
}
|
2020-02-18 00:43:23 +00:00
|
|
|
|
|
|
|
/// Install a method into the object.
|
2020-02-19 03:26:08 +00:00
|
|
|
pub fn install_method(&mut self, name: QName, function: Object<'gc>) {
|
2020-02-18 00:43:23 +00:00
|
|
|
self.values.insert(name, Property::new_method(function));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Install a getter into the object.
|
|
|
|
///
|
|
|
|
/// This is a little more complicated than methods, since virtual property
|
|
|
|
/// slots can be installed in two parts. Thus, we need to support
|
|
|
|
/// installing them in either order.
|
2020-02-20 04:10:21 +00:00
|
|
|
pub fn install_getter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> {
|
2020-02-18 00:43:23 +00:00
|
|
|
if !self.values.contains_key(&name) {
|
|
|
|
self.values.insert(name.clone(), Property::new_virtual());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.values
|
|
|
|
.get_mut(&name)
|
|
|
|
.unwrap()
|
|
|
|
.install_virtual_getter(function)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Install a setter into the object.
|
|
|
|
///
|
|
|
|
/// This is a little more complicated than methods, since virtual property
|
|
|
|
/// slots can be installed in two parts. Thus, we need to support
|
|
|
|
/// installing them in either order.
|
2020-02-20 04:10:21 +00:00
|
|
|
pub fn install_setter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> {
|
2020-02-18 00:43:23 +00:00
|
|
|
if !self.values.contains_key(&name) {
|
|
|
|
self.values.insert(name.clone(), Property::new_virtual());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.values
|
|
|
|
.get_mut(&name)
|
|
|
|
.unwrap()
|
|
|
|
.install_virtual_setter(function)
|
|
|
|
}
|
2020-02-19 03:26:08 +00:00
|
|
|
|
|
|
|
pub fn install_dynamic_property(
|
|
|
|
&mut self,
|
|
|
|
name: QName,
|
|
|
|
value: Value<'gc>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.values
|
|
|
|
.insert(name, Property::new_dynamic_property(value));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-02-12 00:28:42 +00:00
|
|
|
}
|