diff --git a/core/build_playerglobal/src/lib.rs b/core/build_playerglobal/src/lib.rs index 9c77cfe12f90..0f400bb4aec4 100644 --- a/core/build_playerglobal/src/lib.rs +++ b/core/build_playerglobal/src/lib.rs @@ -27,15 +27,17 @@ const RUFFLE_METADATA_NAME: &str = "Ruffle"; // Indicates that we should generate a reference to an instance allocator // method (used as a metadata key with `Ruffle` metadata) const METADATA_INSTANCE_ALLOCATOR: &str = "InstanceAllocator"; -// Indicates that we should generate a reference to a native initializer -// method (used as a metadata key with `Ruffle` metadata) -const METADATA_SUPER_INITIALIZER: &str = "SuperInitializer"; /// Indicates that we should generate a reference to a class call handler /// method (used as a metadata key with `Ruffle` metadata) const METADATA_CALL_HANDLER: &str = "CallHandler"; /// Indicates that we should generate a reference to a custom constructor /// method (used as a metadata key with `Ruffle` metadata) const METADATA_CUSTOM_CONSTRUCTOR: &str = "CustomConstructor"; +/// Indicates that the class can't be directly instantiated (but its child classes might be). +/// Binds to an always-throwing allocator. +/// (This can also be used on final non-abstract classes that you just want to disable `new` for. +/// We just didn't find a better name for this concept than "abstract") +const METADATA_ABSTRACT: &str = "Abstract"; // The name for metadata for namespace versioning- the Flex SDK doesn't // strip versioning metadata, so we have to allow this metadata name const API_METADATA_NAME: &str = "API"; @@ -366,7 +368,6 @@ fn write_native_table(data: &[u8], out_dir: &Path) -> Result, Box Result, Box Result, Box { - rust_super_initializers[class_id as usize] = rust_method_name_and_path( - &abc, - trait_, - None, - "", - &super_init_method_name, - ); - } (None, METADATA_CALL_HANDLER) if !is_versioning => { rust_call_handlers[class_id as usize] = rust_method_name_and_path( &abc, @@ -484,6 +517,14 @@ fn write_native_table(data: &[u8], out_dir: &Path) -> Result, Box { + rust_instance_allocators[class_id as usize] = { + let path = "crate::avm2::object::abstract_class_allocator"; + let path_tokens = TokenStream::from_str(path).unwrap(); + let flash_method_path = "unused".to_string(); + quote! { Some((#flash_method_path, #path_tokens)) } + }; + } (None, _) if is_versioning => {} _ => panic!("Unexpected metadata pair ({key:?}, {value})"), } @@ -491,27 +532,10 @@ fn write_native_table(data: &[u8], out_dir: &Path) -> Result, Box Result, Box] = &[ - #(#rust_super_initializers,)* - ]; - - // This is very similar to `NATIVE_SUPER_INITIALIZER_TABLE`. + // This is very similar to `NATIVE_INSTANCE_ALLOCATOR_TABLE`. // When an entry is `Some(fn_ptr)`, we use `fn_ptr` as the native call // handler for the corresponding class when we load it into Ruffle. pub const NATIVE_CALL_HANDLER_TABLE: &[Option<(&'static str, crate::avm2::method::NativeMethodImpl)>] = &[ #(#rust_call_handlers,)* ]; - // This is very similar to `NATIVE_SUPER_INITIALIZER_TABLE`. + // This is very similar to `NATIVE_INSTANCE_ALLOCATOR_TABLE`. // When an entry is `Some(fn_ptr)`, we use `fn_ptr` as the native custom // constructor for the corresponding class when we load it into Ruffle. pub const NATIVE_CUSTOM_CONSTRUCTOR_TABLE: &[Option<(&'static str, crate::avm2::class::CustomConstructorFn)>] = &[ diff --git a/core/src/avm2.rs b/core/src/avm2.rs index 7b40575d3526..9fef5ed17363 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -147,9 +147,6 @@ pub struct Avm2<'gc> { #[collect(require_static)] native_instance_allocator_table: &'static [Option<(&'static str, AllocatorFn)>], - #[collect(require_static)] - native_super_initializer_table: &'static [Option<(&'static str, NativeMethodImpl)>], - #[collect(require_static)] native_call_handler_table: &'static [Option<(&'static str, NativeMethodImpl)>], @@ -223,7 +220,6 @@ impl<'gc> Avm2<'gc> { native_method_table: Default::default(), native_instance_allocator_table: Default::default(), - native_super_initializer_table: Default::default(), native_call_handler_table: Default::default(), native_custom_constructor_table: Default::default(), broadcast_list: Default::default(), diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index b3980a1cd366..7bddf88bed3d 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -552,7 +552,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { .bound_superclass_object .expect("Superclass object is required to run super_init"); - bound_superclass_object.call_super_init(receiver.into(), args, self) + bound_superclass_object.call_init(receiver.into(), args, self) } /// Retrieve a local register. diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index 25daf1611bb5..cccf7e22f2cb 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -139,21 +139,6 @@ pub struct ClassData<'gc> { /// Must be called each time a new class instance is constructed. instance_init: Method<'gc>, - /// The super initializer for this class, called when super() is called for - /// a subclass. - /// - /// This may be provided to allow natively-constructed classes to - /// initialize themselves in a different manner from user-constructed ones. - /// For example, the user-accessible constructor may error out (as it's not - /// a valid class to construct for users), but native code may still call - /// its constructor stack. - /// - /// By default, a class's `super_init` will be initialized to the - /// same method as the regular one. You must specify a separate super - /// initializer to change initialization behavior based on what code is - /// constructing the class. - super_init: Method<'gc>, - /// Traits for a given class. /// /// These are accessed as normal instance properties; they should not be @@ -238,8 +223,6 @@ impl<'gc> Class<'gc> { class_i_class: Class<'gc>, mc: &Mutation<'gc>, ) -> Self { - let super_init = instance_init; - let instance_allocator = super_class .map(|c| c.instance_allocator()) .unwrap_or(Allocator(scriptobject_allocator)); @@ -256,7 +239,6 @@ impl<'gc> Class<'gc> { all_interfaces: Vec::new(), instance_allocator, instance_init, - super_init, traits: Vec::new(), vtable: VTable::empty(mc), call_handler: None, @@ -287,7 +269,6 @@ impl<'gc> Class<'gc> { all_interfaces: Vec::new(), instance_allocator: Allocator(scriptobject_allocator), instance_init: class_init, - super_init: class_init, traits: Vec::new(), vtable: VTable::empty(mc), call_handler: None, @@ -311,8 +292,6 @@ impl<'gc> Class<'gc> { instance_init: Method<'gc>, mc: &Mutation<'gc>, ) -> Self { - let super_init = instance_init; - Class(GcCell::new( mc, ClassData { @@ -325,7 +304,6 @@ impl<'gc> Class<'gc> { all_interfaces: Vec::new(), instance_allocator: Allocator(scriptobject_allocator), instance_init, - super_init, traits: Vec::new(), vtable: VTable::empty(mc), call_handler: None, @@ -496,7 +474,6 @@ impl<'gc> Class<'gc> { } let instance_init = unit.load_method(abc_instance.init_method, false, activation)?; - let mut super_init = instance_init; let class_init = unit.load_method(abc_class.init_method, false, activation)?; let mut attributes = ClassAttributes::empty(); @@ -515,20 +492,6 @@ impl<'gc> Class<'gc> { [class_index as usize] .map(|(_name, ptr)| Allocator(ptr)); - if let Some((name, table_native_init)) = - activation.avm2().native_super_initializer_table[class_index as usize] - { - let method = Method::from_builtin_and_params( - table_native_init, - name, - instance_init.signature().to_vec(), - instance_init.return_type(), - instance_init.is_variadic(), - activation.context.gc_context, - ); - super_init = method; - } - if let Some((name, table_native_call_handler)) = activation.avm2().native_call_handler_table[class_index as usize] { @@ -569,7 +532,6 @@ impl<'gc> Class<'gc> { all_interfaces: Vec::new(), instance_allocator, instance_init, - super_init, traits: Vec::new(), vtable: VTable::empty(activation.context.gc_context), call_handler, @@ -603,7 +565,6 @@ impl<'gc> Class<'gc> { all_interfaces: Vec::new(), instance_allocator: Allocator(scriptobject_allocator), instance_init: class_init, - super_init: class_init, traits: Vec::new(), vtable: VTable::empty(activation.context.gc_context), call_handler: None, @@ -861,11 +822,6 @@ impl<'gc> Class<'gc> { "", activation.context.gc_context, ), - super_init: Method::from_builtin( - |_, _, _| Ok(Value::Undefined), - "", - activation.context.gc_context, - ), traits, vtable: VTable::empty(activation.context.gc_context), call_handler: None, @@ -903,11 +859,6 @@ impl<'gc> Class<'gc> { "", activation.context.gc_context, ), - super_init: Method::from_builtin( - |_, _, _| Ok(Value::Undefined), - "", - activation.context.gc_context, - ), traits: Vec::new(), vtable: VTable::empty(activation.context.gc_context), call_handler: None, @@ -1285,16 +1236,6 @@ impl<'gc> Class<'gc> { self.0.read().instance_init } - /// Get this class's super() initializer. - pub fn super_init(self) -> Method<'gc> { - self.0.read().super_init - } - - /// Set a super() initializer for this class. - pub fn set_super_init(self, mc: &Mutation<'gc>, new_super_init: Method<'gc>) { - self.0.write(mc).super_init = new_super_init; - } - /// Set a call handler for this class. pub fn set_call_handler(self, mc: &Mutation<'gc>, new_call_handler: Method<'gc>) { self.0.write(mc).call_handler = Some(new_call_handler); diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 9897937be185..2f3d53865169 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -1000,7 +1000,6 @@ fn load_playerglobal<'gc>( ) -> Result<(), Error<'gc>> { activation.avm2().native_method_table = native::NATIVE_METHOD_TABLE; activation.avm2().native_instance_allocator_table = native::NATIVE_INSTANCE_ALLOCATOR_TABLE; - activation.avm2().native_super_initializer_table = native::NATIVE_SUPER_INITIALIZER_TABLE; activation.avm2().native_call_handler_table = native::NATIVE_CALL_HANDLER_TABLE; activation.avm2().native_custom_constructor_table = native::NATIVE_CUSTOM_CONSTRUCTOR_TABLE; diff --git a/core/src/avm2/globals/boolean.rs b/core/src/avm2/globals/boolean.rs index 818066043f6a..8784d3723fc1 100644 --- a/core/src/avm2/globals/boolean.rs +++ b/core/src/avm2/globals/boolean.rs @@ -29,17 +29,6 @@ fn instance_init<'gc>( Ok(Value::Undefined) } -/// Implements `Boolean`'s native instance initializer. -fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, args)?; - - Ok(Value::Undefined) -} - /// Implements `Boolean`'s class initializer. fn class_init<'gc>( activation: &mut Activation<'_, 'gc>, @@ -147,10 +136,6 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { class.set_attributes(mc, ClassAttributes::FINAL | ClassAttributes::SEALED); class.set_instance_allocator(mc, primitive_allocator); - class.set_super_init( - mc, - Method::from_builtin(super_init, "", mc), - ); class.set_call_handler( mc, Method::from_builtin(call_handler, "", mc), diff --git a/core/src/avm2/globals/class.rs b/core/src/avm2/globals/class.rs index e91e2e60da9c..f624a6a67943 100644 --- a/core/src/avm2/globals/class.rs +++ b/core/src/avm2/globals/class.rs @@ -2,28 +2,27 @@ use crate::avm2::activation::Activation; use crate::avm2::class::{Class, ClassAttributes}; +use crate::avm2::error::type_error; use crate::avm2::method::{Method, NativeMethodImpl}; -use crate::avm2::object::{Object, TObject}; +use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Value; use crate::avm2::Error; use crate::avm2::QName; -/// Implements `Class`'s instance initializer. -/// -/// Notably, you cannot construct new classes this way, so this returns an -/// error. -pub fn instance_init<'gc>( - _activation: &mut Activation<'_, 'gc>, - _this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - Err("Classes cannot be constructed.".into()) +pub fn class_allocator<'gc>( + _class: ClassObject<'gc>, + activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + Err(Error::AvmError(type_error( + activation, + "Error #1115: Class$ is not a constructor.", + 1115, + )?)) } -/// Implements `Class`'s native instance initializer. -/// -/// This exists so that super() calls in class initializers will work. -fn super_init<'gc>( +/// Implements `Class`'s instance initializer. +/// This can only be called by subclasses (if at all), so in practice it's a noop. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc>, _this: Object<'gc>, _args: &[Value<'gc>], @@ -70,14 +69,7 @@ pub fn create_i_class<'gc>( // throws a VerifyError class_i_class.set_attributes(gc_context, ClassAttributes::FINAL); - class_i_class.set_super_init( - gc_context, - Method::from_builtin( - super_init, - "", - gc_context, - ), - ); + class_i_class.set_instance_allocator(gc_context, class_allocator); const PUBLIC_INSTANCE_PROPERTIES: &[( &str, diff --git a/core/src/avm2/globals/flash/display.rs b/core/src/avm2/globals/flash/display.rs index f5c0f49d82d5..d32d6de89dba 100644 --- a/core/src/avm2/globals/flash/display.rs +++ b/core/src/avm2/globals/flash/display.rs @@ -9,7 +9,6 @@ pub mod graphics; pub mod interactive_object; pub mod loader; pub mod loader_info; -pub mod morph_shape; pub mod movie_clip; pub mod shader_data; pub mod shader_job; diff --git a/core/src/avm2/globals/flash/display/DisplayObject.as b/core/src/avm2/globals/flash/display/DisplayObject.as index fcb483f4cfc5..d86f921b4e2d 100644 --- a/core/src/avm2/globals/flash/display/DisplayObject.as +++ b/core/src/avm2/globals/flash/display/DisplayObject.as @@ -10,14 +10,11 @@ package flash.display { import flash.geom.Point; import flash.events.EventDispatcher; - [Ruffle(InstanceAllocator)] - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public class DisplayObject extends EventDispatcher implements IBitmapDrawable { private var _accessibilityProperties:AccessibilityProperties; - public function DisplayObject() { - throw new Error("Cannot instantiate abstract DisplayObject class"); - } + public native function DisplayObject(); public function get accessibilityProperties():AccessibilityProperties { return this._accessibilityProperties; diff --git a/core/src/avm2/globals/flash/display/DisplayObjectContainer.as b/core/src/avm2/globals/flash/display/DisplayObjectContainer.as index d64479d46230..898ab09219ec 100644 --- a/core/src/avm2/globals/flash/display/DisplayObjectContainer.as +++ b/core/src/avm2/globals/flash/display/DisplayObjectContainer.as @@ -9,13 +9,8 @@ package flash.display { import flash.display.InteractiveObject; import flash.text.TextSnapshot; - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public class DisplayObjectContainer extends InteractiveObject { - - public function DisplayObjectContainer() { - throw new Error("You cannot construct DisplayObjectContainer directly."); - } - public native function get numChildren():int; public native function get mouseChildren():Boolean; public native function set mouseChildren(value:Boolean):void; diff --git a/core/src/avm2/globals/flash/display/InteractiveObject.as b/core/src/avm2/globals/flash/display/InteractiveObject.as index 5c6be1ea6202..648907472622 100644 --- a/core/src/avm2/globals/flash/display/InteractiveObject.as +++ b/core/src/avm2/globals/flash/display/InteractiveObject.as @@ -5,16 +5,12 @@ package flash.display { import flash.geom.Rectangle; import flash.ui.ContextMenu; - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public class InteractiveObject extends DisplayObject { private var _accessibilityImpl:AccessibilityImplementation = null; private var _needsSoftKeyboard:Boolean = false; private var _softKeyboardInputAreaOfInterest:Rectangle = new Rectangle(); - public function InteractiveObject() { - throw new Error("You cannot directly construct InteractiveObject.") - } - public function get accessibilityImplementation():AccessibilityImplementation { return this._accessibilityImpl; } diff --git a/core/src/avm2/globals/flash/display/LoaderInfo.as b/core/src/avm2/globals/flash/display/LoaderInfo.as index 4343dda6db71..02ccafcfc8c6 100644 --- a/core/src/avm2/globals/flash/display/LoaderInfo.as +++ b/core/src/avm2/globals/flash/display/LoaderInfo.as @@ -4,13 +4,8 @@ package flash.display { import flash.utils.ByteArray; import flash.events.UncaughtErrorEvents; - [Ruffle(InstanceAllocator)] - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public class LoaderInfo extends EventDispatcher { - public function LoaderInfo() { - throw new Error("LoaderInfo cannot be constructed"); - } - public native function get actionScriptVersion():uint; public native function get applicationDomain():ApplicationDomain; public native function get bytesLoaded():uint; diff --git a/core/src/avm2/globals/flash/display/MorphShape.as b/core/src/avm2/globals/flash/display/MorphShape.as index 9b4ad5454646..a9d332fd87ad 100644 --- a/core/src/avm2/globals/flash/display/MorphShape.as +++ b/core/src/avm2/globals/flash/display/MorphShape.as @@ -1,8 +1,5 @@ package flash.display { - [Ruffle(InstanceAllocator)] + [Ruffle(Abstract)] public final class MorphShape extends DisplayObject { - public function MorphShape() { - // We throw an error in `morph_shape_allocator` - } } } \ No newline at end of file diff --git a/core/src/avm2/globals/flash/display/Stage.as b/core/src/avm2/globals/flash/display/Stage.as index a500b45e2685..43cd1231bb58 100644 --- a/core/src/avm2/globals/flash/display/Stage.as +++ b/core/src/avm2/globals/flash/display/Stage.as @@ -10,17 +10,13 @@ package flash.display { import flash.text.TextSnapshot; import flash.ui.ContextMenu; - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public class Stage extends DisplayObjectContainer { private var _colorCorrection:String = ColorCorrection.DEFAULT; private var _mouseLock:Boolean = false; private var _nativeWindow:NativeWindow; private var _fullScreenSourceRect:Rectangle; - public function Stage() { - throw new Error("You cannot construct new instances of the Stage."); - } - override public function set accessibilityProperties(value:AccessibilityProperties):void { throw new IllegalOperationError("Error #2071: The Stage class does not implement this property or method.", 2071); } diff --git a/core/src/avm2/globals/flash/display/display_object.rs b/core/src/avm2/globals/flash/display/display_object.rs index 3ed49d2d5bdf..92f8737b269e 100644 --- a/core/src/avm2/globals/flash/display/display_object.rs +++ b/core/src/avm2/globals/flash/display/display_object.rs @@ -1,9 +1,7 @@ //! `flash.display.DisplayObject` builtin/prototype use crate::avm2::activation::Activation; -use crate::avm2::error::{ - argument_error, illegal_operation_error, make_error_2007, make_error_2008, -}; +use crate::avm2::error::{illegal_operation_error, make_error_2007, make_error_2008}; use crate::avm2::filters::FilterAvm2Ext; use crate::avm2::object::{Object, TObject}; use crate::avm2::parameters::ParametersExt; @@ -21,19 +19,6 @@ use ruffle_render::blend::ExtendedBlendMode; use ruffle_render::filters::Filter; use std::str::FromStr; -pub fn display_object_allocator<'gc>( - class: ClassObject<'gc>, - activation: &mut Activation<'_, 'gc>, -) -> Result, Error<'gc>> { - let class_name = class.inner_class_definition().name().local_name(); - - return Err(Error::AvmError(argument_error( - activation, - &format!("Error #2012: {class_name}$ class cannot be instantiated."), - 2012, - )?)); -} - /// Initializes a DisplayObject created from ActionScript. /// This should be called from the AVM2 class's native allocator /// (e.g. `sprite_allocator`) @@ -63,7 +48,7 @@ pub fn initialize_for_allocator<'gc>( } /// Implements `flash.display.DisplayObject`'s native instance constructor. -pub fn super_init<'gc>( +pub fn display_object_initializer<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], diff --git a/core/src/avm2/globals/flash/display/display_object_container.rs b/core/src/avm2/globals/flash/display/display_object_container.rs index 4adbfb95e6a8..5bc49c68c60e 100644 --- a/core/src/avm2/globals/flash/display/display_object_container.rs +++ b/core/src/avm2/globals/flash/display/display_object_container.rs @@ -15,17 +15,6 @@ use crate::display_object::HitTestOptions; use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer}; use std::cmp::min; -/// Implements `flash.display.DisplayObjectContainer`'s native instance constructor. -pub fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, &[])?; - - Ok(Value::Undefined) -} - /// Validate if we can add a child to a parent at a given index. /// /// There are several conditions which should cause an add operation to fail: diff --git a/core/src/avm2/globals/flash/display/interactive_object.rs b/core/src/avm2/globals/flash/display/interactive_object.rs index acd77c20d356..d187c6c8c937 100644 --- a/core/src/avm2/globals/flash/display/interactive_object.rs +++ b/core/src/avm2/globals/flash/display/interactive_object.rs @@ -8,17 +8,6 @@ use crate::avm2::value::Value; use crate::avm2::Error; use crate::display_object::{TDisplayObject, TInteractiveObject}; -/// Implements `flash.display.InteractiveObject`'s native instance constructor. -pub fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, &[])?; - - Ok(Value::Undefined) -} - /// Implements `InteractiveObject.mouseEnabled`'s getter. pub fn get_mouse_enabled<'gc>( _activation: &mut Activation<'_, 'gc>, diff --git a/core/src/avm2/globals/flash/display/loader_info.rs b/core/src/avm2/globals/flash/display/loader_info.rs index 1c3294cecf46..612e928c5263 100644 --- a/core/src/avm2/globals/flash/display/loader_info.rs +++ b/core/src/avm2/globals/flash/display/loader_info.rs @@ -11,22 +11,9 @@ use crate::loader::ContentType; use crate::{avm2_stub_getter, avm2_stub_method}; use swf::{write_swf, Compression}; -pub use crate::avm2::object::loader_info_allocator; - const INSUFFICIENT: &str = "Error #2099: The loading object is not sufficiently loaded to provide this information."; -/// Implements `flash.display.LoaderInfo`'s native instance constructor. -pub fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, &[])?; - - Ok(Value::Undefined) -} - /// `actionScriptVersion` getter pub fn get_action_script_version<'gc>( _activation: &mut Activation<'_, 'gc>, diff --git a/core/src/avm2/globals/flash/display/morph_shape.rs b/core/src/avm2/globals/flash/display/morph_shape.rs deleted file mode 100644 index e250728eccec..000000000000 --- a/core/src/avm2/globals/flash/display/morph_shape.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::avm2::{error::argument_error, Activation, ClassObject, Error, Object}; - -pub fn morph_shape_allocator<'gc>( - _class: ClassObject<'gc>, - activation: &mut Activation<'_, 'gc>, -) -> Result, Error<'gc>> { - // The actual instantiation happens in `MorphShape::construct_frame` - return Err(Error::AvmError(argument_error( - activation, - "Error #2012: MorphShape$ class cannot be instantiated.", - 2012, - )?)); -} diff --git a/core/src/avm2/globals/flash/display/stage.rs b/core/src/avm2/globals/flash/display/stage.rs index 2b1be0e4d5f9..4ed7bdab2344 100644 --- a/core/src/avm2/globals/flash/display/stage.rs +++ b/core/src/avm2/globals/flash/display/stage.rs @@ -14,17 +14,6 @@ use crate::display_object::{ use crate::string::{AvmString, WString}; use swf::Color; -/// Implements `flash.display.Stage`'s native instance constructor. -pub fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, args)?; - - Ok(Value::Undefined) -} - /// Implement `align`'s getter pub fn get_align<'gc>( activation: &mut Activation<'_, 'gc>, diff --git a/core/src/avm2/globals/flash/system/Worker.as b/core/src/avm2/globals/flash/system/Worker.as index 74c573a285dc..a9e9b9696ce3 100644 --- a/core/src/avm2/globals/flash/system/Worker.as +++ b/core/src/avm2/globals/flash/system/Worker.as @@ -2,11 +2,8 @@ package flash.system { import flash.events.EventDispatcher; [API("682")] + [Ruffle(Abstract)] public final class Worker extends EventDispatcher { - public function Worker() { - throw new ArgumentError("Error #2012: Worker$ class cannot be instantiated.", 2012); - } - public static function get isSupported():Boolean { return false; } diff --git a/core/src/avm2/globals/flash/text/engine/TextLine.as b/core/src/avm2/globals/flash/text/engine/TextLine.as index 183794670909..40a7736eeadc 100644 --- a/core/src/avm2/globals/flash/text/engine/TextLine.as +++ b/core/src/avm2/globals/flash/text/engine/TextLine.as @@ -12,7 +12,7 @@ package flash.text.engine { // the TextLine class in Ruffle, despite the methods working fine in FP- // however, it's unlikely that SWFs will actually attempt to add children // to a TextLine. - [Ruffle(SuperInitializer)] + [Ruffle(Abstract)] public final class TextLine extends DisplayObjectContainer { internal var _specifiedWidth:Number = 0.0; internal var _textBlock:TextBlock = null; @@ -23,10 +23,6 @@ package flash.text.engine { public var userData; - public function TextLine() { - throw new ArgumentError("Error #2012: TextLine$ class cannot be instantiated.", 2012); - } - public function get rawTextLength():int { return this._rawTextLength; } diff --git a/core/src/avm2/globals/flash/text/engine/text_block.rs b/core/src/avm2/globals/flash/text/engine/text_block.rs index ec918e6e60fd..d01c1ff6e193 100644 --- a/core/src/avm2/globals/flash/text/engine/text_block.rs +++ b/core/src/avm2/globals/flash/text/engine/text_block.rs @@ -81,7 +81,7 @@ pub fn create_text_line<'gc>( apply_format(activation, display_object, text.as_wstr(), element_format)?; let instance = initialize_for_allocator(activation, display_object.into(), class)?; - class.call_super_init(instance.into(), &[], activation)?; + class.call_init(instance.into(), &[], activation)?; instance.set_property( &Multiname::new(namespaces.flash_text_engine_internal, "_textBlock"), diff --git a/core/src/avm2/globals/flash/text/engine/text_line.rs b/core/src/avm2/globals/flash/text/engine/text_line.rs index 33a7ff3e81e0..be7de80329ff 100644 --- a/core/src/avm2/globals/flash/text/engine/text_line.rs +++ b/core/src/avm2/globals/flash/text/engine/text_line.rs @@ -4,15 +4,6 @@ use crate::avm2::object::{Object, TObject}; use crate::avm2::value::Value; use crate::display_object::TDisplayObject; -pub fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, &[])?; - Ok(Value::Undefined) -} - pub fn get_text_width<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, diff --git a/core/src/avm2/globals/int.rs b/core/src/avm2/globals/int.rs index db49a2f72448..8958afcea64d 100644 --- a/core/src/avm2/globals/int.rs +++ b/core/src/avm2/globals/int.rs @@ -29,17 +29,6 @@ fn instance_init<'gc>( Ok(Value::Undefined) } -/// Implements `int`'s native instance initializer. -fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, args)?; - - Ok(Value::Undefined) -} - /// Implements `int`'s class initializer. fn class_init<'gc>( activation: &mut Activation<'_, 'gc>, @@ -243,10 +232,6 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { class.set_attributes(mc, ClassAttributes::FINAL | ClassAttributes::SEALED); class.set_instance_allocator(mc, primitive_allocator); - class.set_super_init( - mc, - Method::from_builtin(super_init, "", mc), - ); class.set_call_handler( mc, Method::from_builtin(call_handler, "", mc), diff --git a/core/src/avm2/globals/number.rs b/core/src/avm2/globals/number.rs index a9400ef06dac..7a27cc05aa31 100644 --- a/core/src/avm2/globals/number.rs +++ b/core/src/avm2/globals/number.rs @@ -29,17 +29,6 @@ fn instance_init<'gc>( Ok(Value::Undefined) } -/// Implements `Number`'s native instance initializer. -fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, args)?; - - Ok(Value::Undefined) -} - /// Implements `Number`'s class initializer. fn class_init<'gc>( activation: &mut Activation<'_, 'gc>, @@ -387,10 +376,6 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { class.set_attributes(mc, ClassAttributes::FINAL | ClassAttributes::SEALED); class.set_instance_allocator(mc, primitive_allocator); - class.set_super_init( - mc, - Method::from_builtin(super_init, "", mc), - ); class.set_call_handler( mc, Method::from_builtin(call_handler, "", mc), diff --git a/core/src/avm2/globals/uint.rs b/core/src/avm2/globals/uint.rs index 3b71f73856ba..652173d3f68a 100644 --- a/core/src/avm2/globals/uint.rs +++ b/core/src/avm2/globals/uint.rs @@ -29,17 +29,6 @@ fn instance_init<'gc>( Ok(Value::Undefined) } -/// Implements `uint`'s native instance initializer. -fn super_init<'gc>( - activation: &mut Activation<'_, 'gc>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - activation.super_init(this, args)?; - - Ok(Value::Undefined) -} - /// Implements `uint`'s class initializer. fn class_init<'gc>( activation: &mut Activation<'_, 'gc>, @@ -244,10 +233,6 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { class.set_attributes(mc, ClassAttributes::FINAL | ClassAttributes::SEALED); class.set_instance_allocator(mc, primitive_allocator); - class.set_super_init( - mc, - Method::from_builtin(super_init, "", mc), - ); class.set_call_handler( mc, Method::from_builtin(call_handler, "", mc), diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 0d938e379dcc..741ff8df29ab 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -98,7 +98,7 @@ pub use crate::avm2::object::index_buffer_3d_object::{ IndexBuffer3DObject, IndexBuffer3DObjectWeak, }; pub use crate::avm2::object::loaderinfo_object::{ - loader_info_allocator, LoaderInfoObject, LoaderInfoObjectWeak, LoaderStream, + LoaderInfoObject, LoaderInfoObjectWeak, LoaderStream, }; pub use crate::avm2::object::local_connection_object::{ local_connection_allocator, LocalConnectionObject, LocalConnectionObjectWeak, @@ -1574,3 +1574,18 @@ impl<'gc> WeakObject<'gc> { }) } } + +/// Implements a custom allocator for classes that are not constructible. +/// (but their derived classes can be) +pub fn abstract_class_allocator<'gc>( + class: ClassObject<'gc>, + activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + let class_name = class.instance_class().name().local_name(); + + return Err(Error::AvmError(error::argument_error( + activation, + &format!("Error #2012: {class_name} class cannot be instantiated."), + 2012, + )?)); +} diff --git a/core/src/avm2/object/array_object.rs b/core/src/avm2/object/array_object.rs index 4c451e413db9..8fa28cfd1295 100644 --- a/core/src/avm2/object/array_object.rs +++ b/core/src/avm2/object/array_object.rs @@ -87,7 +87,7 @@ impl<'gc> ArrayObject<'gc> { )) .into(); - class.call_super_init(instance.into(), &[], activation)?; + class.call_init(instance.into(), &[], activation)?; Ok(instance) } diff --git a/core/src/avm2/object/bitmapdata_object.rs b/core/src/avm2/object/bitmapdata_object.rs index 779ed1503c5d..245866f67e78 100644 --- a/core/src/avm2/object/bitmapdata_object.rs +++ b/core/src/avm2/object/bitmapdata_object.rs @@ -94,7 +94,7 @@ impl<'gc> BitmapDataObject<'gc> { // when the custom class makes a super() call, the BitmapData constructor will // load in the real data from the linked SymbolClass. if class != activation.avm2().classes().bitmapdata { - class.call_super_init(instance.into(), &[1.into(), 1.into()], activation)?; + class.call_init(instance.into(), &[1.into(), 1.into()], activation)?; } Ok(instance) diff --git a/core/src/avm2/object/bytearray_object.rs b/core/src/avm2/object/bytearray_object.rs index 522f8b87815c..91d5fb2a5793 100644 --- a/core/src/avm2/object/bytearray_object.rs +++ b/core/src/avm2/object/bytearray_object.rs @@ -98,7 +98,7 @@ impl<'gc> ByteArrayObject<'gc> { )) .into(); - class.call_super_init(instance.into(), &[], activation)?; + class.call_init(instance.into(), &[], activation)?; Ok(instance) } diff --git a/core/src/avm2/object/class_object.rs b/core/src/avm2/object/class_object.rs index d86575f741b6..0315e147169c 100644 --- a/core/src/avm2/object/class_object.rs +++ b/core/src/avm2/object/class_object.rs @@ -355,32 +355,7 @@ impl<'gc> ClassObject<'gc> { activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { let scope = self.0.instance_scope.get(); - let method = self.constructor(); - exec( - method, - scope, - receiver.coerce_to_object(activation)?, - self.superclass_object(), - Some(self.inner_class_definition()), - arguments, - activation, - self.into(), - ) - } - - /// Call the instance's native initializer. - /// - /// The native initializer is called when native code needs to construct an - /// object, or when supercalling into a parent constructor (as there are - /// classes that cannot be constructed but can be supercalled). - pub fn call_super_init( - self, - receiver: Value<'gc>, - arguments: &[Value<'gc>], - activation: &mut Activation<'_, 'gc>, - ) -> Result, Error<'gc>> { - let scope = self.0.instance_scope.get(); - let method = self.super_constructor(); + let method = self.init_method(); exec( method, scope, @@ -660,21 +635,17 @@ impl<'gc> ClassObject<'gc> { } pub fn translation_unit(self) -> Option> { - if let Method::Bytecode(bc) = self.constructor() { + if let Method::Bytecode(bc) = self.init_method() { Some(bc.txunit) } else { None } } - pub fn constructor(self) -> Method<'gc> { + pub fn init_method(self) -> Method<'gc> { self.inner_class_definition().instance_init() } - pub fn super_constructor(self) -> Method<'gc> { - self.inner_class_definition().super_init() - } - pub fn call_handler(self) -> Option> { self.inner_class_definition().call_handler() } diff --git a/core/src/avm2/object/context3d_object.rs b/core/src/avm2/object/context3d_object.rs index 228bfdc24e2e..3cb046e9165e 100644 --- a/core/src/avm2/object/context3d_object.rs +++ b/core/src/avm2/object/context3d_object.rs @@ -49,7 +49,7 @@ impl<'gc> Context3DObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/avm2/object/date_object.rs b/core/src/avm2/object/date_object.rs index 31ece20d7bec..549cd4a5b558 100644 --- a/core/src/avm2/object/date_object.rs +++ b/core/src/avm2/object/date_object.rs @@ -56,7 +56,7 @@ impl<'gc> DateObject<'gc> { )) .into(); - class.call_super_init(instance.into(), &[], activation)?; + class.call_init(instance.into(), &[], activation)?; Ok(instance) } diff --git a/core/src/avm2/object/domain_object.rs b/core/src/avm2/object/domain_object.rs index 8d2eb36a8c30..dd566bd005e1 100644 --- a/core/src/avm2/object/domain_object.rs +++ b/core/src/avm2/object/domain_object.rs @@ -83,7 +83,7 @@ impl<'gc> DomainObject<'gc> { class .superclass_object() .unwrap() - .call_super_init(this.into(), &[], activation)?; + .call_init(this.into(), &[], activation)?; Ok(this) } } diff --git a/core/src/avm2/object/index_buffer_3d_object.rs b/core/src/avm2/object/index_buffer_3d_object.rs index 07c13cfb16ca..bdeddaf31c1f 100644 --- a/core/src/avm2/object/index_buffer_3d_object.rs +++ b/core/src/avm2/object/index_buffer_3d_object.rs @@ -37,7 +37,7 @@ impl<'gc> IndexBuffer3DObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/avm2/object/loaderinfo_object.rs b/core/src/avm2/object/loaderinfo_object.rs index dd0ff39d1ba7..045b7762f72a 100644 --- a/core/src/avm2/object/loaderinfo_object.rs +++ b/core/src/avm2/object/loaderinfo_object.rs @@ -1,9 +1,8 @@ //! Loader-info object use crate::avm2::activation::Activation; -use crate::avm2::error::argument_error; use crate::avm2::object::script_object::ScriptObjectData; -use crate::avm2::object::{ClassObject, Object, ObjectPtr, StageObject, TObject}; +use crate::avm2::object::{Object, ObjectPtr, StageObject, TObject}; use crate::avm2::Avm2; use crate::avm2::Error; use crate::avm2::EventObject; @@ -20,20 +19,6 @@ use gc_arena::{ use std::cell::{Cell, Ref}; use std::sync::Arc; -/// ActionScript cannot construct a LoaderInfo. Note that LoaderInfo isn't a final class. -pub fn loader_info_allocator<'gc>( - class: ClassObject<'gc>, - activation: &mut Activation<'_, 'gc>, -) -> Result, Error<'gc>> { - let class_name = class.inner_class_definition().name().local_name(); - - Err(Error::AvmError(argument_error( - activation, - &format!("Error #2012: {class_name}$ class cannot be instantiated."), - 2012, - )?)) -} - /// Represents a thing which can be loaded by a loader. #[derive(Collect, Clone)] #[collect(no_drop)] @@ -166,7 +151,7 @@ impl<'gc> LoaderInfoObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } @@ -213,7 +198,7 @@ impl<'gc> LoaderInfoObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } @@ -360,7 +345,7 @@ impl<'gc> LoaderInfoObject<'gc> { .expect("for_display_object cannot return Err"); class_object - .call_super_init(object.into(), &[], activation) + .call_init(object.into(), &[], activation) .expect("Native init should succeed"); unlock!( diff --git a/core/src/avm2/object/program_3d_object.rs b/core/src/avm2/object/program_3d_object.rs index 7f25ba563e86..65286bb490bc 100644 --- a/core/src/avm2/object/program_3d_object.rs +++ b/core/src/avm2/object/program_3d_object.rs @@ -37,7 +37,7 @@ impl<'gc> Program3DObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/avm2/object/regexp_object.rs b/core/src/avm2/object/regexp_object.rs index c4f6333b4272..5441add5fec3 100644 --- a/core/src/avm2/object/regexp_object.rs +++ b/core/src/avm2/object/regexp_object.rs @@ -76,7 +76,7 @@ impl<'gc> RegExpObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/avm2/object/soundchannel_object.rs b/core/src/avm2/object/soundchannel_object.rs index 5d8a5d303cc8..236b4dc44479 100644 --- a/core/src/avm2/object/soundchannel_object.rs +++ b/core/src/avm2/object/soundchannel_object.rs @@ -96,7 +96,7 @@ impl<'gc> SoundChannelObject<'gc> { }, )); - class.call_super_init(Value::Object(sound_object.into()), &[], activation)?; + class.call_init(Value::Object(sound_object.into()), &[], activation)?; Ok(sound_object) } diff --git a/core/src/avm2/object/stage_object.rs b/core/src/avm2/object/stage_object.rs index 607bd1a683da..6074ee586919 100644 --- a/core/src/avm2/object/stage_object.rs +++ b/core/src/avm2/object/stage_object.rs @@ -71,7 +71,7 @@ impl<'gc> StageObject<'gc> { ) -> Result> { let this = Self::for_display_object(activation, display_object, class)?; - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } @@ -86,7 +86,7 @@ impl<'gc> StageObject<'gc> { ) -> Result> { let this = Self::for_display_object(activation, display_object, class)?; - class.call_super_init(this.into(), args, activation)?; + class.call_init(this.into(), args, activation)?; Ok(this) } diff --git a/core/src/avm2/object/texture_object.rs b/core/src/avm2/object/texture_object.rs index b5247556c6ed..c19be52f78fc 100644 --- a/core/src/avm2/object/texture_object.rs +++ b/core/src/avm2/object/texture_object.rs @@ -37,7 +37,7 @@ impl<'gc> TextureObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/avm2/object/vertex_buffer_3d_object.rs b/core/src/avm2/object/vertex_buffer_3d_object.rs index 78a976f49e41..a244592accd1 100644 --- a/core/src/avm2/object/vertex_buffer_3d_object.rs +++ b/core/src/avm2/object/vertex_buffer_3d_object.rs @@ -38,7 +38,7 @@ impl<'gc> VertexBuffer3DObject<'gc> { )) .into(); - class.call_super_init(this.into(), &[], activation)?; + class.call_init(this.into(), &[], activation)?; Ok(this) } diff --git a/core/src/display_object/avm2_button.rs b/core/src/display_object/avm2_button.rs index bd752cf43b66..8bc3f1fe5908 100644 --- a/core/src/display_object/avm2_button.rs +++ b/core/src/display_object/avm2_button.rs @@ -552,8 +552,7 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> { if let Some(avm2_object) = self.0.object.get() { let mut activation = Avm2Activation::from_nothing(context); - if let Err(e) = class.call_super_init(avm2_object.into(), &[], &mut activation) - { + if let Err(e) = class.call_init(avm2_object.into(), &[], &mut activation) { tracing::error!("Got {} when constructing AVM2 side of button", e); } } diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 5a7703d0f249..5420a57b59e9 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -2208,7 +2208,7 @@ impl<'gc> MovieClip<'gc> { if let Avm2Value::Object(object) = self.object2() { let mut constr_thing = || { let mut activation = Avm2Activation::from_nothing(context); - class_object.call_super_init(object.into(), &[], &mut activation)?; + class_object.call_init(object.into(), &[], &mut activation)?; Ok(()) }; diff --git a/tests/tests/swfs/avm2/abstract_classes/Test.as b/tests/tests/swfs/avm2/abstract_classes/Test.as new file mode 100644 index 000000000000..424381a518f8 --- /dev/null +++ b/tests/tests/swfs/avm2/abstract_classes/Test.as @@ -0,0 +1,33 @@ +// compiled with mxmlc + +import flash.display.*; +import flash.text.engine.TextLine; +import flash.system.Worker; + +package { + import flash.display.MovieClip; + public class Test extends MovieClip { + public function Test() { + var classes = [ + Stage, + DisplayObject, + DisplayObjectContainer, + InteractiveObject, + LoaderInfo, + TextLine, + Class, + Math, + MorphShape, + Worker, + ] + for each(var t in classes) { + trace(t); + try { + new t(); + } catch(e) { + trace(e.getStackTrace()); + } + } + } + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/abstract_classes/output.txt b/tests/tests/swfs/avm2/abstract_classes/output.txt new file mode 100644 index 000000000000..17868665311b --- /dev/null +++ b/tests/tests/swfs/avm2/abstract_classes/output.txt @@ -0,0 +1,30 @@ +[class Stage] +ArgumentError: Error #2012: Stage$ class cannot be instantiated. + at Test() +[class DisplayObject] +ArgumentError: Error #2012: DisplayObject$ class cannot be instantiated. + at Test() +[class DisplayObjectContainer] +ArgumentError: Error #2012: DisplayObjectContainer$ class cannot be instantiated. + at Test() +[class InteractiveObject] +ArgumentError: Error #2012: InteractiveObject$ class cannot be instantiated. + at Test() +[class LoaderInfo] +ArgumentError: Error #2012: LoaderInfo$ class cannot be instantiated. + at Test() +[class TextLine] +ArgumentError: Error #2012: TextLine$ class cannot be instantiated. + at Test() +[class Class] +TypeError: Error #1115: Class$ is not a constructor. + at Test() +[class Math] +TypeError: Error #1076: Math is not a constructor. + at Test() +[class MorphShape] +ArgumentError: Error #2012: MorphShape$ class cannot be instantiated. + at Test() +[class Worker] +ArgumentError: Error #2012: Worker$ class cannot be instantiated. + at Test() diff --git a/tests/tests/swfs/avm2/abstract_classes/test.swf b/tests/tests/swfs/avm2/abstract_classes/test.swf new file mode 100644 index 000000000000..03f9bf628f29 Binary files /dev/null and b/tests/tests/swfs/avm2/abstract_classes/test.swf differ diff --git a/tests/tests/swfs/avm2/abstract_classes/test.toml b/tests/tests/swfs/avm2/abstract_classes/test.toml new file mode 100644 index 000000000000..dbee897f5863 --- /dev/null +++ b/tests/tests/swfs/avm2/abstract_classes/test.toml @@ -0,0 +1 @@ +num_frames = 1