@@ -18,19 +18,6 @@ use swf::{avm1::types::FunctionFlags, SwfStr};
1818use super :: NativeObject ;
1919
2020/// Represents a function defined in Ruffle's code.
21- ///
22- /// Parameters are as follows:
23- ///
24- /// * The AVM1 runtime
25- /// * The action context
26- /// * The current `this` object
27- /// * The arguments this function was called with
28- ///
29- /// Native functions are allowed to return a value or `None`. `None` indicates
30- /// that the given value will not be returned on the stack and instead will
31- /// resolve on the AVM stack, as if you had called a non-native function. If
32- /// your function yields `None`, you must ensure that the top-most activation
33- /// in the AVM1 runtime will return with the value of this function.
3421pub type NativeFunction = for <' gc > fn (
3522 & mut Activation < ' _ , ' gc > ,
3623 Object < ' gc > ,
@@ -336,6 +323,9 @@ impl<'gc> From<AvmString<'gc>> for ExecutionName<'gc> {
336323}
337324
338325impl < ' gc > Executable < ' gc > {
326+ /// A dummy `Executable` that does nothing, and returns `undefined`.
327+ const EMPTY : Self = Self :: Native ( |_, _, _| Ok ( Value :: Undefined ) ) ;
328+
339329 /// Execute the given code.
340330 ///
341331 /// Execution is not guaranteed to have completed when this function
@@ -489,6 +479,8 @@ pub struct FunctionObject<'gc> {
489479 /// The code that will be invoked when this object is called.
490480 function : Executable < ' gc > ,
491481 /// The code that will be invoked when this object is constructed.
482+ ///
483+ /// If `None`, falls back to `function`.
492484 #[ collect( require_static) ]
493485 constructor : Option < NativeFunction > ,
494486}
@@ -546,27 +538,56 @@ impl<'gc> FunctionObject<'gc> {
546538 function
547539 }
548540
549- /// Construct a regular function from an executable and associated protos.
541+ /// Constructs a function that does nothing.
542+ ///
543+ /// This can also serve as a no-op constructor.
544+ pub fn empty (
545+ context : & StringContext < ' gc > ,
546+ fn_proto : Object < ' gc > ,
547+ prototype : Object < ' gc > ,
548+ ) -> Object < ' gc > {
549+ Self :: allocate_function ( context, Executable :: EMPTY , None , fn_proto, prototype)
550+ }
551+
552+ /// Construct a function from AVM1 bytecode and associated protos.
550553 pub fn function (
551554 context : & StringContext < ' gc > ,
552- function : impl Into < Executable < ' gc > > ,
555+ function : Gc < ' gc , Avm1Function < ' gc > > ,
553556 fn_proto : Object < ' gc > ,
554557 prototype : Object < ' gc > ,
555558 ) -> Object < ' gc > {
556559 Self :: allocate_function ( context, function. into ( ) , None , fn_proto, prototype)
557560 }
558561
559- /// Construct a regular and constructor function from an executable and associated protos.
562+ /// Construct a function from a native executable and associated protos.
563+ pub fn native (
564+ context : & StringContext < ' gc > ,
565+ function : NativeFunction ,
566+ fn_proto : Object < ' gc > ,
567+ prototype : Object < ' gc > ,
568+ ) -> Object < ' gc > {
569+ let function = Executable :: Native ( function) ;
570+ Self :: allocate_function ( context, function, None , fn_proto, prototype)
571+ }
572+
573+ /// Construct a native constructor from native executables and associated protos.
574+ ///
575+ /// This differs from [`Self::native`] in two important ways:
576+ /// - When called through `new`, the return value will always become the result of the
577+ /// operation. Native constructors should therefore generally return either `this`,
578+ /// if the object was successfully constructed, or `undefined` if not.
579+ /// - When called as a normal function, `function` will be called instead of `constructor`;
580+ /// if it is `None`, the return value will be `undefined`.
560581 pub fn constructor (
561582 context : & StringContext < ' gc > ,
562583 constructor : NativeFunction ,
563- function : impl Into < Executable < ' gc > > ,
584+ function : Option < NativeFunction > ,
564585 fn_proto : Object < ' gc > ,
565586 prototype : Object < ' gc > ,
566587 ) -> Object < ' gc > {
567588 Self :: allocate_function (
568589 context,
569- function. into ( ) ,
590+ Executable :: Native ( function. unwrap_or ( |_ , _ , _| Ok ( Value :: Undefined ) ) ) ,
570591 Some ( constructor) ,
571592 fn_proto,
572593 prototype,
@@ -585,6 +606,10 @@ impl<'gc> FunctionObject<'gc> {
585606 }
586607 }
587608
609+ pub fn is_native_constructor ( & self ) -> bool {
610+ self . constructor . is_some ( )
611+ }
612+
588613 pub fn call (
589614 & self ,
590615 name : impl Into < ExecutionName < ' gc > > ,
@@ -611,6 +636,7 @@ impl<'gc> FunctionObject<'gc> {
611636 this : Object < ' gc > ,
612637 args : & [ Value < ' gc > ] ,
613638 ) -> Result < ( ) , Error < ' gc > > {
639+ // TODO: de-duplicate code.
614640 this. define_value (
615641 activation. gc ( ) ,
616642 istr ! ( "__constructor__" ) ,
@@ -625,7 +651,8 @@ impl<'gc> FunctionObject<'gc> {
625651 Attribute :: DONT_ENUM ,
626652 ) ;
627653 }
628- // TODO: de-duplicate code.
654+
655+ // Always ignore the constructor's return value.
629656 let _ = self . as_constructor ( ) . exec (
630657 ExecutionName :: Static ( "[ctor]" ) ,
631658 activation,
@@ -635,6 +662,7 @@ impl<'gc> FunctionObject<'gc> {
635662 ExecutionReason :: FunctionCall ,
636663 callee,
637664 ) ?;
665+
638666 Ok ( ( ) )
639667 }
640668
@@ -647,8 +675,9 @@ impl<'gc> FunctionObject<'gc> {
647675 let prototype = callee
648676 . get ( istr ! ( "prototype" ) , activation) ?
649677 . coerce_to_object ( activation) ;
650- let this = prototype . create_bare_object ( activation, prototype) ? ;
678+ let this = ScriptObject :: new ( activation. strings ( ) , Some ( prototype) ) ;
651679
680+ // TODO: de-duplicate code.
652681 this. define_value (
653682 activation. gc ( ) ,
654683 istr ! ( "__constructor__" ) ,
@@ -663,42 +692,22 @@ impl<'gc> FunctionObject<'gc> {
663692 Attribute :: DONT_ENUM ,
664693 ) ;
665694 }
666- // TODO: de-duplicate code.
667- if let Some ( constr) = & self . constructor {
668- // Native constructors will return the constructed `this`.
669- // This allows for `new Object` etc. returning different types.
670- constr ( activation, this, args)
695+
696+ let result = self . as_constructor ( ) . exec (
697+ ExecutionName :: Static ( "[ctor]" ) ,
698+ activation,
699+ this. into ( ) ,
700+ 1 ,
701+ args,
702+ ExecutionReason :: FunctionCall ,
703+ callee,
704+ ) ?;
705+
706+ if self . is_native_constructor ( ) {
707+ // Propagate the native method's return value.
708+ Ok ( result)
671709 } else {
672- let _ = self . function . exec (
673- ExecutionName :: Static ( "[ctor]" ) ,
674- activation,
675- this. into ( ) ,
676- 1 ,
677- args,
678- ExecutionReason :: FunctionCall ,
679- callee,
680- ) ?;
681710 Ok ( this. into ( ) )
682711 }
683712 }
684713}
685-
686- /// Turns a simple built-in constructor into a function that discards
687- /// the constructor return value.
688- /// Use with `FunctionObject::constructor` when defining constructor of
689- /// built-in objects.
690- #[ macro_export]
691- macro_rules! constructor_to_fn {
692- ( $f: expr) => { {
693- fn _constructor_fn<' gc>(
694- activation: & mut $crate:: avm1:: activation:: Activation <' _, ' gc>,
695- this: $crate:: avm1:: Object <' gc>,
696- args: & [ $crate:: avm1:: Value <' gc>] ,
697- ) -> Result <$crate:: avm1:: Value <' gc>, $crate:: avm1:: error:: Error <' gc>> {
698- #[ allow( clippy:: redundant_closure_call) ]
699- let _ = $f( activation, this, args) ?;
700- Ok ( $crate:: avm1:: Value :: Undefined )
701- }
702- $crate:: avm1:: function:: Executable :: Native ( _constructor_fn)
703- } } ;
704- }
0 commit comments