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::GetLocal { index } => self.op_get_local(index),
|
||||||
Op::SetLocal { index } => self.op_set_local(context, index),
|
Op::SetLocal { index } => self.op_set_local(context, index),
|
||||||
Op::Call { num_args } => self.op_call(context, num_args),
|
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::ReturnValue => self.op_return_value(context),
|
||||||
Op::ReturnVoid => self.op_return_void(context),
|
Op::ReturnVoid => self.op_return_void(context),
|
||||||
Op::GetProperty { index } => self.op_get_property(context, index),
|
Op::GetProperty { index } => self.op_get_property(context, index),
|
||||||
|
@ -562,18 +573,146 @@ impl<'gc> Avm2<'gc> {
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
arg_count: u32,
|
arg_count: u32,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let function = self.pop().as_object()?;
|
|
||||||
let receiver = self.pop().as_object()?;
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
for _ in 0..arg_count {
|
for _ in 0..arg_count {
|
||||||
args.push(self.pop());
|
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);
|
function.call(receiver, &args, self, context)?.push(self);
|
||||||
|
|
||||||
Ok(())
|
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> {
|
fn op_return_value(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||||
let return_value = self.pop();
|
let return_value = self.pop();
|
||||||
|
|
||||||
|
@ -861,7 +1000,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
let object = proto.construct(self, context, &args)?;
|
let object = proto.construct(self, context, &args)?;
|
||||||
ctor.call(object, &args, self, context)?
|
ctor.call(Some(object), &args, self, context)?
|
||||||
.resolve(self, context)?;
|
.resolve(self, context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -899,7 +1038,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
let object = proto.construct(self, context, &args)?;
|
let object = proto.construct(self, context, &args)?;
|
||||||
ctor.call(object, &args, self, context)?
|
ctor.call(Some(object), &args, self, context)?
|
||||||
.resolve(self, context)?;
|
.resolve(self, context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -95,7 +95,7 @@ pub struct Activation<'gc> {
|
||||||
pc: usize,
|
pc: usize,
|
||||||
|
|
||||||
/// The immutable value of `this`.
|
/// The immutable value of `this`.
|
||||||
this: Object<'gc>,
|
this: Option<Object<'gc>>,
|
||||||
|
|
||||||
/// The arguments this function was called by.
|
/// The arguments this function was called by.
|
||||||
arguments: Option<Object<'gc>>,
|
arguments: Option<Object<'gc>>,
|
||||||
|
@ -151,7 +151,7 @@ impl<'gc> Activation<'gc> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
method,
|
method,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
this: global,
|
this: Some(global),
|
||||||
arguments: None,
|
arguments: None,
|
||||||
is_executing: false,
|
is_executing: false,
|
||||||
local_registers,
|
local_registers,
|
||||||
|
@ -164,7 +164,7 @@ impl<'gc> Activation<'gc> {
|
||||||
pub fn from_action(
|
pub fn from_action(
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
action: &Avm2Function<'gc>,
|
action: &Avm2Function<'gc>,
|
||||||
this: Object<'gc>,
|
this: Option<Object<'gc>>,
|
||||||
arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let method = action.method.clone();
|
let method = action.method.clone();
|
||||||
|
@ -178,7 +178,7 @@ impl<'gc> Activation<'gc> {
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut write = local_registers.write(context.gc_context);
|
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 {
|
for i in 0..num_declared_arguments {
|
||||||
*write.get_mut(1 + i).unwrap() = arguments
|
*write.get_mut(1 + i).unwrap() = arguments
|
||||||
|
|
|
@ -34,7 +34,7 @@ use swf::avm2::types::{
|
||||||
pub type NativeFunction<'gc> = fn(
|
pub type NativeFunction<'gc> = fn(
|
||||||
&mut Avm2<'gc>,
|
&mut Avm2<'gc>,
|
||||||
&mut UpdateContext<'_, 'gc, '_>,
|
&mut UpdateContext<'_, 'gc, '_>,
|
||||||
Object<'gc>,
|
Option<Object<'gc>>,
|
||||||
&[Value<'gc>],
|
&[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error>;
|
) -> Result<ReturnValue<'gc>, Error>;
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ impl<'gc> Executable<'gc> {
|
||||||
&self,
|
&self,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
reciever: Object<'gc>,
|
reciever: Option<Object<'gc>>,
|
||||||
arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -316,11 +316,11 @@ impl<'gc> FunctionObject<'gc> {
|
||||||
constr.install_trait(avm, context, class.abc(), trait_entry, scope, fn_proto)?;
|
constr.install_trait(avm, context, class.abc(), trait_entry, scope, fn_proto)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
constr.install_method(
|
constr.install_dynamic_property(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
QName::new(Namespace::public_namespace(), "prototype"),
|
QName::new(Namespace::public_namespace(), "prototype"),
|
||||||
class_proto,
|
class_proto.into(),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
Ok(constr)
|
Ok(constr)
|
||||||
}
|
}
|
||||||
|
@ -453,6 +453,10 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
self.0.write(mc).base.init_slot(id, value, mc)
|
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 {
|
fn has_property(self, name: &QName) -> bool {
|
||||||
self.0.read().base.has_property(name)
|
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
|
self.0.as_ptr() as *const ObjectPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||||
|
self.0.read().exec.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn call(
|
fn call(
|
||||||
self,
|
self,
|
||||||
reciever: Object<'gc>,
|
reciever: Option<Object<'gc>>,
|
||||||
arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
@ -499,26 +507,43 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
fn install_method(
|
||||||
self.0.write(mc).base.install_method(name, function)
|
&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(
|
fn install_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> 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(
|
fn install_setter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> 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(
|
fn install_dynamic_property(
|
||||||
|
|
|
@ -17,7 +17,7 @@ mod object;
|
||||||
fn trace<'gc>(
|
fn trace<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
if let Some(s) = args.get(0) {
|
if let Some(s) = args.get(0) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -12,7 +12,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -14,7 +14,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
@ -24,7 +24,7 @@ pub fn constructor<'gc>(
|
||||||
fn to_string<'gc>(
|
fn to_string<'gc>(
|
||||||
_: &mut Avm2<'gc>,
|
_: &mut Avm2<'gc>,
|
||||||
_: &mut UpdateContext<'_, 'gc, '_>,
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_: Object<'gc>,
|
_: Option<Object<'gc>>,
|
||||||
_: &[Value<'gc>],
|
_: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(ReturnValue::Immediate("[type Function]".into()))
|
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(
|
function_proto.install_method(
|
||||||
gc_context,
|
gc_context,
|
||||||
QName::new(Namespace::public_namespace(), "toString"),
|
QName::new(Namespace::public_namespace(), "toString"),
|
||||||
|
0,
|
||||||
FunctionObject::from_builtin(gc_context, to_string, function_proto),
|
FunctionObject::from_builtin(gc_context, to_string, function_proto),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use gc_arena::MutationContext;
|
||||||
pub fn constructor<'gc>(
|
pub fn constructor<'gc>(
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
_this: Object<'gc>,
|
_this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
//! AVM2 objects.
|
//! AVM2 objects.
|
||||||
|
|
||||||
use crate::avm2::function::{
|
use crate::avm2::function::{Avm2ClassEntry, Avm2MethodEntry, Executable, FunctionObject};
|
||||||
Avm2ClassEntry, Avm2Function, Avm2MethodEntry, Executable, FunctionObject,
|
|
||||||
};
|
|
||||||
use crate::avm2::names::{Multiname, QName};
|
use crate::avm2::names::{Multiname, QName};
|
||||||
use crate::avm2::return_value::ReturnValue;
|
use crate::avm2::return_value::ReturnValue;
|
||||||
use crate::avm2::scope::Scope;
|
use crate::avm2::scope::Scope;
|
||||||
|
@ -72,6 +70,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
) -> Result<(), Error>;
|
) -> 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
|
/// 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> {
|
||||||
|
@ -113,14 +114,21 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
fn proto(&self) -> Option<Object<'gc>>;
|
fn proto(&self) -> Option<Object<'gc>>;
|
||||||
|
|
||||||
/// Install a method (or any other non-slot value) on an object.
|
/// 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.
|
/// Install a getter method on an object property.
|
||||||
fn install_getter(
|
fn install_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Install a setter method on an object property.
|
/// 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,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Install a dynamic or built-in value property on an object.
|
/// 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);
|
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 method = Avm2MethodEntry::from_method_index(abc, method.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_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 method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap();
|
||||||
let exec = Avm2Function::from_method(method, scope).into();
|
let function =
|
||||||
self.install_getter(context.gc_context, trait_name, exec)?;
|
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 method = Avm2MethodEntry::from_method_index(abc, method.clone()).unwrap();
|
||||||
let exec = Avm2Function::from_method(method, scope).into();
|
let function =
|
||||||
self.install_setter(context.gc_context, trait_name, exec)?;
|
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 } => {
|
AbcTraitKind::Class { slot_id, class } => {
|
||||||
let type_entry = Avm2ClassEntry::from_class_index(abc, class.clone()).unwrap();
|
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.
|
/// Call the object.
|
||||||
fn call(
|
fn call(
|
||||||
self,
|
self,
|
||||||
_reciever: Object<'gc>,
|
_reciever: Option<Object<'gc>>,
|
||||||
_arguments: &[Value<'gc>],
|
_arguments: &[Value<'gc>],
|
||||||
_avm: &mut Avm2<'gc>,
|
_avm: &mut Avm2<'gc>,
|
||||||
_context: &mut UpdateContext<'_, '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.
|
/// Get a raw pointer value for this object.
|
||||||
fn as_ptr(&self) -> *const ObjectPtr;
|
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 {}
|
pub enum ObjectPtr {}
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl<'gc> Property<'gc> {
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
match self {
|
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::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()),
|
Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()),
|
||||||
|
@ -168,7 +168,8 @@ impl<'gc> Property<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { set, .. } => {
|
Property::Virtual { set, .. } => {
|
||||||
if let Some(function) = 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())
|
Ok(return_value.is_immediate())
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -211,7 +212,8 @@ impl<'gc> Property<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Property::Virtual { set, .. } => {
|
Property::Virtual { set, .. } => {
|
||||||
if let Some(function) = 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())
|
Ok(return_value.is_immediate())
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
|
@ -27,6 +27,9 @@ pub struct ScriptObjectData<'gc> {
|
||||||
/// Slots stored on this object.
|
/// Slots stored on this object.
|
||||||
slots: Vec<Slot<'gc>>,
|
slots: Vec<Slot<'gc>>,
|
||||||
|
|
||||||
|
/// Methods stored on this object.
|
||||||
|
methods: Vec<Option<Object<'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>>,
|
||||||
}
|
}
|
||||||
|
@ -91,6 +94,10 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
self.0.write(mc).init_slot(id, value, mc)
|
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 {
|
fn has_property(self, name: &QName) -> bool {
|
||||||
self.0.read().has_property(name)
|
self.0.read().has_property(name)
|
||||||
}
|
}
|
||||||
|
@ -113,26 +120,34 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
Ok(ScriptObject::object(context.gc_context, this))
|
Ok(ScriptObject::object(context.gc_context, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
fn install_method(
|
||||||
self.0.write(mc).install_method(name, function)
|
&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(
|
fn install_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.0.write(mc).install_getter(name, function)
|
self.0.write(mc).install_getter(name, disp_id, function)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_setter(
|
fn install_setter(
|
||||||
&mut self,
|
&mut self,
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
name: QName,
|
name: QName,
|
||||||
function: Executable<'gc>,
|
disp_id: u32,
|
||||||
|
function: Object<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.0.write(mc).install_setter(name, function)
|
self.0.write(mc).install_setter(name, disp_id, function)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install_dynamic_property(
|
fn install_dynamic_property(
|
||||||
|
@ -189,6 +204,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
ScriptObjectData {
|
ScriptObjectData {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
slots: Vec::new(),
|
slots: Vec::new(),
|
||||||
|
methods: Vec::new(),
|
||||||
proto,
|
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 {
|
pub fn has_property(&self, name: &QName) -> bool {
|
||||||
self.values.get(name).is_some()
|
self.values.get(name).is_some()
|
||||||
}
|
}
|
||||||
|
@ -315,7 +336,16 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Install a method into the object.
|
/// 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));
|
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
|
/// This is a little more complicated than methods, since virtual property
|
||||||
/// slots can be installed in two parts. Thus, we need to support
|
/// slots can be installed in two parts. Thus, we need to support
|
||||||
/// installing them in either order.
|
/// 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) {
|
if !self.values.contains_key(&name) {
|
||||||
self.values.insert(name.clone(), Property::new_virtual());
|
self.values.insert(name.clone(), Property::new_virtual());
|
||||||
}
|
}
|
||||||
|
@ -332,7 +381,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
self.values
|
self.values
|
||||||
.get_mut(&name)
|
.get_mut(&name)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.install_virtual_getter(function)
|
.install_virtual_getter(executable)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Install a setter into the object.
|
/// 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
|
/// This is a little more complicated than methods, since virtual property
|
||||||
/// slots can be installed in two parts. Thus, we need to support
|
/// slots can be installed in two parts. Thus, we need to support
|
||||||
/// installing them in either order.
|
/// 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) {
|
if !self.values.contains_key(&name) {
|
||||||
self.values.insert(name.clone(), Property::new_virtual());
|
self.values.insert(name.clone(), Property::new_virtual());
|
||||||
}
|
}
|
||||||
|
@ -348,7 +416,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
self.values
|
self.values
|
||||||
.get_mut(&name)
|
.get_mut(&name)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.install_virtual_setter(function)
|
.install_virtual_setter(executable)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_dynamic_property(
|
pub fn install_dynamic_property(
|
||||||
|
|
Loading…
Reference in New Issue