diff --git a/core/common/src/utils.rs b/core/common/src/utils.rs index aa77ee810251..2f507d23b4f6 100644 --- a/core/common/src/utils.rs +++ b/core/common/src/utils.rs @@ -35,6 +35,14 @@ pub unsafe trait HasPrefixField: Sized { } } +// Every type is a trivial prefix of itself. +unsafe impl HasPrefixField for T { + #[inline(always)] + fn as_prefix_gc(gc: Gc<'_, Self>) -> Gc<'_, T> { + gc + } +} + /// A `u8` which is always zero. Useful to artificially introduce niches into a struct. #[derive(Copy, Clone, Collect, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[collect(require_static)] diff --git a/core/macros/src/lib.rs b/core/macros/src/lib.rs index 36b22f0823f3..38c80f1a217d 100644 --- a/core/macros/src/lib.rs +++ b/core/macros/src/lib.rs @@ -249,6 +249,312 @@ pub fn enum_trait_object(args: TokenStream, item: TokenStream) -> TokenStream { out.into() } +/// Sibling of [`enum_trait_object`] that emits a pointer-sized tagged handle +/// instead of an enum. +/// +/// Takes the same input shape: an `enum`-shaped variant list inside the +/// attribute, plus the annotated trait. Generates: +/// +/// * A `#[repr(transparent)]` struct with the enum's name, wrapping +/// `Gc<'gc, crate::avm2::object::ScriptObjectData<'gc, crate::avm2::object::kind::Erased>>`. +/// * An `impl Trait for Struct` whose method bodies match on +/// `self.0.kind()` and dispatch via `crate::avm2::object::cast::downcast_unchecked`. +/// * `From for Struct` impls backed by +/// `crate::avm2::object::cast::upcast`. +/// +/// `#[no_dynamic]` is handled identically to `enum_trait_object`: the original +/// default body is moved into the struct's impl, the trait's default body +/// becomes a delegating `(*self).into().method(...)` call, and the NoDyn +/// pub-in-private trait prevents variants from overriding. +/// +/// The inner Gc type, kind accessor, and cast functions are hardcoded to the +/// paths above — this macro is specifically for the AVM2 `Object` type. +#[proc_macro_attribute] +pub fn tagged_trait_object(args: TokenStream, item: TokenStream) -> TokenStream { + let mut input_trait = parse_macro_input!(item as ItemTrait); + let trait_name = &input_trait.ident; + let trait_generics = &input_trait.generics; + let enum_input = parse_macro_input!(args as ItemEnum); + let struct_name = &enum_input.ident; + + assert!( + trait_generics.lifetimes().count() == 1, + "tagged_trait_object requires exactly one lifetime parameter" + ); + assert!( + trait_generics.type_params().count() == 0, + "Generic type parameters are currently unsupported" + ); + assert_eq!( + trait_generics, &enum_input.generics, + "Trait and struct should have the same generic parameters" + ); + + let lt = &trait_generics.lifetimes().next().unwrap().lifetime; + + // Same NoDyn pub-in-private trick as enum_trait_object for #[no_dynamic] + // methods: variants inherit a delegating default body and cannot override. + struct NoOverrideModule { + mod_name: syn::Ident, + lt: syn::Lifetime, + contents: TokenStream2, + } + impl NoOverrideModule { + fn make(trait_name: &syn::Ident) -> Self { + let mod_name = syn::Ident::new( + &format!("__{trait_name}_do_not_override"), + Span::call_site(), + ); + let lt = syn::Lifetime::new("'no_dyn", Span::call_site()); + let contents = quote! { + #[automatically_derived] + #[doc(hidden)] + mod #mod_name { + pub trait NoDyn<#lt> {} + impl NoDyn<'_> for () {} + } + }; + Self { + mod_name, + lt, + contents, + } + } + + fn adjust_method(&self, method: &mut syn::TraitItemFn) { + let Self { mod_name, lt, .. } = self; + let generics = &mut method.sig.generics; + generics + .params + .insert(0, syn::LifetimeParam::new(lt.clone()).into()); + generics + .make_where_clause() + .predicates + .push(parse_quote!((): #mod_name::NoDyn<#lt>)); + } + } + + let mut no_override: Option = None; + + let trait_methods: Vec<_> = input_trait + .items + .iter_mut() + .map(|item| match item { + TraitItem::Fn(method) => { + let mut is_no_dynamic = false; + method.attrs.retain(|attr| match &attr.meta { + Meta::Path(path) if path.is_ident("no_dynamic") => { + is_no_dynamic = true; + false + } + _ => true, + }); + + let params: Vec<_> = method + .sig + .inputs + .iter() + .filter_map(|arg| { + if let FnArg::Typed(arg) = arg && let Pat::Ident(i) = &*arg.pat { + return Some(i.ident.clone()); + } + None + }) + .collect(); + + let self_token: syn::Token![self] = method + .sig + .receiver() + .map(|r| r.self_token) + .unwrap_or_else(|| syn::Token![self](Span::call_site())); + + let method_block = if is_no_dynamic { + no_override + .get_or_insert_with(|| NoOverrideModule::make(trait_name)) + .adjust_method(method); + + let method_name = &method.sig.ident; + let deref = if let Some(syn::Receiver { + colon_token: None, + reference, + .. + }) = method.sig.receiver() + { + reference.is_some().then(|| quote!(*)) + } else { + panic!("#[no_dynamic] method `{method_name}` must take `self`, `&self`, or `&mut self`") + }; + + method + .default + .replace(parse_quote!({ + let mut o: #struct_name<'_> = (#deref #self_token).into(); + o.#method_name(#(#params),*) + })) + .expect("#[no_dynamic] method `{method_name}` must have a default body") + } else { + let method_name = &method.sig.ident; + let match_arms: Vec<_> = enum_input + .variants + .iter() + .map(|variant| { + let variant_name = &variant.ident; + let variant_type = &variant + .fields + .iter() + .next() + .expect("Missing field for enum variant") + .ty; + quote! { + crate::avm2::object::kind::ObjectKind::#variant_name => { + // SAFETY: the kind tag matches T::Kind::ID for this variant, + // so the allocation is originally of this variant's Data type. + let o: #variant_type = unsafe { + crate::avm2::object::cast::downcast_unchecked(#self_token.0) + }; + o.#method_name(#(#params),*) + } + } + }) + .collect(); + + parse_quote!({ + match #self_token.0.kind() { + #(#match_arms)* + } + }) + }; + + ImplItem::Fn(ImplItemFn { + attrs: method.attrs.clone(), + vis: Visibility::Inherited, + defaultness: None, + sig: method.sig.clone(), + block: method_block, + }) + } + _ => panic!("Unsupported trait item: {item:?}"), + }) + .collect(); + + let (impl_generics, ty_generics, where_clause) = trait_generics.split_for_impl(); + + let from_impls: Vec<_> = enum_input + .variants + .iter() + .map(|variant| { + let variant_type = &variant + .fields + .iter() + .next() + .expect("Missing field for enum variant") + .ty; + quote!( + impl #impl_generics From<#variant_type> for #struct_name #ty_generics { + fn from(obj: #variant_type) -> #struct_name #trait_generics { + #struct_name(crate::avm2::object::cast::upcast(obj)) + } + } + ) + }) + .collect(); + + // Copy the enum's attrs onto the struct, dropping enum-specific lints and + // filtering `Debug` out of `#[derive(...)]` (auto-deriving Debug on the + // struct's `Gc>` field doesn't work because + // `ScriptObjectData` doesn't itself implement Debug; we emit a manual impl + // further down). + let mut derived_debug = false; + let struct_attrs: Vec<_> = enum_input + .attrs + .iter() + .filter_map(|attr| match &attr.meta { + Meta::List(list) if list.path.is_ident("expect") || list.path.is_ident("allow") => { + if list.tokens.to_string().contains("enum_variant_names") { + None + } else { + Some(attr.clone()) + } + } + Meta::List(list) if list.path.is_ident("derive") => { + let tokens = list.tokens.to_string(); + if tokens.split(',').any(|t| t.trim() == "Debug") { + derived_debug = true; + // Strip just `Debug` from the derive list. If removing it leaves + // nothing, drop the whole attribute. + let kept: Vec<_> = tokens + .split(',') + .map(|t| t.trim()) + .filter(|t| *t != "Debug") + .collect(); + if kept.is_empty() { + None + } else { + let list_tokens: TokenStream2 = kept + .join(", ") + .parse() + .expect("Failed to re-parse derive list"); + let path = &list.path; + Some(syn::parse_quote! { #[#path(#list_tokens)] }) + } + } else { + Some(attr.clone()) + } + } + _ => Some(attr.clone()), + }) + .collect(); + let struct_vis = &enum_input.vis; + let struct_generics = &enum_input.generics; + + let debug_impl = if derived_debug { + quote! { + impl #impl_generics ::core::fmt::Debug for #struct_name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple(stringify!(#struct_name)) + .field(&self.0.kind()) + .finish() + } + } + } + } else { + quote!() + }; + + let struct_def = quote! { + #(#struct_attrs)* + #[repr(transparent)] + #struct_vis struct #struct_name #struct_generics( + pub ::gc_arena::Gc< + #lt, + crate::avm2::object::ScriptObjectData< + #lt, + crate::avm2::object::kind::Erased, + >, + >, + ); + }; + + let no_override = no_override.map(|s| s.contents).into_iter(); + let out = quote!( + #(#no_override)* + + #input_trait + + #struct_def + + impl #impl_generics #trait_name #ty_generics for #struct_name #ty_generics #where_clause { + #(#trait_methods)* + } + + #debug_impl + + #(#from_impls)* + ); + + out.into() +} + #[proc_macro_derive(HasPrefixField)] pub fn derive_has_prefix_field(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index df7b209b593d..bc1a172e53f8 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -13,11 +13,11 @@ use crate::avm2::error::{ }; use crate::avm2::function::FunctionArgs; use crate::avm2::method::{Method, NativeMethodImpl, ResolvedParamConfig}; +use crate::avm2::object::TObject; use crate::avm2::object::{ ArrayObject, ByteArrayObject, ClassObject, FunctionObject, NamespaceObject, ScriptObject, XmlListObject, }; -use crate::avm2::object::{Object, TObject}; use crate::avm2::op::{LookupSwitch, Op}; use crate::avm2::scope::{Scope, ScopeChain, search_scope_stack}; use crate::avm2::script::Script; @@ -1420,12 +1420,16 @@ impl<'a, 'gc> Activation<'a, 'gc> { // main path for dynamic names if multiname.has_lazy_name() { let name_value = self.stack.peek(0); - if matches!(name_value, Value::Object(Object::XmlListObject(_))) { + + if let Value::Object(o) = name_value + && o.as_xml_list_object().is_some() + { // ECMA-357 11.3.1 The delete Operator // If the type of the operand is XMLList, then a TypeError exception is thrown. return Err(make_error_1119(self)); } } + let multiname = multiname.fill_with_runtime_params(self)?; let object = self.pop_stack().null_check(self, Some(&multiname))?; @@ -1842,7 +1846,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { let base_class = base_value.coerce_to_type(self, class_class)?; let base_class = match base_class { - Value::Object(Object::ClassObject(c)) => Some(c), + Value::Object(o) if let Some(class_obj) = o.as_class_object() => Some(class_obj), Value::Null => None, _ => unreachable!("Coercion to Class must return Class or null"), }; @@ -2608,26 +2612,22 @@ impl<'a, 'gc> Activation<'a, 'gc> { Value::Object(o) => { let classes = self.avm2().class_defs(); - match o { - Object::FunctionObject(_) => { - if o.instance_class() == classes.function { - istr!(self, "function") - } else { - // Subclasses always have a typeof = "object" - istr!(self, "object") - } + if o.as_function_object().is_some() { + if o.instance_class() == classes.function { + istr!(self, "function") + } else { + // Subclasses always have a typeof = "object" + istr!(self, "object") } - Object::XmlObject(_) | Object::XmlListObject(_) => { - if o.instance_class() == classes.xml_list - || o.instance_class() == classes.xml - { - istr!(self, "xml") - } else { - // Subclasses always have a typeof = "object" - istr!(self, "object") - } + } else if o.as_xml_object().is_some() || o.as_xml_list_object().is_some() { + if o.instance_class() == classes.xml_list || o.instance_class() == classes.xml { + istr!(self, "xml") + } else { + // Subclasses always have a typeof = "object" + istr!(self, "object") } - _ => istr!(self, "object"), + } else { + istr!(self, "object") } } Value::String(_) => istr!(self, "string"), @@ -2665,10 +2665,10 @@ impl<'a, 'gc> Activation<'a, 'gc> { /// Implements `Op::EscXElem` fn op_esc_elem(&mut self) -> Result<(), Error<'gc>> { + // We explicitly call toXMLString on Xml/XmlListObject since the toString of these objects have special handling for simple content, which is not used here. let r = match self.pop_stack() { - // We explicitly call toXMLString on Xml/XmlListObject since the toString of these objects have special handling for simple content, which is not used here. - Value::Object(Object::XmlObject(x)) => x.as_xml_string(self), - Value::Object(Object::XmlListObject(x)) => x.as_xml_string(self), + Value::Object(o) if let Some(x) = o.as_xml_object() => x.as_xml_string(self), + Value::Object(o) if let Some(x) = o.as_xml_list_object() => x.as_xml_string(self), // contrary to the avmplus documentation, this escapes the value on the top of the stack using EscapeElementValue from ECMA-357 *NOT* EscapeAttributeValue. x => AvmString::new(self.gc(), escape_element_value(x.coerce_to_string(self)?)), }; 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 adf0cb3764a4..bb6b2e8eb950 100644 --- a/core/src/avm2/globals/flash/display/display_object_container.rs +++ b/core/src/avm2/globals/flash/display/display_object_container.rs @@ -526,7 +526,7 @@ pub fn get_objects_under_point<'gc>( while let Some(child) = children.pop() { let obj = child.object2(); if let Some(obj) = obj { - let obj = Object::StageObject(obj); + let obj: Object<'gc> = obj.into(); if obj != thisobj && child.hit_test_shape(activation.context, point, options) { under_point.push(Some(obj.into())); diff --git a/core/src/avm2/globals/flash/display/graphics.rs b/core/src/avm2/globals/flash/display/graphics.rs index 95cc417d3b8a..613df529c996 100644 --- a/core/src/avm2/globals/flash/display/graphics.rs +++ b/core/src/avm2/globals/flash/display/graphics.rs @@ -1062,9 +1062,9 @@ pub fn draw_triangles<'gc>( draw_triangles_internal( activation, &mut drawing, - &vertices, - indices.as_ref(), - uvt_data.as_ref(), + vertices, + indices, + uvt_data, culling, )?; } @@ -1116,9 +1116,9 @@ type Triangle = (Point, Point, Point); fn draw_triangles_internal<'gc>( activation: &mut Activation<'_, 'gc>, drawing: &mut Drawing, - vertices: &Object<'gc>, - indices: Option<&Object<'gc>>, - uvt_data: Option<&Object<'gc>>, + vertices: Object<'gc>, + indices: Option>, + uvt_data: Option>, culling: TriangleCulling, ) -> Result<(), Error<'gc>> { // FIXME Triangles should be drawn using non-zero winding rule. @@ -1282,7 +1282,7 @@ pub fn draw_graphics_data<'gc>( if let Some(mut drawing) = this.as_drawing() { for elem in vector.iter() { if let Some(obj) = elem.as_object() { - handle_igraphics_data(activation, &mut drawing, &obj)?; + handle_igraphics_data(activation, &mut drawing, obj)?; } } }; @@ -1468,7 +1468,7 @@ fn process_commands<'gc>( fn handle_igraphics_data<'gc>( activation: &mut Activation<'_, 'gc>, drawing: &mut Drawing, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result<(), Error<'gc>> { let class = obj.instance_class(); @@ -1524,7 +1524,7 @@ fn handle_igraphics_data<'gc>( let fill = obj.get_slot(graphics_stroke_slots::FILL).as_object(); if let Some(fill) = fill { - handle_igraphics_fill(activation, drawing, &fill)? + handle_igraphics_fill(activation, drawing, fill)? } else { None } @@ -1576,7 +1576,7 @@ fn handle_igraphics_data<'gc>( fn handle_graphics_triangle_path<'gc>( activation: &mut Activation<'_, 'gc>, drawing: &mut Drawing, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result<(), Error<'gc>> { let culling = { let culling = obj @@ -1598,14 +1598,7 @@ fn handle_graphics_triangle_path<'gc>( .as_object(); if let Some(vertices) = vertices { - draw_triangles_internal( - activation, - drawing, - &vertices, - indices.as_ref(), - uvt_data.as_ref(), - culling, - )?; + draw_triangles_internal(activation, drawing, vertices, indices, uvt_data, culling)?; } Ok(()) @@ -1614,7 +1607,7 @@ fn handle_graphics_triangle_path<'gc>( fn handle_igraphics_fill<'gc>( activation: &mut Activation<'_, 'gc>, drawing: &mut Drawing, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result, Error<'gc>> { let class = obj.instance_class(); @@ -1640,7 +1633,7 @@ fn handle_igraphics_fill<'gc>( fn handle_solid_fill<'gc>( activation: &mut Activation<'_, 'gc>, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result> { let alpha = obj .get_slot(graphics_solid_fill_slots::ALPHA) @@ -1657,7 +1650,7 @@ fn handle_solid_fill<'gc>( fn handle_gradient_fill<'gc>( activation: &mut Activation<'_, 'gc>, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result> { let alphas = obj .get_slot(graphics_gradient_fill_slots::ALPHAS) @@ -1747,7 +1740,7 @@ fn handle_gradient_fill<'gc>( fn handle_bitmap_fill<'gc>( activation: &mut Activation<'_, 'gc>, drawing: &mut Drawing, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result> { let bitmap_data = obj .get_slot(graphics_bitmap_fill_slots::BITMAP_DATA) diff --git a/core/src/avm2/globals/flash/net.rs b/core/src/avm2/globals/flash/net.rs index 7c3be523feef..de0aab01d20d 100644 --- a/core/src/avm2/globals/flash/net.rs +++ b/core/src/avm2/globals/flash/net.rs @@ -21,7 +21,7 @@ pub mod xml_socket; fn object_to_index_map<'gc>( activation: &mut Activation<'_, 'gc>, - obj: &Object<'gc>, + obj: Object<'gc>, ) -> Result, Error<'gc>> { let mut map = IndexMap::new(); let mut last_index = obj.get_next_enumerant(0, activation)?; @@ -59,7 +59,7 @@ fn parse_data<'gc>( let obj = data .as_object() .expect("URLVariables object should be Value::Object"); - vars = object_to_index_map(activation, &obj).unwrap_or_default(); + vars = object_to_index_map(activation, obj).unwrap_or_default(); } else if *data != Value::Null { let str_data = data.coerce_to_string(activation)?.to_string(); if !url.contains('?') { diff --git a/core/src/avm2/globals/flash/text/text_field.rs b/core/src/avm2/globals/flash/text/text_field.rs index 1e2702b1213a..25565d195b80 100644 --- a/core/src/avm2/globals/flash/text/text_field.rs +++ b/core/src/avm2/globals/flash/text/text_field.rs @@ -1746,7 +1746,7 @@ pub fn get_style_sheet<'gc>( }; Ok(match this.style_sheet_avm2() { - Some(style_sheet) => Value::Object(Object::StyleSheetObject(style_sheet)), + Some(style_sheet) => Value::Object(style_sheet.into()), None => Value::Null, }) } diff --git a/core/src/avm2/globals/flash/utils/proxy.rs b/core/src/avm2/globals/flash/utils/proxy.rs index 5eb838037b47..a7e3c9096202 100644 --- a/core/src/avm2/globals/flash/utils/proxy.rs +++ b/core/src/avm2/globals/flash/utils/proxy.rs @@ -1,6 +1,5 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; -use crate::avm2::object::Object; use crate::avm2::parameters::ParametersExt; use crate::avm2::value::Value; @@ -11,7 +10,9 @@ pub fn is_attribute<'gc>( _this: Value<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Value::Object(Object::QNameObject(qname_object)) = args.get_value(0) { + if let Value::Object(o) = args.get_value(0) + && let Some(qname_object) = o.as_qname_object() + { return Ok(qname_object.name().is_attribute().into()); } Ok(false.into()) diff --git a/core/src/avm2/globals/namespace.rs b/core/src/avm2/globals/namespace.rs index 2c01b2168d6b..0bf25622a273 100644 --- a/core/src/avm2/globals/namespace.rs +++ b/core/src/avm2/globals/namespace.rs @@ -7,7 +7,7 @@ use crate::avm2::Namespace; use crate::avm2::activation::Activation; use crate::avm2::e4x::is_xml_name; use crate::avm2::error::make_error_1098; -use crate::avm2::object::{NamespaceObject, Object}; +use crate::avm2::object::NamespaceObject; use crate::avm2::parameters::ParametersExt; use crate::avm2::value::Value; @@ -24,7 +24,7 @@ pub fn namespace_constructor<'gc>( 1 => { // These cases only activate with exactly one argument passed match args.get_value(0) { - Value::Object(Object::QNameObject(qname)) => { + Value::Object(o) if let Some(qname) = o.as_qname_object() => { let uri = qname.uri(activation.strings()); let ns = uri.map_or_else(Namespace::any, |uri| { Namespace::package(uri, api_version, activation.strings()) @@ -35,7 +35,9 @@ pub fn namespace_constructor<'gc>( }; (prefix, ns) } - Value::Object(Object::NamespaceObject(ns)) => (ns.prefix(), ns.namespace()), + Value::Object(o) if let Some(ns) = o.as_namespace_object() => { + (ns.prefix(), ns.namespace()) + } val => { let name = val.coerce_to_string(activation)?; let ns = Namespace::package(name, api_version, activation.strings()); @@ -48,7 +50,9 @@ pub fn namespace_constructor<'gc>( let prefix = args.get_value(0); let uri = args.get_value(1); - let namespace_uri = if let Value::Object(Object::QNameObject(qname)) = uri { + let namespace_uri = if let Value::Object(o) = uri + && let Some(qname) = o.as_qname_object() + { qname.uri(activation.strings()).unwrap_or_else(|| istr!("")) } else { uri.coerce_to_string(activation)? diff --git a/core/src/avm2/globals/q_name.rs b/core/src/avm2/globals/q_name.rs index aaed2080ae77..992f0344e82c 100644 --- a/core/src/avm2/globals/q_name.rs +++ b/core/src/avm2/globals/q_name.rs @@ -4,7 +4,7 @@ use ruffle_macros::istr; use crate::avm2::activation::Activation; use crate::avm2::api_version::ApiVersion; -use crate::avm2::object::{Object, QNameObject}; +use crate::avm2::object::QNameObject; use crate::avm2::parameters::ParametersExt; use crate::avm2::value::Value; use crate::avm2::{Error, Multiname, Namespace}; @@ -44,8 +44,8 @@ pub fn q_name_constructor<'gc>( let api_version = activation.avm2().root_api_version; let namespace = match ns_arg { - Value::Object(Object::NamespaceObject(ns)) => Some(ns.namespace()), - Value::Object(Object::QNameObject(qname)) => qname + Value::Object(o) if let Some(ns) = o.as_namespace_object() => Some(ns.namespace()), + Value::Object(o) if let Some(qname) = o.as_qname_object() => qname .uri(activation.strings()) .map(|uri| Namespace::package(uri, ApiVersion::AllVersions, activation.strings())), Value::Null => None, @@ -63,7 +63,9 @@ pub fn q_name_constructor<'gc>( // Parse the local name let local_name = match local_arg { - Value::Object(Object::QNameObject(qname)) => qname.local_name(activation.strings()), + Value::Object(o) if let Some(qname) = o.as_qname_object() => { + qname.local_name(activation.strings()) + } Value::Undefined => istr!(""), other => other.coerce_to_string(activation)?, }; @@ -71,7 +73,9 @@ pub fn q_name_constructor<'gc>( (namespace, Some(local_name)) } else { let qname_arg = args.get_optional(0).unwrap_or(Value::Undefined); - if let Value::Object(Object::QNameObject(qname_obj)) = qname_arg { + if let Value::Object(o) = qname_arg + && let Some(qname_obj) = o.as_qname_object() + { let new_qname = QNameObject::from_name(activation, qname_obj.name().clone()); return Ok(new_qname.into()); } diff --git a/core/src/avm2/globals/reg_exp.rs b/core/src/avm2/globals/reg_exp.rs index 039b0aabc967..f560236d2843 100644 --- a/core/src/avm2/globals/reg_exp.rs +++ b/core/src/avm2/globals/reg_exp.rs @@ -5,7 +5,7 @@ use ruffle_macros::istr; use crate::avm2::Error; use crate::avm2::activation::Activation; use crate::avm2::error::make_error_1100; -use crate::avm2::object::{ArrayObject, Object, TObject as _}; +use crate::avm2::object::{ArrayObject, TObject as _}; use crate::avm2::parameters::ParametersExt; use crate::avm2::regexp::RegExpFlags; use crate::avm2::value::Value; @@ -22,15 +22,17 @@ pub fn init<'gc>( let this = this.as_object().unwrap(); if let Some(mut regexp) = this.as_regexp_mut(activation.gc()) { - let source: AvmString<'gc> = match args.get_value(0) { + let source = match args.get_value(0) { Value::Undefined => istr!(""), - Value::Object(Object::RegExpObject(o)) => { + Value::Object(o) if let Some(re) = o.as_regexp_object() => { if !matches!(args.get_value(1), Value::Undefined) { return Err(make_error_1100(activation)); } - let other = o.regexp(); + + let other = re.regexp(); regexp.set_source(other.source()); regexp.set_flags(other.flags()); + return Ok(Value::Undefined); } arg => arg.coerce_to_string(activation)?, diff --git a/core/src/avm2/globals/xml.rs b/core/src/avm2/globals/xml.rs index af291aa28a85..7d440ab39392 100644 --- a/core/src/avm2/globals/xml.rs +++ b/core/src/avm2/globals/xml.rs @@ -8,7 +8,7 @@ pub use crate::avm2::object::xml_allocator; use crate::avm2::object::{E4XOrXml, QNameObject, TObject, XmlListObject, XmlObject}; use crate::avm2::parameters::ParametersExt; use crate::avm2::string::AvmString; -use crate::avm2::{Activation, ArrayObject, ArrayStorage, Error, Multiname, Object, Value}; +use crate::avm2::{Activation, ArrayObject, ArrayStorage, Error, Multiname, Value}; use crate::avm2_stub_method; pub fn init<'gc>( @@ -243,7 +243,10 @@ pub fn set_name<'gc>( let name = match args.get_value(0) { // 2. If (Type(name) is Object) and (name.[[Class]] == "QName") and (name.uri == null) - Value::Object(Object::QNameObject(qname)) if qname.is_any_namespace() => { + Value::Object(o) + if let Some(qname) = o.as_qname_object() + && qname.is_any_namespace() => + { // a. Let name = name.localName qname.local_name(activation.strings()).into() } diff --git a/core/src/avm2/multiname.rs b/core/src/avm2/multiname.rs index 796097e8e675..8184d2aed87c 100644 --- a/core/src/avm2/multiname.rs +++ b/core/src/avm2/multiname.rs @@ -1,10 +1,10 @@ use crate::avm2::Error; use crate::avm2::QName; +use crate::avm2::Value; use crate::avm2::activation::Activation; use crate::avm2::error::{make_error_1032, make_error_1080, make_error_1107}; use crate::avm2::namespace::Namespace; use crate::avm2::script::TranslationUnit; -use crate::avm2::{Object, Value}; use crate::string::{AvmString, StringContext, WStr, WString}; use bitflags::bitflags; use gc_arena::Gc; @@ -299,7 +299,9 @@ impl<'gc> Multiname<'gc> { let name = if self.has_lazy_name() { let name_value = activation.pop_stack(); - if let Value::Object(Object::QNameObject(qname_object)) = name_value { + if let Value::Object(o) = name_value + && let Some(qname_object) = o.as_qname_object() + { if self.has_lazy_ns() { let _ = activation.pop_stack(); // ignore the ns component } diff --git a/core/src/avm2/object/array_object.rs b/core/src/avm2/object/array_object.rs index 852754058943..1565e6eb9fd2 100644 --- a/core/src/avm2/object/array_object.rs +++ b/core/src/avm2/object/array_object.rs @@ -4,6 +4,7 @@ use crate::avm2::Error; use crate::avm2::Multiname; use crate::avm2::activation::Activation; use crate::avm2::array::ArrayStorage; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Value; @@ -11,7 +12,7 @@ use crate::context::UpdateContext; use crate::string::{AvmString, WStr}; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use std::cell::{Ref, RefMut}; @@ -37,10 +38,6 @@ pub fn array_allocator<'gc>( #[collect(no_drop)] pub struct ArrayObject<'gc>(pub Gc<'gc, ArrayObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct ArrayObjectWeak<'gc>(pub GcWeak<'gc, ArrayObjectData<'gc>>); - impl fmt::Debug for ArrayObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ArrayObject") @@ -54,7 +51,7 @@ impl fmt::Debug for ArrayObject<'_> { #[repr(C, align(8))] pub struct ArrayObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ArrayObject>, /// Array-structured properties array: RefLock>, @@ -134,7 +131,7 @@ impl<'gc> ArrayObject<'gc> { impl<'gc> TObject<'gc> for ArrayObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn get_property_local( diff --git a/core/src/avm2/object/bitmapdata_object.rs b/core/src/avm2/object/bitmapdata_object.rs index 59b32c886c4c..5da35ed2c10a 100644 --- a/core/src/avm2/object/bitmapdata_object.rs +++ b/core/src/avm2/object/bitmapdata_object.rs @@ -2,13 +2,14 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::bitmap::bitmap_data::BitmapData; use crate::context::UpdateContext; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; /// A class instance allocator that allocates BitmapData objects. @@ -35,10 +36,6 @@ pub fn bitmap_data_allocator<'gc>( #[collect(no_drop)] pub struct BitmapDataObject<'gc>(pub Gc<'gc, BitmapDataObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct BitmapDataObjectWeak<'gc>(pub GcWeak<'gc, BitmapDataObjectData<'gc>>); - impl fmt::Debug for BitmapDataObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BitmapDataObject") @@ -52,7 +49,7 @@ impl fmt::Debug for BitmapDataObject<'_> { #[repr(C, align(8))] pub struct BitmapDataObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::BitmapDataObject>, bitmap_data: Lock>, } @@ -110,6 +107,6 @@ impl<'gc> BitmapDataObject<'gc> { impl<'gc> TObject<'gc> for BitmapDataObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/bytearray_object.rs b/core/src/avm2/object/bytearray_object.rs index df4144828ffa..b4d1c5cc1abc 100644 --- a/core/src/avm2/object/bytearray_object.rs +++ b/core/src/avm2/object/bytearray_object.rs @@ -2,13 +2,14 @@ use crate::avm2::Error; use crate::avm2::Multiname; use crate::avm2::activation::Activation; use crate::avm2::bytearray::ByteArrayStorage; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ArrayObject, ClassObject, Object, TObject}; use crate::avm2::value::Value; use crate::character::Character; use crate::context::UpdateContext; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::{Ref, RefCell, RefMut}; @@ -59,10 +60,6 @@ pub fn byte_array_allocator<'gc>( #[collect(no_drop)] pub struct ByteArrayObject<'gc>(pub Gc<'gc, ByteArrayObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ByteArrayObjectWeak<'gc>(pub GcWeak<'gc, ByteArrayObjectData<'gc>>); - impl fmt::Debug for ByteArrayObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ByteArrayObject") @@ -76,7 +73,7 @@ impl fmt::Debug for ByteArrayObject<'_> { #[repr(C, align(8))] pub struct ByteArrayObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ByteArrayObject>, storage: RefCell, } @@ -120,7 +117,7 @@ impl<'gc> ByteArrayObject<'gc> { impl<'gc> TObject<'gc> for ByteArrayObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn get_property_local( diff --git a/core/src/avm2/object/cast.rs b/core/src/avm2/object/cast.rs new file mode 100644 index 000000000000..043a9723225c --- /dev/null +++ b/core/src/avm2/object/cast.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] + +use gc_arena::Gc; +use ruffle_common::utils::HasPrefixField; + +use super::kind::{Erased, Kind}; +use super::{ObjectVariant, ScriptObjectData}; + +pub fn upcast<'gc, T: ObjectVariant<'gc>>(value: T) -> Gc<'gc, ScriptObjectData<'gc, Erased>> { + let typed = HasPrefixField::as_prefix_gc(value.data_gc()); + ScriptObjectData::erase_kind(typed) +} + +pub fn downcast<'gc, T: ObjectVariant<'gc>>( + base: Gc<'gc, ScriptObjectData<'gc, Erased>>, +) -> Option { + if base.kind() == T::Kind::ID { + // SAFETY: the tag check proves the allocation was originally T::Data. + // Every ObjectVariant impl is generated by define_variants! with a + // unique Kind, so kind match implies data type match. + Some(unsafe { downcast_unchecked(base) }) + } else { + None + } +} + +/// # Safety +/// `base` must point to an allocation that was originally allocated as `T::Data`. +pub unsafe fn downcast_unchecked<'gc, T: ObjectVariant<'gc>>( + base: Gc<'gc, ScriptObjectData<'gc, Erased>>, +) -> T { + // SAFETY: ScriptObjectData<'gc, Erased> and ScriptObjectData<'gc, T::Kind> + // have identical layout (K is phantom, repr(C)). + let typed: Gc<'gc, ScriptObjectData<'gc, T::Kind>> = unsafe { Gc::cast(base) }; + // SAFETY: the caller guarantees the allocation is T::Data, whose prefix + // field is ScriptObjectData<'gc, T::Kind>. + let data: Gc<'gc, T::Data> = unsafe { Gc::cast(typed) }; + // SAFETY: the allocation is T::Data. + unsafe { T::from_data_gc_unchecked(data) } +} diff --git a/core/src/avm2/object/class_object.rs b/core/src/avm2/object/class_object.rs index 5b16234bc8b1..177a673b577d 100644 --- a/core/src/avm2/object/class_object.rs +++ b/core/src/avm2/object/class_object.rs @@ -12,6 +12,7 @@ use crate::avm2::error::{ use crate::avm2::function::{FunctionArgs, exec}; use crate::avm2::method::{Method, MethodAssociation, NativeMethodImpl}; use crate::avm2::object::function_object::FunctionObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, ScriptObject, TObject}; use crate::avm2::property::Property; @@ -23,7 +24,7 @@ use crate::string::AvmString; use fnv::FnvHashMap; use gc_arena::barrier::unlock; use gc_arena::{ - Collect, Gc, GcWeak, Mutation, + Collect, Gc, Mutation, lock::{Lock, RefLock}, }; use ruffle_common::utils::HasPrefixField; @@ -36,16 +37,12 @@ use std::hash::{Hash, Hasher}; #[collect(no_drop)] pub struct ClassObject<'gc>(pub Gc<'gc, ClassObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct ClassObjectWeak<'gc>(pub GcWeak<'gc, ClassObjectData<'gc>>); - #[derive(Collect, Clone, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct ClassObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ClassObject>, /// The class associated with this class object. class: Class<'gc>, @@ -780,7 +777,7 @@ impl<'gc> ClassObject<'gc> { impl<'gc> TObject<'gc> for ClassObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn to_string(&self, mc: &Mutation<'gc>) -> AvmString<'gc> { diff --git a/core/src/avm2/object/context3d_object.rs b/core/src/avm2/object/context3d_object.rs index d3e915b22097..091732685b88 100644 --- a/core/src/avm2/object/context3d_object.rs +++ b/core/src/avm2/object/context3d_object.rs @@ -2,13 +2,14 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; use crate::avm2::value::Value; use crate::avm2_stub_method; use crate::bitmap::bitmap_data::BitmapRawData; use crate::context::RenderContext; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use naga_agal::AgalError; use ruffle_common::utils::HasPrefixField; use ruffle_render::backend::{ @@ -27,11 +28,7 @@ use super::{ClassObject, IndexBuffer3DObject, Stage3DObject, VertexBuffer3DObjec #[derive(Clone, Collect, Copy)] #[collect(no_drop)] -pub struct Context3DObject<'gc>(pub Gc<'gc, Context3DData<'gc>>); - -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct Context3DObjectWeak<'gc>(pub GcWeak<'gc, Context3DData<'gc>>); +pub struct Context3DObject<'gc>(pub Gc<'gc, Context3DObjectData<'gc>>); impl<'gc> Context3DObject<'gc> { pub fn from_context( @@ -43,7 +40,7 @@ impl<'gc> Context3DObject<'gc> { Context3DObject(Gc::new( activation.gc(), - Context3DData { + Context3DObjectData { base: ScriptObjectData::new(class), render_context: Cell::new(Some(context)), stage3d, @@ -497,9 +494,9 @@ impl<'gc> Context3DObject<'gc> { #[derive(Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] -pub struct Context3DData<'gc> { +pub struct Context3DObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::Context3DObject>, #[collect(require_static)] render_context: Cell>>, @@ -509,7 +506,7 @@ pub struct Context3DData<'gc> { impl<'gc> TObject<'gc> for Context3DObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/date_object.rs b/core/src/avm2/object/date_object.rs index ef3609f21aba..780b07d25aa5 100644 --- a/core/src/avm2/object/date_object.rs +++ b/core/src/avm2/object/date_object.rs @@ -1,12 +1,13 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Hint; use crate::context::UpdateContext; use chrono::{DateTime, Utc}; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::Cell; @@ -28,10 +29,6 @@ pub fn date_allocator<'gc>( #[collect(no_drop)] pub struct DateObject<'gc>(pub Gc<'gc, DateObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct DateObjectWeak<'gc>(pub GcWeak<'gc, DateObjectData<'gc>>); - impl fmt::Debug for DateObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DateObject") @@ -95,14 +92,14 @@ impl<'gc> DateObject<'gc> { #[repr(C, align(8))] pub struct DateObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::DateObject>, date_time: Cell>>, } impl<'gc> TObject<'gc> for DateObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn default_hint(&self) -> Hint { diff --git a/core/src/avm2/object/dictionary_object.rs b/core/src/avm2/object/dictionary_object.rs index 095b4c03df8a..b3f0540bfa55 100644 --- a/core/src/avm2/object/dictionary_object.rs +++ b/core/src/avm2/object/dictionary_object.rs @@ -3,12 +3,13 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; use crate::avm2::dynamic_map::DynamicKey; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Value; use crate::string::AvmString; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak, Mutation}; +use gc_arena::{Collect, Gc, Mutation}; use ruffle_common::utils::HasPrefixField; /// A class instance allocator that allocates Dictionary objects. @@ -30,10 +31,6 @@ pub fn dictionary_allocator<'gc>( #[collect(no_drop)] pub struct DictionaryObject<'gc>(pub Gc<'gc, DictionaryObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct DictionaryObjectWeak<'gc>(pub GcWeak<'gc, DictionaryObjectData<'gc>>); - impl fmt::Debug for DictionaryObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DictionaryObject") @@ -47,7 +44,7 @@ impl fmt::Debug for DictionaryObject<'_> { #[repr(C, align(8))] pub struct DictionaryObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::DictionaryObject>, } impl<'gc> DictionaryObject<'gc> { @@ -79,7 +76,7 @@ impl<'gc> DictionaryObject<'gc> { impl<'gc> TObject<'gc> for DictionaryObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } // Calling `setPropertyIsEnumerable` on a `Dictionary` has no effect - diff --git a/core/src/avm2/object/dispatch_object.rs b/core/src/avm2/object/dispatch_object.rs index 3cb0afa73bb1..9183d1d30621 100644 --- a/core/src/avm2/object/dispatch_object.rs +++ b/core/src/avm2/object/dispatch_object.rs @@ -2,11 +2,12 @@ use crate::avm2::activation::Activation; use crate::avm2::events::DispatchList; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use std::cell::RefMut; @@ -39,10 +40,6 @@ use std::cell::RefMut; #[collect(no_drop)] pub struct DispatchObject<'gc>(pub Gc<'gc, DispatchObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct DispatchObjectWeak<'gc>(pub GcWeak<'gc, DispatchObjectData<'gc>>); - impl fmt::Debug for DispatchObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DispatchObject") @@ -56,7 +53,7 @@ impl fmt::Debug for DispatchObject<'_> { #[repr(C, align(8))] pub struct DispatchObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::DispatchObject>, /// The dispatch list this object holds. dispatch: RefLock>, @@ -84,6 +81,6 @@ impl<'gc> DispatchObject<'gc> { impl<'gc> TObject<'gc> for DispatchObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/domain_object.rs b/core/src/avm2/object/domain_object.rs index 122c4d809652..0a680ebaec94 100644 --- a/core/src/avm2/object/domain_object.rs +++ b/core/src/avm2/object/domain_object.rs @@ -3,11 +3,12 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; use crate::avm2::domain::Domain; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; /// A class instance allocator that allocates AppDomain objects. @@ -32,10 +33,6 @@ pub fn application_domain_allocator<'gc>( #[collect(no_drop)] pub struct DomainObject<'gc>(pub Gc<'gc, DomainObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct DomainObjectWeak<'gc>(pub GcWeak<'gc, DomainObjectData<'gc>>); - impl fmt::Debug for DomainObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DomainObject") @@ -49,7 +46,7 @@ impl fmt::Debug for DomainObject<'_> { #[repr(C, align(8))] pub struct DomainObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::DomainObject>, /// The domain this object holds domain: Lock>, @@ -83,6 +80,6 @@ impl<'gc> DomainObject<'gc> { impl<'gc> TObject<'gc> for DomainObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/error_object.rs b/core/src/avm2/object/error_object.rs index 3f4c227aede5..0a750d025600 100644 --- a/core/src/avm2/object/error_object.rs +++ b/core/src/avm2/object/error_object.rs @@ -5,14 +5,14 @@ use crate::avm2::activation::Activation; use crate::avm2::call_stack::CallStack; use crate::avm2::function::FunctionArgs; use crate::avm2::globals::slots::error as error_slots; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Value; use crate::string::{AvmString, WStr, WString}; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; -use std::fmt::Debug; /// A class instance allocator that allocates Error objects. pub fn error_allocator<'gc>( @@ -28,10 +28,6 @@ pub fn error_allocator<'gc>( #[collect(no_drop)] pub struct ErrorObject<'gc>(pub Gc<'gc, ErrorObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ErrorObjectWeak<'gc>(pub GcWeak<'gc, ErrorObjectData<'gc>>); - impl fmt::Debug for ErrorObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ErrorObject") @@ -46,7 +42,7 @@ impl fmt::Debug for ErrorObject<'_> { #[repr(C, align(8))] pub struct ErrorObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ErrorObject>, call_stack: CallStack<'gc>, } @@ -126,6 +122,6 @@ impl<'gc> ErrorObject<'gc> { impl<'gc> TObject<'gc> for ErrorObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/event_object.rs b/core/src/avm2/object/event_object.rs index 71e1ced39c0f..23b529d724b8 100644 --- a/core/src/avm2/object/event_object.rs +++ b/core/src/avm2/object/event_object.rs @@ -3,6 +3,7 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; use crate::avm2::events::Event; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, ScriptObject, TObject}; use crate::avm2::value::Value; @@ -12,7 +13,7 @@ use crate::display_object::{DisplayObject, InteractiveObject, TInteractiveObject use crate::events::{KeyCode, MouseButton}; use crate::string::AvmString; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; use std::cell::{Ref, RefMut}; @@ -39,16 +40,12 @@ pub fn event_allocator<'gc>( #[collect(no_drop)] pub struct EventObject<'gc>(pub Gc<'gc, EventObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct EventObjectWeak<'gc>(pub GcWeak<'gc, EventObjectData<'gc>>); - #[derive(Clone, Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct EventObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::EventObject>, /// The event this object holds. event: RefLock>, @@ -363,7 +360,7 @@ impl<'gc> EventObject<'gc> { impl<'gc> TObject<'gc> for EventObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/file_reference_object.rs b/core/src/avm2/object/file_reference_object.rs index 9b419b490005..d4318eeb09e5 100644 --- a/core/src/avm2/object/file_reference_object.rs +++ b/core/src/avm2/object/file_reference_object.rs @@ -1,9 +1,10 @@ +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::{Activation, Error}; use crate::backend::ui::FileDialogResult; use crate::context::UpdateContext; -use gc_arena::{Collect, DynamicRoot, Gc, GcWeak, Rootable}; +use gc_arena::{Collect, DynamicRoot, Gc, Rootable}; use ruffle_common::utils::HasPrefixField; use std::cell::{Cell, Ref, RefCell}; use std::fmt; @@ -29,10 +30,6 @@ pub fn file_reference_allocator<'gc>( #[collect(no_drop)] pub struct FileReferenceObject<'gc>(pub Gc<'gc, FileReferenceObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct FileReferenceObjectWeak<'gc>(pub GcWeak<'gc, FileReferenceObjectData<'gc>>); - #[derive(Clone)] pub struct FileReferenceObjectHandle(DynamicRoot]>); @@ -48,7 +45,7 @@ impl FileReferenceObjectHandle { impl<'gc> TObject<'gc> for FileReferenceObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } @@ -82,7 +79,7 @@ pub enum FileReference { #[repr(C, align(8))] pub struct FileReferenceObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::FileReferenceObject>, reference: RefCell, diff --git a/core/src/avm2/object/font_object.rs b/core/src/avm2/object/font_object.rs index 5b282d126e2e..7bf43a83fe3a 100644 --- a/core/src/avm2/object/font_object.rs +++ b/core/src/avm2/object/font_object.rs @@ -1,9 +1,10 @@ +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; use crate::avm2::{Activation, ClassObject, Error}; use crate::character::Character; use crate::font::Font; -use gc_arena::{Collect, Gc, GcWeak, Mutation}; +use gc_arena::{Collect, Gc, Mutation}; use ruffle_common::utils::HasPrefixField; use std::fmt; @@ -40,10 +41,6 @@ pub fn font_allocator<'gc>( #[collect(no_drop)] pub struct FontObject<'gc>(pub Gc<'gc, FontObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct FontObjectWeak<'gc>(pub GcWeak<'gc, FontObjectData<'gc>>); - impl<'gc> FontObject<'gc> { pub fn for_font(mc: &Mutation<'gc>, class: ClassObject<'gc>, font: Font<'gc>) -> Object<'gc> { let base = ScriptObjectData::new(class); @@ -64,7 +61,7 @@ impl<'gc> FontObject<'gc> { impl<'gc> TObject<'gc> for FontObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } @@ -73,7 +70,7 @@ impl<'gc> TObject<'gc> for FontObject<'gc> { #[repr(C, align(8))] pub struct FontObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::FontObject>, font: Option>, } diff --git a/core/src/avm2/object/function_object.rs b/core/src/avm2/object/function_object.rs index 11d24f776687..b127c70dcc16 100644 --- a/core/src/avm2/object/function_object.rs +++ b/core/src/avm2/object/function_object.rs @@ -5,6 +5,7 @@ use crate::avm2::activation::Activation; use crate::avm2::error::make_error_1064; use crate::avm2::function::{BoundMethod, FunctionArgs}; use crate::avm2::method::Method; +use crate::avm2::object::kind; use crate::avm2::object::script_object::{ScriptObject, ScriptObjectData}; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::scope::ScopeChain; @@ -13,7 +14,7 @@ use crate::context::UpdateContext; use crate::string::AvmString; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; @@ -22,10 +23,6 @@ use ruffle_macros::istr; #[collect(no_drop)] pub struct FunctionObject<'gc>(pub Gc<'gc, FunctionObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct FunctionObjectWeak<'gc>(pub GcWeak<'gc, FunctionObjectData<'gc>>); - impl fmt::Debug for FunctionObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FunctionObject") @@ -40,7 +37,7 @@ impl fmt::Debug for FunctionObject<'_> { #[repr(C, align(8))] pub struct FunctionObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::FunctionObject>, /// Executable code exec: BoundMethod<'gc>, @@ -154,7 +151,7 @@ impl<'gc> FunctionObject<'gc> { impl<'gc> TObject<'gc> for FunctionObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn to_string(&self, mc: &Mutation<'gc>) -> AvmString<'gc> { diff --git a/core/src/avm2/object/index_buffer_3d_object.rs b/core/src/avm2/object/index_buffer_3d_object.rs index 9b51a878c495..4d35593310ff 100644 --- a/core/src/avm2/object/index_buffer_3d_object.rs +++ b/core/src/avm2/object/index_buffer_3d_object.rs @@ -1,9 +1,10 @@ //! Object representation for IndexBuffer3D objects use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_render::backend::IndexBuffer; use std::cell::{Cell, RefCell, RefMut}; @@ -14,10 +15,6 @@ use super::Context3DObject; #[collect(no_drop)] pub struct IndexBuffer3DObject<'gc>(pub Gc<'gc, IndexBuffer3DObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct IndexBuffer3DObjectWeak<'gc>(pub GcWeak<'gc, IndexBuffer3DObjectData<'gc>>); - impl<'gc> IndexBuffer3DObject<'gc> { pub fn from_handle( activation: &mut Activation<'_, 'gc>, @@ -60,7 +57,7 @@ impl<'gc> IndexBuffer3DObject<'gc> { #[repr(C, align(8))] pub struct IndexBuffer3DObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::IndexBuffer3DObject>, handle: RefCell>, @@ -71,7 +68,7 @@ pub struct IndexBuffer3DObjectData<'gc> { impl<'gc> TObject<'gc> for IndexBuffer3DObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/kind.rs b/core/src/avm2/object/kind.rs new file mode 100644 index 000000000000..9ed570debdf7 --- /dev/null +++ b/core/src/avm2/object/kind.rs @@ -0,0 +1,130 @@ +#![allow(dead_code)] + +use gc_arena::Collect; + +use super::{ + ScriptObjectData, array_object::ArrayObjectData, bitmapdata_object::BitmapDataObjectData, + bytearray_object::ByteArrayObjectData, class_object::ClassObjectData, + context3d_object::Context3DObjectData, date_object::DateObjectData, + dictionary_object::DictionaryObjectData, dispatch_object::DispatchObjectData, + domain_object::DomainObjectData, error_object::ErrorObjectData, event_object::EventObjectData, + file_reference_object::FileReferenceObjectData, font_object::FontObjectData, + function_object::FunctionObjectData, index_buffer_3d_object::IndexBuffer3DObjectData, + loaderinfo_object::LoaderInfoObjectData, local_connection_object::LocalConnectionObjectData, + message_channel_object::MessageChannelObjectData, namespace_object::NamespaceObjectData, + net_connection_object::NetConnectionObjectData, netstream_object::NetStreamObjectData, + program_3d_object::Program3DObjectData, proxy_object::ProxyObjectData, + qname_object::QNameObjectData, regexp_object::RegExpObjectData, + responder_object::ResponderObjectData, security_domain_object::SecurityDomainObjectData, + shader_data_object::ShaderDataObjectData, shared_object_object::SharedObjectObjectData, + socket_object::SocketObjectData, sound_object::SoundObjectData, + soundchannel_object::SoundChannelObjectData, soundtransform_object::SoundTransformObjectData, + stage_object::StageObjectData, stage3d_object::Stage3DObjectData, + stylesheet_object::StyleSheetObjectData, textformat_object::TextFormatObjectData, + texture_object::TextureObjectData, vector_object::VectorObjectData, + vertex_buffer_3d_object::VertexBuffer3DObjectData, + worker_domain_object::WorkerDomainObjectData, worker_object::WorkerObjectData, + xml_list_object::XmlListObjectData, xml_object::XmlObjectData, +}; + +mod sealed { + pub trait Sealed {} +} + +pub trait Kind: sealed::Sealed + 'static { + const ID: ObjectKind; +} + +macro_rules! define_marker { + ($name:ident) => { + #[derive(Clone, Collect)] + #[collect(require_static)] + pub enum $name {} + + impl sealed::Sealed for $name {} + }; +} + +macro_rules! define_variants { + ($($name:ident => $data:ty),* $(,)?) => { + #[expect(clippy::enum_variant_names)] + #[derive(Clone, Copy, Debug, Eq, PartialEq, Collect)] + #[collect(require_static)] + pub enum ObjectKind { + $($name,)* + } + + $( + define_marker!($name); + + impl Kind for $name { + const ID: ObjectKind = ObjectKind::$name; + } + + unsafe impl<'gc> super::ObjectVariant<'gc> for super::$name<'gc> { + type Kind = $name; + type Data = $data; + + fn data_gc(self) -> ::gc_arena::Gc<'gc, Self::Data> { + self.0 + } + + unsafe fn from_data_gc_unchecked( + gc: ::gc_arena::Gc<'gc, Self::Data>, + ) -> Self { + super::$name(gc) + } + } + )* + }; +} + +define_marker!(Erased); + +define_variants!( + ScriptObject => ScriptObjectData<'gc, ScriptObject>, + FunctionObject => FunctionObjectData<'gc>, + NamespaceObject => NamespaceObjectData<'gc>, + ArrayObject => ArrayObjectData<'gc>, + StageObject => StageObjectData<'gc>, + DomainObject => DomainObjectData<'gc>, + EventObject => EventObjectData<'gc>, + DispatchObject => DispatchObjectData<'gc>, + XmlObject => XmlObjectData<'gc>, + XmlListObject => XmlListObjectData<'gc>, + RegExpObject => RegExpObjectData<'gc>, + ByteArrayObject => ByteArrayObjectData<'gc>, + LoaderInfoObject => LoaderInfoObjectData<'gc>, + ClassObject => ClassObjectData<'gc>, + VectorObject => VectorObjectData<'gc>, + SoundObject => SoundObjectData<'gc>, + SoundChannelObject => SoundChannelObjectData<'gc>, + BitmapDataObject => BitmapDataObjectData<'gc>, + DateObject => DateObjectData<'gc>, + DictionaryObject => DictionaryObjectData<'gc>, + QNameObject => QNameObjectData<'gc>, + TextFormatObject => TextFormatObjectData<'gc>, + ProxyObject => ProxyObjectData<'gc>, + ErrorObject => ErrorObjectData<'gc>, + Stage3DObject => Stage3DObjectData<'gc>, + Context3DObject => Context3DObjectData<'gc>, + IndexBuffer3DObject => IndexBuffer3DObjectData<'gc>, + VertexBuffer3DObject => VertexBuffer3DObjectData<'gc>, + TextureObject => TextureObjectData<'gc>, + Program3DObject => Program3DObjectData<'gc>, + NetStreamObject => NetStreamObjectData<'gc>, + NetConnectionObject => NetConnectionObjectData<'gc>, + ResponderObject => ResponderObjectData<'gc>, + ShaderDataObject => ShaderDataObjectData<'gc>, + SocketObject => SocketObjectData<'gc>, + FileReferenceObject => FileReferenceObjectData<'gc>, + FontObject => FontObjectData<'gc>, + LocalConnectionObject => LocalConnectionObjectData<'gc>, + SharedObjectObject => SharedObjectObjectData<'gc>, + SoundTransformObject => SoundTransformObjectData<'gc>, + StyleSheetObject => StyleSheetObjectData<'gc>, + WorkerObject => WorkerObjectData<'gc>, + WorkerDomainObject => WorkerDomainObjectData<'gc>, + MessageChannelObject => MessageChannelObjectData<'gc>, + SecurityDomainObject => SecurityDomainObjectData<'gc>, +); diff --git a/core/src/avm2/object/loaderinfo_object.rs b/core/src/avm2/object/loaderinfo_object.rs index 9906b5dcf6e1..3e952b46c205 100644 --- a/core/src/avm2/object/loaderinfo_object.rs +++ b/core/src/avm2/object/loaderinfo_object.rs @@ -1,6 +1,7 @@ //! Loader-info object use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{EventObject, Object, StageObject, TObject}; use crate::avm2::{Avm2, Error}; @@ -10,7 +11,7 @@ use crate::loader::ContentType; use crate::tag_utils::SwfMovie; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use std::cell::{Cell, Ref}; use std::sync::Arc; @@ -56,10 +57,6 @@ impl LoaderStream<'_> { #[collect(no_drop)] pub struct LoaderInfoObject<'gc>(pub Gc<'gc, LoaderInfoObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct LoaderInfoObjectWeak<'gc>(pub GcWeak<'gc, LoaderInfoObjectData<'gc>>); - impl fmt::Debug for LoaderInfoObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LoaderInfoObject") @@ -73,7 +70,7 @@ impl fmt::Debug for LoaderInfoObject<'_> { #[repr(C, align(8))] pub struct LoaderInfoObjectData<'gc> { /// All normal script data. - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::LoaderInfoObject>, /// The loaded stream that this gets its info from. loaded_stream: RefLock>, @@ -289,6 +286,6 @@ impl<'gc> LoaderInfoObject<'gc> { impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/local_connection_object.rs b/core/src/avm2/object/local_connection_object.rs index d3a6b7219fde..e864b657d235 100644 --- a/core/src/avm2/object/local_connection_object.rs +++ b/core/src/avm2/object/local_connection_object.rs @@ -1,6 +1,7 @@ use crate::avm2::activation::Activation; use crate::avm2::amf::deserialize_value; use crate::avm2::function::FunctionArgs; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, EventObject, Object, TObject}; use crate::avm2::value::Value; @@ -11,7 +12,7 @@ use crate::string::AvmString; use core::fmt; use flash_lso::types::Value as AmfValue; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; use std::cell::RefCell; @@ -41,10 +42,6 @@ pub fn local_connection_allocator<'gc>( #[collect(no_drop)] pub struct LocalConnectionObject<'gc>(pub Gc<'gc, LocalConnectionObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct LocalConnectionObjectWeak<'gc>(pub GcWeak<'gc, LocalConnectionObjectData<'gc>>); - impl fmt::Debug for LocalConnectionObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LocalConnectionObject") @@ -58,7 +55,7 @@ impl fmt::Debug for LocalConnectionObject<'_> { #[repr(C, align(8))] pub struct LocalConnectionObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::LocalConnectionObject>, connection_handle: RefCell>, @@ -177,6 +174,6 @@ impl<'gc> LocalConnectionObject<'gc> { impl<'gc> TObject<'gc> for LocalConnectionObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/message_channel_object.rs b/core/src/avm2/object/message_channel_object.rs index 51b438d873c9..b3d12c3e8826 100644 --- a/core/src/avm2/object/message_channel_object.rs +++ b/core/src/avm2/object/message_channel_object.rs @@ -1,18 +1,15 @@ use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; #[derive(Clone, Collect, Copy)] #[collect(no_drop)] pub struct MessageChannelObject<'gc>(pub Gc<'gc, MessageChannelObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct MessageChannelObjectWeak<'gc>(pub GcWeak<'gc, MessageChannelObjectData<'gc>>); - impl fmt::Debug for MessageChannelObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MessageChannelObject") @@ -26,12 +23,12 @@ impl fmt::Debug for MessageChannelObject<'_> { #[repr(C, align(8))] pub struct MessageChannelObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::MessageChannelObject>, } impl<'gc> TObject<'gc> for MessageChannelObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object.rs b/core/src/avm2/object/mod.rs similarity index 77% rename from core/src/avm2/object.rs rename to core/src/avm2/object/mod.rs index bb1eb1a58be1..515c4ea0ed10 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object/mod.rs @@ -22,7 +22,8 @@ use crate::html::TextFormat; use crate::streams::NetStream; use crate::string::AvmString; use gc_arena::{Collect, Gc, GcWeak, Mutation}; -use ruffle_macros::enum_trait_object; +use ruffle_common::utils::HasPrefixField; +use ruffle_macros::tagged_trait_object; use std::cell::{Ref, RefMut}; use std::fmt::Debug; use std::hash::{Hash, Hasher}; @@ -73,102 +74,90 @@ mod worker_object; mod xml_list_object; mod xml_object; -pub use crate::avm2::object::array_object::{ArrayObject, ArrayObjectWeak, array_allocator}; -pub use crate::avm2::object::bitmapdata_object::{ - BitmapDataObject, BitmapDataObjectWeak, bitmap_data_allocator, -}; -pub use crate::avm2::object::bytearray_object::{ - ByteArrayObject, ByteArrayObjectWeak, byte_array_allocator, -}; -pub use crate::avm2::object::class_object::{ClassObject, ClassObjectWeak}; -pub use crate::avm2::object::context3d_object::{Context3DObject, Context3DObjectWeak}; -pub use crate::avm2::object::date_object::{DateObject, DateObjectWeak, date_allocator}; -pub use crate::avm2::object::dictionary_object::{ - DictionaryObject, DictionaryObjectWeak, dictionary_allocator, -}; -pub use crate::avm2::object::dispatch_object::{DispatchObject, DispatchObjectWeak}; -pub use crate::avm2::object::domain_object::{ - DomainObject, DomainObjectWeak, application_domain_allocator, -}; -pub use crate::avm2::object::error_object::{ErrorObject, ErrorObjectWeak, error_allocator}; -pub use crate::avm2::object::event_object::{EventObject, EventObjectWeak, event_allocator}; +pub(crate) mod cast; +pub mod kind; + +pub use crate::avm2::object::array_object::{ArrayObject, array_allocator}; +pub use crate::avm2::object::bitmapdata_object::{BitmapDataObject, bitmap_data_allocator}; +pub use crate::avm2::object::bytearray_object::{ByteArrayObject, byte_array_allocator}; +pub use crate::avm2::object::class_object::ClassObject; +pub use crate::avm2::object::context3d_object::Context3DObject; +pub use crate::avm2::object::date_object::{DateObject, date_allocator}; +pub use crate::avm2::object::dictionary_object::{DictionaryObject, dictionary_allocator}; +pub use crate::avm2::object::dispatch_object::DispatchObject; +pub use crate::avm2::object::domain_object::{DomainObject, application_domain_allocator}; +pub use crate::avm2::object::error_object::{ErrorObject, error_allocator}; +pub use crate::avm2::object::event_object::{EventObject, event_allocator}; pub use crate::avm2::object::file_reference_object::{ - FileReference, FileReferenceObject, FileReferenceObjectHandle, FileReferenceObjectWeak, - file_reference_allocator, -}; -pub use crate::avm2::object::font_object::{FontObject, FontObjectWeak, font_allocator}; -pub use crate::avm2::object::function_object::{FunctionObject, FunctionObjectWeak}; -pub use crate::avm2::object::index_buffer_3d_object::{ - IndexBuffer3DObject, IndexBuffer3DObjectWeak, -}; -pub use crate::avm2::object::loaderinfo_object::{ - LoaderInfoObject, LoaderInfoObjectWeak, LoaderStream, + FileReference, FileReferenceObject, FileReferenceObjectHandle, file_reference_allocator, }; +pub use crate::avm2::object::font_object::{FontObject, font_allocator}; +pub use crate::avm2::object::function_object::FunctionObject; +pub use crate::avm2::object::index_buffer_3d_object::IndexBuffer3DObject; +pub use crate::avm2::object::loaderinfo_object::{LoaderInfoObject, LoaderStream}; pub use crate::avm2::object::local_connection_object::{ - LocalConnectionObject, LocalConnectionObjectWeak, local_connection_allocator, -}; -pub use crate::avm2::object::message_channel_object::{ - MessageChannelObject, MessageChannelObjectWeak, + LocalConnectionObject, local_connection_allocator, }; -pub use crate::avm2::object::namespace_object::{NamespaceObject, NamespaceObjectWeak}; +pub use crate::avm2::object::message_channel_object::MessageChannelObject; +pub use crate::avm2::object::namespace_object::NamespaceObject; pub use crate::avm2::object::net_connection_object::{ - NetConnectionObject, NetConnectionObjectWeak, net_connection_allocator, -}; -pub use crate::avm2::object::netstream_object::{ - NetStreamObject, NetStreamObjectWeak, netstream_allocator, -}; -pub use crate::avm2::object::program_3d_object::{Program3DObject, Program3DObjectWeak}; -pub use crate::avm2::object::proxy_object::{ProxyObject, ProxyObjectWeak, proxy_allocator}; -pub use crate::avm2::object::qname_object::{QNameObject, QNameObjectWeak}; -pub use crate::avm2::object::regexp_object::{RegExpObject, RegExpObjectWeak, reg_exp_allocator}; -pub use crate::avm2::object::responder_object::{ - ResponderObject, ResponderObjectWeak, responder_allocator, + NetConnectionObject, net_connection_allocator, }; +pub use crate::avm2::object::netstream_object::{NetStreamObject, netstream_allocator}; +pub use crate::avm2::object::program_3d_object::Program3DObject; +pub use crate::avm2::object::proxy_object::{ProxyObject, proxy_allocator}; +pub use crate::avm2::object::qname_object::QNameObject; +pub use crate::avm2::object::regexp_object::{RegExpObject, reg_exp_allocator}; +pub use crate::avm2::object::responder_object::{ResponderObject, responder_allocator}; pub use crate::avm2::object::script_object::{ - ScriptObject, ScriptObjectData, ScriptObjectHandle, ScriptObjectWeak, ScriptObjectWrapper, - get_dynamic_property, scriptobject_allocator, -}; -pub use crate::avm2::object::security_domain_object::{ - SecurityDomainObject, SecurityDomainObjectWeak, + ScriptObject, ScriptObjectData, ScriptObjectHandle, ScriptObjectWrapper, get_dynamic_property, + scriptobject_allocator, }; -pub use crate::avm2::object::shader_data_object::{ - ShaderDataObject, ShaderDataObjectWeak, shader_data_allocator, -}; -pub use crate::avm2::object::shared_object_object::{SharedObjectObject, SharedObjectObjectWeak}; -pub use crate::avm2::object::socket_object::{SocketObject, SocketObjectWeak, socket_allocator}; +pub use crate::avm2::object::security_domain_object::SecurityDomainObject; +pub use crate::avm2::object::shader_data_object::{ShaderDataObject, shader_data_allocator}; +pub use crate::avm2::object::shared_object_object::SharedObjectObject; +pub use crate::avm2::object::socket_object::{SocketObject, socket_allocator}; pub use crate::avm2::object::sound_object::{ - QueuedPlay, SoundLoadingState, SoundObject, SoundObjectHandle, SoundObjectWeak, sound_allocator, -}; -pub use crate::avm2::object::soundchannel_object::{ - SoundChannelObject, SoundChannelObjectWeak, sound_channel_allocator, + QueuedPlay, SoundLoadingState, SoundObject, SoundObjectHandle, sound_allocator, }; +pub use crate::avm2::object::soundchannel_object::{SoundChannelObject, sound_channel_allocator}; pub use crate::avm2::object::soundtransform_object::{ - SoundTransformObject, SoundTransformObjectWeak, sound_transform_allocator, -}; -pub use crate::avm2::object::stage_object::{StageObject, StageObjectWeak}; -pub use crate::avm2::object::stage3d_object::{Stage3DObject, Stage3DObjectWeak}; -pub use crate::avm2::object::stylesheet_object::{ - StyleSheetObject, StyleSheetObjectWeak, style_sheet_allocator, -}; -pub use crate::avm2::object::textformat_object::{ - TextFormatObject, TextFormatObjectWeak, textformat_allocator, -}; -pub use crate::avm2::object::texture_object::{TextureObject, TextureObjectWeak}; -pub use crate::avm2::object::vector_object::{VectorObject, VectorObjectWeak, vector_allocator}; -pub use crate::avm2::object::vertex_buffer_3d_object::{ - VertexBuffer3DObject, VertexBuffer3DObjectWeak, + SoundTransformObject, sound_transform_allocator, }; -pub use crate::avm2::object::worker_domain_object::{WorkerDomainObject, WorkerDomainObjectWeak}; -pub use crate::avm2::object::worker_object::{WorkerObject, WorkerObjectWeak}; -pub use crate::avm2::object::xml_list_object::{ - E4XOrXml, XmlListObject, XmlListObjectWeak, xml_list_allocator, -}; -pub use crate::avm2::object::xml_object::{XmlObject, XmlObjectWeak, xml_allocator}; +pub use crate::avm2::object::stage_object::StageObject; +pub use crate::avm2::object::stage3d_object::Stage3DObject; +pub use crate::avm2::object::stylesheet_object::{StyleSheetObject, style_sheet_allocator}; +pub use crate::avm2::object::textformat_object::{TextFormatObject, textformat_allocator}; +pub use crate::avm2::object::texture_object::TextureObject; +pub use crate::avm2::object::vector_object::{VectorObject, vector_allocator}; +pub use crate::avm2::object::vertex_buffer_3d_object::VertexBuffer3DObject; +pub use crate::avm2::object::worker_domain_object::WorkerDomainObject; +pub use crate::avm2::object::worker_object::WorkerObject; +pub use crate::avm2::object::xml_list_object::{E4XOrXml, XmlListObject, xml_list_allocator}; +pub use crate::avm2::object::xml_object::{XmlObject, xml_allocator}; use crate::font::Font; +/// # Safety +/// - `Self` is a `Copy` newtype wrapping `Gc<'gc, Self::Data>`. +/// - `Self::Data` has `ScriptObjectData<'gc, Self::Kind>` as its first field at +/// offset 0 (expressed by the `HasPrefixField` bound). +/// - Every safely constructed `Self::Data` has its base's runtime kind equal to +/// `Self::Kind::ID`. +#[allow(dead_code)] +pub(crate) unsafe trait ObjectVariant<'gc>: Copy { + type Kind: kind::Kind; + type Data: HasPrefixField> + 'gc; + + fn data_gc(self) -> Gc<'gc, Self::Data>; + + /// # Safety + /// `gc` must point to an allocation originally allocated as `Self::Data`. + unsafe fn from_data_gc_unchecked(gc: Gc<'gc, Self::Data>) -> Self; +} + /// Represents an object that can be directly interacted with by the AVM2 /// runtime. -#[enum_trait_object( +#[tagged_trait_object( #[expect(clippy::enum_variant_names)] #[derive(Clone, Collect, Debug, Copy)] #[collect(no_drop)] @@ -217,8 +206,8 @@ use crate::font::Font; WorkerObject(WorkerObject<'gc>), WorkerDomainObject(WorkerDomainObject<'gc>), MessageChannelObject(MessageChannelObject<'gc>), - SecurityDomainObject(SecurityDomainObject<'gc>), - } + SecurityDomainObject(SecurityDomainObject<'gc>) +} )] pub trait TObject<'gc>: 'gc + Collect<'gc> + Debug + Into> + Clone + Copy { /// Get the base of this object. @@ -716,11 +705,7 @@ macro_rules! impl_downcast_methods { #[doc = concat!("Downcast this object as a `", stringify!($variant), "`.")] #[inline(always)] $vis fn $fn_name(self) -> Option<$variant<'gc>> { - if let Self::$variant(obj) = self { - Some(obj) - } else { - None - } + cast::downcast::<$variant<'gc>>(self.0) } )* } } @@ -840,7 +825,7 @@ impl<'gc> Object<'gc> { } /// Unwrap this object as a font. - pub fn as_font(&self) -> Option> { + pub fn as_font(self) -> Option> { self.as_font_object().and_then(|o| o.font()) } @@ -855,7 +840,7 @@ impl<'gc> Object<'gc> { } /// Unwrap this object as a bitmap data. - pub fn as_bitmap_data(&self) -> Option> { + pub fn as_bitmap_data(self) -> Option> { self.as_bitmap_data_object().map(|o| o.get_bitmap_data()) } @@ -888,92 +873,35 @@ impl Hash for Object<'_> { } } -macro_rules! define_weak_enum { - ( - $(#[$attrs:meta])* - $vis:ident enum $weak_enum:ident<'gc> for $strong_enum:ident<'gc> { - $( $variant:ident($weak_ty:ident<'gc>) ),* $(,)? - } - ) => { - $(#[$attrs])* - $vis enum $weak_enum<'gc> { - $( $variant($weak_ty<'gc>), )* - } +/// Pointer-sized weak reference to an `Object`. +/// +/// Like `Object` itself, this erases the concrete variant type: the kind tag +/// stays in the `ScriptObjectData` allocation, so upgrading yields a full +/// `Object` without a separate variant match. +#[derive(Clone, Copy, Collect)] +#[collect(no_drop)] +#[repr(transparent)] +pub struct WeakObject<'gc>(pub GcWeak<'gc, ScriptObjectData<'gc, kind::Erased>>); - impl<'gc> $weak_enum<'gc> { - $vis fn as_ptr(self) -> *const ObjectPtr { - match self { - $( Self::$variant(o) => GcWeak::as_ptr(o.0).cast(), )* - } - } +impl<'gc> WeakObject<'gc> { + pub fn as_ptr(self) -> *const ObjectPtr { + GcWeak::as_ptr(self.0).cast() + } - $vis fn upgrade(self, mc: &Mutation<'gc>) -> Option<$strong_enum<'gc>> { - match self { - $( Self::$variant(o) => $strong_enum::$variant($variant(o.0.upgrade(mc)?)).into(), )* - } - } - } + pub fn upgrade(self, mc: &Mutation<'gc>) -> Option> { + Some(Object(self.0.upgrade(mc)?)) + } +} - impl<'gc> $strong_enum<'gc> { - $vis fn downgrade(self) -> $weak_enum<'gc> { - match self { - $( Self::$variant(o) => $weak_enum::$variant($weak_ty(Gc::downgrade(o.0))), )* - } - } - } +impl Debug for WeakObject<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("WeakObject").field(&self.as_ptr()).finish() } } -define_weak_enum! { - #[expect(clippy::enum_variant_names)] - #[derive(Clone, Collect, Debug, Copy)] - #[collect(no_drop)] - pub enum WeakObject<'gc> for Object<'gc> { - ScriptObject(ScriptObjectWeak<'gc>), - FunctionObject(FunctionObjectWeak<'gc>), - NamespaceObject(NamespaceObjectWeak<'gc>), - ArrayObject(ArrayObjectWeak<'gc>), - StageObject(StageObjectWeak<'gc>), - DomainObject(DomainObjectWeak<'gc>), - EventObject(EventObjectWeak<'gc>), - DispatchObject(DispatchObjectWeak<'gc>), - XmlObject(XmlObjectWeak<'gc>), - XmlListObject(XmlListObjectWeak<'gc>), - RegExpObject(RegExpObjectWeak<'gc>), - ByteArrayObject(ByteArrayObjectWeak<'gc>), - LoaderInfoObject(LoaderInfoObjectWeak<'gc>), - ClassObject(ClassObjectWeak<'gc>), - VectorObject(VectorObjectWeak<'gc>), - SoundObject(SoundObjectWeak<'gc>), - SoundChannelObject(SoundChannelObjectWeak<'gc>), - BitmapDataObject(BitmapDataObjectWeak<'gc>), - DateObject(DateObjectWeak<'gc>), - DictionaryObject(DictionaryObjectWeak<'gc>), - QNameObject(QNameObjectWeak<'gc>), - TextFormatObject(TextFormatObjectWeak<'gc>), - ProxyObject(ProxyObjectWeak<'gc>), - ErrorObject(ErrorObjectWeak<'gc>), - Stage3DObject(Stage3DObjectWeak<'gc>), - Context3DObject(Context3DObjectWeak<'gc>), - IndexBuffer3DObject(IndexBuffer3DObjectWeak<'gc>), - VertexBuffer3DObject(VertexBuffer3DObjectWeak<'gc>), - TextureObject(TextureObjectWeak<'gc>), - Program3DObject(Program3DObjectWeak<'gc>), - NetStreamObject(NetStreamObjectWeak<'gc>), - NetConnectionObject(NetConnectionObjectWeak<'gc>), - ResponderObject(ResponderObjectWeak<'gc>), - ShaderDataObject(ShaderDataObjectWeak<'gc>), - SocketObject(SocketObjectWeak<'gc>), - FileReferenceObject(FileReferenceObjectWeak<'gc>), - FontObject(FontObjectWeak<'gc>), - LocalConnectionObject(LocalConnectionObjectWeak<'gc>), - SharedObjectObject(SharedObjectObjectWeak<'gc>), - SoundTransformObject(SoundTransformObjectWeak<'gc>), - StyleSheetObject(StyleSheetObjectWeak<'gc>), - WorkerObject(WorkerObjectWeak<'gc>), - WorkerDomainObject(WorkerDomainObjectWeak<'gc>), - MessageChannelObject(MessageChannelObjectWeak<'gc>), - SecurityDomainObject(SecurityDomainObjectWeak<'gc>), +impl<'gc> Object<'gc> { + pub fn downgrade(self) -> WeakObject<'gc> { + WeakObject(Gc::downgrade(self.0)) } } diff --git a/core/src/avm2/object/namespace_object.rs b/core/src/avm2/object/namespace_object.rs index 5881b0b63b72..8a0ee029eca0 100644 --- a/core/src/avm2/object/namespace_object.rs +++ b/core/src/avm2/object/namespace_object.rs @@ -4,11 +4,12 @@ use crate::avm2::Error; use crate::avm2::Namespace; use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::value::Value; use crate::string::AvmString; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; @@ -17,10 +18,6 @@ use ruffle_macros::istr; #[collect(no_drop)] pub struct NamespaceObject<'gc>(pub Gc<'gc, NamespaceObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct NamespaceObjectWeak<'gc>(pub GcWeak<'gc, NamespaceObjectData<'gc>>); - impl fmt::Debug for NamespaceObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NamespaceObject") @@ -34,7 +31,7 @@ impl fmt::Debug for NamespaceObject<'_> { #[repr(C, align(8))] pub struct NamespaceObjectData<'gc> { /// All normal script data. - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::NamespaceObject>, /// The namespace name this object is associated with. namespace: Namespace<'gc>, @@ -92,7 +89,7 @@ impl<'gc> NamespaceObject<'gc> { impl<'gc> TObject<'gc> for NamespaceObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn property_is_enumerable(&self, name: AvmString<'gc>) -> bool { diff --git a/core/src/avm2/object/net_connection_object.rs b/core/src/avm2/object/net_connection_object.rs index 3e8c48a39d12..41909de9fddb 100644 --- a/core/src/avm2/object/net_connection_object.rs +++ b/core/src/avm2/object/net_connection_object.rs @@ -2,10 +2,11 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::net_connection::NetConnectionHandle; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::Cell; use std::fmt; @@ -32,22 +33,18 @@ pub fn net_connection_allocator<'gc>( #[collect(no_drop)] pub struct NetConnectionObject<'gc>(pub Gc<'gc, NetConnectionObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct NetConnectionObjectWeak<'gc>(pub GcWeak<'gc, NetConnectionObjectData<'gc>>); - #[derive(Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct NetConnectionObjectData<'gc> { - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::NetConnectionObject>, handle: Cell>, } impl<'gc> TObject<'gc> for NetConnectionObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/netstream_object.rs b/core/src/avm2/object/netstream_object.rs index e3f862db45eb..26132d5efd93 100644 --- a/core/src/avm2/object/netstream_object.rs +++ b/core/src/avm2/object/netstream_object.rs @@ -2,10 +2,11 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::streams::NetStream; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::fmt::Debug; @@ -29,15 +30,11 @@ pub fn netstream_allocator<'gc>( #[collect(no_drop)] pub struct NetStreamObject<'gc>(pub Gc<'gc, NetStreamObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct NetStreamObjectWeak<'gc>(pub GcWeak<'gc, NetStreamObjectData<'gc>>); - #[derive(Clone, Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct NetStreamObjectData<'gc> { - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::NetStreamObject>, ns: NetStream<'gc>, } @@ -49,7 +46,7 @@ impl<'gc> NetStreamObject<'gc> { impl<'gc> TObject<'gc> for NetStreamObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/program_3d_object.rs b/core/src/avm2/object/program_3d_object.rs index 35fee58153b1..86e1ba4cf653 100644 --- a/core/src/avm2/object/program_3d_object.rs +++ b/core/src/avm2/object/program_3d_object.rs @@ -1,9 +1,10 @@ //! Object representation for VertexBuffer3D objects use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_render::backend::ShaderModule; use std::cell::RefCell; @@ -15,10 +16,6 @@ use super::Context3DObject; #[collect(no_drop)] pub struct Program3DObject<'gc>(pub Gc<'gc, Program3DObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct Program3DObjectWeak<'gc>(pub GcWeak<'gc, Program3DObjectData<'gc>>); - impl<'gc> Program3DObject<'gc> { pub fn from_context( activation: &mut Activation<'_, 'gc>, @@ -52,7 +49,7 @@ impl<'gc> Program3DObject<'gc> { #[repr(C, align(8))] pub struct Program3DObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::Program3DObject>, context3d: Context3DObject<'gc>, @@ -61,7 +58,7 @@ pub struct Program3DObjectData<'gc> { impl<'gc> TObject<'gc> for Program3DObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/proxy_object.rs b/core/src/avm2/object/proxy_object.rs index dcf81209b1d2..ac4a9dbad595 100644 --- a/core/src/avm2/object/proxy_object.rs +++ b/core/src/avm2/object/proxy_object.rs @@ -5,12 +5,13 @@ use crate::avm2::Multiname; use crate::avm2::activation::Activation; use crate::avm2::function::FunctionArgs; use crate::avm2::globals::methods::flash_utils_proxy as proxy_methods; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, QNameObject, TObject}; use crate::avm2::string::AvmString; use crate::avm2::value::Value; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; @@ -28,10 +29,6 @@ pub fn proxy_allocator<'gc>( #[collect(no_drop)] pub struct ProxyObject<'gc>(pub Gc<'gc, ProxyObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ProxyObjectWeak<'gc>(pub GcWeak<'gc, ProxyObjectData<'gc>>); - impl fmt::Debug for ProxyObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ProxyObject") @@ -45,12 +42,12 @@ impl fmt::Debug for ProxyObject<'_> { #[repr(C, align(8))] pub struct ProxyObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ProxyObject>, } impl<'gc> TObject<'gc> for ProxyObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn get_property_local( diff --git a/core/src/avm2/object/qname_object.rs b/core/src/avm2/object/qname_object.rs index 730a1331cc7b..4bc3b17f5f76 100644 --- a/core/src/avm2/object/qname_object.rs +++ b/core/src/avm2/object/qname_object.rs @@ -5,11 +5,12 @@ use crate::avm2::Error; use crate::avm2::Multiname; use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::value::Value; use crate::string::StringContext; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; @@ -18,10 +19,6 @@ use ruffle_macros::istr; #[collect(no_drop)] pub struct QNameObject<'gc>(pub Gc<'gc, QNameObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct QNameObjectWeak<'gc>(pub GcWeak<'gc, QNameObjectData<'gc>>); - impl fmt::Debug for QNameObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("QNameObject") @@ -35,7 +32,7 @@ impl fmt::Debug for QNameObject<'_> { #[repr(C, align(8))] pub struct QNameObjectData<'gc> { /// All normal script data. - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::QNameObject>, /// The Multiname this object is associated with. name: Multiname<'gc>, @@ -82,7 +79,7 @@ impl<'gc> QNameObject<'gc> { impl<'gc> TObject<'gc> for QNameObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn get_next_enumerant( diff --git a/core/src/avm2/object/regexp_object.rs b/core/src/avm2/object/regexp_object.rs index 6c81317b53dd..d7afebdc744e 100644 --- a/core/src/avm2/object/regexp_object.rs +++ b/core/src/avm2/object/regexp_object.rs @@ -2,12 +2,13 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::regexp::RegExp; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; use std::cell::{Ref, RefMut}; @@ -33,10 +34,6 @@ pub fn reg_exp_allocator<'gc>( #[collect(no_drop)] pub struct RegExpObject<'gc>(pub Gc<'gc, RegExpObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct RegExpObjectWeak<'gc>(pub GcWeak<'gc, RegExpObjectData<'gc>>); - impl fmt::Debug for RegExpObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RegExpObject") @@ -50,7 +47,7 @@ impl fmt::Debug for RegExpObject<'_> { #[repr(C, align(8))] pub struct RegExpObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::RegExpObject>, regexp: RefLock>, } @@ -67,6 +64,6 @@ impl<'gc> RegExpObject<'gc> { impl<'gc> TObject<'gc> for RegExpObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/responder_object.rs b/core/src/avm2/object/responder_object.rs index 46384f3f8f6e..b5874f7e606a 100644 --- a/core/src/avm2/object/responder_object.rs +++ b/core/src/avm2/object/responder_object.rs @@ -1,11 +1,12 @@ use crate::avm2::function::FunctionArgs; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, FunctionObject, Object, TObject}; use crate::avm2::{Activation, Error}; use crate::net_connection::ResponderCallback; use flash_lso::types::Value as AMFValue; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; use std::fmt; @@ -31,13 +32,9 @@ pub fn responder_allocator<'gc>( #[collect(no_drop)] pub struct ResponderObject<'gc>(pub Gc<'gc, ResponderObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ResponderObjectWeak<'gc>(pub GcWeak<'gc, ResponderObjectData<'gc>>); - impl<'gc> TObject<'gc> for ResponderObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } @@ -87,7 +84,7 @@ impl<'gc> ResponderObject<'gc> { #[repr(C, align(8))] pub struct ResponderObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ResponderObject>, /// Method to call with any result result: Lock>>, diff --git a/core/src/avm2/object/script_object.rs b/core/src/avm2/object/script_object.rs index 8e8934e23c29..e2fd7163e4af 100644 --- a/core/src/avm2/object/script_object.rs +++ b/core/src/avm2/object/script_object.rs @@ -4,6 +4,7 @@ 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::kind::{self, Erased, Kind, ObjectKind}; use crate::avm2::object::{ArrayObject, ClassObject, FunctionObject, Object, TObject}; use crate::avm2::value::Value; use crate::avm2::vtable::VTable; @@ -12,11 +13,12 @@ use crate::context::UpdateContext; use crate::string::AvmString; use gc_arena::barrier::{field, unlock}; use gc_arena::{ - Collect, DynamicRoot, Gc, GcWeak, Mutation, Rootable, + Collect, DynamicRoot, Gc, Mutation, Rootable, lock::{Lock, RefLock}, }; use std::cell::{Ref, RefMut}; use std::fmt::Debug; +use std::marker::PhantomData; /// A class instance allocator that allocates `ScriptObject`s. pub fn scriptobject_allocator<'gc>( @@ -31,14 +33,11 @@ pub fn scriptobject_allocator<'gc>( /// Default implementation of `avm2::Object`. #[derive(Clone, Collect, Copy)] #[collect(no_drop)] -pub struct ScriptObject<'gc>(pub Gc<'gc, ScriptObjectData<'gc>>); - -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ScriptObjectWeak<'gc>(pub GcWeak<'gc, ScriptObjectData<'gc>>); +pub struct ScriptObject<'gc>(pub Gc<'gc, ScriptObjectData<'gc, kind::ScriptObject>>); #[derive(Clone)] -pub struct ScriptObjectHandle(DynamicRoot]>); +#[allow(clippy::type_complexity)] +pub struct ScriptObjectHandle(DynamicRoot]>); impl ScriptObjectHandle { pub fn stash<'gc>(context: &UpdateContext<'gc>, this: ScriptObject<'gc>) -> Self { @@ -57,14 +56,25 @@ impl ScriptObjectHandle { /// struct. #[derive(Clone, Collect)] #[collect(no_drop)] -#[repr(align(8))] -pub struct ScriptObjectData<'gc> { - /// Values stored on this object. - values: RefLock, Value<'gc>>>, +#[repr(C, align(8))] +pub struct ScriptObjectData<'gc, K = Erased> { + kind: ObjectKind, /// Slots stored on this object. + /// + /// Hot field — touched on every `op_get_slot` / `op_set_slot`. Declared + /// early so that under `#[repr(C)]` the slice header lands in the same + /// cache line as `kind`. slots: Box<[Lock>]>, + /// The table used for non-dynamic property lookups. + /// + /// Also hot — every property dispatch reads through it. + vtable: Lock>, + + /// Values stored on this object. + values: RefLock, Value<'gc>>>, + /// Methods stored on this object. bound_methods: RefLock>>>, @@ -74,13 +84,12 @@ pub struct ScriptObjectData<'gc> { /// The `Class` that this is an instance of. instance_class: Class<'gc>, - /// The table used for non-dynamic property lookups. - vtable: Lock>, + _kind: PhantomData K>, } impl<'gc> TObject<'gc> for ScriptObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - self.0 + ScriptObjectData::erase_kind(self.0) } } @@ -129,7 +138,21 @@ impl<'gc> ScriptObject<'gc> { } } -impl<'gc> ScriptObjectData<'gc> { +impl<'gc, K> ScriptObjectData<'gc, K> { + #[inline(always)] + pub fn kind(&self) -> ObjectKind { + self.kind + } + + #[inline(always)] + pub fn erase_kind(this: Gc<'gc, Self>) -> Gc<'gc, ScriptObjectData<'gc, Erased>> { + // SAFETY: K only appears in PhantomData and the struct is #[repr(C)], so + // ScriptObjectData<'gc, K> has the same layout as ScriptObjectData<'gc, Erased>. + unsafe { Gc::cast(this) } + } +} + +impl<'gc, K: Kind> ScriptObjectData<'gc, K> { /// Create new object data of a given class. /// This is a low-level function used to implement things like object allocators. pub fn new(instance_of: ClassObject<'gc>) -> Self { @@ -160,12 +183,14 @@ impl<'gc> ScriptObjectData<'gc> { .collect::>(); ScriptObjectData { + kind: K::ID, values: RefLock::new(Default::default()), slots, bound_methods: RefLock::new(Vec::new()), proto: Lock::new(proto), instance_class, vtable: Lock::new(vtable), + _kind: PhantomData, } } } @@ -395,7 +420,10 @@ impl<'gc> ScriptObjectWrapper<'gc> { impl Debug for ScriptObject<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("ScriptObject") - .field("name", &ScriptObjectWrapper(self.0).class_name()) + .field( + "name", + &ScriptObjectWrapper(ScriptObjectData::erase_kind(self.0)).class_name(), + ) .field("ptr", &Gc::as_ptr(self.0)) .finish() } diff --git a/core/src/avm2/object/security_domain_object.rs b/core/src/avm2/object/security_domain_object.rs index 03722a724135..1bd566995cde 100644 --- a/core/src/avm2/object/security_domain_object.rs +++ b/core/src/avm2/object/security_domain_object.rs @@ -1,18 +1,15 @@ use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; #[derive(Clone, Collect, Copy)] #[collect(no_drop)] pub struct SecurityDomainObject<'gc>(pub Gc<'gc, SecurityDomainObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SecurityDomainObjectWeak<'gc>(pub GcWeak<'gc, SecurityDomainObjectData<'gc>>); - impl fmt::Debug for SecurityDomainObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SecurityDomainObject") @@ -26,12 +23,12 @@ impl fmt::Debug for SecurityDomainObject<'_> { #[repr(C, align(8))] pub struct SecurityDomainObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SecurityDomainObject>, } impl<'gc> TObject<'gc> for SecurityDomainObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/shader_data_object.rs b/core/src/avm2/object/shader_data_object.rs index 290ffeda7c56..6da9e1665911 100644 --- a/core/src/avm2/object/shader_data_object.rs +++ b/core/src/avm2/object/shader_data_object.rs @@ -2,10 +2,11 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_render::pixel_bender::PixelBenderShaderHandle; use std::cell::Cell; @@ -29,10 +30,6 @@ pub fn shader_data_allocator<'gc>( #[collect(no_drop)] pub struct ShaderDataObject<'gc>(pub Gc<'gc, ShaderDataObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct ShaderDataObjectWeak<'gc>(pub GcWeak<'gc, ShaderDataObjectData<'gc>>); - impl fmt::Debug for ShaderDataObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ShaderDataObject") @@ -58,13 +55,13 @@ impl ShaderDataObject<'_> { #[repr(C, align(8))] pub struct ShaderDataObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::ShaderDataObject>, shader: Cell>, } impl<'gc> TObject<'gc> for ShaderDataObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/shared_object_object.rs b/core/src/avm2/object/shared_object_object.rs index 9605b0e27dea..c26402fe7579 100644 --- a/core/src/avm2/object/shared_object_object.rs +++ b/core/src/avm2/object/shared_object_object.rs @@ -1,28 +1,24 @@ //! Object representation for SharedObjects use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, ScriptObject, TObject}; use crate::context::UpdateContext; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, lock::Lock}; +use gc_arena::{Collect, Gc, lock::Lock}; use ruffle_common::utils::HasPrefixField; -use std::fmt::Debug; #[derive(Clone, Collect, Copy)] #[collect(no_drop)] pub struct SharedObjectObject<'gc>(pub Gc<'gc, SharedObjectObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SharedObjectObjectWeak<'gc>(pub GcWeak<'gc, SharedObjectObjectData<'gc>>); - #[derive(Clone, Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct SharedObjectObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SharedObjectObject>, /// The SharedObject data that this SharedObjectObject holds. data: Lock>, @@ -72,7 +68,7 @@ impl<'gc> SharedObjectObject<'gc> { impl<'gc> TObject<'gc> for SharedObjectObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/socket_object.rs b/core/src/avm2/object/socket_object.rs index c502c2e5bc77..05c567351554 100644 --- a/core/src/avm2/object/socket_object.rs +++ b/core/src/avm2/object/socket_object.rs @@ -1,10 +1,10 @@ use crate::avm2::Activation; use crate::avm2::bytearray::{ByteArrayError, Endian, ObjectEncoding}; use crate::avm2::error::{Error, make_error_2006}; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::socket::SocketHandle; -use gc_arena::GcWeak; use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::{Cell, RefCell, RefMut}; @@ -38,13 +38,9 @@ pub fn socket_allocator<'gc>( #[collect(no_drop)] pub struct SocketObject<'gc>(pub Gc<'gc, SocketObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SocketObjectWeak<'gc>(pub GcWeak<'gc, SocketObjectData<'gc>>); - impl<'gc> TObject<'gc> for SocketObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } @@ -191,7 +187,7 @@ impl_read!(read_float 4; f32, read_double 8; f64, read_int 4; i32, read_unsigned #[repr(C, align(8))] pub struct SocketObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SocketObject>, handle: Cell>, diff --git a/core/src/avm2/object/sound_object.rs b/core/src/avm2/object/sound_object.rs index c9f50d31106b..84dea2ab5ea9 100644 --- a/core/src/avm2/object/sound_object.rs +++ b/core/src/avm2/object/sound_object.rs @@ -5,6 +5,7 @@ use crate::avm2::Error; use crate::avm2::EventObject; use crate::avm2::activation::Activation; use crate::avm2::globals::slots::flash_media_id3info as id3_slots; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::backend::audio::{AudioManager, SoundHandle}; @@ -14,7 +15,7 @@ use crate::string::AvmString; use core::fmt; use gc_arena::barrier::unlock; use gc_arena::{ - Collect, DynamicRoot, Gc, GcWeak, Mutation, Rootable, + Collect, DynamicRoot, Gc, Mutation, Rootable, lock::{Lock, RefLock}, }; use id3::{Tag, TagLike}; @@ -50,10 +51,6 @@ pub fn sound_allocator<'gc>( #[collect(no_drop)] pub struct SoundObject<'gc>(pub Gc<'gc, SoundObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SoundObjectWeak<'gc>(pub GcWeak<'gc, SoundObjectData<'gc>>); - impl fmt::Debug for SoundObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SoundObject") @@ -80,7 +77,7 @@ impl SoundObjectHandle { #[repr(C, align(8))] pub struct SoundObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SoundObject>, /// Loading state of the sound. loading_state: Cell, @@ -301,6 +298,6 @@ fn play_queued<'gc>( impl<'gc> TObject<'gc> for SoundObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/soundchannel_object.rs b/core/src/avm2/object/soundchannel_object.rs index 6f8419665ddb..94acb90f7af6 100644 --- a/core/src/avm2/object/soundchannel_object.rs +++ b/core/src/avm2/object/soundchannel_object.rs @@ -2,13 +2,14 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::backend::audio::SoundInstanceHandle; use crate::context::UpdateContext; use crate::display_object::SoundTransform; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::{Cell, RefCell}; @@ -37,10 +38,6 @@ pub fn sound_channel_allocator<'gc>( #[collect(no_drop)] pub struct SoundChannelObject<'gc>(pub Gc<'gc, SoundChannelObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SoundChannelObjectWeak<'gc>(pub GcWeak<'gc, SoundChannelObjectData<'gc>>); - impl fmt::Debug for SoundChannelObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SoundChannelObject") @@ -54,7 +51,7 @@ impl fmt::Debug for SoundChannelObject<'_> { #[repr(C, align(8))] pub struct SoundChannelObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SoundChannelObject>, /// The sound this object holds. sound_channel_data: RefCell, @@ -195,6 +192,6 @@ impl<'gc> SoundChannelObject<'gc> { impl<'gc> TObject<'gc> for SoundChannelObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/soundtransform_object.rs b/core/src/avm2/object/soundtransform_object.rs index b4a86730cd44..bf7486b60919 100644 --- a/core/src/avm2/object/soundtransform_object.rs +++ b/core/src/avm2/object/soundtransform_object.rs @@ -1,9 +1,10 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::Cell; @@ -32,10 +33,6 @@ pub fn sound_transform_allocator<'gc>( #[collect(no_drop)] pub struct SoundTransformObject<'gc>(pub Gc<'gc, SoundTransformObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct SoundTransformObjectWeak<'gc>(pub GcWeak<'gc, SoundTransformObjectData<'gc>>); - impl fmt::Debug for SoundTransformObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SoundTransformObject") @@ -49,7 +46,7 @@ impl fmt::Debug for SoundTransformObject<'_> { #[repr(C, align(8))] pub struct SoundTransformObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::SoundTransformObject>, left_to_left: Cell, left_to_right: Cell, @@ -103,6 +100,6 @@ impl SoundTransformObject<'_> { impl<'gc> TObject<'gc> for SoundTransformObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/stage3d_object.rs b/core/src/avm2/object/stage3d_object.rs index f7713ac676cb..650523bf609a 100644 --- a/core/src/avm2/object/stage3d_object.rs +++ b/core/src/avm2/object/stage3d_object.rs @@ -1,12 +1,13 @@ //! Object representation for Stage3D objects +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; use crate::context::UpdateContext; use core::fmt; use gc_arena::barrier::unlock; use gc_arena::lock::Lock; -use gc_arena::{Collect, Gc, GcWeak, Mutation}; +use gc_arena::{Collect, Gc, Mutation}; use ruffle_common::utils::HasPrefixField; use std::cell::Cell; @@ -14,10 +15,6 @@ use std::cell::Cell; #[collect(no_drop)] pub struct Stage3DObject<'gc>(pub Gc<'gc, Stage3DObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct Stage3DObjectWeak<'gc>(pub GcWeak<'gc, Stage3DObjectData<'gc>>); - impl fmt::Debug for Stage3DObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Stage3DObject") @@ -61,7 +58,7 @@ impl<'gc> Stage3DObject<'gc> { #[repr(C, align(8))] pub struct Stage3DObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::Stage3DObject>, /// The context3D object associated with this Stage3D object, /// if it's been created with `requestContext3D` @@ -71,6 +68,6 @@ pub struct Stage3DObjectData<'gc> { impl<'gc> TObject<'gc> for Stage3DObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/stage_object.rs b/core/src/avm2/object/stage_object.rs index b6d1936f62fd..074b39b22dc8 100644 --- a/core/src/avm2/object/stage_object.rs +++ b/core/src/avm2/object/stage_object.rs @@ -3,10 +3,11 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; use crate::avm2::function::FunctionArgs; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, TObject}; use crate::display_object::DisplayObject; -use gc_arena::{Collect, Gc, GcWeak, Mutation}; +use gc_arena::{Collect, Gc, Mutation}; use ruffle_common::utils::HasPrefixField; use std::fmt::Debug; @@ -14,16 +15,12 @@ use std::fmt::Debug; #[collect(no_drop)] pub struct StageObject<'gc>(pub Gc<'gc, StageObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct StageObjectWeak<'gc>(pub GcWeak<'gc, StageObjectData<'gc>>); - #[derive(Clone, Collect, HasPrefixField)] #[collect(no_drop)] #[repr(C, align(8))] pub struct StageObjectData<'gc> { /// The base data common to all AVM2 objects. - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::StageObject>, /// The associated display object. display_object: DisplayObject<'gc>, @@ -95,7 +92,7 @@ impl<'gc> StageObject<'gc> { impl<'gc> TObject<'gc> for StageObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/stylesheet_object.rs b/core/src/avm2/object/stylesheet_object.rs index 16effc509a01..8c158520cb8e 100644 --- a/core/src/avm2/object/stylesheet_object.rs +++ b/core/src/avm2/object/stylesheet_object.rs @@ -1,10 +1,11 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::html::{StyleSheet, TextFormat}; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_wstr::{WStr, WString}; @@ -29,10 +30,6 @@ pub fn style_sheet_allocator<'gc>( #[collect(no_drop)] pub struct StyleSheetObject<'gc>(pub Gc<'gc, StyleSheetObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct StyleSheetObjectWeak<'gc>(pub GcWeak<'gc, StyleSheetObjectData<'gc>>); - impl fmt::Debug for StyleSheetObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StyleSheetObject") @@ -47,14 +44,14 @@ impl fmt::Debug for StyleSheetObject<'_> { #[repr(C, align(8))] pub struct StyleSheetObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::StyleSheetObject>, style_sheet: StyleSheet<'gc>, } impl<'gc> TObject<'gc> for StyleSheetObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/textformat_object.rs b/core/src/avm2/object/textformat_object.rs index e848891024f7..566f3a1a4a37 100644 --- a/core/src/avm2/object/textformat_object.rs +++ b/core/src/avm2/object/textformat_object.rs @@ -2,11 +2,12 @@ use crate::avm2::Error; use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::html::TextFormat; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use std::cell::{Ref, RefCell, RefMut}; @@ -29,10 +30,6 @@ pub fn textformat_allocator<'gc>( #[collect(no_drop)] pub struct TextFormatObject<'gc>(pub Gc<'gc, TextFormatObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct TextFormatObjectWeak<'gc>(pub GcWeak<'gc, TextFormatObjectData<'gc>>); - impl fmt::Debug for TextFormatObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TextFormatObject") @@ -46,7 +43,7 @@ impl fmt::Debug for TextFormatObject<'_> { #[repr(C, align(8))] pub struct TextFormatObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::TextFormatObject>, text_format: RefCell, } @@ -81,6 +78,6 @@ impl<'gc> TextFormatObject<'gc> { impl<'gc> TObject<'gc> for TextFormatObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/texture_object.rs b/core/src/avm2/object/texture_object.rs index 3c87c6352689..c008ebe17ce2 100644 --- a/core/src/avm2/object/texture_object.rs +++ b/core/src/avm2/object/texture_object.rs @@ -1,9 +1,10 @@ //! Object representation for Texture3D objects use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_render::backend::{Context3DTextureFormat, Texture}; use std::rc::Rc; @@ -14,10 +15,6 @@ use super::{ClassObject, Context3DObject}; #[collect(no_drop)] pub struct TextureObject<'gc>(pub Gc<'gc, TextureObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct TextureObjectWeak<'gc>(pub GcWeak<'gc, TextureObjectData<'gc>>); - impl<'gc> TextureObject<'gc> { pub fn from_handle( activation: &mut Activation<'_, 'gc>, @@ -56,7 +53,7 @@ impl<'gc> TextureObject<'gc> { #[repr(C, align(8))] pub struct TextureObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::TextureObject>, context3d: Context3DObject<'gc>, @@ -69,7 +66,7 @@ pub struct TextureObjectData<'gc> { impl<'gc> TObject<'gc> for TextureObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/vector_object.rs b/core/src/avm2/object/vector_object.rs index cab72466afe2..93c701eb008b 100644 --- a/core/src/avm2/object/vector_object.rs +++ b/core/src/avm2/object/vector_object.rs @@ -3,6 +3,7 @@ use crate::avm2::Multiname; use crate::avm2::activation::Activation; use crate::avm2::error::{Error, ReferenceErrorCode, make_error_1125, make_reference_error}; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, Object, TObject}; use crate::avm2::value::Value; @@ -10,7 +11,7 @@ use crate::avm2::vector::VectorStorage; use crate::string::WStr; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::RefLock}; +use gc_arena::{Collect, Gc, Mutation, lock::RefLock}; use ruffle_common::utils::HasPrefixField; use std::cell::{Ref, RefMut}; @@ -41,10 +42,6 @@ pub fn vector_allocator<'gc>( #[collect(no_drop)] pub struct VectorObject<'gc>(pub Gc<'gc, VectorObjectData<'gc>>); -#[derive(Collect, Clone, Copy, Debug)] -#[collect(no_drop)] -pub struct VectorObjectWeak<'gc>(pub GcWeak<'gc, VectorObjectData<'gc>>); - impl fmt::Debug for VectorObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("VectorObject") @@ -58,7 +55,7 @@ impl fmt::Debug for VectorObject<'_> { #[repr(C, align(8))] pub struct VectorObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::VectorObject>, /// Vector-structured properties vector: RefLock>, @@ -175,7 +172,7 @@ impl<'gc> VectorObject<'gc> { impl<'gc> TObject<'gc> for VectorObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn get_property_local( diff --git a/core/src/avm2/object/vertex_buffer_3d_object.rs b/core/src/avm2/object/vertex_buffer_3d_object.rs index d734d69d3b17..4aa1f2167b41 100644 --- a/core/src/avm2/object/vertex_buffer_3d_object.rs +++ b/core/src/avm2/object/vertex_buffer_3d_object.rs @@ -1,9 +1,10 @@ //! Object representation for VertexBuffer3D objects use crate::avm2::activation::Activation; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; use ruffle_render::backend::VertexBuffer; use std::rc::Rc; @@ -14,10 +15,6 @@ use super::Context3DObject; #[collect(no_drop)] pub struct VertexBuffer3DObject<'gc>(pub Gc<'gc, VertexBuffer3DObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct VertexBuffer3DObjectWeak<'gc>(pub GcWeak<'gc, VertexBuffer3DObjectData<'gc>>); - impl<'gc> VertexBuffer3DObject<'gc> { pub fn from_handle( activation: &mut Activation<'_, 'gc>, @@ -57,7 +54,7 @@ impl<'gc> VertexBuffer3DObject<'gc> { #[repr(C, align(8))] pub struct VertexBuffer3DObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::VertexBuffer3DObject>, context3d: Context3DObject<'gc>, @@ -72,7 +69,7 @@ pub struct VertexBuffer3DObjectData<'gc> { impl<'gc> TObject<'gc> for VertexBuffer3DObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/worker_domain_object.rs b/core/src/avm2/object/worker_domain_object.rs index 329c0bd381c4..b5c48f75f5d3 100644 --- a/core/src/avm2/object/worker_domain_object.rs +++ b/core/src/avm2/object/worker_domain_object.rs @@ -1,18 +1,15 @@ use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; #[derive(Clone, Collect, Copy)] #[collect(no_drop)] pub struct WorkerDomainObject<'gc>(pub Gc<'gc, WorkerDomainObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct WorkerDomainObjectWeak<'gc>(pub GcWeak<'gc, WorkerDomainObjectData<'gc>>); - impl fmt::Debug for WorkerDomainObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WorkerDomainObject") @@ -26,12 +23,12 @@ impl fmt::Debug for WorkerDomainObject<'_> { #[repr(C, align(8))] pub struct WorkerDomainObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::WorkerDomainObject>, } impl<'gc> TObject<'gc> for WorkerDomainObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/worker_object.rs b/core/src/avm2/object/worker_object.rs index d83c26ea0315..73a65f142bb6 100644 --- a/core/src/avm2/object/worker_object.rs +++ b/core/src/avm2/object/worker_object.rs @@ -1,18 +1,15 @@ use crate::avm2::activation::Activation; use crate::avm2::object::TObject; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use core::fmt; -use gc_arena::{Collect, Gc, GcWeak}; +use gc_arena::{Collect, Gc}; use ruffle_common::utils::HasPrefixField; #[derive(Clone, Collect, Copy)] #[collect(no_drop)] pub struct WorkerObject<'gc>(pub Gc<'gc, WorkerObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct WorkerObjectWeak<'gc>(pub GcWeak<'gc, WorkerObjectData<'gc>>); - impl fmt::Debug for WorkerObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WorkerObject") @@ -26,12 +23,12 @@ impl fmt::Debug for WorkerObject<'_> { #[repr(C, align(8))] pub struct WorkerObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::WorkerObject>, } impl<'gc> TObject<'gc> for WorkerObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } } diff --git a/core/src/avm2/object/xml_list_object.rs b/core/src/avm2/object/xml_list_object.rs index a79f3b1369d4..f9a62d7be1a7 100644 --- a/core/src/avm2/object/xml_list_object.rs +++ b/core/src/avm2/object/xml_list_object.rs @@ -6,6 +6,7 @@ use crate::avm2::e4x::{ }; use crate::avm2::error::make_error_1089; use crate::avm2::function::FunctionArgs; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{Object, TObject}; use crate::avm2::value::Value; @@ -13,7 +14,7 @@ use crate::avm2::{Error, Multiname, Namespace}; use crate::string::AvmString; use gc_arena::barrier::unlock; use gc_arena::{ - Collect, Gc, GcWeak, Mutation, + Collect, Gc, Mutation, lock::{Lock, RefLock}, }; use ruffle_common::utils::HasPrefixField; @@ -50,10 +51,6 @@ pub fn xml_list_allocator<'gc>( #[collect(no_drop)] pub struct XmlListObject<'gc>(pub Gc<'gc, XmlListObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct XmlListObjectWeak<'gc>(pub GcWeak<'gc, XmlListObjectData<'gc>>); - impl Debug for XmlListObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("XmlListObject") @@ -355,7 +352,7 @@ impl<'gc> XmlListObject<'gc> { #[repr(C, align(8))] pub struct XmlListObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::XmlListObject>, /// The children stored by this list. children: RefLock>>, @@ -431,8 +428,8 @@ impl<'gc> XmlOrXmlListObject<'gc> { pub fn as_object(&self) -> Object<'gc> { match self { - XmlOrXmlListObject::Xml(x) => Object::XmlObject(*x), - XmlOrXmlListObject::XmlList(x) => Object::XmlListObject(*x), + XmlOrXmlListObject::Xml(x) => (*x).into(), + XmlOrXmlListObject::XmlList(x) => (*x).into(), } } @@ -476,7 +473,7 @@ impl<'gc> From> for XmlOrXmlListObject<'gc> { impl<'gc> TObject<'gc> for XmlListObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn xml_descendants( diff --git a/core/src/avm2/object/xml_object.rs b/core/src/avm2/object/xml_object.rs index 4b36652fb2ee..eb140d053156 100644 --- a/core/src/avm2/object/xml_object.rs +++ b/core/src/avm2/object/xml_object.rs @@ -7,6 +7,7 @@ use crate::avm2::e4x::{ }; use crate::avm2::error::make_error_1087; use crate::avm2::function::FunctionArgs; +use crate::avm2::object::kind; use crate::avm2::object::script_object::ScriptObjectData; use crate::avm2::object::{ClassObject, NamespaceObject, Object, TObject, XmlListObject}; use crate::avm2::string::AvmString; @@ -14,7 +15,7 @@ use crate::avm2::value::Value; use crate::avm2::{Error, Multiname}; use core::fmt; use gc_arena::barrier::unlock; -use gc_arena::{Collect, Gc, GcWeak, Mutation, lock::Lock}; +use gc_arena::{Collect, Gc, Mutation, lock::Lock}; use ruffle_common::utils::HasPrefixField; use ruffle_macros::istr; use ruffle_wstr::WString; @@ -42,10 +43,6 @@ pub fn xml_allocator<'gc>( #[collect(no_drop)] pub struct XmlObject<'gc>(pub Gc<'gc, XmlObjectData<'gc>>); -#[derive(Clone, Collect, Copy, Debug)] -#[collect(no_drop)] -pub struct XmlObjectWeak<'gc>(pub GcWeak<'gc, XmlObjectData<'gc>>); - impl fmt::Debug for XmlObject<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("XmlObject") @@ -59,7 +56,7 @@ impl fmt::Debug for XmlObject<'_> { #[repr(C, align(8))] pub struct XmlObjectData<'gc> { /// Base script object - base: ScriptObjectData<'gc>, + base: ScriptObjectData<'gc, kind::XmlObject>, node: Lock>, } @@ -323,7 +320,7 @@ impl<'gc> XmlObject<'gc> { impl<'gc> TObject<'gc> for XmlObject<'gc> { fn gc_base(&self) -> Gc<'gc, ScriptObjectData<'gc>> { - HasPrefixField::as_prefix_gc(self.0) + ScriptObjectData::erase_kind(HasPrefixField::as_prefix_gc(self.0)) } fn xml_descendants( diff --git a/core/src/avm2/parameters.rs b/core/src/avm2/parameters.rs index da8456ec411e..1138d68cbd48 100644 --- a/core/src/avm2/parameters.rs +++ b/core/src/avm2/parameters.rs @@ -82,7 +82,9 @@ pub trait ParametersExt<'gc> { fn try_get_function(&self, index: usize) -> Option> { match self.get_value(index) { Value::Null => None, - Value::Object(Object::FunctionObject(f)) => Some(f), + Value::Object(o) if let Some(function_object) = o.as_function_object() => { + Some(function_object) + } _ => panic!("Expected FunctionObject or null as parameter"), } } diff --git a/core/src/avm2/value.rs b/core/src/avm2/value.rs index 2e3265deb8af..f99c3a3f41c9 100644 --- a/core/src/avm2/value.rs +++ b/core/src/avm2/value.rs @@ -1516,9 +1516,11 @@ impl<'gc> Value<'gc> { receiver: Value<'gc>, args: FunctionArgs<'_, 'gc>, ) -> Result, Error<'gc>> { - match self.as_object() { - Some(Object::ClassObject(class_object)) => class_object.call(activation, args), - Some(Object::FunctionObject(function_object)) => { + match self { + Value::Object(o) if let Some(class_object) = o.as_class_object() => { + class_object.call(activation, args) + } + Value::Object(o) if let Some(function_object) = o.as_function_object() => { function_object.call(activation, receiver, args) } _ => Err(make_error_1006(activation)), @@ -1530,11 +1532,11 @@ impl<'gc> Value<'gc> { activation: &mut Activation<'_, 'gc>, args: FunctionArgs<'_, 'gc>, ) -> Result, Error<'gc>> { - match self.as_object() { - Some(Object::ClassObject(class_object)) => { + match self { + Value::Object(o) if let Some(class_object) = o.as_class_object() => { class_object.construct_with_args(activation, args) } - Some(Object::FunctionObject(function_object)) => { + Value::Object(o) if let Some(function_object) = o.as_function_object() => { function_object.construct(activation, args).map(Into::into) } _ => Err(make_error_1007(activation)), @@ -1740,10 +1742,12 @@ impl<'gc> Value<'gc> { activation: &mut Activation<'_, 'gc>, class_or_function_object: Object<'gc>, ) -> bool { - let type_proto = match class_or_function_object { - Object::ClassObject(class_object) => Some(class_object.prototype()), - Object::FunctionObject(function_object) => function_object.prototype(), - _ => panic!("Object must be either ClassObject or FunctionObject"), + let type_proto = if let Some(class_object) = class_or_function_object.as_class_object() { + Some(class_object.prototype()) + } else if let Some(function_object) = class_or_function_object.as_function_object() { + function_object.prototype() + } else { + panic!("Object must be either ClassObject or FunctionObject") }; if let Some(type_proto) = type_proto { @@ -1800,7 +1804,8 @@ impl<'gc> Value<'gc> { } if let Some(self_qname) = obj.as_qname_object() - && let Value::Object(Object::QNameObject(other_qname)) = other + && let Value::Object(o) = other + && let Some(other_qname) = o.as_qname_object() { return Ok(self_qname.uri(activation.strings()) == other_qname.uri(activation.strings()) @@ -1809,7 +1814,8 @@ impl<'gc> Value<'gc> { } if let Some(self_ns) = obj.as_namespace_object() - && let Value::Object(Object::NamespaceObject(other_ns)) = other + && let Value::Object(o) = other + && let Some(other_ns) = o.as_namespace_object() { return Ok(self_ns.namespace().as_uri(activation.strings()) == other_ns.namespace().as_uri(activation.strings())); diff --git a/core/src/debug_ui/display_object.rs b/core/src/debug_ui/display_object.rs index b8fdebee1e38..26334843cfe1 100644 --- a/core/src/debug_ui/display_object.rs +++ b/core/src/debug_ui/display_object.rs @@ -569,7 +569,7 @@ impl DisplayObjectWindow { if ui.button(format!("{:p}", style_sheet.as_ptr())).clicked() { messages.push(Message::TrackAVM2Object(AVM2ObjectHandle::new( context, - crate::avm2::Object::StyleSheetObject(style_sheet), + style_sheet.into(), ))); } } else { diff --git a/core/src/external.rs b/core/src/external.rs index 0daf372908b6..28733925dfb5 100644 --- a/core/src/external.rs +++ b/core/src/external.rs @@ -5,7 +5,7 @@ use crate::avm1::{ArrayBuilder as Avm1ArrayBuilder, Error as Avm1Error, Object a use crate::avm2::activation::Activation as Avm2Activation; use crate::avm2::error::Error as Avm2Error; use crate::avm2::object::{ - ArrayObject as Avm2ArrayObject, FunctionObject as Avm2FunctionObject, Object as Avm2Object, + ArrayObject as Avm2ArrayObject, FunctionObject as Avm2FunctionObject, ScriptObject as Avm2ScriptObject, TObject as _, }; use crate::avm2::{Avm2, FunctionArgs, Value as Avm2Value}; @@ -213,7 +213,7 @@ impl Value { }) .collect::, Avm2Error>>()?; Value::List(values) - } else if matches!(obj, Avm2Object::ScriptObject(_)) { + } else if obj.as_script_object().is_some() { let mut values = BTreeMap::new(); let mut last_index = obj.get_next_enumerant(0, activation)?; diff --git a/core/src/pixel_bender.rs b/core/src/pixel_bender.rs index 88a392aa8f42..d164388c758d 100644 --- a/core/src/pixel_bender.rs +++ b/core/src/pixel_bender.rs @@ -2,7 +2,7 @@ use either::Either; use ruffle_render::pixel_bender::{PixelBenderType, PixelBenderTypeOpcode}; use crate::avm2::error::{Error2004Type, make_error_2004}; -use crate::avm2::{Activation, ArrayObject, ArrayStorage, Error, Object, Value}; +use crate::avm2::{Activation, ArrayObject, ArrayStorage, Error, Value}; use crate::context::UpdateContext; use crate::ecma_conversions::f64_to_wrapping_i32; use crate::string::AvmString; @@ -45,7 +45,7 @@ impl PixelBenderTypeExt for PixelBenderType { } let array_storage = match value { - Value::Object(Object::ArrayObject(o)) => Some(o.storage()), + Value::Object(o) if let Some(array) = o.as_array_object() => Some(array.storage()), Value::Null => None, _ => unreachable!("value should be an array"), };