Implement slots and related opcodes.
This commit is contained in:
parent
88957b2b3d
commit
1ab4091050
|
@ -393,6 +393,10 @@ impl<'gc> Avm2<'gc> {
|
||||||
Op::FindProperty { index } => self.op_find_property(context, index),
|
Op::FindProperty { index } => self.op_find_property(context, index),
|
||||||
Op::FindPropStrict { index } => self.op_find_prop_strict(context, index),
|
Op::FindPropStrict { index } => self.op_find_prop_strict(context, index),
|
||||||
Op::GetLex { index } => self.op_get_lex(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),
|
_ => self.unknown_op(op),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -641,4 +645,42 @@ impl<'gc> Avm2<'gc> {
|
||||||
|
|
||||||
Ok(())
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,19 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
.set_property(name, value, avm, context, self.into())
|
.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 {
|
fn has_property(self, name: &QName) -> bool {
|
||||||
self.0.read().base.has_property(name)
|
self.0.read().base.has_property(name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error>;
|
) -> 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
|
/// Resolve a multiname into a single QName, if any of the namespaces
|
||||||
/// match.
|
/// match.
|
||||||
fn resolve_multiname(self, multiname: &Multiname) -> Option<QName> {
|
fn resolve_multiname(self, multiname: &Multiname) -> Option<QName> {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use self::Attribute::*;
|
use self::Attribute::*;
|
||||||
use crate::avm2::function::Executable;
|
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::return_value::ReturnValue;
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::{Avm2, Error};
|
use crate::avm2::{Avm2, Error};
|
||||||
|
@ -33,6 +33,10 @@ pub enum Property<'gc> {
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
attributes: EnumSet<Attribute>,
|
attributes: EnumSet<Attribute>,
|
||||||
},
|
},
|
||||||
|
Slot {
|
||||||
|
slot_id: u32,
|
||||||
|
attributes: EnumSet<Attribute>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'gc> Collect for Property<'gc> {
|
unsafe impl<'gc> Collect for Property<'gc> {
|
||||||
|
@ -43,6 +47,7 @@ unsafe impl<'gc> Collect for Property<'gc> {
|
||||||
set.trace(cc);
|
set.trace(cc);
|
||||||
}
|
}
|
||||||
Property::Stored { value, .. } => value.trace(cc),
|
Property::Stored { value, .. } => value.trace(cc),
|
||||||
|
Property::Slot { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +88,7 @@ impl<'gc> Property<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { get, .. } => *get = Some(getter_impl),
|
Property::Virtual { get, .. } => *get = Some(getter_impl),
|
||||||
Property::Stored { .. } => return Err("Not a virtual property".into()),
|
Property::Stored { .. } => return Err("Not a virtual property".into()),
|
||||||
|
Property::Slot { .. } => return Err("Not a virtual property".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -96,6 +102,7 @@ impl<'gc> Property<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { set, .. } => *set = Some(setter_impl),
|
Property::Virtual { set, .. } => *set = Some(setter_impl),
|
||||||
Property::Stored { .. } => return Err("Not a virtual property".into()),
|
Property::Stored { .. } => return Err("Not a virtual property".into()),
|
||||||
|
Property::Slot { .. } => return Err("Not a virtual property".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -115,6 +122,7 @@ impl<'gc> Property<'gc> {
|
||||||
Property::Virtual { get: Some(get), .. } => get.exec(avm, context, this, &[]),
|
Property::Virtual { get: Some(get), .. } => get.exec(avm, context, this, &[]),
|
||||||
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
|
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
|
||||||
Property::Stored { value, .. } => Ok(value.to_owned().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)
|
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 {
|
match self {
|
||||||
Property::Virtual { attributes, .. } => *attributes,
|
Property::Virtual { attributes, .. } => *attributes,
|
||||||
Property::Stored { attributes, .. } => *attributes,
|
Property::Stored { attributes, .. } => *attributes,
|
||||||
|
Property::Slot { attributes, .. } => *attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +181,9 @@ impl<'gc> Property<'gc> {
|
||||||
Property::Stored {
|
Property::Stored {
|
||||||
ref mut attributes, ..
|
ref mut attributes, ..
|
||||||
} => *attributes = new_attributes,
|
} => *attributes = new_attributes,
|
||||||
|
Property::Slot {
|
||||||
|
ref mut attributes, ..
|
||||||
|
} => *attributes = new_attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +191,7 @@ impl<'gc> Property<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { attributes, .. } => !attributes.contains(DontDelete),
|
Property::Virtual { attributes, .. } => !attributes.contains(DontDelete),
|
||||||
Property::Stored { 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 {
|
match self {
|
||||||
Property::Virtual { attributes, .. } => !attributes.contains(DontEnum),
|
Property::Virtual { attributes, .. } => !attributes.contains(DontEnum),
|
||||||
Property::Stored { 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, set, ..
|
||||||
} => !attributes.contains(ReadOnly) && !set.is_none(),
|
} => !attributes.contains(ReadOnly) && !set.is_none(),
|
||||||
Property::Stored { attributes, .. } => !attributes.contains(ReadOnly),
|
Property::Stored { attributes, .. } => !attributes.contains(ReadOnly),
|
||||||
|
Property::Slot { attributes, .. } => !attributes.contains(ReadOnly),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ pub struct ScriptObjectData<'gc> {
|
||||||
/// Properties stored on this object.
|
/// Properties stored on this object.
|
||||||
values: HashMap<QName, Property<'gc>>,
|
values: HashMap<QName, Property<'gc>>,
|
||||||
|
|
||||||
|
/// Slots stored on this object.
|
||||||
|
slots: Vec<Value<'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>>,
|
||||||
}
|
}
|
||||||
|
@ -54,6 +57,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
.set_property(name, value, avm, context, self.into())
|
.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 {
|
fn has_property(self, name: &QName) -> bool {
|
||||||
self.0.read().has_property(name)
|
self.0.read().has_property(name)
|
||||||
}
|
}
|
||||||
|
@ -133,6 +149,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
pub fn base_new(proto: Option<Object<'gc>>) -> Self {
|
pub fn base_new(proto: Option<Object<'gc>>) -> Self {
|
||||||
ScriptObjectData {
|
ScriptObjectData {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
|
slots: Vec::new(),
|
||||||
proto,
|
proto,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,6 +189,29 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
Ok(())
|
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 {
|
pub fn has_property(&self, name: &QName) -> bool {
|
||||||
self.values.get(name).is_some()
|
self.values.get(name).is_some()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue