Implement slots and related opcodes.

This commit is contained in:
David Wendt 2020-02-19 14:17:33 -05:00
parent 88957b2b3d
commit 1ab4091050
5 changed files with 125 additions and 1 deletions

View File

@ -393,6 +393,10 @@ impl<'gc> Avm2<'gc> {
Op::FindProperty { index } => self.op_find_property(context, index),
Op::FindPropStrict { index } => self.op_find_prop_strict(context, index),
Op::GetLex { index } => self.op_get_lex(context, index),
Op::GetSlot { index } => self.op_get_slot(index),
Op::SetSlot { index } => self.op_set_slot(context, index),
Op::GetGlobalSlot { index } => self.op_get_global_slot(index),
Op::SetGlobalSlot { index } => self.op_set_global_slot(context, index),
_ => self.unknown_op(op),
};
@ -641,4 +645,42 @@ impl<'gc> Avm2<'gc> {
Ok(())
}
fn op_get_slot(&mut self, index: u32) -> Result<(), Error> {
let object = self.pop().as_object()?;
let value = object.get_slot(index)?;
self.push(value);
Ok(())
}
fn op_set_slot(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
index: u32,
) -> Result<(), Error> {
let object = self.pop().as_object()?;
let value = self.pop();
object.set_slot(index, value, context.gc_context)
}
fn op_get_global_slot(&mut self, index: u32) -> Result<(), Error> {
let value = self.globals.get_slot(index)?;
self.push(value);
Ok(())
}
fn op_set_global_slot(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
index: u32,
) -> Result<(), Error> {
let value = self.pop();
self.globals.set_slot(index, value, context.gc_context)
}
}

View File

@ -339,6 +339,19 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
.set_property(name, value, avm, context, self.into())
}
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)
}
fn has_property(self, name: &QName) -> bool {
self.0.read().base.has_property(name)
}

View File

@ -44,6 +44,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error>;
/// Retrieve a slot by it's index.
fn get_slot(self, id: u32) -> Result<Value<'gc>, Error>;
/// Set a slot by it's index.
fn set_slot(
self,
id: u32,
value: Value<'gc>,
mc: MutationContext<'gc, '_>,
) -> Result<(), Error>;
/// Resolve a multiname into a single QName, if any of the namespaces
/// match.
fn resolve_multiname(self, multiname: &Multiname) -> Option<QName> {

View File

@ -2,7 +2,7 @@
use self::Attribute::*;
use crate::avm2::function::Executable;
use crate::avm2::object::Object;
use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue;
use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error};
@ -33,6 +33,10 @@ pub enum Property<'gc> {
value: Value<'gc>,
attributes: EnumSet<Attribute>,
},
Slot {
slot_id: u32,
attributes: EnumSet<Attribute>,
},
}
unsafe impl<'gc> Collect for Property<'gc> {
@ -43,6 +47,7 @@ unsafe impl<'gc> Collect for Property<'gc> {
set.trace(cc);
}
Property::Stored { value, .. } => value.trace(cc),
Property::Slot { .. } => {}
}
}
}
@ -83,6 +88,7 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { get, .. } => *get = Some(getter_impl),
Property::Stored { .. } => return Err("Not a virtual property".into()),
Property::Slot { .. } => return Err("Not a virtual property".into()),
};
Ok(())
@ -96,6 +102,7 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { set, .. } => *set = Some(setter_impl),
Property::Stored { .. } => return Err("Not a virtual property".into()),
Property::Slot { .. } => return Err("Not a virtual property".into()),
};
Ok(())
@ -115,6 +122,7 @@ impl<'gc> Property<'gc> {
Property::Virtual { get: Some(get), .. } => get.exec(avm, context, this, &[]),
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
Property::Stored { value, .. } => Ok(value.to_owned().into()),
Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()),
}
}
@ -149,6 +157,9 @@ impl<'gc> Property<'gc> {
Ok(true)
}
Property::Slot { slot_id, .. } => this
.set_slot(*slot_id, new_value.into(), context.gc_context)
.map(|v| true),
}
}
@ -157,6 +168,7 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { attributes, .. } => *attributes,
Property::Stored { attributes, .. } => *attributes,
Property::Slot { attributes, .. } => *attributes,
}
}
@ -169,6 +181,9 @@ impl<'gc> Property<'gc> {
Property::Stored {
ref mut attributes, ..
} => *attributes = new_attributes,
Property::Slot {
ref mut attributes, ..
} => *attributes = new_attributes,
}
}
@ -176,6 +191,7 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { attributes, .. } => !attributes.contains(DontDelete),
Property::Stored { attributes, .. } => !attributes.contains(DontDelete),
Property::Slot { attributes, .. } => !attributes.contains(DontDelete),
}
}
@ -183,6 +199,7 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { attributes, .. } => !attributes.contains(DontEnum),
Property::Stored { attributes, .. } => !attributes.contains(DontEnum),
Property::Slot { attributes, .. } => !attributes.contains(DontEnum),
}
}
@ -192,6 +209,7 @@ impl<'gc> Property<'gc> {
attributes, set, ..
} => !attributes.contains(ReadOnly) && !set.is_none(),
Property::Stored { attributes, .. } => !attributes.contains(ReadOnly),
Property::Slot { attributes, .. } => !attributes.contains(ReadOnly),
}
}
}

View File

@ -28,6 +28,9 @@ pub struct ScriptObjectData<'gc> {
/// Properties stored on this object.
values: HashMap<QName, Property<'gc>>,
/// Slots stored on this object.
slots: Vec<Value<'gc>>,
/// Implicit prototype (or declared base class) of this script object.
proto: Option<Object<'gc>>,
}
@ -54,6 +57,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
.set_property(name, value, avm, context, self.into())
}
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)
}
fn has_property(self, name: &QName) -> bool {
self.0.read().has_property(name)
}
@ -133,6 +149,7 @@ impl<'gc> ScriptObjectData<'gc> {
pub fn base_new(proto: Option<Object<'gc>>) -> Self {
ScriptObjectData {
values: HashMap::new(),
slots: Vec::new(),
proto,
}
}
@ -172,6 +189,29 @@ impl<'gc> ScriptObjectData<'gc> {
Ok(())
}
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>,
mc: MutationContext<'gc, '_>,
) -> 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())
}
}
pub fn has_property(&self, name: &QName) -> bool {
self.values.get(name).is_some()
}