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 return_value;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod script_object;
|
mod script_object;
|
||||||
|
mod slot;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
/// Boxed error alias.
|
/// Boxed error alias.
|
||||||
|
|
|
@ -479,4 +479,14 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
) {
|
) {
|
||||||
self.0.write(mc).base.install_slot(name, id, value)
|
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>,
|
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.
|
/// Install a trait from an ABC file on an object.
|
||||||
fn install_trait(
|
fn install_trait(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -250,7 +259,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
&type_entry.abc(),
|
&type_entry.abc(),
|
||||||
type_entry.instance().name.clone(),
|
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 {
|
AbcTraitKind::Function {
|
||||||
slot_id, 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 method = Avm2MethodEntry::from_method_index(abc, function.clone()).unwrap();
|
||||||
let function =
|
let function =
|
||||||
FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto);
|
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(())
|
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.
|
/// Convert a value into a dynamic property.
|
||||||
pub fn new_dynamic_property(value: impl Into<Value<'gc>>) -> Self {
|
pub fn new_dynamic_property(value: impl Into<Value<'gc>>) -> Self {
|
||||||
Property::Stored {
|
Property::Stored {
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::avm2::names::QName;
|
||||||
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
||||||
use crate::avm2::property::Property;
|
use crate::avm2::property::Property;
|
||||||
use crate::avm2::return_value::ReturnValue;
|
use crate::avm2::return_value::ReturnValue;
|
||||||
|
use crate::avm2::slot::Slot;
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::{Avm2, Error};
|
use crate::avm2::{Avm2, Error};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
|
@ -24,7 +25,7 @@ pub struct ScriptObjectData<'gc> {
|
||||||
values: HashMap<QName, Property<'gc>>,
|
values: HashMap<QName, Property<'gc>>,
|
||||||
|
|
||||||
/// Slots stored on this object.
|
/// Slots stored on this object.
|
||||||
slots: Vec<Value<'gc>>,
|
slots: Vec<Slot<'gc>>,
|
||||||
|
|
||||||
/// Implicit prototype (or declared base class) of this script object.
|
/// Implicit prototype (or declared base class) of this script object.
|
||||||
proto: Option<Object<'gc>>,
|
proto: Option<Object<'gc>>,
|
||||||
|
@ -127,6 +128,16 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
) {
|
) {
|
||||||
self.0.write(mc).install_slot(name, id, value)
|
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> {
|
impl<'gc> ScriptObject<'gc> {
|
||||||
|
@ -193,10 +204,12 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_slot(&self, id: u32) -> Result<Value<'gc>, Error> {
|
pub fn get_slot(&self, id: u32) -> Result<Value<'gc>, Error> {
|
||||||
|
//TODO: slot inheritance, I think?
|
||||||
self.slots
|
self.slots
|
||||||
.get(id as usize)
|
.get(id as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| format!("Slot index {} out of bounds!", id).into())
|
.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.
|
/// Set a slot by it's index.
|
||||||
|
@ -207,9 +220,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
_mc: MutationContext<'gc, '_>,
|
_mc: MutationContext<'gc, '_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if let Some(slot) = self.slots.get_mut(id as usize) {
|
if let Some(slot) = self.slots.get_mut(id as usize) {
|
||||||
*slot = value;
|
slot.set(value)
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Slot index {} out of bounds!", id).into())
|
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));
|
self.values.insert(name, Property::new_stored(value));
|
||||||
} else {
|
} else {
|
||||||
self.values.insert(name, Property::new_slot(id));
|
self.values.insert(name, Property::new_slot(id));
|
||||||
if self.slots.len() < id as usize {
|
if self.slots.len() < id as usize + 1 {
|
||||||
self.slots.resize(id as usize, Value::Undefined);
|
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