avm2: Make `Executable` zero-alloc
This commit is contained in:
parent
cea3997396
commit
8d40e41ee1
|
@ -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,
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)?),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue