2020-02-06 04:15:03 +00:00
|
|
|
//! AVM2 executables.
|
|
|
|
|
2020-02-08 03:42:04 +00:00
|
|
|
use crate::avm2::activation::Activation;
|
2020-07-03 01:49:53 +00:00
|
|
|
use crate::avm2::method::{BytecodeMethod, Method, NativeMethod};
|
2020-08-08 21:10:05 +00:00
|
|
|
use crate::avm2::object::Object;
|
2020-02-12 23:52:00 +00:00
|
|
|
use crate::avm2::scope::Scope;
|
2020-02-06 04:15:03 +00:00
|
|
|
use crate::avm2::value::Value;
|
2020-07-04 21:18:41 +00:00
|
|
|
use crate::avm2::Error;
|
2020-07-04 21:56:27 +00:00
|
|
|
use gc_arena::{Collect, CollectionContext, Gc, GcCell, MutationContext};
|
2020-02-06 04:15:03 +00:00
|
|
|
use std::fmt;
|
2020-07-02 04:01:07 +00:00
|
|
|
|
2020-07-04 21:56:27 +00:00
|
|
|
/// 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-07-04 21:56:27 +00:00
|
|
|
///
|
2020-09-19 14:27:24 +00:00
|
|
|
/// If `None`, then the receiver provided by the caller is used. A
|
2020-07-04 21:56:27 +00:00
|
|
|
/// `Some` value indicates a bound executable.
|
2020-09-19 14:27:24 +00:00
|
|
|
receiver: Option<Object<'gc>>,
|
2020-07-04 21:56:27 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
/// Represents code that can be executed by some means.
|
2020-07-04 21:56:27 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2020-02-06 04:15:03 +00:00
|
|
|
pub enum Executable<'gc> {
|
2020-07-01 03:44:14 +00:00
|
|
|
/// Code defined in Ruffle's binary.
|
|
|
|
///
|
2020-09-19 14:27:24 +00:00
|
|
|
/// The second parameter stores the bound receiver for this function.
|
2021-05-05 12:26:20 +00:00
|
|
|
Native(NativeMethod, Option<Object<'gc>>),
|
2020-07-01 03:44:14 +00:00
|
|
|
|
|
|
|
/// Code defined in a loaded ABC file.
|
2020-07-04 21:56:27 +00:00
|
|
|
Action(Gc<'gc, BytecodeExecutable<'gc>>),
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<'gc> Collect for Executable<'gc> {
|
|
|
|
fn trace(&self, cc: CollectionContext) {
|
|
|
|
match self {
|
2020-07-04 21:56:27 +00:00
|
|
|
Self::Action(be) => be.trace(cc),
|
2020-09-19 14:27:24 +00:00
|
|
|
Self::Native(_nf, receiver) => receiver.trace(cc),
|
2020-02-06 04:15:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 01:30:19 +00:00
|
|
|
impl<'gc> Executable<'gc> {
|
2020-07-01 03:44:14 +00:00
|
|
|
/// 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>>,
|
2020-07-04 21:56:27 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
2020-07-01 03:44:14 +00:00
|
|
|
) -> Self {
|
|
|
|
match method {
|
2020-09-19 14:27:24 +00:00
|
|
|
Method::Native(nf) => Self::Native(nf, receiver),
|
2020-07-18 21:02:32 +00:00
|
|
|
Method::Entry(method) => Self::Action(Gc::allocate(
|
2020-07-04 21:56:27 +00:00
|
|
|
mc,
|
|
|
|
BytecodeExecutable {
|
2020-07-18 21:02:32 +00:00
|
|
|
method,
|
2020-07-04 21:56:27 +00:00
|
|
|
scope,
|
2020-09-19 14:27:24 +00:00
|
|
|
receiver,
|
2020-07-04 21:56:27 +00:00
|
|
|
},
|
|
|
|
)),
|
2020-07-01 03:44:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 23:07:53 +00:00
|
|
|
/// Execute a method.
|
|
|
|
///
|
|
|
|
/// The function will either be called directly if it is a Rust builtin, or
|
2020-07-04 21:18:41 +00:00
|
|
|
/// 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
|
2020-07-04 21:18:41 +00:00
|
|
|
/// reachable object is currently under a GcCell write lock.
|
2021-06-10 01:08:13 +00:00
|
|
|
///
|
|
|
|
/// 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,
|
Allow binding a reciever to a function, and make all method traits bind themselves to the object they were constructed on.
Our already odd `super` handling throws up another subtlety regarding bound recievers. Since we have to construct an instance of a parent class in order to get traits on it, we also have to make sure that we initialize traits with the correct reciever. I'll demonstrate here:
```let mut base = base_proto.construct(avm, context, &[])?;
let name = base.resolve_multiname(&multiname).unwrap();
let value = base.get_property(object, &name, avm, context)?.resolve(avm, context)?```
In this case, if `name` is the name of a method, getter, or setter trait, then `get_property` will instantiate that trait on `base` but bound to `reciever`. This is correct behavior for this case, but more generally, trait instantiation is permenant and therefore there's potential for confusing shenanigans if you `get_property` with the wrong reciever.
To be very clear, `reciever` should *always* be the same object that is getting `get_property` et. all called on it. In the event that you need to instantiate traits with a different `reciever`, you should construct a one-off object and retrieve prototypes from that.
2020-03-04 03:10:27 +00:00
|
|
|
unbound_reciever: Option<Object<'gc>>,
|
2021-06-11 01:40:45 +00:00
|
|
|
mut arguments: &[Value<'gc>],
|
2020-07-28 03:19:43 +00:00
|
|
|
activation: &mut Activation<'_, 'gc, '_>,
|
2021-05-22 02:46:17 +00:00
|
|
|
base_constr: Option<Object<'gc>>,
|
2020-12-11 03:21:36 +00:00
|
|
|
callee: Object<'gc>,
|
2021-06-11 01:40:45 +00:00
|
|
|
error_on_too_many_arguments: bool,
|
2020-07-04 21:18:41 +00:00
|
|
|
) -> Result<Value<'gc>, Error> {
|
2020-02-25 23:07:53 +00:00
|
|
|
match self {
|
2020-09-19 14:27:24 +00:00
|
|
|
Executable::Native(nf, receiver) => {
|
2020-12-18 04:01:09 +00:00
|
|
|
let receiver = receiver.or(unbound_reciever);
|
2020-12-19 18:03:37 +00:00
|
|
|
let scope = activation.scope();
|
|
|
|
let mut activation = Activation::from_builtin(
|
|
|
|
activation.context.reborrow(),
|
|
|
|
scope,
|
|
|
|
receiver,
|
2021-05-22 02:46:17 +00:00
|
|
|
base_constr,
|
2020-12-19 18:03:37 +00:00
|
|
|
)?;
|
2020-12-18 04:01:09 +00:00
|
|
|
|
|
|
|
nf(&mut activation, receiver, arguments)
|
2020-07-28 03:19:43 +00:00
|
|
|
}
|
2020-07-04 21:56:27 +00:00
|
|
|
Executable::Action(bm) => {
|
2021-06-11 01:40:45 +00:00
|
|
|
if !error_on_too_many_arguments {
|
|
|
|
let max_args = bm.method.method_params().len();
|
|
|
|
if arguments.len() > max_args && !bm.method.is_variadic() {
|
|
|
|
arguments = &arguments[..max_args];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 14:27:24 +00:00
|
|
|
let receiver = bm.receiver.or(unbound_reciever);
|
2020-07-04 21:18:41 +00:00
|
|
|
let mut activation = Activation::from_method(
|
2020-07-28 03:19:43 +00:00
|
|
|
activation.context.reborrow(),
|
2020-07-04 21:56:27 +00:00
|
|
|
bm.method,
|
|
|
|
bm.scope,
|
2020-09-19 14:27:24 +00:00
|
|
|
receiver,
|
2020-07-04 21:18:41 +00:00
|
|
|
arguments,
|
2021-05-22 02:46:17 +00:00
|
|
|
base_constr,
|
2020-12-11 03:21:36 +00:00
|
|
|
callee,
|
2020-07-08 01:28:24 +00:00
|
|
|
)?;
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 04:15:03 +00:00
|
|
|
impl<'gc> fmt::Debug for Executable<'gc> {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2020-07-04 21:56:27 +00:00
|
|
|
Self::Action(be) => fmt
|
2020-07-01 03:44:14 +00:00
|
|
|
.debug_struct("Executable::Action")
|
2020-07-04 21:56:27 +00:00
|
|
|
.field("method", &be.method)
|
|
|
|
.field("scope", &be.scope)
|
2020-09-19 14:27:24 +00:00
|
|
|
.field("receiver", &be.receiver)
|
2020-07-01 03:44:14 +00:00
|
|
|
.finish(),
|
2020-09-19 14:27:24 +00:00
|
|
|
Self::Native(nf, receiver) => fmt
|
2020-02-06 04:15:03 +00:00
|
|
|
.debug_tuple("Executable::Native")
|
|
|
|
.field(&format!("{:p}", nf))
|
2020-09-19 14:27:24 +00:00
|
|
|
.field(receiver)
|
2020-02-06 04:15:03 +00:00
|
|
|
.finish(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|