Impl `callmethod`, `callproperty`, `callproplex`, `callpropvoid`, and `callstatic`.
Also, implement a method table that method traits can optionally add themselves to. Also also, add the ability to invoke a method without a `this` object. This required a non-trivial refactoring of the activation machinery, and changes to the signature of `NativeFunction`, and all native AVM2 functions.
This commit is contained in:
parent
68cf9e8869
commit
a0ab978bed
147
core/src/avm2.rs
147
core/src/avm2.rs
|
@ -426,6 +426,17 @@ impl<'gc> Avm2<'gc> {
|
|||
Op::GetLocal { index } => self.op_get_local(index),
|
||||
Op::SetLocal { index } => self.op_set_local(context, index),
|
||||
Op::Call { num_args } => self.op_call(context, num_args),
|
||||
Op::CallMethod { index, num_args } => self.op_call_method(context, index, num_args),
|
||||
Op::CallProperty { index, num_args } => {
|
||||
self.op_call_property(context, index, num_args)
|
||||
}
|
||||
Op::CallPropLex { index, num_args } => {
|
||||
self.op_call_prop_lex(context, index, num_args)
|
||||
}
|
||||
Op::CallPropVoid { index, num_args } => {
|
||||
self.op_call_prop_void(context, index, num_args)
|
||||
}
|
||||
Op::CallStatic { index, num_args } => self.op_call_static(context, index, num_args),
|
||||
Op::ReturnValue => self.op_return_value(context),
|
||||
Op::ReturnVoid => self.op_return_void(context),
|
||||
Op::GetProperty { index } => self.op_get_property(context, index),
|
||||
|
@ -562,18 +573,146 @@ impl<'gc> Avm2<'gc> {
|
|||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let function = self.pop().as_object()?;
|
||||
let receiver = self.pop().as_object()?;
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let receiver = self.pop().as_object().ok();
|
||||
let function = self.pop().as_object()?;
|
||||
|
||||
function.call(receiver, &args, self, context)?.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_call_method(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMethod>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let receiver = self.pop().as_object()?;
|
||||
let function: Result<Object<'gc>, Error> = receiver
|
||||
.get_method(index.0)
|
||||
.ok_or_else(|| format!("Object method {} does not exist", index.0).into());
|
||||
|
||||
function?
|
||||
.call(Some(receiver), &args, self, context)?
|
||||
.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_call_property(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMultiname>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let multiname = self.pool_multiname(index)?;
|
||||
let receiver = self.pop().as_object()?;
|
||||
let name: Result<QName, Error> = receiver
|
||||
.resolve_multiname(&multiname)
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let function = receiver
|
||||
.get_property(&name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
function
|
||||
.call(Some(receiver), &args, self, context)?
|
||||
.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_call_prop_lex(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMultiname>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let multiname = self.pool_multiname(index)?;
|
||||
let receiver = self.pop().as_object()?;
|
||||
let name: Result<QName, Error> = receiver
|
||||
.resolve_multiname(&multiname)
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let function = receiver
|
||||
.get_property(&name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
function.call(None, &args, self, context)?.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_call_prop_void(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMultiname>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let multiname = self.pool_multiname(index)?;
|
||||
let receiver = self.pop().as_object()?;
|
||||
let name: Result<QName, Error> = receiver
|
||||
.resolve_multiname(&multiname)
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let function = receiver
|
||||
.get_property(&name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
function
|
||||
.call(Some(receiver), &args, self, context)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_call_static(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMethod>,
|
||||
arg_count: u32,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..arg_count {
|
||||
args.push(self.pop());
|
||||
}
|
||||
let receiver = self.pop().as_object()?;
|
||||
let method = self.table_method(index)?;
|
||||
let scope = self.current_stack_frame().unwrap().read().scope(); //TODO: Is this correct?
|
||||
let function = FunctionObject::from_abc_method(
|
||||
context.gc_context,
|
||||
method,
|
||||
scope,
|
||||
self.system_prototypes.function,
|
||||
);
|
||||
|
||||
function
|
||||
.call(Some(receiver), &args, self, context)?
|
||||
.push(self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_return_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||
let return_value = self.pop();
|
||||
|
||||
|
@ -861,7 +1000,7 @@ impl<'gc> Avm2<'gc> {
|
|||
.as_object()?;
|
||||
|
||||
let object = proto.construct(self, context, &args)?;
|
||||
ctor.call(object, &args, self, context)?
|
||||
ctor.call(Some(object), &args, self, context)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -899,7 +1038,7 @@ impl<'gc> Avm2<'gc> {
|
|||
.as_object()?;
|
||||
|
||||
let object = proto.construct(self, context, &args)?;
|
||||
ctor.call(object, &args, self, context)?
|
||||
ctor.call(Some(object), &args, self, context)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -95,7 +95,7 @@ pub struct Activation<'gc> {
|
|||
pc: usize,
|
||||
|
||||
/// The immutable value of `this`.
|
||||
this: Object<'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
|
||||
/// The arguments this function was called by.
|
||||
arguments: Option<Object<'gc>>,
|
||||
|
@ -151,7 +151,7 @@ impl<'gc> Activation<'gc> {
|
|||
Ok(Self {
|
||||
method,
|
||||
pc: 0,
|
||||
this: global,
|
||||
this: Some(global),
|
||||
arguments: None,
|
||||
is_executing: false,
|
||||
local_registers,
|
||||
|
@ -164,7 +164,7 @@ impl<'gc> Activation<'gc> {
|
|||
pub fn from_action(
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
action: &Avm2Function<'gc>,
|
||||
this: Object<'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
) -> Result<Self, Error> {
|
||||
let method = action.method.clone();
|
||||
|
@ -178,7 +178,7 @@ impl<'gc> Activation<'gc> {
|
|||
|
||||
{
|
||||
let mut write = local_registers.write(context.gc_context);
|
||||
*write.get_mut(0).unwrap() = this.into();
|
||||
*write.get_mut(0).unwrap() = this.map(|t| t.into()).unwrap_or(Value::Null);
|
||||
|
||||
for i in 0..num_declared_arguments {
|
||||
*write.get_mut(1 + i).unwrap() = arguments
|
||||
|
|
|
@ -34,7 +34,7 @@ use swf::avm2::types::{
|
|||
pub type NativeFunction<'gc> = fn(
|
||||
&mut Avm2<'gc>,
|
||||
&mut UpdateContext<'_, 'gc, '_>,
|
||||
Object<'gc>,
|
||||
Option<Object<'gc>>,
|
||||
&[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error>;
|
||||
|
||||
|
@ -131,7 +131,7 @@ impl<'gc> Executable<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
reciever: Object<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -316,11 +316,11 @@ impl<'gc> FunctionObject<'gc> {
|
|||
constr.install_trait(avm, context, class.abc(), trait_entry, scope, fn_proto)?;
|
||||
}
|
||||
|
||||
constr.install_method(
|
||||
constr.install_dynamic_property(
|
||||
context.gc_context,
|
||||
QName::new(Namespace::public_namespace(), "prototype"),
|
||||
class_proto,
|
||||
);
|
||||
class_proto.into(),
|
||||
)?;
|
||||
|
||||
Ok(constr)
|
||||
}
|
||||
|
@ -453,6 +453,10 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.0.write(mc).base.init_slot(id, value, mc)
|
||||
}
|
||||
|
||||
fn get_method(self, id: u32) -> Option<Object<'gc>> {
|
||||
self.0.read().base.get_method(id)
|
||||
}
|
||||
|
||||
fn has_property(self, name: &QName) -> bool {
|
||||
self.0.read().base.has_property(name)
|
||||
}
|
||||
|
@ -465,9 +469,13 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.0.as_ptr() as *const ObjectPtr
|
||||
}
|
||||
|
||||
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||
self.0.read().exec.clone()
|
||||
}
|
||||
|
||||
fn call(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
reciever: Option<Object<'gc>>,
|
||||
arguments: &[Value<'gc>],
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -499,26 +507,43 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
.into())
|
||||
}
|
||||
|
||||
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
||||
self.0.write(mc).base.install_method(name, function)
|
||||
fn install_method(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) {
|
||||
self.0
|
||||
.write(mc)
|
||||
.base
|
||||
.install_method(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_getter(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.write(mc).base.install_getter(name, function)
|
||||
self.0
|
||||
.write(mc)
|
||||
.base
|
||||
.install_getter(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_setter(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.write(mc).base.install_setter(name, function)
|
||||
self.0
|
||||
.write(mc)
|
||||
.base
|
||||
.install_setter(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_dynamic_property(
|
||||
|
|
|
@ -17,7 +17,7 @@ mod object;
|
|||
fn trace<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(s) = args.get(0) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -14,7 +14,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -24,7 +24,7 @@ pub fn constructor<'gc>(
|
|||
fn to_string<'gc>(
|
||||
_: &mut Avm2<'gc>,
|
||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_: Object<'gc>,
|
||||
_: Option<Object<'gc>>,
|
||||
_: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(ReturnValue::Immediate("[type Function]".into()))
|
||||
|
@ -43,6 +43,7 @@ pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc
|
|||
function_proto.install_method(
|
||||
gc_context,
|
||||
QName::new(Namespace::public_namespace(), "toString"),
|
||||
0,
|
||||
FunctionObject::from_builtin(gc_context, to_string, function_proto),
|
||||
);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use gc_arena::MutationContext;
|
|||
pub fn constructor<'gc>(
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_this: Object<'gc>,
|
||||
_this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
Ok(Value::Undefined.into())
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
//! AVM2 objects.
|
||||
|
||||
use crate::avm2::function::{
|
||||
Avm2ClassEntry, Avm2Function, Avm2MethodEntry, Executable, FunctionObject,
|
||||
};
|
||||
use crate::avm2::function::{Avm2ClassEntry, Avm2MethodEntry, Executable, FunctionObject};
|
||||
use crate::avm2::names::{Multiname, QName};
|
||||
use crate::avm2::return_value::ReturnValue;
|
||||
use crate::avm2::scope::Scope;
|
||||
|
@ -72,6 +70,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Retrieve a method by it's index.
|
||||
fn get_method(self, id: u32) -> Option<Object<'gc>>;
|
||||
|
||||
/// Resolve a multiname into a single QName, if any of the namespaces
|
||||
/// match.
|
||||
fn resolve_multiname(self, multiname: &Multiname) -> Option<QName> {
|
||||
|
@ -113,14 +114,21 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
fn proto(&self) -> Option<Object<'gc>>;
|
||||
|
||||
/// Install a method (or any other non-slot value) on an object.
|
||||
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>);
|
||||
fn install_method(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
);
|
||||
|
||||
/// Install a getter method on an object property.
|
||||
fn install_getter(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Install a setter method on an object property.
|
||||
|
@ -128,7 +136,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Install a dynamic or built-in value property on an object.
|
||||
|
@ -183,21 +192,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
};
|
||||
self.install_slot(context.gc_context, trait_name, *slot_id, value);
|
||||
}
|
||||
AbcTraitKind::Method { method, .. } => {
|
||||
AbcTraitKind::Method {
|
||||
disp_id, method, ..
|
||||
} => {
|
||||
let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap();
|
||||
let function =
|
||||
FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto);
|
||||
self.install_method(context.gc_context, trait_name, function);
|
||||
self.install_method(context.gc_context, trait_name, *disp_id, function);
|
||||
}
|
||||
AbcTraitKind::Getter { method, .. } => {
|
||||
AbcTraitKind::Getter {
|
||||
disp_id, method, ..
|
||||
} => {
|
||||
let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap();
|
||||
let exec = Avm2Function::from_method(method, scope).into();
|
||||
self.install_getter(context.gc_context, trait_name, exec)?;
|
||||
let function =
|
||||
FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto);
|
||||
self.install_getter(context.gc_context, trait_name, *disp_id, function)?;
|
||||
}
|
||||
AbcTraitKind::Setter { method, .. } => {
|
||||
AbcTraitKind::Setter {
|
||||
disp_id, method, ..
|
||||
} => {
|
||||
let method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap();
|
||||
let exec = Avm2Function::from_method(method, scope).into();
|
||||
self.install_setter(context.gc_context, trait_name, exec)?;
|
||||
let function =
|
||||
FunctionObject::from_abc_method(context.gc_context, method, scope, fn_proto);
|
||||
self.install_setter(context.gc_context, trait_name, *disp_id, function)?;
|
||||
}
|
||||
AbcTraitKind::Class { slot_id, class } => {
|
||||
let type_entry = Avm2ClassEntry::from_class_index(abc, class.clone()).unwrap();
|
||||
|
@ -251,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Call the object.
|
||||
fn call(
|
||||
self,
|
||||
_reciever: Object<'gc>,
|
||||
_reciever: Option<Object<'gc>>,
|
||||
_arguments: &[Value<'gc>],
|
||||
_avm: &mut Avm2<'gc>,
|
||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -282,6 +299,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
|
||||
/// Get a raw pointer value for this object.
|
||||
fn as_ptr(&self) -> *const ObjectPtr;
|
||||
|
||||
/// Get this object's `Executable`, if it has one.
|
||||
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ObjectPtr {}
|
||||
|
|
|
@ -142,7 +142,7 @@ impl<'gc> Property<'gc> {
|
|||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
Property::Virtual { get: Some(get), .. } => get.exec(avm, context, this, &[]),
|
||||
Property::Virtual { get: Some(get), .. } => get.exec(avm, context, Some(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()),
|
||||
|
@ -168,7 +168,8 @@ impl<'gc> Property<'gc> {
|
|||
match self {
|
||||
Property::Virtual { set, .. } => {
|
||||
if let Some(function) = set {
|
||||
let return_value = function.exec(avm, context, this, &[new_value.into()])?;
|
||||
let return_value =
|
||||
function.exec(avm, context, Some(this), &[new_value.into()])?;
|
||||
Ok(return_value.is_immediate())
|
||||
} else {
|
||||
Ok(true)
|
||||
|
@ -211,7 +212,8 @@ impl<'gc> Property<'gc> {
|
|||
match self {
|
||||
Property::Virtual { set, .. } => {
|
||||
if let Some(function) = set {
|
||||
let return_value = function.exec(avm, context, this, &[new_value.into()])?;
|
||||
let return_value =
|
||||
function.exec(avm, context, Some(this), &[new_value.into()])?;
|
||||
Ok(return_value.is_immediate())
|
||||
} else {
|
||||
Ok(true)
|
||||
|
|
|
@ -27,6 +27,9 @@ pub struct ScriptObjectData<'gc> {
|
|||
/// Slots stored on this object.
|
||||
slots: Vec<Slot<'gc>>,
|
||||
|
||||
/// Methods stored on this object.
|
||||
methods: Vec<Option<Object<'gc>>>,
|
||||
|
||||
/// Implicit prototype (or declared base class) of this script object.
|
||||
proto: Option<Object<'gc>>,
|
||||
}
|
||||
|
@ -91,6 +94,10 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
self.0.write(mc).init_slot(id, value, mc)
|
||||
}
|
||||
|
||||
fn get_method(self, id: u32) -> Option<Object<'gc>> {
|
||||
self.0.read().get_method(id)
|
||||
}
|
||||
|
||||
fn has_property(self, name: &QName) -> bool {
|
||||
self.0.read().has_property(name)
|
||||
}
|
||||
|
@ -113,26 +120,34 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
Ok(ScriptObject::object(context.gc_context, this))
|
||||
}
|
||||
|
||||
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
||||
self.0.write(mc).install_method(name, function)
|
||||
fn install_method(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) {
|
||||
self.0.write(mc).install_method(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_getter(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.write(mc).install_getter(name, function)
|
||||
self.0.write(mc).install_getter(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_setter(
|
||||
&mut self,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
name: QName,
|
||||
function: Executable<'gc>,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.write(mc).install_setter(name, function)
|
||||
self.0.write(mc).install_setter(name, disp_id, function)
|
||||
}
|
||||
|
||||
fn install_dynamic_property(
|
||||
|
@ -189,6 +204,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
ScriptObjectData {
|
||||
values: HashMap::new(),
|
||||
slots: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
proto,
|
||||
}
|
||||
}
|
||||
|
@ -306,6 +322,11 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Retrieve a method from the method table.
|
||||
pub fn get_method(&self, id: u32) -> Option<Object<'gc>> {
|
||||
self.methods.get(id as usize).and_then(|v| *v)
|
||||
}
|
||||
|
||||
pub fn has_property(&self, name: &QName) -> bool {
|
||||
self.values.get(name).is_some()
|
||||
}
|
||||
|
@ -315,7 +336,16 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
|
||||
/// Install a method into the object.
|
||||
pub fn install_method(&mut self, name: QName, function: Object<'gc>) {
|
||||
pub fn install_method(&mut self, name: QName, disp_id: u32, function: Object<'gc>) {
|
||||
if disp_id > 0 {
|
||||
if self.methods.len() <= disp_id as usize {
|
||||
self.methods
|
||||
.resize_with(disp_id as usize + 1, Default::default);
|
||||
}
|
||||
|
||||
*self.methods.get_mut(disp_id as usize).unwrap() = Some(function);
|
||||
}
|
||||
|
||||
self.values.insert(name, Property::new_method(function));
|
||||
}
|
||||
|
||||
|
@ -324,7 +354,26 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
/// This is a little more complicated than methods, since virtual property
|
||||
/// slots can be installed in two parts. Thus, we need to support
|
||||
/// installing them in either order.
|
||||
pub fn install_getter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> {
|
||||
pub fn install_getter(
|
||||
&mut self,
|
||||
name: QName,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
let executable: Result<Executable<'gc>, Error> = function
|
||||
.as_executable()
|
||||
.ok_or_else(|| "Attempted to install getter without a valid method".into());
|
||||
let executable = executable?;
|
||||
|
||||
if disp_id > 0 {
|
||||
if self.methods.len() <= disp_id as usize {
|
||||
self.methods
|
||||
.resize_with(disp_id as usize + 1, Default::default);
|
||||
}
|
||||
|
||||
*self.methods.get_mut(disp_id as usize).unwrap() = Some(function);
|
||||
}
|
||||
|
||||
if !self.values.contains_key(&name) {
|
||||
self.values.insert(name.clone(), Property::new_virtual());
|
||||
}
|
||||
|
@ -332,7 +381,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.values
|
||||
.get_mut(&name)
|
||||
.unwrap()
|
||||
.install_virtual_getter(function)
|
||||
.install_virtual_getter(executable)
|
||||
}
|
||||
|
||||
/// Install a setter into the object.
|
||||
|
@ -340,7 +389,26 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
/// This is a little more complicated than methods, since virtual property
|
||||
/// slots can be installed in two parts. Thus, we need to support
|
||||
/// installing them in either order.
|
||||
pub fn install_setter(&mut self, name: QName, function: Executable<'gc>) -> Result<(), Error> {
|
||||
pub fn install_setter(
|
||||
&mut self,
|
||||
name: QName,
|
||||
disp_id: u32,
|
||||
function: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
let executable: Result<Executable<'gc>, Error> = function
|
||||
.as_executable()
|
||||
.ok_or_else(|| "Attempted to install setter without a valid method".into());
|
||||
let executable = executable?;
|
||||
|
||||
if disp_id > 0 {
|
||||
if self.methods.len() <= disp_id as usize {
|
||||
self.methods
|
||||
.resize_with(disp_id as usize + 1, Default::default);
|
||||
}
|
||||
|
||||
*self.methods.get_mut(disp_id as usize).unwrap() = Some(function);
|
||||
}
|
||||
|
||||
if !self.values.contains_key(&name) {
|
||||
self.values.insert(name.clone(), Property::new_virtual());
|
||||
}
|
||||
|
@ -348,7 +416,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.values
|
||||
.get_mut(&name)
|
||||
.unwrap()
|
||||
.install_virtual_setter(function)
|
||||
.install_virtual_setter(executable)
|
||||
}
|
||||
|
||||
pub fn install_dynamic_property(
|
||||
|
|
Loading…
Reference in New Issue