Skip to content

Commit bd553b9

Browse files
committed
avm1: Further simplify function object creation
This splits creating the raw `FunctionObject`, and builf the actual AVM1 `Object` that contains it.
1 parent f016342 commit bd553b9

File tree

4 files changed

+48
-79
lines changed

4 files changed

+48
-79
lines changed

core/src/avm1/activation.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,11 +886,10 @@ impl<'a, 'gc> Activation<'a, 'gc> {
886886
&self.context.strings,
887887
Some(self.context.avm1.prototypes().object),
888888
);
889-
let func_obj = FunctionObject::function(
889+
let func_obj = FunctionObject::bytecode(Gc::new(self.gc(), func)).build(
890890
&self.context.strings,
891-
Gc::new(self.gc(), func),
892891
self.context.avm1.prototypes().function,
893-
prototype,
892+
Some(prototype),
894893
);
895894
if let Some(name) = name {
896895
self.define_local(name, func_obj.into())?;

core/src/avm1/function.rs

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -446,26 +446,19 @@ pub struct FunctionObject<'gc> {
446446
}
447447

448448
impl<'gc> FunctionObject<'gc> {
449-
/// Construct a function with any combination of regular and constructor parts.
449+
/// Builds a new function object.
450450
///
451451
/// `fn_proto` refers to the implicit proto of the function object, and the
452-
/// `prototype` refers to the explicit prototype of the function.
453-
/// The function and its prototype will be linked to each other.
454-
fn allocate_function(
452+
/// `prototype` refers to the explicit prototype of the function. If the
453+
/// prototype is present, it will be linked with the newly-created object.
454+
pub fn build(
455+
self,
455456
context: &StringContext<'gc>,
456-
function: Executable<'gc>,
457-
constructor: Option<NativeFunction>,
458457
fn_proto: Object<'gc>,
459458
prototype: Option<Object<'gc>>,
460459
) -> Object<'gc> {
461460
let obj = Object::new(context, Some(fn_proto));
462-
let native = NativeObject::Function(Gc::new(
463-
context.gc(),
464-
Self {
465-
function,
466-
constructor,
467-
},
468-
));
461+
let native = NativeObject::Function(Gc::new(context.gc(), self));
469462
obj.set_native(context.gc(), native);
470463

471464
if let Some(prototype) = prototype {
@@ -486,62 +479,45 @@ impl<'gc> FunctionObject<'gc> {
486479
obj
487480
}
488481

489-
/// Constructs a function that does nothing.
482+
/// A function that does nothing.
490483
///
491484
/// This can also serve as a no-op constructor.
492-
pub fn empty(
493-
context: &StringContext<'gc>,
494-
fn_proto: Object<'gc>,
495-
prototype: Object<'gc>,
496-
) -> Object<'gc> {
497-
let empty = Executable::Native(|_, _, _| Ok(Value::Undefined));
498-
Self::allocate_function(context, empty, None, fn_proto, Some(prototype))
485+
pub fn empty() -> Self {
486+
Self {
487+
function: Executable::Native(|_, _, _| Ok(Value::Undefined)),
488+
constructor: None,
489+
}
499490
}
500491

501-
/// Construct a function from AVM1 bytecode and associated protos.
502-
pub fn function(
503-
context: &StringContext<'gc>,
504-
function: Gc<'gc, Avm1Function<'gc>>,
505-
fn_proto: Object<'gc>,
506-
prototype: Object<'gc>,
507-
) -> Object<'gc> {
508-
let function = Executable::Action(function);
509-
Self::allocate_function(context, function, None, fn_proto, Some(prototype))
492+
/// A function with AVM1 bytecode.
493+
pub fn bytecode(function: Gc<'gc, Avm1Function<'gc>>) -> Self {
494+
Self {
495+
function: Executable::Action(function),
496+
constructor: None,
497+
}
510498
}
511499

512-
/// Construct a function from a native executable and associated protos.
513-
pub fn native(
514-
context: &StringContext<'gc>,
515-
function: NativeFunction,
516-
fn_proto: Object<'gc>,
517-
prototype: Option<Object<'gc>>,
518-
) -> Object<'gc> {
519-
let function = Executable::Native(function);
520-
Self::allocate_function(context, function, None, fn_proto, prototype)
500+
/// A function with a native executable.
501+
pub fn native(function: NativeFunction) -> Self {
502+
Self {
503+
function: Executable::Native(function),
504+
constructor: None,
505+
}
521506
}
522507

523-
/// Construct a native constructor from native executables and associated protos.
508+
/// A native constructor.
524509
///
525510
/// This differs from [`Self::native`] in two important ways:
526511
/// - When called through `new`, the return value will always become the result of the
527512
/// operation. Native constructors should therefore generally return either `this`,
528513
/// if the object was successfully constructed, or `undefined` if not.
529514
/// - When called as a normal function, `function` will be called instead of `constructor`;
530515
/// if it is `None`, the return value will be `undefined`.
531-
pub fn constructor(
532-
context: &StringContext<'gc>,
533-
constructor: NativeFunction,
534-
function: Option<NativeFunction>,
535-
fn_proto: Object<'gc>,
536-
prototype: Object<'gc>,
537-
) -> Object<'gc> {
538-
Self::allocate_function(
539-
context,
540-
Executable::Native(function.unwrap_or(|_, _, _| Ok(Value::Undefined))),
541-
Some(constructor),
542-
fn_proto,
543-
Some(prototype),
544-
)
516+
pub fn constructor(constructor: NativeFunction, function: Option<NativeFunction>) -> Self {
517+
Self {
518+
function: Executable::Native(function.unwrap_or(|_, _, _| Ok(Value::Undefined))),
519+
constructor: Some(constructor),
520+
}
545521
}
546522

547523
/// Execute the given code.

core/src/avm1/property_decl.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ impl<'gc> DeclContext<'_, 'gc> {
3333

3434
pub fn empty_class(&self, super_proto: Object<'gc>) -> SystemClass<'gc> {
3535
let proto = Object::new(self.strings, Some(super_proto));
36-
let constr = FunctionObject::empty(self.strings, self.fn_proto, proto);
36+
let constr = FunctionObject::empty().build(self.strings, self.fn_proto, Some(proto));
3737
SystemClass { proto, constr }
3838
}
3939

4040
/// Creates a class with a 'normal' constructor. This should be used for classes whose constructor
4141
/// is implemented in bytecode in Flash Player's `playerglobals.swf`.
4242
pub fn class(&self, function: NativeFunction, super_proto: Object<'gc>) -> SystemClass<'gc> {
4343
let proto = Object::new(self.strings, Some(super_proto));
44-
let constr = FunctionObject::native(self.strings, function, self.fn_proto, Some(proto));
44+
let constr =
45+
FunctionObject::native(function).build(self.strings, self.fn_proto, Some(proto));
4546
SystemClass { proto, constr }
4647
}
4748

@@ -63,8 +64,11 @@ impl<'gc> DeclContext<'_, 'gc> {
6364
function: Option<NativeFunction>,
6465
proto: Object<'gc>,
6566
) -> SystemClass<'gc> {
66-
let constr =
67-
FunctionObject::constructor(self.strings, constructor, function, self.fn_proto, proto);
67+
let constr = FunctionObject::constructor(constructor, function).build(
68+
self.strings,
69+
self.fn_proto,
70+
Some(proto),
71+
);
6872
SystemClass { proto, constr }
6973
}
7074
}
@@ -127,16 +131,16 @@ impl Declaration {
127131
let name = context.intern_static(WStr::from_units(self.name));
128132
let value = match self.kind {
129133
DeclKind::Property { getter, setter } => {
130-
let getter = FunctionObject::native(context, getter, fn_proto, Some(fn_proto));
131-
let setter = setter.map(|setter| {
132-
FunctionObject::native(context, setter, fn_proto, Some(fn_proto))
133-
});
134+
// Property objects are unobservable by user code, so a bare function is enough.
135+
let getter = FunctionObject::native(getter).build(context, fn_proto, None);
136+
let setter = setter
137+
.map(|setter| FunctionObject::native(setter).build(context, fn_proto, None));
134138
this.add_property(mc, name.into(), getter, setter, self.attributes);
135139
return Value::Undefined;
136140
}
137-
DeclKind::Method(func) => FunctionObject::native(context, func, fn_proto, None).into(),
138-
DeclKind::Function(func) => {
139-
FunctionObject::native(context, func, fn_proto, Some(fn_proto)).into()
141+
DeclKind::Method(f) | DeclKind::Function(f) => {
142+
let p = matches!(self.kind, DeclKind::Function(_)).then_some(fn_proto);
143+
FunctionObject::native(f).build(context, fn_proto, p).into()
140144
}
141145
DeclKind::String(s) => context.intern_static(WStr::from_units(s)).into(),
142146
DeclKind::Bool(b) => b.into(),

core/src/avm1/value.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,6 @@ fn string_to_f64(mut s: &WStr, swf_version: u8) -> f64 {
891891
#[cfg(test)]
892892
#[expect(clippy::unreadable_literal)] // Large numeric literals in tests
893893
mod test {
894-
use crate::avm1::activation::Activation;
895894
use crate::avm1::error::Error;
896895
use crate::avm1::function::FunctionObject;
897896
use crate::avm1::globals::create_globals;
@@ -923,17 +922,8 @@ mod test {
923922

924923
assert_eq!(vglobal.to_primitive_num(activation).unwrap(), undefined);
925924

926-
fn value_of_impl<'gc>(
927-
_activation: &mut Activation<'_, 'gc>,
928-
_: Object<'gc>,
929-
_: &[Value<'gc>],
930-
) -> Result<Value<'gc>, Error<'gc>> {
931-
Ok(5.into())
932-
}
933-
934-
let valueof = FunctionObject::native(
925+
let valueof = FunctionObject::native(|_, _, _| Ok(5.into())).build(
935926
&activation.context.strings,
936-
value_of_impl,
937927
protos.function,
938928
Some(protos.function),
939929
);

0 commit comments

Comments
 (0)