From 2c9028d2f98c6b7c43d0c3f3899e99f3312e01e4 Mon Sep 17 00:00:00 2001 From: Lord-McSweeney Date: Sun, 9 Jun 2024 09:46:06 -0700 Subject: [PATCH] avm2: Replace most uses of `instance_of` with `instance_class` Also store a Class -> alias mapping instead of a ClassObject -> alias mapping for AMF. --- core/src/avm2.rs | 8 +- core/src/avm2/activation.rs | 6 +- core/src/avm2/amf.rs | 28 ++-- core/src/avm2/class.rs | 103 +++++++++++---- core/src/avm2/error.rs | 10 +- core/src/avm2/globals.rs | 7 +- .../avm2/globals/flash/display/bitmap_data.rs | 4 +- .../avm2/globals/flash/display/graphics.rs | 114 +++++++++++++--- core/src/avm2/globals/flash/display/stage.rs | 2 +- core/src/avm2/globals/flash/media/video.rs | 2 +- .../flash/system/application_domain.rs | 2 +- core/src/avm2/globals/flash/utils.rs | 12 +- core/src/avm2/globals/vector.rs | 51 +++----- core/src/avm2/object.rs | 26 ++-- core/src/avm2/object/class_object.rs | 122 ++++++------------ core/src/avm2/object/error_object.rs | 4 +- core/src/avm2/object/primitive_object.rs | 2 +- core/src/avm2/object/script_object.rs | 23 ++-- core/src/avm2/object/vector_object.rs | 21 +-- core/src/avm2/optimize.rs | 15 +-- core/src/avm2/scope.rs | 7 +- core/src/avm2/script.rs | 6 +- core/src/avm2/vector.rs | 37 +++--- core/src/debug_ui/avm2.rs | 2 +- core/src/display_object/bitmap.rs | 7 +- 25 files changed, 342 insertions(+), 279 deletions(-) diff --git a/core/src/avm2.rs b/core/src/avm2.rs index cc96d2863..f5b216ab1 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -72,6 +72,7 @@ mod vtable; pub use crate::avm2::activation::Activation; pub use crate::avm2::array::ArrayStorage; pub use crate::avm2::call_stack::{CallNode, CallStack}; +pub use crate::avm2::class::Class; #[allow(unused)] // For debug_ui pub use crate::avm2::domain::{Domain, DomainPtr}; pub use crate::avm2::error::Error; @@ -179,7 +180,7 @@ pub struct Avm2<'gc> { orphan_objects: Rc>>, alias_to_class_map: FnvHashMap, ClassObject<'gc>>, - class_to_alias_map: FnvHashMap, AvmString<'gc>>, + class_to_alias_map: FnvHashMap, AvmString<'gc>>, /// The api version of our root movie clip. Note - this is used as the /// api version for swfs loaded via `Loader`, overriding the api version @@ -293,14 +294,15 @@ impl<'gc> Avm2<'gc> { pub fn register_class_alias(&mut self, name: AvmString<'gc>, class_object: ClassObject<'gc>) { self.alias_to_class_map.insert(name, class_object); - self.class_to_alias_map.insert(class_object, name); + self.class_to_alias_map + .insert(class_object.inner_class_definition(), name); } pub fn get_class_by_alias(&self, name: AvmString<'gc>) -> Option> { self.alias_to_class_map.get(&name).copied() } - pub fn get_alias_by_class(&self, cls: ClassObject<'gc>) -> Option> { + pub fn get_alias_by_class(&self, cls: Class<'gc>) -> Option> { self.class_to_alias_map.get(&cls).copied() } diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index 28aacf6e5..13f9b0546 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -1632,6 +1632,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { fn op_get_outer_scope(&mut self, index: u32) -> Result, Error<'gc>> { // Verifier ensures that this points to a valid outer scope + let scope = self.outer.get_unchecked(index as usize); self.push_stack(scope.values()); @@ -1730,10 +1731,9 @@ impl<'a, 'gc> Activation<'a, 'gc> { } else { // Even if it's an object with the "descendants" property, we won't support it. let class_name = object - .instance_of() + .instance_class() .map(|cls| { - cls.inner_class_definition() - .name() + cls.name() .to_qualified_name_err_message(self.context.gc_context) }) .unwrap_or_else(|| AvmString::from("")); diff --git a/core/src/avm2/amf.rs b/core/src/avm2/amf.rs index b67003b6d..77e800a3b 100644 --- a/core/src/avm2/amf.rs +++ b/core/src/avm2/amf.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use crate::avm2::bytearray::ByteArrayStorage; +use crate::avm2::class::Class; use crate::avm2::object::{ByteArrayObject, ClassObject, TObject, VectorObject}; use crate::avm2::vector::VectorStorage; use crate::avm2::ArrayObject; @@ -72,7 +73,7 @@ pub fn serialize_value<'gc>( Some(AmfValue::ECMAArray(dense, sparse, len)) } else if let Some(vec) = o.as_vector_storage() { let val_type = vec.value_type(); - if val_type == Some(activation.avm2().classes().int) { + if val_type == Some(activation.avm2().classes().int.inner_class_definition()) { let int_vec: Vec<_> = vec .iter() .map(|v| { @@ -81,7 +82,9 @@ pub fn serialize_value<'gc>( }) .collect(); Some(AmfValue::VectorInt(int_vec, vec.is_fixed())) - } else if val_type == Some(activation.avm2().classes().uint) { + } else if val_type + == Some(activation.avm2().classes().uint.inner_class_definition()) + { let uint_vec: Vec<_> = vec .iter() .map(|v| { @@ -90,7 +93,9 @@ pub fn serialize_value<'gc>( }) .collect(); Some(AmfValue::VectorUInt(uint_vec, vec.is_fixed())) - } else if val_type == Some(activation.avm2().classes().number) { + } else if val_type + == Some(activation.avm2().classes().number.inner_class_definition()) + { let num_vec: Vec<_> = vec .iter() .map(|v| { @@ -108,7 +113,8 @@ pub fn serialize_value<'gc>( }) .collect(); - let val_type = val_type.unwrap_or(activation.avm2().classes().object); + let val_type = val_type + .unwrap_or(activation.avm2().classes().object.inner_class_definition()); let name = class_to_alias(activation, val_type); Some(AmfValue::VectorObject(obj_vec, name, vec.is_fixed())) @@ -125,11 +131,11 @@ pub fn serialize_value<'gc>( } else if let Some(bytearray) = o.as_bytearray() { Some(AmfValue::ByteArray(bytearray.bytes().to_vec())) } else { - let class = o.instance_of().expect("Missing ClassObject"); + let class = o.instance_class().expect("Missing Class"); let name = class_to_alias(activation, class); let mut attributes = EnumSet::empty(); - if !class.inner_class_definition().is_sealed() { + if !class.is_sealed() { attributes.insert(Attribute::Dynamic); } @@ -173,7 +179,7 @@ fn alias_to_class<'gc>( } } -fn class_to_alias<'gc>(activation: &mut Activation<'_, 'gc>, class: ClassObject<'gc>) -> String { +fn class_to_alias<'gc>(activation: &mut Activation<'_, 'gc>, class: Class<'gc>) -> String { if let Some(alias) = activation.avm2().get_alias_by_class(class) { alias.to_string() } else { @@ -359,7 +365,7 @@ pub fn deserialize_value<'gc>( let storage = VectorStorage::from_values( vec.iter().map(|v| (*v).into()).collect(), *is_fixed, - Some(activation.avm2().classes().number), + Some(activation.avm2().classes().number.inner_class_definition()), ); VectorObject::from_vector(storage, activation)?.into() } @@ -367,7 +373,7 @@ pub fn deserialize_value<'gc>( let storage = VectorStorage::from_values( vec.iter().map(|v| (*v).into()).collect(), *is_fixed, - Some(activation.avm2().classes().uint), + Some(activation.avm2().classes().uint.inner_class_definition()), ); VectorObject::from_vector(storage, activation)?.into() } @@ -375,7 +381,7 @@ pub fn deserialize_value<'gc>( let storage = VectorStorage::from_values( vec.iter().map(|v| (*v).into()).collect(), *is_fixed, - Some(activation.avm2().classes().int), + Some(activation.avm2().classes().int.inner_class_definition()), ); VectorObject::from_vector(storage, activation)?.into() } @@ -397,7 +403,7 @@ pub fn deserialize_value<'gc>( }) .collect::, _>>()?, *is_fixed, - Some(class), + Some(class.inner_class_definition()), ); VectorObject::from_vector(storage, activation)?.into() } diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index f6a118361..69902b5ac 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -104,6 +104,9 @@ pub struct ClassData<'gc> { /// superinterfaces, nor interfaces implemented by the superclass. direct_interfaces: Vec>, + /// The list of all interfaces implemented by this class. + all_interfaces: Vec>, + /// The instance allocator for this class. /// /// If `None`, then instances of this object will be allocated the same way @@ -163,7 +166,7 @@ pub struct ClassData<'gc> { /// Maps a type parameter to the application of this class with that parameter. /// /// Only applicable if this class is generic. - applications: FnvHashMap>, Class<'gc>>, + applications: FnvHashMap>, Class<'gc>>, /// Whether or not this is a system-defined class. /// @@ -184,32 +187,20 @@ impl PartialEq for Class<'_> { } } +impl Eq for Class<'_> {} + +impl Hash for Class<'_> { + fn hash(&self, state: &mut H) { + self.0.as_ptr().hash(state); + } +} + impl<'gc> core::fmt::Debug for Class<'gc> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("Class").field("name", &self.name()).finish() } } -/// Allows using a `Class<'gc>` as a HashMap key, -/// using the pointer address for hashing/equality. -#[derive(Collect, Copy, Clone)] -#[collect(no_drop)] -struct ClassKey<'gc>(Class<'gc>); - -impl PartialEq for ClassKey<'_> { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for ClassKey<'_> {} - -impl Hash for ClassKey<'_> { - fn hash(&self, state: &mut H) { - self.0 .0.as_ptr().hash(state); - } -} - impl<'gc> Class<'gc> { /// Create a new class. /// @@ -237,6 +228,7 @@ impl<'gc> Class<'gc> { attributes: ClassAttributes::empty(), protected_namespace: None, direct_interfaces: Vec::new(), + all_interfaces: Vec::new(), instance_allocator: None, instance_init, native_instance_init, @@ -255,8 +247,7 @@ impl<'gc> Class<'gc> { } pub fn add_application(self, mc: &Mutation<'gc>, param: Option>, cls: Class<'gc>) { - let key = param.map(ClassKey); - self.0.write(mc).applications.insert(key, cls); + self.0.write(mc).applications.insert(param, cls); } /// Apply type parameters to an existing class. @@ -271,9 +262,7 @@ impl<'gc> Class<'gc> { let mc = context.gc_context; let this_read = this.0.read(); - let key = param.map(ClassKey); - - if let Some(application) = this_read.applications.get(&key) { + if let Some(application) = this_read.applications.get(¶m) { return *application; } @@ -312,7 +301,7 @@ impl<'gc> Class<'gc> { drop(this_read); - this.0.write(mc).applications.insert(key, new_class); + this.0.write(mc).applications.insert(Some(param), new_class); new_class } @@ -464,6 +453,7 @@ impl<'gc> Class<'gc> { attributes, protected_namespace, direct_interfaces: interfaces, + all_interfaces: Vec::new(), instance_allocator, instance_init, native_instance_init, @@ -658,7 +648,7 @@ impl<'gc> Class<'gc> { // interfaces (i.e. those that were not already implemented by the superclass) // Otherwise, our behavior diverges from Flash Player in certain cases. // See the ignored test 'tests/tests/swfs/avm2/weird_superinterface_properties/' - for interface in interfaces { + for interface in &interfaces { for interface_trait in &*interface.instance_traits() { if !interface_trait.name().namespace().is_public() { let public_name = QName::new( @@ -674,6 +664,8 @@ impl<'gc> Class<'gc> { } } + self.0.write(context.gc_context).all_interfaces = interfaces; + Ok(()) } @@ -704,6 +696,7 @@ impl<'gc> Class<'gc> { attributes: ClassAttributes::empty(), protected_namespace: None, direct_interfaces: Vec::new(), + all_interfaces: Vec::new(), instance_allocator: None, instance_init: Method::from_builtin( |_, _, _| Ok(Value::Undefined), @@ -737,6 +730,36 @@ impl<'gc> Class<'gc> { Ok(class) } + /// Determine if this class has a given type in its superclass chain. + /// + /// The given class `test_class` should be either a superclass or + /// interface we are checking against this class. + /// + /// To test if a class *instance* is of a given type, see `Object::is_of_type`. + pub fn has_class_in_chain(self, test_class: Class<'gc>) -> bool { + let mut my_class = Some(self); + + while let Some(class) = my_class { + if class == test_class { + return true; + } + + my_class = class.super_class() + } + + // A `Class` stores all of the interfaces it implements, including + // those from superinterfaces and superclasses (recursively). + if test_class.is_interface() { + for interface in &*self.all_interfaces() { + if *interface == test_class { + return true; + } + } + } + + false + } + pub fn instance_vtable(self) -> VTable<'gc> { self.0.read().instance_vtable } @@ -749,6 +772,26 @@ impl<'gc> Class<'gc> { self.0.try_read().map(|r| r.name) } + /// Attempts to obtain the name of this class. + /// If we are unable to read from the necessary `GcCell`, + /// the returned value will be some kind of error message. + /// + /// This should only be used in a debug context, where + /// we need infallible access to *something* to print + /// out. + pub fn debug_name(self) -> Box { + let class_name = self.try_name(); + + match class_name { + Ok(class_name) => Box::new(class_name), + Err(err) => Box::new(err), + } + } + + pub fn param(self) -> Option>> { + self.0.read().param + } + pub fn set_param(self, mc: &Mutation<'gc>, param: Option>>) { self.0.write(mc).param = param; } @@ -1079,6 +1122,10 @@ impl<'gc> Class<'gc> { Ref::map(self.0.read(), |c| &c.direct_interfaces) } + pub fn all_interfaces(&self) -> Ref>> { + Ref::map(self.0.read(), |c| &c.all_interfaces) + } + /// Determine if this class is sealed (no dynamic properties) pub fn is_sealed(self) -> bool { self.0.read().attributes.contains(ClassAttributes::SEALED) diff --git a/core/src/avm2/error.rs b/core/src/avm2/error.rs index 72ad5da41..b6a96bde0 100644 --- a/core/src/avm2/error.rs +++ b/core/src/avm2/error.rs @@ -1,10 +1,7 @@ use ruffle_wstr::WString; use crate::avm2::object::TObject; -use crate::avm2::Activation; -use crate::avm2::AvmString; -use crate::avm2::Multiname; -use crate::avm2::Value; +use crate::avm2::{Activation, AvmString, Class, Multiname, Value}; use std::borrow::Cow; use std::fmt::Debug; use std::mem::size_of; @@ -94,13 +91,12 @@ pub fn make_reference_error<'gc>( activation: &mut Activation<'_, 'gc>, code: ReferenceErrorCode, multiname: &Multiname<'gc>, - object_class: Option>, + object_class: Option>, ) -> Error<'gc> { let qualified_name = multiname.as_uri(activation.context.gc_context); let class_name = object_class .map(|cls| { - cls.inner_class_definition() - .name() + cls.name() .to_qualified_name_err_message(activation.context.gc_context) }) .unwrap_or_else(|| AvmString::from("")); diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 8b1eac154..e2b83236b 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -419,18 +419,17 @@ fn vector_class<'gc>( let mc = activation.context.gc_context; let (_, global, mut domain) = script.init(); - let cls = param_class.map(|c| c.inner_class_definition()); + let param_class = param_class.map(|c| c.inner_class_definition()); let vector_cls = class( - vector::create_builtin_class(activation, cls), + vector::create_builtin_class(activation, param_class), script, activation, )?; - vector_cls.set_param(mc, Some(param_class)); let generic_vector = activation.avm2().classes().generic_vector; generic_vector.add_application(mc, param_class, vector_cls); let generic_cls = generic_vector.inner_class_definition(); - generic_cls.add_application(mc, cls, vector_cls.inner_class_definition()); + generic_cls.add_application(mc, param_class, vector_cls.inner_class_definition()); let legacy_name = QName::new(activation.avm2().vector_internal_namespace, legacy_name); global.install_const_late( diff --git a/core/src/avm2/globals/flash/display/bitmap_data.rs b/core/src/avm2/globals/flash/display/bitmap_data.rs index b6d925cf2..dcf0ffd3c 100644 --- a/core/src/avm2/globals/flash/display/bitmap_data.rs +++ b/core/src/avm2/globals/flash/display/bitmap_data.rs @@ -92,7 +92,7 @@ pub fn init<'gc>( ) -> Result, Error<'gc>> { // We set the underlying BitmapData instance - we start out with a dummy BitmapDataWrapper, // which makes custom classes see a disposed BitmapData before they call super() - let name = this.instance_of_class_definition().map(|c| c.name()); + let name = this.instance_class().map(|c| c.name()); let character = this .instance_of() .and_then(|t| { @@ -374,7 +374,7 @@ pub fn get_vector<'gc>( height, ); - let value_type = activation.avm2().classes().uint; + let value_type = activation.avm2().classes().uint.inner_class_definition(); let new_storage = VectorStorage::from_values(pixels, false, Some(value_type)); return Ok(VectorObject::from_vector(new_storage, activation)?.into()); diff --git a/core/src/avm2/globals/flash/display/graphics.rs b/core/src/avm2/globals/flash/display/graphics.rs index 65dd6aa6d..251032c36 100644 --- a/core/src/avm2/globals/flash/display/graphics.rs +++ b/core/src/avm2/globals/flash/display/graphics.rs @@ -1166,7 +1166,11 @@ pub fn read_graphics_data<'gc>( _args: &[Value<'gc>], ) -> Result, Error<'gc>> { avm2_stub_method!(activation, "flash.display.Graphics", "readGraphicsData"); - let value_type = activation.avm2().classes().igraphicsdata; + let value_type = activation + .avm2() + .classes() + .igraphicsdata + .inner_class_definition(); let new_storage = VectorStorage::new(0, false, Some(value_type), activation); Ok(VectorObject::from_vector(new_storage, activation)?.into()) } @@ -1298,17 +1302,41 @@ fn handle_igraphics_data<'gc>( drawing: &mut Drawing, obj: &Object<'gc>, ) -> Result<(), Error<'gc>> { - let class = obj.instance_of().expect("No class"); + let class = obj.instance_class().expect("No class"); - if class == activation.avm2().classes().graphicsbitmapfill { + if class + == activation + .avm2() + .classes() + .graphicsbitmapfill + .inner_class_definition() + { let style = handle_bitmap_fill(activation, drawing, obj)?; drawing.set_fill_style(Some(style)); - } else if class == activation.avm2().classes().graphicsendfill { + } else if class + == activation + .avm2() + .classes() + .graphicsendfill + .inner_class_definition() + { drawing.set_fill_style(None); - } else if class == activation.avm2().classes().graphicsgradientfill { + } else if class + == activation + .avm2() + .classes() + .graphicsgradientfill + .inner_class_definition() + { let style = handle_gradient_fill(activation, obj)?; drawing.set_fill_style(Some(style)); - } else if class == activation.avm2().classes().graphicspath { + } else if class + == activation + .avm2() + .classes() + .graphicspath + .inner_class_definition() + { let commands = obj .get_public_property("commands", activation)? .coerce_to_object(activation)?; @@ -1330,13 +1358,31 @@ fn handle_igraphics_data<'gc>( .expect("commands is not a Vector"), &data.as_vector_storage().expect("data is not a Vector"), )?; - } else if class == activation.avm2().classes().graphicssolidfill { + } else if class + == activation + .avm2() + .classes() + .graphicssolidfill + .inner_class_definition() + { let style = handle_solid_fill(activation, obj)?; drawing.set_fill_style(Some(style)); - } else if class == activation.avm2().classes().graphicsshaderfill { + } else if class + == activation + .avm2() + .classes() + .graphicsshaderfill + .inner_class_definition() + { tracing::warn!("Graphics shader fill unimplemented {:?}", class); drawing.set_fill_style(None); - } else if class == activation.avm2().classes().graphicsstroke { + } else if class + == activation + .avm2() + .classes() + .graphicsstroke + .inner_class_definition() + { let thickness = obj .get_public_property("thickness", activation)? .coerce_to_number(activation)?; @@ -1392,7 +1438,13 @@ fn handle_igraphics_data<'gc>( drawing.set_line_style(Some(line_style)); } - } else if class == activation.avm2().classes().graphicstrianglepath { + } else if class + == activation + .avm2() + .classes() + .graphicstrianglepath + .inner_class_definition() + { handle_graphics_triangle_path(activation, drawing, obj)?; } else { panic!("Unknown graphics data class {:?}", class); @@ -1528,20 +1580,50 @@ fn handle_igraphics_fill<'gc>( drawing: &mut Drawing, obj: &Object<'gc>, ) -> Result, Error<'gc>> { - let class = obj.instance_of().expect("No class"); + let class = obj.instance_class().expect("No class"); - if class == activation.avm2().classes().graphicsbitmapfill { + if class + == activation + .avm2() + .classes() + .graphicsbitmapfill + .inner_class_definition() + { let style = handle_bitmap_fill(activation, drawing, obj)?; Ok(Some(style)) - } else if class == activation.avm2().classes().graphicsendfill { + } else if class + == activation + .avm2() + .classes() + .graphicsendfill + .inner_class_definition() + { Ok(None) - } else if class == activation.avm2().classes().graphicsgradientfill { + } else if class + == activation + .avm2() + .classes() + .graphicsgradientfill + .inner_class_definition() + { let style = handle_gradient_fill(activation, obj)?; Ok(Some(style)) - } else if class == activation.avm2().classes().graphicssolidfill { + } else if class + == activation + .avm2() + .classes() + .graphicssolidfill + .inner_class_definition() + { let style = handle_solid_fill(activation, obj)?; Ok(Some(style)) - } else if class == activation.avm2().classes().graphicsshaderfill { + } else if class + == activation + .avm2() + .classes() + .graphicsshaderfill + .inner_class_definition() + { tracing::warn!("Graphics shader fill unimplemented {:?}", class); Ok(None) } else { diff --git a/core/src/avm2/globals/flash/display/stage.rs b/core/src/avm2/globals/flash/display/stage.rs index 9425beaba..20bb59f19 100644 --- a/core/src/avm2/globals/flash/display/stage.rs +++ b/core/src/avm2/globals/flash/display/stage.rs @@ -423,7 +423,7 @@ pub fn get_stage3ds<'gc>( .map(|obj| Value::Object(*obj)) .collect(), false, - Some(activation.avm2().classes().stage3d), + Some(activation.avm2().classes().stage3d.inner_class_definition()), ); let stage3ds = VectorObject::from_vector(storage, activation)?; return Ok(stage3ds.into()); diff --git a/core/src/avm2/globals/flash/media/video.rs b/core/src/avm2/globals/flash/media/video.rs index 7648d8ce2..cdcf05f1a 100644 --- a/core/src/avm2/globals/flash/media/video.rs +++ b/core/src/avm2/globals/flash/media/video.rs @@ -68,7 +68,7 @@ pub fn attach_net_stream<'gc>( return Err(format!( "Cannot use value of type {:?} as video source", source - .and_then(|o| o.instance_of_class_definition()) + .and_then(|o| o.instance_class()) .map(|c| c.name().local_name()) .unwrap_or_else(|| "Object".into()) ) diff --git a/core/src/avm2/globals/flash/system/application_domain.rs b/core/src/avm2/globals/flash/system/application_domain.rs index 2c3d4c95e..961602aba 100644 --- a/core/src/avm2/globals/flash/system/application_domain.rs +++ b/core/src/avm2/globals/flash/system/application_domain.rs @@ -123,7 +123,7 @@ pub fn get_qualified_definition_names<'gc>( .map(|name| Value::String(name.to_qualified_name(activation.context.gc_context))) .collect(), false, - Some(activation.avm2().classes().string), + Some(activation.avm2().classes().string.inner_class_definition()), ); let name_vector = VectorObject::from_vector(storage, activation)?; diff --git a/core/src/avm2/globals/flash/utils.rs b/core/src/avm2/globals/flash/utils.rs index 5d88578a9..fac3c95c7 100644 --- a/core/src/avm2/globals/flash/utils.rs +++ b/core/src/avm2/globals/flash/utils.rs @@ -199,15 +199,14 @@ pub fn get_qualified_class_name<'gc>( let obj = val.coerce_to_object(activation)?; let class = match obj.as_class_object() { - Some(class) => class, - None => match obj.instance_of() { + Some(class) => class.inner_class_definition(), + None => match obj.instance_class() { Some(cls) => cls, None => return Ok(Value::Null), }, }; Ok(class - .inner_class_definition() .name() .to_qualified_name(activation.context.gc_context) .into()) @@ -225,16 +224,15 @@ pub fn get_qualified_superclass_name<'gc>( .coerce_to_object(activation)?; let class = match obj.as_class_object() { - Some(class) => class, - None => match obj.instance_of() { + Some(class) => class.inner_class_definition(), + None => match obj.instance_class() { Some(cls) => cls, None => return Ok(Value::Null), }, }; - if let Some(super_class) = class.superclass_object() { + if let Some(super_class) = class.super_class() { Ok(super_class - .inner_class_definition() .name() .to_qualified_name(activation.context.gc_context) .into()) diff --git a/core/src/avm2/globals/vector.rs b/core/src/avm2/globals/vector.rs index 466dcb0b5..21f335d28 100644 --- a/core/src/avm2/globals/vector.rs +++ b/core/src/avm2/globals/vector.rs @@ -74,13 +74,14 @@ fn class_call<'gc>( let this_class = activation.subclass_object().unwrap(); let value_type = this_class - .as_class_params() + .inner_class_definition() + .param() .ok_or("Cannot convert to unparametrized Vector")?; // technically unreachable let arg = args.get(0).cloned().unwrap(); let arg = arg.as_object().ok_or("Cannot convert to Vector")?; - if arg.instance_of() == Some(this_class) { + if arg.instance_class() == Some(this_class.inner_class_definition()) { return Ok(arg.into()); } @@ -91,9 +92,7 @@ fn class_call<'gc>( let mut new_storage = VectorStorage::new(0, false, value_type, activation); new_storage.reserve_exact(length as usize); - let value_type_for_coercion = new_storage - .value_type_for_coercion(activation) - .inner_class_definition(); + let value_type_for_coercion = new_storage.value_type_for_coercion(activation); let mut iter = ArrayIter::new(activation, arg)?; @@ -248,9 +247,7 @@ pub fn concat<'gc>( return Err("Not a vector-structured object".into()); }; - let val_class = new_vector_storage - .value_type_for_coercion(activation) - .inner_class_definition(); + let val_class = new_vector_storage.value_type_for_coercion(activation); for arg in args { let arg_obj = arg @@ -260,10 +257,10 @@ pub fn concat<'gc>( // this is Vector. let my_base_vector_class = activation .subclass_object() - .expect("Method call without bound class?"); - if !arg.is_of_type(activation, my_base_vector_class.inner_class_definition()) { + .expect("Method call without bound class?") + .inner_class_definition(); + if !arg.is_of_type(activation, my_base_vector_class) { let base_vector_name = my_base_vector_class - .inner_class_definition() .name() .to_qualified_name_err_message(activation.context.gc_context); @@ -289,7 +286,7 @@ pub fn concat<'gc>( if let Ok(val_obj) = val.coerce_to_object(activation) { if !val.is_of_type(activation, val_class) { let other_val_class = val_obj - .instance_of_class_definition() + .instance_class() .ok_or("TypeError: Tried to concat a bare object into a Vector")?; return Err(format!( "TypeError: Cannot coerce Vector value of type {:?} to type {:?}", @@ -449,9 +446,9 @@ pub fn filter<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let value_type = this - .instance_of() + .instance_class() .unwrap() - .as_class_params() + .param() .ok_or("Cannot filter unparameterized vector")?; // technically unreachable let mut new_storage = VectorStorage::new(0, false, value_type, activation); let mut iter = ArrayIter::new(activation, this)?; @@ -578,14 +575,12 @@ pub fn map<'gc>( let receiver = args.get(1).cloned().unwrap_or(Value::Null); let value_type = this - .instance_of() + .instance_class() .unwrap() - .as_class_params() + .param() .ok_or("Cannot filter unparameterized vector")?; // technically unreachable let mut new_storage = VectorStorage::new(0, false, value_type, activation); - let value_type_for_coercion = new_storage - .value_type_for_coercion(activation) - .inner_class_definition(); + let value_type_for_coercion = new_storage.value_type_for_coercion(activation); let mut iter = ArrayIter::new(activation, this)?; while let Some(r) = iter.next(activation) { @@ -620,9 +615,7 @@ pub fn push<'gc>( args: &[Value<'gc>], ) -> Result, Error<'gc>> { if let Some(mut vs) = this.as_vector_storage_mut(activation.context.gc_context) { - let value_type = vs - .value_type_for_coercion(activation) - .inner_class_definition(); + let value_type = vs.value_type_for_coercion(activation); // Pushing nothing will still throw if the Vector is fixed. vs.check_fixed(activation)?; @@ -659,9 +652,7 @@ pub fn unshift<'gc>( args: &[Value<'gc>], ) -> Result, Error<'gc>> { if let Some(mut vs) = this.as_vector_storage_mut(activation.context.gc_context) { - let value_type = vs - .value_type_for_coercion(activation) - .inner_class_definition(); + let value_type = vs.value_type_for_coercion(activation); for arg in args.iter().rev() { let coerced_arg = arg.coerce_to_type(activation, value_type)?; @@ -687,9 +678,9 @@ pub fn insert_at<'gc>( .cloned() .unwrap_or(Value::Undefined) .coerce_to_i32(activation)?; - let value_type = vs - .value_type_for_coercion(activation) - .inner_class_definition(); + + let value_type = vs.value_type_for_coercion(activation); + let value = args .get(1) .cloned() @@ -877,9 +868,7 @@ pub fn splice<'gc>( .unwrap_or(Value::Undefined) .coerce_to_i32(activation)?; let value_type = vs.value_type(); - let value_type_for_coercion = vs - .value_type_for_coercion(activation) - .inner_class_definition(); + let value_type_for_coercion = vs.value_type_for_coercion(activation); let start = vs.clamp_parameter_index(start_len); let end = max( diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index db71c0008..159a367d6 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -268,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::ReadFromWriteOnly, multiname, - self.instance_of(), + self.instance_class(), )); } None => self.get_property_local(multiname, activation), @@ -359,7 +359,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::AssignToMethod, multiname, - self.instance_of(), + self.instance_class(), )); } Some(Property::Virtual { set: Some(set), .. }) => { @@ -370,7 +370,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::WriteToReadOnly, multiname, - self.instance_of(), + self.instance_class(), )); } None => self.set_property_local(multiname, value, activation), @@ -437,7 +437,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::AssignToMethod, multiname, - self.instance_of(), + self.instance_class(), )); } Some(Property::Virtual { set: Some(set), .. }) => { @@ -448,7 +448,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::WriteToReadOnly, multiname, - self.instance_of(), + self.instance_class(), )); } None => self.init_property_local(multiname, value, activation), @@ -518,7 +518,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::ReadFromWriteOnly, multiname, - self.instance_of(), + self.instance_class(), )); } None => self.call_property_local(multiname, arguments, activation), @@ -705,14 +705,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation, error::ReferenceErrorCode::InvalidDelete, multiname, - self.instance_of(), + self.instance_class(), )); } match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { None => { if self - .instance_of_class_definition() + .instance_class() .map(|c| c.is_sealed()) .unwrap_or(false) { @@ -968,7 +968,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// coercions. fn to_string(&self, activation: &mut Activation<'_, 'gc>) -> Result, Error<'gc>> { let class_name = self - .instance_of_class_definition() + .instance_class() .map(|c| c.name().local_name()) .unwrap_or_else(|| "Object".into()); @@ -988,7 +988,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { let class_name = self - .instance_of_class_definition() + .instance_class() .map(|c| c.name().local_name()) .unwrap_or_else(|| "Object".into()); @@ -1086,7 +1086,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// The given object should be the class object for the given type we are /// checking against this object. fn is_of_type(&self, test_class: Class<'gc>, context: &mut UpdateContext<'_, 'gc>) -> bool { - let my_class = self.instance_of(); + let my_class = self.instance_class(); // ES3 objects are not class instances but are still treated as // instances of Object, which is an ES4 class. @@ -1123,13 +1123,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy } /// Get this object's class's `Class`, if it has one. - fn instance_of_class_definition(&self) -> Option> { + fn instance_class(&self) -> Option> { self.instance_of().map(|cls| cls.inner_class_definition()) } /// Get this object's class's name, formatted for debug output. fn instance_of_class_name(&self, mc: &Mutation<'gc>) -> AvmString<'gc> { - self.instance_of_class_definition() + self.instance_class() .map(|r| r.name().to_qualified_name(mc)) .unwrap_or_else(|| "".into()) } diff --git a/core/src/avm2/object/class_object.rs b/core/src/avm2/object/class_object.rs index baf2a55a9..874a55d2a 100644 --- a/core/src/avm2/object/class_object.rs +++ b/core/src/avm2/object/class_object.rs @@ -72,14 +72,6 @@ pub struct ClassObjectData<'gc> { /// If None, a simple coercion is done. call_handler: Option>, - /// The parameters of this specialized class. - /// - /// None flags that this class has not been specialized. - /// - /// An individual parameter of `None` signifies the parameter `*`, which is - /// represented in AVM2 as `null` with regards to type application. - params: Option>>, - /// List of all applications of this class. /// /// Only applicable if this class is generic. @@ -88,7 +80,7 @@ pub struct ClassObjectData<'gc> { /// as `None` here. AVM2 considers both applications to be separate /// classes, though we consider the parameter to be the class `Object` when /// we get a param of `null`. - applications: FnvHashMap>, ClassObject<'gc>>, + applications: FnvHashMap>, ClassObject<'gc>>, /// Interfaces implemented by this class, including interfaces /// from parent classes and superinterfaces (recursively). @@ -212,7 +204,6 @@ impl<'gc> ClassObject<'gc> { constructor: class.instance_init(), native_constructor: class.native_instance_init(), call_handler: class.call_handler(), - params: None, applications: Default::default(), interfaces: Vec::new(), instance_vtable: VTable::empty(activation.context.gc_context), @@ -429,39 +420,6 @@ impl<'gc> ClassObject<'gc> { Ok(()) } - /// Determine if this class has a given type in its superclass chain. - /// - /// The given object `test_class` should be either a superclass or - /// interface we are checking against this class. - /// - /// To test if a class *instance* is of a given type, see is_of_type. - pub fn has_class_in_chain(self, test_class: Class<'gc>) -> bool { - let mut my_class = Some(self); - - while let Some(class) = my_class { - if class.inner_class_definition() == test_class { - return true; - } - - my_class = class.superclass_object() - } - - // A `ClassObject` stores all of the interfaces it implements, - // including those from superinterfaces and superclasses (recursively). - // Therefore, we only need to check interfaces once, and we can skip - // checking them when we processing superclasses in the `while` - // further down in this method. - if test_class.is_interface() { - for interface in self.interfaces() { - if interface == test_class { - return true; - } - } - } - - false - } - /// Call the instance initializer. pub fn call_init( self, @@ -722,12 +680,46 @@ impl<'gc> ClassObject<'gc> { pub fn add_application( &self, gc_context: &Mutation<'gc>, - param: Option>, + param: Option>, cls: ClassObject<'gc>, ) { self.0.write(gc_context).applications.insert(param, cls); } + /// Parametrize this class. This does not check to ensure that this class is generic. + pub fn parametrize( + &self, + activation: &mut Activation<'_, 'gc>, + class_param: Option>, + ) -> Result, Error<'gc>> { + let self_class = self.inner_class_definition(); + + if let Some(application) = self.0.read().applications.get(&class_param) { + return Ok(*application); + } + + // if it's not a known application, then it's not int/uint/Number/*, + // so it must be a simple Vector.<*>-derived class. + + let parameterized_class = + Class::with_type_param(&mut activation.context, self_class, class_param); + + // NOTE: this isn't fully accurate, but much simpler. + // FP's Vector is more of special case that literally copies some parent class's properties + // main example: Vector..prototype === Vector.<*>.prototype + + let vector_star_cls = activation.avm2().classes().object_vector; + let class_object = + Self::from_class(activation, parameterized_class, Some(vector_star_cls))?; + + self.0 + .write(activation.context.gc_context) + .applications + .insert(class_param, class_object); + + Ok(class_object) + } + pub fn translation_unit(self) -> Option> { if let Method::Bytecode(bc) = self.0.read().constructor { Some(bc.txunit) @@ -781,14 +773,6 @@ impl<'gc> ClassObject<'gc> { self.0.read().superclass_object } - pub fn set_param(self, gc_context: &Mutation<'gc>, param: Option>>) { - self.0.write(gc_context).params = param; - } - - pub fn as_class_params(self) -> Option>> { - self.0.read().params - } - fn instance_allocator(self) -> Option { Some(self.0.read().instance_allocator.0) } @@ -947,7 +931,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> { Value::Null => None, v => Some(v), }; - let object_param = match object_param { + let class_param = match object_param { None => None, Some(cls) => Some( cls.as_object() @@ -958,38 +942,12 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> { "Cannot apply class {:?} with non-class parameter", self_class.name() ) - })?, + })? + .inner_class_definition(), ), }; - if let Some(application) = self.0.read().applications.get(&object_param) { - return Ok(*application); - } - - // if it's not a known application, then it's not int/uint/Number/*, - // so it must be a simple Vector.<*>-derived class. - - let class_param = object_param.map(|c| c.inner_class_definition()); - - let parameterized_class = - Class::with_type_param(&mut activation.context, self_class, class_param); - - // NOTE: this isn't fully accurate, but much simpler. - // FP's Vector is more of special case that literally copies some parent class's properties - // main example: Vector..prototype === Vector.<*>.prototype - - let vector_star_cls = activation.avm2().classes().object_vector; - let class_object = - Self::from_class(activation, parameterized_class, Some(vector_star_cls))?; - - class_object.0.write(activation.context.gc_context).params = Some(object_param); - - self.0 - .write(activation.context.gc_context) - .applications - .insert(object_param, class_object); - - Ok(class_object) + self.parametrize(activation, class_param) } } diff --git a/core/src/avm2/object/error_object.rs b/core/src/avm2/object/error_object.rs index c538368d0..42c066c1f 100644 --- a/core/src/avm2/object/error_object.rs +++ b/core/src/avm2/object/error_object.rs @@ -113,8 +113,8 @@ impl<'gc> ErrorObject<'gc> { .try_read() .map(|obj| { obj.base - .instance_of() - .map(|cls| cls.debug_class_name()) + .instance_class() + .map(|cls| cls.debug_name()) .unwrap_or_else(|| Box::new("None")) }) .unwrap_or_else(|err| Box::new(err)) diff --git a/core/src/avm2/object/primitive_object.rs b/core/src/avm2/object/primitive_object.rs index 985d073aa..a6e86c9d4 100644 --- a/core/src/avm2/object/primitive_object.rs +++ b/core/src/avm2/object/primitive_object.rs @@ -123,7 +123,7 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> { val @ Value::Integer(_) => Ok(val), _ => { let class_name = self - .instance_of_class_definition() + .instance_class() .map(|c| c.name().local_name()) .unwrap_or_else(|| "Object".into()); diff --git a/core/src/avm2/object/script_object.rs b/core/src/avm2/object/script_object.rs index 9d1bbf449..8c984d1cd 100644 --- a/core/src/avm2/object/script_object.rs +++ b/core/src/avm2/object/script_object.rs @@ -1,6 +1,7 @@ //! Default AVM2 object impl use crate::avm2::activation::Activation; +use crate::avm2::class::Class; use crate::avm2::dynamic_map::{DynamicKey, DynamicMap}; use crate::avm2::error; use crate::avm2::object::{ClassObject, FunctionObject, Object, ObjectPtr, TObject}; @@ -164,7 +165,7 @@ impl<'gc> ScriptObjectData<'gc> { activation, error::ReferenceErrorCode::InvalidRead, multiname, - self.instance_of(), + self.instance_class(), )); } @@ -174,7 +175,7 @@ impl<'gc> ScriptObjectData<'gc> { activation, error::ReferenceErrorCode::InvalidRead, multiname, - self.instance_of(), + self.instance_class(), )); }; @@ -205,7 +206,7 @@ impl<'gc> ScriptObjectData<'gc> { activation, error::ReferenceErrorCode::InvalidRead, multiname, - self.instance_of(), + self.instance_class(), )); } else { Ok(Value::Undefined) @@ -223,7 +224,7 @@ impl<'gc> ScriptObjectData<'gc> { activation, error::ReferenceErrorCode::InvalidWrite, multiname, - self.instance_of(), + self.instance_class(), )); } @@ -232,7 +233,7 @@ impl<'gc> ScriptObjectData<'gc> { activation, error::ReferenceErrorCode::InvalidWrite, multiname, - self.instance_of(), + self.instance_class(), )); }; @@ -394,14 +395,18 @@ impl<'gc> ScriptObjectData<'gc> { self.instance_of } + pub fn instance_class(&self) -> Option> { + self.instance_of.map(|cls| cls.inner_class_definition()) + } + /// Get the vtable for this object, if it has one. pub fn vtable(&self) -> Option> { self.vtable } pub fn is_sealed(&self) -> bool { - self.instance_of() - .map(|cls| cls.inner_class_definition().is_sealed()) + self.instance_class() + .map(|cls| cls.is_sealed()) .unwrap_or(false) } @@ -416,9 +421,7 @@ impl<'gc> ScriptObjectData<'gc> { } pub fn debug_class_name(&self) -> Box { - let class_name = self - .instance_of() - .map(|class_obj| class_obj.debug_class_name()); + let class_name = self.instance_class().map(|cls| cls.debug_name()); match class_name { Some(class_name) => Box::new(class_name), diff --git a/core/src/avm2/object/vector_object.rs b/core/src/avm2/object/vector_object.rs index b4fba8607..3bef1b9ae 100644 --- a/core/src/avm2/object/vector_object.rs +++ b/core/src/avm2/object/vector_object.rs @@ -19,7 +19,8 @@ pub fn vector_allocator<'gc>( let base = ScriptObjectData::new(class); let param_type = class - .as_class_params() + .inner_class_definition() + .param() .ok_or("Cannot convert to unparametrized Vector")?; Ok(VectorObject(GcCell::new( @@ -65,10 +66,10 @@ impl<'gc> VectorObject<'gc> { vector: VectorStorage<'gc>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { - let value_type = vector.value_type().map(|o| o.into()).unwrap_or(Value::Null); + let value_type = vector.value_type(); let vector_class = activation.avm2().classes().generic_vector; - let applied_class = vector_class.apply(activation, &[value_type])?; + let applied_class = vector_class.parametrize(activation, value_type)?; let object: Object<'gc> = VectorObject(GcCell::new( activation.context.gc_context, @@ -129,12 +130,7 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> { if name.contains_public_namespace() { if let Some(name) = name.local_name() { if let Ok(index) = name.parse::() { - let type_of = self - .0 - .read() - .vector - .value_type_for_coercion(activation) - .inner_class_definition(); + let type_of = self.0.read().vector.value_type_for_coercion(activation); let value = match value.coerce_to_type(activation, type_of)? { Value::Undefined => self.0.read().vector.default(activation), Value::Null => self.0.read().vector.default(activation), @@ -165,12 +161,7 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> { if name.contains_public_namespace() { if let Some(name) = name.local_name() { if let Ok(index) = name.parse::() { - let type_of = self - .0 - .read() - .vector - .value_type_for_coercion(activation) - .inner_class_definition(); + let type_of = self.0.read().vector.value_type_for_coercion(activation); let value = match value.coerce_to_type(activation, type_of)? { Value::Undefined => self.0.read().vector.default(activation), Value::Null => self.0.read().vector.default(activation), diff --git a/core/src/avm2/optimize.rs b/core/src/avm2/optimize.rs index 79e0ada3e..69eaa579f 100644 --- a/core/src/avm2/optimize.rs +++ b/core/src/avm2/optimize.rs @@ -2,7 +2,7 @@ use crate::avm2::activation::Activation; use crate::avm2::class::Class; use crate::avm2::method::{BytecodeMethod, ResolvedParamConfig}; use crate::avm2::multiname::Multiname; -use crate::avm2::object::{ClassObject, TObject}; +use crate::avm2::object::TObject; use crate::avm2::op::Op; use crate::avm2::property::Property; use crate::avm2::verify::JumpSources; @@ -147,11 +147,6 @@ impl<'gc> Stack<'gc> { Self(Vec::new()) } - fn push_class_object(&mut self, class: ClassObject<'gc>) { - self.0 - .push(OptValue::of_type(class.inner_class_definition())); - } - fn push_class(&mut self, class: Class<'gc>) { self.0.push(OptValue::of_type(class)); } @@ -867,7 +862,7 @@ pub fn optimize<'gc>( stack_push_done = true; if let Some(class) = class { - stack.push_class_object(class); + stack.push_class(class); } else { stack.push_any(); } @@ -1292,8 +1287,8 @@ pub fn optimize<'gc>( let global_scope = outer_scope.get_unchecked(0); stack_push_done = true; - if let Some(class) = global_scope.values().instance_of() { - stack.push_class_object(class); + if let Some(class) = global_scope.values().instance_class() { + stack.push_class(class); } else { stack.push_any(); } @@ -1310,7 +1305,7 @@ pub fn optimize<'gc>( if !outer_scope.is_empty() { let global_scope = outer_scope.get_unchecked(0); - if let Some(class) = global_scope.values().instance_of() { + if let Some(class) = global_scope.values().instance_class() { let mut value_class = class.instance_vtable().slot_classes()[*slot_id as usize]; let resolved_value_class = value_class.get_class(activation); diff --git a/core/src/avm2/scope.rs b/core/src/avm2/scope.rs index 4f80ebfaf..2d8658e3b 100644 --- a/core/src/avm2/scope.rs +++ b/core/src/avm2/scope.rs @@ -1,8 +1,9 @@ //! Represents AVM2 scope chain resolution. use crate::avm2::activation::Activation; +use crate::avm2::class::Class; use crate::avm2::domain::Domain; -use crate::avm2::object::{ClassObject, Object, TObject}; +use crate::avm2::object::{Object, TObject}; use crate::avm2::value::Value; use crate::avm2::Error; use crate::avm2::{Multiname, Namespace}; @@ -237,7 +238,7 @@ impl<'gc> ScopeChain<'gc> { pub fn get_entry_for_multiname( &self, multiname: &Multiname<'gc>, - ) -> Option>, u32)>> { + ) -> Option>, u32)>> { if let Some(container) = self.container { for (index, scope) in container.scopes.iter().enumerate().skip(1).rev() { if scope.with() { @@ -248,7 +249,7 @@ impl<'gc> ScopeChain<'gc> { let values = scope.values(); if values.has_trait(multiname) { - return Some(Some((values.instance_of(), index as u32))); + return Some(Some((values.instance_class(), index as u32))); } } } diff --git a/core/src/avm2/script.rs b/core/src/avm2/script.rs index 4941200ec..e05d52cb6 100644 --- a/core/src/avm2/script.rs +++ b/core/src/avm2/script.rs @@ -620,11 +620,7 @@ impl<'gc> Script<'gc> { globals.vtable().unwrap().init_vtable( globals.instance_of(), - globals - .instance_of() - .unwrap() - .inner_class_definition() - .protected_namespace(), + globals.instance_class().unwrap().protected_namespace(), &self.traits()?, Some(scope), None, diff --git a/core/src/avm2/vector.rs b/core/src/avm2/vector.rs index 9a64affa6..5f37cfc59 100644 --- a/core/src/avm2/vector.rs +++ b/core/src/avm2/vector.rs @@ -1,8 +1,8 @@ //! Storage for AS3 Vectors use crate::avm2::activation::Activation; +use crate::avm2::class::Class; use crate::avm2::error::range_error; -use crate::avm2::object::{ClassObject, Object}; use crate::avm2::value::Value; use crate::avm2::Error; use gc_arena::Collect; @@ -29,21 +29,21 @@ pub struct VectorStorage<'gc> { is_fixed: bool, /// The allowed type of the contents of the vector, in the form of a class - /// object. None represents a Vector.<*>. + /// None represents a Vector.<*>. /// /// Vector typing is enforced by one of two ways: either by generating /// exceptions on values that are not of the given type, or by coercing /// incorrectly typed values to the given type if possible. Values that do /// not coerce are replaced with the default value for the given value /// type. - value_type: Option>, + value_type: Option>, } impl<'gc> VectorStorage<'gc> { pub fn new( length: usize, is_fixed: bool, - value_type: Option>, + value_type: Option>, activation: &mut Activation<'_, 'gc>, ) -> Self { let storage = Vec::new(); @@ -79,7 +79,7 @@ impl<'gc> VectorStorage<'gc> { pub fn from_values( storage: Vec>, is_fixed: bool, - value_type: Option>, + value_type: Option>, ) -> Self { VectorStorage { storage, @@ -118,11 +118,11 @@ impl<'gc> VectorStorage<'gc> { /// Get the default value for this vector. pub fn default(&self, activation: &mut Activation<'_, 'gc>) -> Value<'gc> { if let Some(value_type) = self.value_type { - if Object::ptr_eq(value_type, activation.avm2().classes().int) - || Object::ptr_eq(value_type, activation.avm2().classes().uint) + if value_type == activation.avm2().classes().int.inner_class_definition() + || value_type == activation.avm2().classes().uint.inner_class_definition() { Value::Integer(0) - } else if Object::ptr_eq(value_type, activation.avm2().classes().number) { + } else if value_type == activation.avm2().classes().number.inner_class_definition() { Value::Number(0.0) } else { Value::Null @@ -133,17 +133,14 @@ impl<'gc> VectorStorage<'gc> { } /// Get the value type stored in this vector (same as the class type). - pub fn value_type(&self) -> Option> { + pub fn value_type(&self) -> Option> { self.value_type } /// Get the value type this vector coerces things to. - pub fn value_type_for_coercion( - &self, - activation: &mut Activation<'_, 'gc>, - ) -> ClassObject<'gc> { + pub fn value_type_for_coercion(&self, activation: &mut Activation<'_, 'gc>) -> Class<'gc> { self.value_type - .unwrap_or_else(|| activation.avm2().classes().object) + .unwrap_or_else(|| activation.avm2().classes().object.inner_class_definition()) } /// Check if a vector index is in bounds. @@ -251,11 +248,11 @@ impl<'gc> VectorStorage<'gc> { if let Some(v) = self.storage.pop() { Ok(v) } else if let Some(value_type) = self.value_type() { - if Object::ptr_eq(value_type, activation.avm2().classes().uint) - || Object::ptr_eq(value_type, activation.avm2().classes().int) + if value_type == activation.avm2().classes().uint.inner_class_definition() + || value_type == activation.avm2().classes().int.inner_class_definition() { Ok(Value::Integer(0)) - } else if Object::ptr_eq(value_type, activation.avm2().classes().number) { + } else if value_type == activation.avm2().classes().number.inner_class_definition() { Ok(Value::Number(0.0)) } else { Ok(Value::Undefined) @@ -297,11 +294,11 @@ impl<'gc> VectorStorage<'gc> { if !self.storage.is_empty() { Ok(self.storage.remove(0)) } else if let Some(value_type) = self.value_type() { - if Object::ptr_eq(value_type, activation.avm2().classes().uint) - || Object::ptr_eq(value_type, activation.avm2().classes().int) + if value_type == activation.avm2().classes().uint.inner_class_definition() + || value_type == activation.avm2().classes().int.inner_class_definition() { Ok(Value::Integer(0)) - } else if Object::ptr_eq(value_type, activation.avm2().classes().number) { + } else if value_type == activation.avm2().classes().number.inner_class_definition() { Ok(Value::Number(0.0)) } else { Ok(Value::Undefined) diff --git a/core/src/debug_ui/avm2.rs b/core/src/debug_ui/avm2.rs index 1eedbfc9f..e9754dee1 100644 --- a/core/src/debug_ui/avm2.rs +++ b/core/src/debug_ui/avm2.rs @@ -438,7 +438,7 @@ fn object_name<'gc>(mc: &Mutation<'gc>, object: Object<'gc>) -> String { .to_string() } else { let name = object - .instance_of_class_definition() + .instance_class() .map(|r| Cow::Owned(r.name().local_name().to_string())) .unwrap_or(Cow::Borrowed("Object")); format!("{} {:p}", name, object.as_ptr()) diff --git a/core/src/display_object/bitmap.rs b/core/src/display_object/bitmap.rs index 4643816ac..4fdce262e 100644 --- a/core/src/display_object/bitmap.rs +++ b/core/src/display_object/bitmap.rs @@ -64,9 +64,12 @@ impl<'gc> BitmapClass<'gc> { class: Avm2ClassObject<'gc>, context: &mut UpdateContext<'_, 'gc>, ) -> Option { - if class.has_class_in_chain(context.avm2.classes().bitmap.inner_class_definition()) { + let class_definition = class.inner_class_definition(); + if class_definition + .has_class_in_chain(context.avm2.classes().bitmap.inner_class_definition()) + { Some(BitmapClass::Bitmap(class)) - } else if class + } else if class_definition .has_class_in_chain(context.avm2.classes().bitmapdata.inner_class_definition()) { Some(BitmapClass::BitmapData(class))