ruffle/core/src/avm2/function.rs

175 lines
5.7 KiB
Rust
Raw Normal View History

//! AVM2 executables.
use crate::avm2::activation::Activation;
use crate::avm2::method::{BytecodeMethod, Method, NativeMethod};
use crate::avm2::object::Object;
2020-02-12 23:52:00 +00:00
use crate::avm2::scope::Scope;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{Collect, CollectionContext, Gc, GcCell, MutationContext};
use std::fmt;
/// Represents code written in AVM2 bytecode that can be executed by some
/// means.
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct BytecodeExecutable<'gc> {
/// The method code to execute from a given ABC file.
method: Gc<'gc, BytecodeMethod<'gc>>,
/// The scope stack to pull variables from.
scope: Option<GcCell<'gc, Scope<'gc>>>,
2020-09-19 14:27:24 +00:00
/// The receiver that this function is always called with.
///
2020-09-19 14:27:24 +00:00
/// If `None`, then the receiver provided by the caller is used. A
/// `Some` value indicates a bound executable.
2020-09-19 14:27:24 +00:00
receiver: Option<Object<'gc>>,
}
#[derive(Clone)]
pub struct NativeExecutable<'gc> {
/// The method associated with the executable.
method: Gc<'gc, NativeMethod<'gc>>,
/// The bound reciever for this method.
bound_receiver: Option<Object<'gc>>,
}
unsafe impl<'gc> Collect for NativeExecutable<'gc> {
fn trace(&self, cc: CollectionContext) {
self.method.trace(cc);
self.bound_receiver.trace(cc);
}
}
/// Represents code that can be executed by some means.
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub enum Executable<'gc> {
/// Code defined in Ruffle's binary.
Native(Gc<'gc, NativeExecutable<'gc>>),
/// Code defined in a loaded ABC file.
Action(Gc<'gc, BytecodeExecutable<'gc>>),
}
2020-02-15 01:30:19 +00:00
impl<'gc> Executable<'gc> {
/// Convert a method into an executable.
pub fn from_method(
method: Method<'gc>,
scope: Option<GcCell<'gc, Scope<'gc>>>,
2020-09-19 14:27:24 +00:00
receiver: Option<Object<'gc>>,
mc: MutationContext<'gc, '_>,
) -> Self {
match method {
Method::Native(method) => Self::Native(Gc::allocate(
mc,
NativeExecutable {
method,
bound_receiver: receiver,
},
)),
Method::Bytecode(method) => Self::Action(Gc::allocate(
mc,
BytecodeExecutable {
method,
scope,
2020-09-19 14:27:24 +00:00
receiver,
},
)),
}
}
/// Execute a method.
///
/// The function will either be called directly if it is a Rust builtin, or
/// executed on the same AVM2 instance as the activation passed in here.
/// The value returned in either case will be provided here.
///
2020-09-19 14:27:24 +00:00
/// It is a panicking logic error to attempt to execute user code while any
/// reachable object is currently under a GcCell write lock.
///
/// Passed-in arguments will be conformed to the set of method parameters
/// declared on the function.
2020-02-15 01:30:19 +00:00
pub fn exec(
&self,
unbound_receiver: Option<Object<'gc>>,
mut arguments: &[Value<'gc>],
2020-07-28 03:19:43 +00:00
activation: &mut Activation<'_, 'gc, '_>,
subclass_object: Option<Object<'gc>>,
2020-12-11 03:21:36 +00:00
callee: Object<'gc>,
) -> Result<Value<'gc>, Error> {
match self {
Executable::Native(bm) => {
let method = bm.method.method;
let receiver = bm.bound_receiver.or(unbound_receiver);
let scope = activation.scope();
let mut activation = Activation::from_builtin(
activation.context.reborrow(),
scope,
receiver,
subclass_object,
)?;
if arguments.len() > bm.method.signature.len() && !bm.method.is_variadic {
return Err(format!(
"Attempted to call {:?} with {} arguments (more than {} is prohibited)",
bm.method.name,
arguments.len(),
bm.method.signature.len()
)
.into());
}
let arguments = activation.resolve_parameters(
bm.method.name,
arguments,
&bm.method.signature,
)?;
method(&mut activation, receiver, &arguments)
2020-07-28 03:19:43 +00:00
}
Executable::Action(bm) => {
if bm.method.is_unchecked() {
let max_args = bm.method.signature().len();
if arguments.len() > max_args && !bm.method.is_variadic() {
arguments = &arguments[..max_args];
}
}
let receiver = bm.receiver.or(unbound_receiver);
let mut activation = Activation::from_method(
2020-07-28 03:19:43 +00:00
activation.context.reborrow(),
bm.method,
bm.scope,
2020-09-19 14:27:24 +00:00
receiver,
arguments,
subclass_object,
2020-12-11 03:21:36 +00:00
callee,
)?;
2020-02-15 01:30:19 +00:00
2020-07-28 03:19:43 +00:00
activation.run_actions(bm.method)
2020-02-15 01:30:19 +00:00
}
}
}
}
impl<'gc> fmt::Debug for Executable<'gc> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Action(be) => fmt
.debug_struct("Executable::Action")
.field("method", &be.method)
.field("scope", &be.scope)
2020-09-19 14:27:24 +00:00
.field("receiver", &be.receiver)
.finish(),
Self::Native(bm) => fmt
.debug_struct("Executable::Native")
.field("method", &bm.method)
.field("bound_receiver", &bm.bound_receiver)
.finish(),
}
}
}