avm2: Refactor Executable->BoundMethod, introduce freestanding exec()
This commit is contained in:
parent
3bb47db5f9
commit
2532d7e927
|
@ -4,7 +4,6 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use crate::avm2::class::AllocatorFn;
|
use crate::avm2::class::AllocatorFn;
|
||||||
use crate::avm2::error::make_error_1107;
|
use crate::avm2::error::make_error_1107;
|
||||||
use crate::avm2::function::Executable;
|
|
||||||
use crate::avm2::globals::SystemClasses;
|
use crate::avm2::globals::SystemClasses;
|
||||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||||
use crate::avm2::scope::ScopeChain;
|
use crate::avm2::scope::ScopeChain;
|
||||||
|
@ -598,8 +597,13 @@ impl<'gc> Avm2<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes an executable on the call stack
|
/// Pushes an executable on the call stack
|
||||||
pub fn push_call(&self, mc: &Mutation<'gc>, calling: &Executable<'gc>) {
|
pub fn push_call(
|
||||||
self.call_stack.write(mc).push(calling)
|
&self,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
method: Method<'gc>,
|
||||||
|
superclass: Option<ClassObject<'gc>>,
|
||||||
|
) {
|
||||||
|
self.call_stack.write(mc).push(method, superclass)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes script initializer (global init) on the call stack
|
/// Pushes script initializer (global init) on the call stack
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::avm2::function::{display_function, Executable};
|
use crate::avm2::function::display_function;
|
||||||
use crate::avm2::method::Method;
|
use crate::avm2::method::Method;
|
||||||
use crate::avm2::object::ClassObject;
|
use crate::avm2::object::ClassObject;
|
||||||
use crate::string::WString;
|
use crate::string::WString;
|
||||||
|
@ -27,11 +27,8 @@ impl<'gc> CallStack<'gc> {
|
||||||
Self { stack: Vec::new() }
|
Self { stack: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, exec: &Executable<'gc>) {
|
pub fn push(&mut self, method: Method<'gc>, superclass: Option<ClassObject<'gc>>) {
|
||||||
self.stack.push(CallNode::Method {
|
self.stack.push(CallNode::Method { method, superclass })
|
||||||
method: exec.as_method(),
|
|
||||||
superclass: exec.bound_superclass(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_global_init(&mut self, script: Script<'gc>) {
|
pub fn push_global_init(&mut self, script: Script<'gc>) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! AVM2 executables.
|
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::method::{BytecodeMethod, Method, NativeMethod, ParamConfig};
|
use crate::avm2::method::{Method, ParamConfig};
|
||||||
use crate::avm2::object::{ClassObject, Object};
|
use crate::avm2::object::{ClassObject, Object};
|
||||||
use crate::avm2::scope::ScopeChain;
|
use crate::avm2::scope::ScopeChain;
|
||||||
use crate::avm2::traits::TraitKind;
|
use crate::avm2::traits::TraitKind;
|
||||||
|
@ -11,13 +9,12 @@ use crate::string::WString;
|
||||||
use gc_arena::{Collect, Gc};
|
use gc_arena::{Collect, Gc};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Represents code written in AVM2 bytecode that can be executed by some
|
/// Represents a bound method.
|
||||||
/// means.
|
|
||||||
#[derive(Clone, Collect)]
|
#[derive(Clone, Collect)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct BytecodeExecutable<'gc> {
|
pub struct BoundMethod<'gc> {
|
||||||
/// The method code to execute from a given ABC file.
|
/// The method code to execute from a given ABC file.
|
||||||
method: Gc<'gc, BytecodeMethod<'gc>>,
|
method: Method<'gc>,
|
||||||
|
|
||||||
/// The scope this method was defined in.
|
/// The scope this method was defined in.
|
||||||
scope: ScopeChain<'gc>,
|
scope: ScopeChain<'gc>,
|
||||||
|
@ -26,206 +23,66 @@ pub struct BytecodeExecutable<'gc> {
|
||||||
///
|
///
|
||||||
/// If `None`, then the receiver provided by the caller is used. A
|
/// If `None`, then the receiver provided by the caller is used. A
|
||||||
/// `Some` value indicates a bound executable.
|
/// `Some` value indicates a bound executable.
|
||||||
receiver: Option<Object<'gc>>,
|
|
||||||
|
|
||||||
/// The bound superclass for this method.
|
|
||||||
///
|
|
||||||
/// The `superclass` is the class that defined this method. If `None`,
|
|
||||||
/// then there is no defining superclass and `super` operations should fall
|
|
||||||
/// back to the `receiver`.
|
|
||||||
bound_superclass: Option<ClassObject<'gc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Collect)]
|
|
||||||
#[collect(no_drop)]
|
|
||||||
pub struct NativeExecutable<'gc> {
|
|
||||||
/// The method associated with the executable.
|
|
||||||
method: Gc<'gc, NativeMethod<'gc>>,
|
|
||||||
|
|
||||||
/// The scope this method was defined in.
|
|
||||||
scope: ScopeChain<'gc>,
|
|
||||||
|
|
||||||
/// The bound receiver for this method.
|
|
||||||
bound_receiver: Option<Object<'gc>>,
|
bound_receiver: Option<Object<'gc>>,
|
||||||
|
|
||||||
/// The bound superclass for this method.
|
/// The bound class for this method.
|
||||||
///
|
///
|
||||||
/// The `superclass` is the class that defined this method. If `None`,
|
/// The `class` is the class that defined this method. If `None`,
|
||||||
/// then there is no defining superclass and `super` operations should fall
|
/// then there is no defining class and `super` operations should fall
|
||||||
/// back to the `receiver`.
|
/// back to the `receiver`.
|
||||||
bound_superclass: Option<ClassObject<'gc>>,
|
bound_class: Option<ClassObject<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents code that can be executed by some means.
|
impl<'gc> BoundMethod<'gc> {
|
||||||
#[derive(Clone, Collect)]
|
|
||||||
#[collect(no_drop)]
|
|
||||||
pub enum Executable<'gc> {
|
|
||||||
/// Code defined in Ruffle's binary.
|
|
||||||
Native(NativeExecutable<'gc>),
|
|
||||||
|
|
||||||
/// Code defined in a loaded ABC file.
|
|
||||||
Action(BytecodeExecutable<'gc>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'gc> Executable<'gc> {
|
|
||||||
/// Convert a method into an executable.
|
|
||||||
pub fn from_method(
|
pub fn from_method(
|
||||||
method: Method<'gc>,
|
method: Method<'gc>,
|
||||||
scope: ScopeChain<'gc>,
|
scope: ScopeChain<'gc>,
|
||||||
receiver: Option<Object<'gc>>,
|
receiver: Option<Object<'gc>>,
|
||||||
superclass: Option<ClassObject<'gc>>,
|
superclass: Option<ClassObject<'gc>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match method {
|
Self {
|
||||||
Method::Native(method) => Self::Native(NativeExecutable {
|
method,
|
||||||
method,
|
scope,
|
||||||
scope,
|
bound_receiver: receiver,
|
||||||
bound_receiver: receiver,
|
bound_class: superclass,
|
||||||
bound_superclass: superclass,
|
|
||||||
}),
|
|
||||||
Method::Bytecode(method) => Self::Action(BytecodeExecutable {
|
|
||||||
method,
|
|
||||||
scope,
|
|
||||||
receiver,
|
|
||||||
bound_superclass: superclass,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
pub fn exec(
|
pub fn exec(
|
||||||
&self,
|
&self,
|
||||||
unbound_receiver: Value<'gc>,
|
unbound_receiver: Value<'gc>,
|
||||||
mut arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
callee: Object<'gc>,
|
callee: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let ret = match self {
|
let receiver = if let Some(receiver) = self.bound_receiver {
|
||||||
Executable::Native(bm) => {
|
receiver
|
||||||
let method = bm.method.method;
|
} else if matches!(unbound_receiver, Value::Null | Value::Undefined) {
|
||||||
|
self.scope
|
||||||
let receiver = if let Some(receiver) = bm.bound_receiver {
|
.get(0)
|
||||||
receiver
|
.expect("No global scope for function call")
|
||||||
} else if matches!(unbound_receiver, Value::Null | Value::Undefined) {
|
.values()
|
||||||
bm.scope
|
} else {
|
||||||
.get(0)
|
unbound_receiver.coerce_to_object(activation)?
|
||||||
.expect("No global scope for function call")
|
|
||||||
.values()
|
|
||||||
} else {
|
|
||||||
unbound_receiver.coerce_to_object(activation)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let caller_domain = activation.caller_domain();
|
|
||||||
let caller_movie = activation.caller_movie();
|
|
||||||
let subclass_object = bm.bound_superclass;
|
|
||||||
let mut activation = Activation::from_builtin(
|
|
||||||
activation.context.reborrow(),
|
|
||||||
subclass_object,
|
|
||||||
bm.scope,
|
|
||||||
caller_domain,
|
|
||||||
caller_movie,
|
|
||||||
);
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
if bm.method.resolved_signature.read().is_none() {
|
|
||||||
bm.method.resolve_signature(&mut activation)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolved_signature = bm.method.resolved_signature.read();
|
|
||||||
let resolved_signature = resolved_signature.as_ref().unwrap();
|
|
||||||
|
|
||||||
let arguments = activation.resolve_parameters(
|
|
||||||
Method::Native(bm.method),
|
|
||||||
arguments,
|
|
||||||
resolved_signature,
|
|
||||||
Some(callee),
|
|
||||||
)?;
|
|
||||||
activation
|
|
||||||
.context
|
|
||||||
.avm2
|
|
||||||
.push_call(activation.context.gc_context, self);
|
|
||||||
method(&mut activation, receiver, &arguments)
|
|
||||||
}
|
|
||||||
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 = if let Some(receiver) = bm.receiver {
|
|
||||||
receiver
|
|
||||||
} else if matches!(unbound_receiver, Value::Null | Value::Undefined) {
|
|
||||||
bm.scope
|
|
||||||
.get(0)
|
|
||||||
.expect("No global scope for function call")
|
|
||||||
.values()
|
|
||||||
} else {
|
|
||||||
unbound_receiver.coerce_to_object(activation)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let subclass_object = bm.bound_superclass;
|
|
||||||
|
|
||||||
// This used to be a one step called Activation::from_method,
|
|
||||||
// but avoiding moving an Activation around helps perf
|
|
||||||
let mut activation = Activation::from_nothing(activation.context.reborrow());
|
|
||||||
activation.init_from_method(
|
|
||||||
bm.method,
|
|
||||||
bm.scope,
|
|
||||||
receiver,
|
|
||||||
arguments,
|
|
||||||
subclass_object,
|
|
||||||
callee,
|
|
||||||
)?;
|
|
||||||
activation
|
|
||||||
.context
|
|
||||||
.avm2
|
|
||||||
.push_call(activation.context.gc_context, self);
|
|
||||||
activation.run_actions(bm.method)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
activation
|
|
||||||
.context
|
exec(
|
||||||
.avm2
|
self.method,
|
||||||
.pop_call(activation.context.gc_context);
|
self.scope,
|
||||||
ret
|
receiver,
|
||||||
|
self.bound_class,
|
||||||
|
arguments,
|
||||||
|
activation,
|
||||||
|
callee,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bound_superclass(&self) -> Option<ClassObject<'gc>> {
|
pub fn bound_superclass(&self) -> Option<ClassObject<'gc>> {
|
||||||
match self {
|
self.bound_class
|
||||||
Executable::Native(NativeExecutable {
|
|
||||||
bound_superclass, ..
|
|
||||||
}) => *bound_superclass,
|
|
||||||
Executable::Action(BytecodeExecutable {
|
|
||||||
bound_superclass, ..
|
|
||||||
}) => *bound_superclass,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_method(&self) -> Method<'gc> {
|
pub fn as_method(&self) -> Method<'gc> {
|
||||||
match self {
|
self.method
|
||||||
Executable::Native(nm) => Method::Native(nm.method),
|
|
||||||
Executable::Action(bm) => Method::Bytecode(bm.method),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_full_name(&self) -> WString {
|
pub fn debug_full_name(&self) -> WString {
|
||||||
|
@ -235,47 +92,135 @@ impl<'gc> Executable<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_parameters(&self) -> usize {
|
pub fn num_parameters(&self) -> usize {
|
||||||
match self {
|
match self.method {
|
||||||
Executable::Native(NativeExecutable { method, .. }) => method.signature.len(),
|
Method::Native(method) => method.signature.len(),
|
||||||
Executable::Action(BytecodeExecutable { method, .. }) => method.signature.len(),
|
Method::Bytecode(method) => method.signature.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn signature(&self) -> &[ParamConfig<'gc>] {
|
pub fn signature(&self) -> &[ParamConfig<'gc>] {
|
||||||
match self {
|
match &self.method {
|
||||||
Executable::Native(NativeExecutable { method, .. }) => &method.signature,
|
Method::Native(method) => &method.signature,
|
||||||
Executable::Action(BytecodeExecutable { method, .. }) => method.signature(),
|
Method::Bytecode(method) => method.signature(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_variadic(&self) -> bool {
|
pub fn is_variadic(&self) -> bool {
|
||||||
match self {
|
match self.method {
|
||||||
Executable::Native(NativeExecutable { method, .. }) => method.is_variadic,
|
Method::Native(method) => method.is_variadic,
|
||||||
Executable::Action(BytecodeExecutable { method, .. }) => method.is_variadic(),
|
Method::Bytecode(method) => method.is_variadic(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type(&self) -> &Multiname<'gc> {
|
pub fn return_type(&self) -> &Multiname<'gc> {
|
||||||
match self {
|
match &self.method {
|
||||||
Executable::Native(NativeExecutable { method, .. }) => &method.return_type,
|
Method::Native(method) => &method.return_type,
|
||||||
Executable::Action(BytecodeExecutable { method, .. }) => &method.return_type,
|
Method::Bytecode(method) => &method.return_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> fmt::Debug for Executable<'gc> {
|
/// 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.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
pub fn exec<'gc>(
|
||||||
|
method: Method<'gc>,
|
||||||
|
scope: ScopeChain<'gc>,
|
||||||
|
receiver: Object<'gc>,
|
||||||
|
bound_class: Option<ClassObject<'gc>>,
|
||||||
|
mut arguments: &[Value<'gc>],
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
callee: Object<'gc>,
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
let ret = match method {
|
||||||
|
Method::Native(bm) => {
|
||||||
|
let caller_domain = activation.caller_domain();
|
||||||
|
let caller_movie = activation.caller_movie();
|
||||||
|
let mut activation = Activation::from_builtin(
|
||||||
|
activation.context.reborrow(),
|
||||||
|
bound_class,
|
||||||
|
scope,
|
||||||
|
caller_domain,
|
||||||
|
caller_movie,
|
||||||
|
);
|
||||||
|
|
||||||
|
if arguments.len() > bm.signature.len() && !bm.is_variadic {
|
||||||
|
return Err(format!(
|
||||||
|
"Attempted to call {:?} with {} arguments (more than {} is prohibited)",
|
||||||
|
bm.name,
|
||||||
|
arguments.len(),
|
||||||
|
bm.signature.len()
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if bm.resolved_signature.read().is_none() {
|
||||||
|
bm.resolve_signature(&mut activation)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolved_signature = bm.resolved_signature.read();
|
||||||
|
let resolved_signature = resolved_signature.as_ref().unwrap();
|
||||||
|
|
||||||
|
let arguments = activation.resolve_parameters(
|
||||||
|
method,
|
||||||
|
arguments,
|
||||||
|
resolved_signature,
|
||||||
|
Some(callee),
|
||||||
|
)?;
|
||||||
|
activation
|
||||||
|
.context
|
||||||
|
.avm2
|
||||||
|
.push_call(activation.context.gc_context, method, bound_class);
|
||||||
|
(bm.method)(&mut activation, receiver, &arguments)
|
||||||
|
}
|
||||||
|
Method::Bytecode(bm) => {
|
||||||
|
if bm.is_unchecked() {
|
||||||
|
let max_args = bm.signature().len();
|
||||||
|
if arguments.len() > max_args && !bm.is_variadic() {
|
||||||
|
arguments = &arguments[..max_args];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This used to be a one step called Activation::from_method,
|
||||||
|
// but avoiding moving an Activation around helps perf
|
||||||
|
let mut activation = Activation::from_nothing(activation.context.reborrow());
|
||||||
|
activation.init_from_method(bm, scope, receiver, arguments, bound_class, callee)?;
|
||||||
|
activation
|
||||||
|
.context
|
||||||
|
.avm2
|
||||||
|
.push_call(activation.context.gc_context, method, bound_class);
|
||||||
|
activation.run_actions(bm)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
activation
|
||||||
|
.context
|
||||||
|
.avm2
|
||||||
|
.pop_call(activation.context.gc_context);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> fmt::Debug for BoundMethod<'gc> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self.method {
|
||||||
Self::Action(be) => fmt
|
Method::Bytecode(be) => fmt
|
||||||
.debug_struct("Executable::Action")
|
.debug_struct("BoundMethod")
|
||||||
.field("method", &Gc::as_ptr(be.method))
|
.field("method", &Gc::as_ptr(be))
|
||||||
.field("scope", &be.scope)
|
.field("scope", &self.scope)
|
||||||
.field("receiver", &be.receiver)
|
.field("receiver", &self.bound_receiver)
|
||||||
.finish(),
|
.finish(),
|
||||||
Self::Native(bm) => fmt
|
Method::Native(bm) => fmt
|
||||||
.debug_struct("Executable::Native")
|
.debug_struct("BoundMethod")
|
||||||
.field("method", &bm.method)
|
.field("method", &bm)
|
||||||
.field("bound_receiver", &bm.bound_receiver)
|
.field("scope", &self.scope)
|
||||||
|
.field("bound_receiver", &self.bound_receiver)
|
||||||
.finish(),
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::avm2::class::Class;
|
||||||
use crate::avm2::domain::Domain;
|
use crate::avm2::domain::Domain;
|
||||||
use crate::avm2::error;
|
use crate::avm2::error;
|
||||||
use crate::avm2::events::{DispatchList, Event};
|
use crate::avm2::events::{DispatchList, Event};
|
||||||
use crate::avm2::function::Executable;
|
use crate::avm2::function::{exec, BoundMethod};
|
||||||
use crate::avm2::property::Property;
|
use crate::avm2::property::Property;
|
||||||
use crate::avm2::regexp::RegExp;
|
use crate::avm2::regexp::RegExp;
|
||||||
use crate::avm2::value::{Hint, Value};
|
use crate::avm2::value::{Hint, Value};
|
||||||
|
@ -600,11 +600,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
class,
|
class,
|
||||||
} = full_method;
|
} = full_method;
|
||||||
|
|
||||||
return Executable::from_method(method, scope, None, Some(class)).exec(
|
return exec(
|
||||||
Value::from(self.into()),
|
method,
|
||||||
|
scope,
|
||||||
|
self.into(),
|
||||||
|
Some(class),
|
||||||
arguments,
|
arguments,
|
||||||
activation,
|
activation,
|
||||||
class.into(), //Deliberately invalid.
|
class.into(), //Callee deliberately invalid.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1153,8 +1156,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this object's `Executable`, if it has one.
|
/// Get this object's `BoundMethod`, if it has one.
|
||||||
fn as_executable(&self) -> Option<Ref<Executable<'gc>>> {
|
fn as_executable(&self) -> Option<Ref<BoundMethod<'gc>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::class::{Allocator, AllocatorFn, Class, ClassHashWrapper};
|
use crate::avm2::class::{Allocator, AllocatorFn, Class, ClassHashWrapper};
|
||||||
use crate::avm2::error::{argument_error, make_error_1127, reference_error, type_error};
|
use crate::avm2::error::{argument_error, make_error_1127, reference_error, type_error};
|
||||||
use crate::avm2::function::Executable;
|
use crate::avm2::function::exec;
|
||||||
use crate::avm2::method::Method;
|
use crate::avm2::method::Method;
|
||||||
use crate::avm2::object::function_object::FunctionObject;
|
use crate::avm2::object::function_object::FunctionObject;
|
||||||
use crate::avm2::object::script_object::{scriptobject_allocator, ScriptObjectData};
|
use crate::avm2::object::script_object::{scriptobject_allocator, ScriptObjectData};
|
||||||
|
@ -477,10 +477,16 @@ impl<'gc> ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scope = self.0.read().instance_scope;
|
let scope = self.0.read().instance_scope;
|
||||||
let constructor =
|
let method = self.0.read().constructor;
|
||||||
Executable::from_method(self.0.read().constructor, scope, None, Some(self));
|
exec(
|
||||||
|
method,
|
||||||
constructor.exec(receiver, arguments, activation, self.into())
|
scope,
|
||||||
|
receiver.coerce_to_object(activation)?,
|
||||||
|
Some(self),
|
||||||
|
arguments,
|
||||||
|
activation,
|
||||||
|
self.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call the instance's native initializer.
|
/// Call the instance's native initializer.
|
||||||
|
@ -495,10 +501,16 @@ impl<'gc> ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scope = self.0.read().instance_scope;
|
let scope = self.0.read().instance_scope;
|
||||||
let constructor =
|
let method = self.0.read().native_constructor;
|
||||||
Executable::from_method(self.0.read().native_constructor, scope, None, Some(self));
|
exec(
|
||||||
|
method,
|
||||||
constructor.exec(receiver, arguments, activation, self.into())
|
scope,
|
||||||
|
receiver.coerce_to_object(activation)?,
|
||||||
|
Some(self),
|
||||||
|
arguments,
|
||||||
|
activation,
|
||||||
|
self.into(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supercall a method defined in this class.
|
/// Supercall a method defined in this class.
|
||||||
|
@ -842,9 +854,15 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(call_handler) = self.0.read().call_handler {
|
if let Some(call_handler) = self.0.read().call_handler {
|
||||||
let scope = self.0.read().class_scope;
|
let scope = self.0.read().class_scope;
|
||||||
let func = Executable::from_method(call_handler, scope, None, Some(self));
|
exec(
|
||||||
|
call_handler,
|
||||||
func.exec(receiver, arguments, activation, self.into())
|
scope,
|
||||||
|
receiver.coerce_to_object(activation)?,
|
||||||
|
Some(self),
|
||||||
|
arguments,
|
||||||
|
activation,
|
||||||
|
self.into(),
|
||||||
|
)
|
||||||
} else if arguments.len() == 1 {
|
} else if arguments.len() == 1 {
|
||||||
arguments[0].coerce_to_type(activation, self.inner_class_definition())
|
arguments[0].coerce_to_type(activation, self.inner_class_definition())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Function object impl
|
//! Function object impl
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::function::Executable;
|
use crate::avm2::function::BoundMethod;
|
||||||
use crate::avm2::method::{Method, NativeMethod};
|
use crate::avm2::method::{Method, NativeMethod};
|
||||||
use crate::avm2::object::script_object::{ScriptObject, ScriptObjectData};
|
use crate::avm2::object::script_object::{ScriptObject, ScriptObjectData};
|
||||||
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
|
use crate::avm2::object::{ClassObject, Object, ObjectPtr, TObject};
|
||||||
|
@ -41,7 +41,7 @@ pub fn function_allocator<'gc>(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
FunctionObjectData {
|
FunctionObjectData {
|
||||||
base,
|
base,
|
||||||
exec: Executable::from_method(
|
exec: BoundMethod::from_method(
|
||||||
Method::Native(dummy),
|
Method::Native(dummy),
|
||||||
activation.create_scopechain(),
|
activation.create_scopechain(),
|
||||||
None,
|
None,
|
||||||
|
@ -78,7 +78,7 @@ pub struct FunctionObjectData<'gc> {
|
||||||
base: ScriptObjectData<'gc>,
|
base: ScriptObjectData<'gc>,
|
||||||
|
|
||||||
/// Executable code
|
/// Executable code
|
||||||
exec: Executable<'gc>,
|
exec: BoundMethod<'gc>,
|
||||||
|
|
||||||
/// Attached prototype (note: not the same thing as base object's proto)
|
/// Attached prototype (note: not the same thing as base object's proto)
|
||||||
prototype: Option<Object<'gc>>,
|
prototype: Option<Object<'gc>>,
|
||||||
|
@ -120,7 +120,7 @@ impl<'gc> FunctionObject<'gc> {
|
||||||
subclass_object: Option<ClassObject<'gc>>,
|
subclass_object: Option<ClassObject<'gc>>,
|
||||||
) -> FunctionObject<'gc> {
|
) -> FunctionObject<'gc> {
|
||||||
let fn_class = activation.avm2().classes().function;
|
let fn_class = activation.avm2().classes().function;
|
||||||
let exec = Executable::from_method(method, scope, receiver, subclass_object);
|
let exec = BoundMethod::from_method(method, scope, receiver, subclass_object);
|
||||||
|
|
||||||
FunctionObject(GcCell::new(
|
FunctionObject(GcCell::new(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
|
@ -169,7 +169,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
Ok(Value::Object(Object::from(*self)))
|
Ok(Value::Object(Object::from(*self)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_executable(&self) -> Option<Ref<Executable<'gc>>> {
|
fn as_executable(&self) -> Option<Ref<BoundMethod<'gc>>> {
|
||||||
Some(Ref::map(self.0.read(), |r| &r.exec))
|
Some(Ref::map(self.0.read(), |r| &r.exec))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::avm2::dynamic_map::DynamicKey;
|
use crate::avm2::dynamic_map::DynamicKey;
|
||||||
use crate::avm2::function::Executable;
|
use crate::avm2::function::BoundMethod;
|
||||||
use crate::avm2::method::{Method, ParamConfig};
|
use crate::avm2::method::{Method, ParamConfig};
|
||||||
use crate::avm2::object::TObject;
|
use crate::avm2::object::TObject;
|
||||||
use crate::avm2::traits::{Trait, TraitKind};
|
use crate::avm2::traits::{Trait, TraitKind};
|
||||||
|
@ -166,7 +166,7 @@ impl FunctionInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_executable(executable: &Executable, stubbed: bool) -> Self {
|
pub fn from_bound_method(executable: &BoundMethod, stubbed: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
returns: executable
|
returns: executable
|
||||||
.return_type()
|
.return_type()
|
||||||
|
@ -345,7 +345,7 @@ impl Definition {
|
||||||
if let Some(executable) = object.as_executable() {
|
if let Some(executable) = object.as_executable() {
|
||||||
output.get_or_insert_with(Default::default).function.insert(
|
output.get_or_insert_with(Default::default).function.insert(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
FunctionInfo::from_executable(&executable, false),
|
FunctionInfo::from_bound_method(&executable, false),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -494,7 +494,7 @@ pub fn capture_specification(context: &mut UpdateContext, output: &Path) {
|
||||||
.get_or_insert_with(Default::default);
|
.get_or_insert_with(Default::default);
|
||||||
instance_traits.function.insert(
|
instance_traits.function.insert(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
FunctionInfo::from_executable(
|
FunctionInfo::from_bound_method(
|
||||||
&executable,
|
&executable,
|
||||||
namespace_stubs.has_method(&name.to_string()),
|
namespace_stubs.has_method(&name.to_string()),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue