ruffle/core/src/avm2/property.rs

311 lines
9.4 KiB
Rust
Raw Normal View History

2020-02-15 01:30:19 +00:00
//! Property data structures
use crate::avm2::object::{ClassObject, Object, TObject};
2020-02-15 01:30:19 +00:00
use crate::avm2::return_value::ReturnValue;
use crate::avm2::value::Value;
use crate::avm2::Error;
use bitflags::bitflags;
use gc_arena::Collect;
2020-02-15 01:30:19 +00:00
bitflags! {
/// Attributes of properties in the AVM runtime.
///
/// TODO: Replace with AVM2 properties for traits
#[derive(Collect)]
#[collect(require_static)]
pub struct Attribute: u8 {
2021-06-08 22:21:00 +00:00
/// Property cannot be deleted in user code.
const DONT_DELETE = 1 << 0;
2021-06-08 22:21:00 +00:00
/// Property cannot be set.
const READ_ONLY = 1 << 1;
2021-06-08 22:21:00 +00:00
/// Property cannot be overridden in subclasses.
const FINAL = 1 << 2;
}
2020-02-15 01:30:19 +00:00
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Collect)]
#[collect(no_drop)]
2020-02-15 01:30:19 +00:00
pub enum Property<'gc> {
Virtual {
2020-12-11 03:21:36 +00:00
get: Option<Object<'gc>>,
set: Option<Object<'gc>>,
attributes: Attribute,
2020-02-15 01:30:19 +00:00
},
Stored {
value: Value<'gc>,
attributes: Attribute,
2020-02-15 01:30:19 +00:00
},
2020-02-19 19:17:33 +00:00
Slot {
slot_id: u32,
attributes: Attribute,
2020-02-19 19:17:33 +00:00
},
2020-02-15 01:30:19 +00:00
}
impl<'gc> Property<'gc> {
/// Create a new stored property.
2021-06-08 22:21:00 +00:00
pub fn new_stored(value: impl Into<Value<'gc>>, is_final: bool) -> Self {
let mut attributes = Attribute::DONT_DELETE;
if is_final {
attributes |= Attribute::FINAL;
}
Property::Stored {
value: value.into(),
2021-06-08 22:21:00 +00:00
attributes,
}
}
/// Create a new stored property.
2021-06-08 22:21:00 +00:00
pub fn new_const(value: impl Into<Value<'gc>>, is_final: bool) -> Self {
let mut attributes = Attribute::DONT_DELETE | Attribute::READ_ONLY;
if is_final {
attributes |= Attribute::FINAL;
}
Property::Stored {
value: value.into(),
2021-06-08 22:21:00 +00:00
attributes,
}
}
2020-02-15 01:30:19 +00:00
/// Convert a value into a dynamic property.
pub fn new_dynamic_property(value: impl Into<Value<'gc>>) -> Self {
Property::Stored {
value: value.into(),
attributes: Attribute::empty(),
2020-02-15 01:30:19 +00:00
}
}
/// Convert a function into a method.
///
/// This applies READ_ONLY/DONT_DELETE to the property.
2021-06-08 22:21:00 +00:00
pub fn new_method(fn_obj: Object<'gc>, is_final: bool) -> Self {
let mut attributes = Attribute::DONT_DELETE | Attribute::READ_ONLY;
if is_final {
attributes |= Attribute::FINAL;
}
Property::Stored {
value: fn_obj.into(),
2021-06-08 22:21:00 +00:00
attributes,
}
}
/// Create a new, unconfigured virtual property item.
2021-06-08 22:21:00 +00:00
pub fn new_virtual(is_final: bool) -> Self {
let mut attributes = Attribute::DONT_DELETE | Attribute::READ_ONLY;
if is_final {
attributes |= Attribute::FINAL;
}
Property::Virtual {
get: None,
set: None,
2021-06-08 22:21:00 +00:00
attributes,
}
}
/// Create a new slot property.
2021-06-08 22:21:00 +00:00
pub fn new_slot(slot_id: u32, is_final: bool) -> Self {
let mut attributes = Attribute::DONT_DELETE;
if is_final {
attributes |= Attribute::FINAL;
}
Property::Slot {
slot_id,
attributes: Attribute::DONT_DELETE,
}
}
/// Install a getter into this property.
///
/// This function errors if attempting to install executables into a
/// non-virtual property.
2020-12-11 03:21:36 +00:00
///
/// The implementation must be a valid function, otherwise the VM will
/// panic when the property is accessed.
pub fn install_virtual_getter(&mut self, getter_impl: Object<'gc>) -> Result<(), Error> {
match self {
Property::Virtual { get, .. } => *get = Some(getter_impl),
Property::Stored { .. } => return Err("Not a virtual property".into()),
2020-02-19 19:17:33 +00:00
Property::Slot { .. } => return Err("Not a virtual property".into()),
};
Ok(())
}
/// Install a setter into this property.
///
/// This function errors if attempting to install executables into a
/// non-virtual property.
2020-12-11 03:21:36 +00:00
///
/// The implementation must be a valid function, otherwise the VM will
/// panic when the property is accessed.
pub fn install_virtual_setter(&mut self, setter_impl: Object<'gc>) -> Result<(), Error> {
match self {
Property::Virtual { set, .. } => *set = Some(setter_impl),
Property::Stored { .. } => return Err("Not a virtual property".into()),
2020-02-19 19:17:33 +00:00
Property::Slot { .. } => return Err("Not a virtual property".into()),
};
Ok(())
}
2020-02-15 01:30:19 +00:00
/// Get the value of a property slot.
///
/// This function yields `ReturnValue` because some properties may be
/// user-defined.
pub fn get(
&self,
this: Object<'gc>,
subclass_object: Option<ClassObject<'gc>>,
2020-02-15 01:30:19 +00:00
) -> Result<ReturnValue<'gc>, Error> {
match self {
Property::Virtual { get: Some(get), .. } => Ok(ReturnValue::defer_execution(
*get,
Some(this),
vec![],
subclass_object,
)),
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
2020-02-15 01:30:19 +00:00
Property::Stored { value, .. } => Ok(value.to_owned().into()),
// This doesn't need the non-local version of this property because
// by the time this has called the slot was already installed
Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()),
2020-02-15 01:30:19 +00:00
}
}
/// Set a property slot.
///
/// This function returns a `ReturnValue` which should be resolved. The
/// resulting `Value` is unimportant and should be discarded.
///
/// This function cannot set slot properties and will panic if one
/// is encountered.
2020-02-15 01:30:19 +00:00
pub fn set(
&mut self,
this: Object<'gc>,
subclass_object: Option<ClassObject<'gc>>,
2020-02-15 01:30:19 +00:00
new_value: impl Into<Value<'gc>>,
) -> Result<ReturnValue<'gc>, Error> {
2020-02-15 01:30:19 +00:00
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
return Ok(ReturnValue::defer_execution(
*function,
Some(this),
vec![new_value.into()],
subclass_object,
));
2020-02-15 01:30:19 +00:00
}
Ok(Value::Undefined.into())
2020-02-15 01:30:19 +00:00
}
Property::Stored {
value, attributes, ..
} => {
if !attributes.contains(Attribute::READ_ONLY) {
2020-02-22 21:21:28 +00:00
*value = new_value.into();
2020-02-15 01:30:19 +00:00
}
Ok(Value::Undefined.into())
2020-02-15 01:30:19 +00:00
}
Property::Slot { .. } => panic!("Cannot recursively set slots"),
2020-02-15 01:30:19 +00:00
}
}
2020-02-22 21:21:28 +00:00
/// Init a property slot.
///
/// The difference between `set` and `init` is that this function does not
/// respect `ReadOnly` and will allow initializing nominally `const`
/// properties, at least once. Virtual properties with no setter cannot be
/// initialized.
///
/// This function returns a `ReturnValue` which should be resolved. The
/// resulting `Value` is unimportant and should be discarded.
///
/// This function cannot initialize slot properties and will panic if one
/// is encountered.
2020-02-22 21:21:28 +00:00
pub fn init(
&mut self,
this: Object<'gc>,
subclass_object: Option<ClassObject<'gc>>,
2020-02-22 21:21:28 +00:00
new_value: impl Into<Value<'gc>>,
) -> Result<ReturnValue<'gc>, Error> {
2020-02-22 21:21:28 +00:00
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
return Ok(ReturnValue::defer_execution(
*function,
Some(this),
vec![new_value.into()],
subclass_object,
));
2020-02-22 21:21:28 +00:00
}
Ok(Value::Undefined.into())
2020-02-22 21:21:28 +00:00
}
Property::Stored { value, .. } => {
*value = new_value.into();
Ok(Value::Undefined.into())
2020-02-22 21:21:28 +00:00
}
Property::Slot { .. } => panic!("Cannot recursively init slots"),
}
}
/// Retrieve the slot ID of a property.
///
/// This function yields `None` if this property is not a slot.
pub fn slot_id(&self) -> Option<u32> {
match self {
Property::Slot { slot_id, .. } => Some(*slot_id),
_ => None,
2020-02-22 21:21:28 +00:00
}
}
2020-02-15 01:30:19 +00:00
pub fn can_delete(&self) -> bool {
let attributes = match self {
Property::Virtual { attributes, .. } => attributes,
Property::Stored { attributes, .. } => attributes,
Property::Slot { attributes, .. } => attributes,
};
!attributes.contains(Attribute::DONT_DELETE)
2020-02-15 01:30:19 +00:00
}
pub fn is_overwritable(&self) -> bool {
let attributes = match self {
2020-02-15 01:30:19 +00:00
Property::Virtual {
attributes, set, ..
} => {
if set.is_none() {
return false;
}
attributes
}
Property::Stored { attributes, .. } => attributes,
Property::Slot { attributes, .. } => attributes,
};
!attributes.contains(Attribute::READ_ONLY)
2020-02-15 01:30:19 +00:00
}
2021-06-08 22:21:00 +00:00
pub fn is_final(&self) -> bool {
let attributes = match self {
Property::Virtual { attributes, .. } => attributes,
Property::Stored { attributes, .. } => attributes,
Property::Slot { attributes, .. } => attributes,
};
attributes.contains(Attribute::FINAL)
}
2020-02-15 01:30:19 +00:00
}