@@ -14,7 +14,7 @@ use crate::string::{AvmString, StringContext, SwfStrExt as _};
1414use crate :: tag_utils:: SwfSlice ;
1515use gc_arena:: { Collect , Gc , Mutation } ;
1616use ruffle_macros:: istr;
17- use std:: { borrow:: Cow , fmt , num:: NonZeroU8 } ;
17+ use std:: { borrow:: Cow , num:: NonZeroU8 } ;
1818use swf:: { avm1:: types:: FunctionFlags , SwfStr } ;
1919
2020/// Represents a function defined in Ruffle's code.
@@ -406,7 +406,7 @@ struct Param<'gc> {
406406/// AVM1 bytecode itself.
407407#[ derive( Copy , Clone , Collect ) ]
408408#[ collect( no_drop) ]
409- pub enum Executable < ' gc > {
409+ enum Executable < ' gc > {
410410 /// A function provided by the Ruffle runtime and implemented in Rust.
411411 Native ( #[ collect( require_static) ] NativeFunction ) ,
412412
@@ -415,21 +415,6 @@ pub enum Executable<'gc> {
415415 Action ( Gc < ' gc , Avm1Function < ' gc > > ) ,
416416}
417417
418- impl fmt:: Debug for Executable < ' _ > {
419- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
420- match self {
421- Executable :: Native ( nf) => f
422- . debug_tuple ( "Executable::Native" )
423- . field ( & format ! ( "{nf:p}" ) )
424- . finish ( ) ,
425- Executable :: Action ( af) => f
426- . debug_tuple ( "Executable::Action" )
427- . field ( & Gc :: as_ptr ( * af) )
428- . finish ( ) ,
429- }
430- }
431- }
432-
433418/// Indicates the default name to use for this execution in debug builds.
434419pub enum ExecutionName < ' gc > {
435420 Static ( & ' static str ) ,
@@ -448,46 +433,7 @@ impl<'gc> From<AvmString<'gc>> for ExecutionName<'gc> {
448433 }
449434}
450435
451- impl < ' gc > Executable < ' gc > {
452- /// A dummy `Executable` that does nothing, and returns `undefined`.
453- const EMPTY : Self = Self :: Native ( |_, _, _| Ok ( Value :: Undefined ) ) ;
454-
455- /// Execute the given code.
456- #[ expect( clippy:: too_many_arguments) ]
457- pub fn exec (
458- & self ,
459- name : ExecutionName < ' gc > ,
460- activation : & mut Activation < ' _ , ' gc > ,
461- this : Value < ' gc > ,
462- depth : u8 ,
463- args : & [ Value < ' gc > ] ,
464- reason : ExecutionReason ,
465- callee : Object < ' gc > ,
466- ) -> Result < Value < ' gc > , Error < ' gc > > {
467- match self {
468- Executable :: Native ( nf) => {
469- // TODO: Change NativeFunction to accept `this: Value`.
470- let this = this. coerce_to_object ( activation) ;
471- nf ( activation, this, args)
472- }
473- Executable :: Action ( af) => af. exec ( name, activation, this, depth, args, reason, callee) ,
474- }
475- }
476- }
477-
478- impl From < NativeFunction > for Executable < ' _ > {
479- fn from ( nf : NativeFunction ) -> Self {
480- Executable :: Native ( nf)
481- }
482- }
483-
484- impl < ' gc > From < Gc < ' gc , Avm1Function < ' gc > > > for Executable < ' gc > {
485- fn from ( af : Gc < ' gc , Avm1Function < ' gc > > ) -> Self {
486- Executable :: Action ( af)
487- }
488- }
489-
490- #[ derive( Collect ) ]
436+ #[ derive( Copy , Clone , Collect ) ]
491437#[ collect( no_drop) ]
492438pub struct FunctionObject < ' gc > {
493439 /// The code that will be invoked when this object is called.
@@ -500,12 +446,17 @@ pub struct FunctionObject<'gc> {
500446}
501447
502448impl < ' gc > FunctionObject < ' gc > {
503- /// Construct a function sans prototype.
504- pub fn bare_function (
449+ /// Construct a function with any combination of regular and constructor parts.
450+ ///
451+ /// `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 (
505455 context : & StringContext < ' gc > ,
506456 function : Executable < ' gc > ,
507457 constructor : Option < NativeFunction > ,
508458 fn_proto : Object < ' gc > ,
459+ prototype : Option < Object < ' gc > > ,
509460 ) -> Object < ' gc > {
510461 let obj = Object :: new ( context, Some ( fn_proto) ) ;
511462 let native = NativeObject :: Function ( Gc :: new (
@@ -516,37 +467,23 @@ impl<'gc> FunctionObject<'gc> {
516467 } ,
517468 ) ) ;
518469 obj. set_native ( context. gc ( ) , native) ;
519- obj
520- }
521470
522- /// Construct a function with any combination of regular and constructor parts.
523- ///
524- /// `fn_proto` refers to the implicit proto of the function object, and the
525- /// `prototype` refers to the explicit prototype of the function.
526- /// The function and its prototype will be linked to each other.
527- fn allocate_function (
528- context : & StringContext < ' gc > ,
529- function : Executable < ' gc > ,
530- constructor : Option < NativeFunction > ,
531- fn_proto : Object < ' gc > ,
532- prototype : Object < ' gc > ,
533- ) -> Object < ' gc > {
534- let function = Self :: bare_function ( context, function, constructor, fn_proto) ;
535-
536- prototype. define_value (
537- context. gc ( ) ,
538- istr ! ( context, "constructor" ) ,
539- Value :: Object ( function) ,
540- Attribute :: DONT_ENUM ,
541- ) ;
542- function. define_value (
543- context. gc ( ) ,
544- istr ! ( context, "prototype" ) ,
545- prototype. into ( ) ,
546- Attribute :: empty ( ) ,
547- ) ;
471+ if let Some ( prototype) = prototype {
472+ prototype. define_value (
473+ context. gc ( ) ,
474+ istr ! ( context, "constructor" ) ,
475+ Value :: Object ( obj) ,
476+ Attribute :: DONT_ENUM ,
477+ ) ;
478+ obj. define_value (
479+ context. gc ( ) ,
480+ istr ! ( context, "prototype" ) ,
481+ prototype. into ( ) ,
482+ Attribute :: empty ( ) ,
483+ ) ;
484+ }
548485
549- function
486+ obj
550487 }
551488
552489 /// Constructs a function that does nothing.
@@ -557,7 +494,8 @@ impl<'gc> FunctionObject<'gc> {
557494 fn_proto : Object < ' gc > ,
558495 prototype : Object < ' gc > ,
559496 ) -> Object < ' gc > {
560- Self :: allocate_function ( context, Executable :: EMPTY , None , fn_proto, prototype)
497+ let empty = Executable :: Native ( |_, _, _| Ok ( Value :: Undefined ) ) ;
498+ Self :: allocate_function ( context, empty, None , fn_proto, Some ( prototype) )
561499 }
562500
563501 /// Construct a function from AVM1 bytecode and associated protos.
@@ -567,15 +505,16 @@ impl<'gc> FunctionObject<'gc> {
567505 fn_proto : Object < ' gc > ,
568506 prototype : Object < ' gc > ,
569507 ) -> Object < ' gc > {
570- Self :: allocate_function ( context, function. into ( ) , None , fn_proto, prototype)
508+ let function = Executable :: Action ( function) ;
509+ Self :: allocate_function ( context, function, None , fn_proto, Some ( prototype) )
571510 }
572511
573512 /// Construct a function from a native executable and associated protos.
574513 pub fn native (
575514 context : & StringContext < ' gc > ,
576515 function : NativeFunction ,
577516 fn_proto : Object < ' gc > ,
578- prototype : Object < ' gc > ,
517+ prototype : Option < Object < ' gc > > ,
579518 ) -> Object < ' gc > {
580519 let function = Executable :: Native ( function) ;
581520 Self :: allocate_function ( context, function, None , fn_proto, prototype)
@@ -601,35 +540,71 @@ impl<'gc> FunctionObject<'gc> {
601540 Executable :: Native ( function. unwrap_or ( |_, _, _| Ok ( Value :: Undefined ) ) ) ,
602541 Some ( constructor) ,
603542 fn_proto,
604- prototype,
543+ Some ( prototype) ,
605544 )
606545 }
607546
608- pub fn as_executable ( & self ) -> Executable < ' gc > {
609- self . function
610- }
611-
612- pub fn as_constructor ( & self ) -> Executable < ' gc > {
613- if let Some ( constr) = self . constructor {
614- Executable :: Native ( constr)
615- } else {
616- self . function
547+ /// Execute the given code.
548+ ///
549+ /// This is fairly low-level; prefer using other call methods if possible.
550+ #[ expect( clippy:: too_many_arguments) ]
551+ pub fn exec (
552+ self ,
553+ name : ExecutionName < ' gc > ,
554+ activation : & mut Activation < ' _ , ' gc > ,
555+ this : Value < ' gc > ,
556+ depth : u8 ,
557+ args : & [ Value < ' gc > ] ,
558+ reason : ExecutionReason ,
559+ callee : Object < ' gc > ,
560+ ) -> Result < Value < ' gc > , Error < ' gc > > {
561+ match self . function {
562+ Executable :: Native ( nf) => {
563+ // TODO: Change NativeFunction to accept `this: Value`.
564+ let this = this. coerce_to_object ( activation) ;
565+ nf ( activation, this, args)
566+ }
567+ Executable :: Action ( af) => af. exec ( name, activation, this, depth, args, reason, callee) ,
617568 }
618569 }
619570
620- pub fn is_native_constructor ( & self ) -> bool {
621- self . constructor . is_some ( )
571+ /// Execute the given code as a constructor.
572+ ///
573+ /// This is fairly low-level; prefer using other call methods if possible.
574+ #[ expect( clippy:: too_many_arguments) ]
575+ pub fn exec_constructor (
576+ self ,
577+ name : ExecutionName < ' gc > ,
578+ activation : & mut Activation < ' _ , ' gc > ,
579+ this : Value < ' gc > ,
580+ depth : u8 ,
581+ args : & [ Value < ' gc > ] ,
582+ reason : ExecutionReason ,
583+ callee : Object < ' gc > ,
584+ ) -> Result < Value < ' gc > , Error < ' gc > > {
585+ let constr = match self . constructor {
586+ Some ( constr) => Executable :: Native ( constr) ,
587+ None => self . function ,
588+ } ;
589+ match constr {
590+ Executable :: Native ( nf) => {
591+ // TODO: Change NativeFunction to accept `this: Value`.
592+ let this = this. coerce_to_object ( activation) ;
593+ nf ( activation, this, args)
594+ }
595+ Executable :: Action ( af) => af. exec ( name, activation, this, depth, args, reason, callee) ,
596+ }
622597 }
623598
624599 pub fn call (
625- & self ,
600+ self ,
626601 name : impl Into < ExecutionName < ' gc > > ,
627602 activation : & mut Activation < ' _ , ' gc > ,
628603 callee : Object < ' gc > ,
629604 this : Value < ' gc > ,
630605 args : & [ Value < ' gc > ] ,
631606 ) -> Result < Value < ' gc > , Error < ' gc > > {
632- self . function . exec (
607+ self . exec (
633608 name. into ( ) ,
634609 activation,
635610 this,
@@ -641,7 +616,7 @@ impl<'gc> FunctionObject<'gc> {
641616 }
642617
643618 pub fn construct_on_existing (
644- & self ,
619+ self ,
645620 activation : & mut Activation < ' _ , ' gc > ,
646621 callee : Object < ' gc > ,
647622 this : Object < ' gc > ,
@@ -650,7 +625,7 @@ impl<'gc> FunctionObject<'gc> {
650625 Self :: define_constructor_props ( activation, this, callee. into ( ) ) ;
651626
652627 // Always ignore the constructor's return value.
653- let _ = self . as_constructor ( ) . exec (
628+ let _ = self . exec_constructor (
654629 ExecutionName :: Static ( "[ctor]" ) ,
655630 activation,
656631 this. into ( ) ,
@@ -664,7 +639,7 @@ impl<'gc> FunctionObject<'gc> {
664639 }
665640
666641 pub fn construct (
667- & self ,
642+ self ,
668643 activation : & mut Activation < ' _ , ' gc > ,
669644 callee : Object < ' gc > ,
670645 args : & [ Value < ' gc > ] ,
@@ -676,7 +651,9 @@ impl<'gc> FunctionObject<'gc> {
676651
677652 Self :: define_constructor_props ( activation, this, callee. into ( ) ) ;
678653
679- let result = self . as_constructor ( ) . exec (
654+ // Propagate the return value only for native constructors.
655+ let propagate = self . constructor . is_some ( ) ;
656+ let ret = self . exec_constructor (
680657 ExecutionName :: Static ( "[ctor]" ) ,
681658 activation,
682659 this. into ( ) ,
@@ -685,13 +662,7 @@ impl<'gc> FunctionObject<'gc> {
685662 ExecutionReason :: FunctionCall ,
686663 callee,
687664 ) ?;
688-
689- if self . is_native_constructor ( ) {
690- // Propagate the native method's return value.
691- Ok ( result)
692- } else {
693- Ok ( this. into ( ) )
694- }
665+ Ok ( if propagate { ret } else { this. into ( ) } )
695666 }
696667
697668 fn define_constructor_props (
0 commit comments