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::scope::ScopeChain;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::{Collect, Gc, MutationContext}; use gc_arena::{Collect, Gc};
use std::fmt; use std::fmt;
/// Represents code written in AVM2 bytecode that can be executed by some /// Represents code written in AVM2 bytecode that can be executed by some
@ -59,10 +59,10 @@ pub struct NativeExecutable<'gc> {
#[collect(no_drop)] #[collect(no_drop)]
pub enum Executable<'gc> { pub enum Executable<'gc> {
/// Code defined in Ruffle's binary. /// Code defined in Ruffle's binary.
Native(Gc<'gc, NativeExecutable<'gc>>), Native(NativeExecutable<'gc>),
/// Code defined in a loaded ABC file. /// Code defined in a loaded ABC file.
Action(Gc<'gc, BytecodeExecutable<'gc>>), Action(BytecodeExecutable<'gc>),
} }
impl<'gc> Executable<'gc> { impl<'gc> Executable<'gc> {
@ -72,27 +72,20 @@ impl<'gc> Executable<'gc> {
scope: ScopeChain<'gc>, scope: ScopeChain<'gc>,
receiver: Option<Object<'gc>>, receiver: Option<Object<'gc>>,
superclass: Option<ClassObject<'gc>>, superclass: Option<ClassObject<'gc>>,
mc: MutationContext<'gc, '_>,
) -> Self { ) -> Self {
match method { match method {
Method::Native(method) => Self::Native(Gc::allocate( Method::Native(method) => Self::Native(NativeExecutable {
mc, method,
NativeExecutable { scope,
method, bound_receiver: receiver,
scope, bound_superclass: superclass,
bound_receiver: receiver, }),
bound_superclass: superclass, Method::Bytecode(method) => Self::Action(BytecodeExecutable {
}, method,
)), scope,
Method::Bytecode(method) => Self::Action(Gc::allocate( receiver,
mc, bound_superclass: superclass,
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 let (compare_fnc, options) = if fn_or_options
.coerce_to_object(activation) .coerce_to_object(activation)
.ok() .ok()
.and_then(|o| o.as_executable()) .map(|o| o.as_executable().is_some())
.is_some() .unwrap_or(false)
{ {
( (
Some(fn_or_options.coerce_to_object(activation)?), 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() { if !method.needs_arguments_object() {
let scope = class.instance_scope(); let scope = class.instance_scope();
return Executable::from_method( return Executable::from_method(method, scope, None, Some(superclass)).exec(
method,
scope,
None,
Some(superclass),
activation.context.gc_context,
)
.exec(
Some(self.into()), Some(self.into()),
arguments, arguments,
activation, activation,
@ -424,14 +417,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
if !method.needs_arguments_object() { if !method.needs_arguments_object() {
let scope = class.class_scope(); let scope = class.class_scope();
return Executable::from_method( return Executable::from_method(method, scope, None, Some(class)).exec(
method,
scope,
None,
Some(class),
activation.context.gc_context,
)
.exec(
Some(self.into()), Some(self.into()),
arguments, arguments,
activation, 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. /// 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 None
} }

View File

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

View File

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