Implement constant slots and traits.
Class and Function traits now generate const slots, too.
This commit is contained in:
parent
412c3d8489
commit
fd275bdcf3
|
@ -36,6 +36,7 @@ mod property;
|
|||
mod return_value;
|
||||
mod scope;
|
||||
mod script_object;
|
||||
mod slot;
|
||||
mod value;
|
||||
|
||||
/// Boxed error alias.
|
||||
|
|
|
@ -479,4 +479,14 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
) {
|
||||
self.0.write(mc).base.install_slot(name, id, value)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,15 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
value: Value<'gc>,
|
||||
);
|
||||
|
||||
/// Install a const on an object property.
|
||||
fn install_const(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
id: u32,
|
||||
value: Value<'gc>,
|
||||
);
|
||||
|
||||
/// Install a trait from an ABC file on an object.
|
||||
fn install_trait(
|
||||
&mut self,
|
||||
|
@ -250,7 +259,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&type_entry.abc(),
|
||||
type_entry.instance().name.clone(),
|
||||
)?;
|
||||
self.install_slot(context.gc_context, class_name, *slot_id, class.into());
|
||||
self.install_const(context.gc_context, class_name, *slot_id, class.into());
|
||||
}
|
||||
AbcTraitKind::Function {
|
||||
slot_id, function, ..
|
||||
|
@ -258,9 +267,16 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
let method = Avm2MethodEntry::from_method_index(abc, function.clone()).unwrap();
|
||||
let function =
|
||||
FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto);
|
||||
self.install_slot(context.gc_context, trait_name, *slot_id, function.into());
|
||||
self.install_const(context.gc_context, trait_name, *slot_id, function.into());
|
||||
}
|
||||
AbcTraitKind::Const { slot_id, value, .. } => {
|
||||
let value = if let Some(value) = value {
|
||||
abc_default_value(&abc, value)?
|
||||
} else {
|
||||
Value::Undefined
|
||||
};
|
||||
self.install_const(context.gc_context, trait_name, *slot_id, value);
|
||||
}
|
||||
_ => return Err("".into()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -61,6 +61,14 @@ impl<'gc> Property<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a new stored property.
|
||||
pub fn new_const(value: impl Into<Value<'gc>>) -> Self {
|
||||
Property::Stored {
|
||||
value: value.into(),
|
||||
attributes: Attribute::ReadOnly | Attribute::DontDelete,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a value into a dynamic property.
|
||||
pub fn new_dynamic_property(value: impl Into<Value<'gc>>) -> Self {
|
||||
Property::Stored {
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::avm2::names::QName;
|
|||
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
||||
use crate::avm2::property::Property;
|
||||
use crate::avm2::return_value::ReturnValue;
|
||||
use crate::avm2::slot::Slot;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::context::UpdateContext;
|
||||
|
@ -24,7 +25,7 @@ pub struct ScriptObjectData<'gc> {
|
|||
values: HashMap<QName, Property<'gc>>,
|
||||
|
||||
/// Slots stored on this object.
|
||||
slots: Vec<Value<'gc>>,
|
||||
slots: Vec<Slot<'gc>>,
|
||||
|
||||
/// Implicit prototype (or declared base class) of this script object.
|
||||
proto: Option<Object<'gc>>,
|
||||
|
@ -127,6 +128,16 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
) {
|
||||
self.0.write(mc).install_slot(name, id, value)
|
||||
}
|
||||
|
||||
fn install_const(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
id: u32,
|
||||
value: Value<'gc>,
|
||||
) {
|
||||
self.0.write(mc).install_const(name, id, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> ScriptObject<'gc> {
|
||||
|
@ -193,10 +204,12 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
|
||||
pub fn get_slot(&self, id: u32) -> Result<Value<'gc>, Error> {
|
||||
//TODO: slot inheritance, I think?
|
||||
self.slots
|
||||
.get(id as usize)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("Slot index {} out of bounds!", id).into())
|
||||
.map(|slot| slot.get().unwrap_or(Value::Undefined))
|
||||
}
|
||||
|
||||
/// Set a slot by it's index.
|
||||
|
@ -207,9 +220,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
_mc: MutationContext<'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(slot) = self.slots.get_mut(id as usize) {
|
||||
*slot = value;
|
||||
|
||||
Ok(())
|
||||
slot.set(value)
|
||||
} else {
|
||||
Err(format!("Slot index {} out of bounds!", id).into())
|
||||
}
|
||||
|
@ -281,8 +292,32 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.values.insert(name, Property::new_stored(value));
|
||||
} else {
|
||||
self.values.insert(name, Property::new_slot(id));
|
||||
if self.slots.len() < id as usize {
|
||||
self.slots.resize(id as usize, Value::Undefined);
|
||||
if self.slots.len() < id as usize + 1 {
|
||||
self.slots.resize_with(id as usize + 1, Default::default);
|
||||
}
|
||||
|
||||
if let Some(slot) = self.slots.get_mut(id as usize) {
|
||||
*slot = Slot::new(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Install a const onto the object.
|
||||
///
|
||||
/// Slot number zero indicates a slot ID that is unknown and should be
|
||||
/// allocated by the VM - as far as I know, there is no way to discover
|
||||
/// slot IDs, so we don't allocate a slot for them at all.
|
||||
pub fn install_const(&mut self, name: QName, id: u32, value: Value<'gc>) {
|
||||
if id == 0 {
|
||||
self.values.insert(name, Property::new_const(value));
|
||||
} else {
|
||||
self.values.insert(name, Property::new_slot(id));
|
||||
if self.slots.len() < id as usize + 1 {
|
||||
self.slots.resize_with(id as usize + 1, Default::default);
|
||||
}
|
||||
|
||||
if let Some(slot) = self.slots.get_mut(id as usize) {
|
||||
*slot = Slot::new_const(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
//! Slot contents type
|
||||
|
||||
use crate::avm2::property::Attribute;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{Collect, CollectionContext};
|
||||
|
||||
/// Represents a single slot on an object.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Slot<'gc> {
|
||||
/// An unoccupied slot.
|
||||
///
|
||||
/// Attempts to read an unoccupied slot proceed up the prototype chain.
|
||||
/// Writing an unoccupied slot will always fail.
|
||||
Unoccupied,
|
||||
|
||||
/// An occupied slot.
|
||||
Occupied {
|
||||
value: Value<'gc>,
|
||||
attributes: EnumSet<Attribute>,
|
||||
},
|
||||
}
|
||||
|
||||
unsafe impl<'gc> Collect for Slot<'gc> {
|
||||
fn trace(&self, cc: CollectionContext) {
|
||||
match self {
|
||||
Self::Unoccupied => {}
|
||||
Self::Occupied { value, .. } => value.trace(cc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Default for Slot<'gc> {
|
||||
fn default() -> Self {
|
||||
Self::Unoccupied
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Slot<'gc> {
|
||||
/// Create a normal slot with a given value.
|
||||
pub fn new(value: impl Into<Value<'gc>>) -> Self {
|
||||
Self::Occupied {
|
||||
value: value.into(),
|
||||
attributes: EnumSet::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `const` slot that cannot be overwritten.
|
||||
pub fn new_const(value: impl Into<Value<'gc>>) -> Self {
|
||||
Self::Occupied {
|
||||
value: value.into(),
|
||||
attributes: EnumSet::from(Attribute::ReadOnly),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the value of this slot.
|
||||
pub fn get(&self) -> Option<Value<'gc>> {
|
||||
match self {
|
||||
Self::Unoccupied => None,
|
||||
Self::Occupied { value, .. } => Some(value.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the value of this slot.
|
||||
pub fn set(&mut self, new_value: impl Into<Value<'gc>>) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Unoccupied => Err("Cannot overwrite unoccupied slot".into()),
|
||||
Self::Occupied { value, attributes } => {
|
||||
if attributes.contains(Attribute::ReadOnly) {
|
||||
return Err("Cannot overwrite const slot".into());
|
||||
}
|
||||
|
||||
//TODO: Type assert
|
||||
|
||||
*value = new_value.into();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue