2020-07-03 01:49:53 +00:00
|
|
|
//! AVM2 methods
|
|
|
|
|
2020-07-04 21:18:41 +00:00
|
|
|
use crate::avm2::activation::Activation;
|
2020-07-03 01:49:53 +00:00
|
|
|
use crate::avm2::object::Object;
|
|
|
|
use crate::avm2::script::TranslationUnit;
|
|
|
|
use crate::avm2::value::Value;
|
2020-07-04 21:18:41 +00:00
|
|
|
use crate::avm2::Error;
|
2020-08-15 00:52:09 +00:00
|
|
|
use crate::collect::CollectWrapper;
|
2020-07-04 21:56:27 +00:00
|
|
|
use gc_arena::{Collect, CollectionContext, Gc, MutationContext};
|
2020-07-03 01:49:53 +00:00
|
|
|
use std::fmt;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use swf::avm2::types::{AbcFile, Index, Method as AbcMethod, MethodBody as AbcMethodBody};
|
|
|
|
|
|
|
|
/// Represents a function defined in Ruffle's code.
|
|
|
|
///
|
|
|
|
/// Parameters are as follows:
|
|
|
|
///
|
|
|
|
/// * The AVM2 runtime
|
|
|
|
/// * The action context
|
|
|
|
/// * The current `this` object
|
|
|
|
/// * The arguments this function was called with
|
|
|
|
///
|
|
|
|
/// Native functions are allowed to return a value or `None`. `None` indicates
|
|
|
|
/// that the given value will not be returned on the stack and instead will
|
|
|
|
/// resolve on the AVM stack, as if you had called a non-native function. If
|
|
|
|
/// your function yields `None`, you must ensure that the top-most activation
|
|
|
|
/// in the AVM1 runtime will return with the value of this function.
|
|
|
|
|
2021-05-05 12:26:20 +00:00
|
|
|
pub type NativeMethod = for<'gc> fn(
|
2021-05-04 17:52:40 +00:00
|
|
|
&mut Activation<'_, 'gc, '_>,
|
|
|
|
Option<Object<'gc>>,
|
|
|
|
&[Value<'gc>],
|
|
|
|
) -> Result<Value<'gc>, Error>;
|
|
|
|
|
2020-07-03 01:49:53 +00:00
|
|
|
/// Represents a reference to an AVM2 method and body.
|
|
|
|
#[derive(Collect, Clone, Debug)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct BytecodeMethod<'gc> {
|
|
|
|
/// The translation unit this function was defined in.
|
|
|
|
pub txunit: TranslationUnit<'gc>,
|
|
|
|
|
|
|
|
/// The underlying ABC file of the above translation unit.
|
|
|
|
pub abc: CollectWrapper<Rc<AbcFile>>,
|
|
|
|
|
|
|
|
/// The ABC method this function uses.
|
|
|
|
pub abc_method: u32,
|
|
|
|
|
|
|
|
/// The ABC method body this function uses.
|
2020-07-08 01:28:24 +00:00
|
|
|
pub abc_method_body: Option<u32>,
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> BytecodeMethod<'gc> {
|
|
|
|
/// Construct an `BytecodeMethod` from an `AbcFile` and method index.
|
|
|
|
///
|
|
|
|
/// The method body index will be determined by searching through the ABC
|
|
|
|
/// for a matching method. If none exists, this function returns `None`.
|
|
|
|
pub fn from_method_index(
|
|
|
|
txunit: TranslationUnit<'gc>,
|
|
|
|
abc_method: Index<AbcMethod>,
|
2020-07-04 21:56:27 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
) -> Option<Gc<'gc, Self>> {
|
2020-07-03 01:49:53 +00:00
|
|
|
let abc = txunit.abc();
|
|
|
|
|
|
|
|
if abc.methods.get(abc_method.0 as usize).is_some() {
|
|
|
|
for (index, method_body) in abc.method_bodies.iter().enumerate() {
|
|
|
|
if method_body.method.0 == abc_method.0 {
|
2020-07-04 21:56:27 +00:00
|
|
|
return Some(Gc::allocate(
|
|
|
|
mc,
|
|
|
|
Self {
|
|
|
|
txunit,
|
|
|
|
abc: CollectWrapper(txunit.abc()),
|
|
|
|
abc_method: abc_method.0,
|
2020-07-08 01:28:24 +00:00
|
|
|
abc_method_body: Some(index as u32),
|
2020-07-04 21:56:27 +00:00
|
|
|
},
|
|
|
|
));
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:28:24 +00:00
|
|
|
Some(Gc::allocate(
|
|
|
|
mc,
|
|
|
|
Self {
|
|
|
|
txunit,
|
|
|
|
abc: CollectWrapper(txunit.abc()),
|
|
|
|
abc_method: abc_method.0,
|
|
|
|
abc_method_body: None,
|
|
|
|
},
|
|
|
|
))
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the underlying ABC file.
|
|
|
|
pub fn abc(&self) -> Rc<AbcFile> {
|
|
|
|
self.txunit.abc()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get the underlying translation unit this method was defined in.
|
|
|
|
pub fn translation_unit(&self) -> TranslationUnit<'gc> {
|
|
|
|
self.txunit
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the ABC method entry this refers to.
|
|
|
|
pub fn method(&self) -> &AbcMethod {
|
|
|
|
&self.abc.0.methods.get(self.abc_method as usize).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the ABC method body entry this refers to.
|
2020-07-08 01:28:24 +00:00
|
|
|
///
|
|
|
|
/// Some methods do not have bodies; this returns `None` in that case.
|
|
|
|
pub fn body(&self) -> Option<&AbcMethodBody> {
|
|
|
|
if let Some(abc_method_body) = self.abc_method_body {
|
|
|
|
self.abc.0.method_bodies.get(abc_method_body as usize)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An uninstantiated method that can either be natively implemented or sourced
|
|
|
|
/// from an ABC file.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum Method<'gc> {
|
|
|
|
/// A native method.
|
2021-05-05 12:26:20 +00:00
|
|
|
Native(NativeMethod),
|
2020-07-03 01:49:53 +00:00
|
|
|
|
|
|
|
/// An ABC-provided method entry.
|
2020-07-04 21:56:27 +00:00
|
|
|
Entry(Gc<'gc, BytecodeMethod<'gc>>),
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> Collect for Method<'gc> {
|
|
|
|
fn trace(&self, cc: CollectionContext) {
|
|
|
|
match self {
|
|
|
|
Method::Native(_nf) => {}
|
2020-07-18 21:02:32 +00:00
|
|
|
Method::Entry(entry) => entry.trace(cc),
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> fmt::Debug for Method<'gc> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Method::Native(_nf) => f
|
|
|
|
.debug_tuple("Method::Native")
|
|
|
|
.field(&"<native code>".to_string())
|
|
|
|
.finish(),
|
2020-07-18 21:02:32 +00:00
|
|
|
Method::Entry(entry) => f.debug_tuple("Method::Entry").field(entry).finish(),
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-05 12:26:20 +00:00
|
|
|
impl<'gc> From<NativeMethod> for Method<'gc> {
|
|
|
|
fn from(nf: NativeMethod) -> Self {
|
2020-07-03 01:49:53 +00:00
|
|
|
Self::Native(nf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-04 21:56:27 +00:00
|
|
|
impl<'gc> From<Gc<'gc, BytecodeMethod<'gc>>> for Method<'gc> {
|
|
|
|
fn from(bm: Gc<'gc, BytecodeMethod<'gc>>) -> Self {
|
|
|
|
Self::Entry(bm)
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'gc> Method<'gc> {
|
2020-07-07 04:20:57 +00:00
|
|
|
/// Builtin method constructor, because for some reason `nf.into()` just
|
|
|
|
/// causes odd lifetime mismatches.
|
2021-05-05 12:26:20 +00:00
|
|
|
pub fn from_builtin(nf: NativeMethod) -> Self {
|
2020-07-07 04:20:57 +00:00
|
|
|
Self::Native(nf)
|
|
|
|
}
|
|
|
|
|
2020-07-04 21:56:27 +00:00
|
|
|
/// Access the bytecode of this method.
|
|
|
|
///
|
|
|
|
/// This function returns `Err` if there is no bytecode for this method.
|
|
|
|
pub fn into_bytecode(self) -> Result<Gc<'gc, BytecodeMethod<'gc>>, Error> {
|
2020-07-03 01:49:53 +00:00
|
|
|
match self {
|
|
|
|
Method::Native(_) => {
|
|
|
|
Err("Attempted to unwrap a native method as a user-defined one".into())
|
|
|
|
}
|
2020-07-04 21:56:27 +00:00
|
|
|
Method::Entry(bm) => Ok(bm),
|
2020-07-03 01:49:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|