avm2: Make `Executable` zero-alloc

This commit is contained in:
David Wendt 2021-10-25 22:11:32 -04:00 committed by Mike Welsh
parent cea3997396
commit 8d40e41ee1
5 changed files with 32 additions and 65 deletions

View File

@ -6,7 +6,7 @@ use crate::avm2::object::{ClassObject, Object};
use crate::avm2::scope::ScopeChain;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{Collect, Gc, MutationContext};
use gc_arena::{Collect, Gc};
use std::fmt;
/// Represents code written in AVM2 bytecode that can be executed by some
@ -59,10 +59,10 @@ pub struct NativeExecutable<'gc> {
#[collect(no_drop)]
pub enum Executable<'gc> {
/// Code defined in Ruffle's binary.
Native(Gc<'gc, NativeExecutable<'gc>>),
Native(NativeExecutable<'gc>),
/// Code defined in a loaded ABC file.
Action(Gc<'gc, BytecodeExecutable<'gc>>),
Action(BytecodeExecutable<'gc>),
}
impl<'gc> Executable<'gc> {
@ -72,27 +72,20 @@ impl<'gc> Executable<'gc> {
scope: ScopeChain<'gc>,
receiver: Option<Object<'gc>>,
superclass: Option<ClassObject<'gc>>,
mc: MutationContext<'gc, '_>,
) -> Self {
match method {
Method::Native(method) => Self::Native(Gc::allocate(
mc,
NativeExecutable {
method,
scope,
bound_receiver: receiver,
bound_superclass: superclass,
},
)),
Method::Bytecode(method) => Self::Action(Gc::allocate(
mc,
BytecodeExecutable {
method,
scope,
receiver,
bound_superclass: superclass,
},
)),
Method::Native(method) => Self::Native(NativeExecutable {
method,
scope,
bound_receiver: receiver,
bound_superclass: superclass,
}),
Method::Bytecode(method) => Self::Action(BytecodeExecutable {
method,
scope,
receiver,
bound_superclass: superclass,
}),
}
}

View File

@ -873,8 +873,8 @@ pub fn sort<'gc>(
let (compare_fnc, options) = if fn_or_options
.coerce_to_object(activation)
.ok()
.and_then(|o| o.as_executable())
.is_some()
.map(|o| o.as_executable().is_some())
.unwrap_or(false)
{
(
Some(fn_or_options.coerce_to_object(activation)?),

View File

@ -401,14 +401,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
if !method.needs_arguments_object() {
let scope = class.instance_scope();
return Executable::from_method(
method,
scope,
None,
Some(superclass),
activation.context.gc_context,
)
.exec(
return Executable::from_method(method, scope, None, Some(superclass)).exec(
Some(self.into()),
arguments,
activation,
@ -424,14 +417,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
if !method.needs_arguments_object() {
let scope = class.class_scope();
return Executable::from_method(
method,
scope,
None,
Some(class),
activation.context.gc_context,
)
.exec(
return Executable::from_method(method, scope, None, Some(class)).exec(
Some(self.into()),
arguments,
activation,
@ -1257,7 +1243,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
}
/// Get this object's `Executable`, if it has one.
fn as_executable(&self) -> Option<Executable<'gc>> {
fn as_executable(&self) -> Option<Ref<Executable<'gc>>> {
None
}

View File

@ -452,13 +452,8 @@ impl<'gc> ClassObject<'gc> {
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
let scope = self.0.read().instance_scope;
let constructor = Executable::from_method(
self.0.read().constructor.clone(),
scope,
None,
Some(self),
activation.context.gc_context,
);
let constructor =
Executable::from_method(self.0.read().constructor.clone(), scope, None, Some(self));
constructor.exec(receiver, arguments, activation, self.into())
}
@ -480,7 +475,6 @@ impl<'gc> ClassObject<'gc> {
scope,
None,
Some(self),
activation.context.gc_context,
);
constructor.exec(receiver, arguments, activation, self.into())

View File

@ -24,7 +24,7 @@ pub struct FunctionObjectData<'gc> {
base: ScriptObjectData<'gc>,
/// Executable code
exec: Option<Executable<'gc>>,
exec: Executable<'gc>,
}
impl<'gc> FunctionObject<'gc> {
@ -66,13 +66,7 @@ impl<'gc> FunctionObject<'gc> {
subclass_object: Option<ClassObject<'gc>>,
) -> Object<'gc> {
let fn_proto = activation.avm2().prototypes().function;
let exec = Some(Executable::from_method(
method,
scope,
receiver,
subclass_object,
activation.context.gc_context,
));
let exec = Executable::from_method(method, scope, receiver, subclass_object);
FunctionObject(GcCell::allocate(
activation.context.gc_context,
@ -110,8 +104,8 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
Ok(Value::Object(Object::from(*self)))
}
fn as_executable(&self) -> Option<Executable<'gc>> {
self.0.read().exec.clone()
fn as_executable(&self) -> Option<Ref<Executable<'gc>>> {
Some(Ref::map(self.0.read(), |r| &r.exec))
}
fn call(
@ -120,11 +114,10 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
arguments: &[Value<'gc>],
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
if let Some(exec) = &self.0.read().exec {
exec.exec(receiver, arguments, activation, self.into())
} else {
Err("Not a callable function!".into())
}
self.0
.read()
.exec
.exec(receiver, arguments, activation, self.into())
}
fn construct(
@ -151,10 +144,11 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
let this: Object<'gc> = Object::FunctionObject(*self);
let base = ScriptObjectData::base_new(Some(this), None);
let exec = self.0.read().exec.clone();
Ok(FunctionObject(GcCell::allocate(
activation.context.gc_context,
FunctionObjectData { base, exec: None },
FunctionObjectData { base, exec },
))
.into())
}