diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 149c80f0d..f0d5c97e2 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -599,7 +599,7 @@ impl<'gc> MovieClip<'gc> { .context .library .avm2_constructor_registry_mut() - .set_proto_symbol(proto, id), + .set_proto_symbol(proto, movie.clone(), id), Err(e) => log::warn!( "Got AVM2 error {} when getting prototype of symbol class {}", e, diff --git a/core/src/library.rs b/core/src/library.rs index 4ff37ac1a..36be02227 100644 --- a/core/src/library.rs +++ b/core/src/library.rs @@ -13,7 +13,7 @@ use gc_arena::{Collect, Gc, GcCell, MutationContext}; use std::collections::HashMap; use std::sync::{Arc, Weak}; use swf::{CharacterId, TagCode}; -use weak_table::PtrWeakKeyHashMap; +use weak_table::{traits::WeakElement, PtrWeakKeyHashMap, WeakValueHashMap}; /// Boxed error alias. type Error = Box; @@ -57,14 +57,42 @@ impl<'gc> Avm1ConstructorRegistry<'gc> { } } +#[derive(Clone)] +struct MovieSymbol(Arc, CharacterId); + +#[derive(Clone)] +struct WeakMovieSymbol(Weak, CharacterId); + +impl WeakElement for WeakMovieSymbol { + type Strong = MovieSymbol; + + fn new(view: &Self::Strong) -> Self { + Self(Arc::downgrade(&view.0), view.1) + } + + fn view(&self) -> Option { + if let Some(strong) = self.0.upgrade() { + Some(MovieSymbol(strong, self.1)) + } else { + None + } + } +} + /// The mappings between prototypes and library characters defined by /// `SymbolClass`. -#[derive(Collect)] -#[collect(no_drop)] pub struct Avm2ConstructorRegistry<'gc> { /// A list of AVM2 class prototypes and the character IDs they are expected /// to instantiate. - proto_map: HashMap, CharacterId>, + proto_map: WeakValueHashMap, WeakMovieSymbol>, +} + +unsafe impl Collect for Avm2ConstructorRegistry<'_> { + fn trace(&self, cc: gc_arena::CollectionContext) { + for (k, _) in self.proto_map.iter() { + k.trace(cc); + } + } } impl Default for Avm2ConstructorRegistry<'_> { @@ -76,7 +104,7 @@ impl Default for Avm2ConstructorRegistry<'_> { impl<'gc> Avm2ConstructorRegistry<'gc> { pub fn new() -> Self { Self { - proto_map: HashMap::new(), + proto_map: WeakValueHashMap::new(), } } @@ -84,13 +112,21 @@ impl<'gc> Avm2ConstructorRegistry<'gc> { /// /// A value of `None` indicates that this AVM2 class is not associated with /// a library symbol. - pub fn proto_symbol(&self, proto: Avm2Object<'gc>) -> Option { - self.proto_map.get(&proto).copied() + pub fn proto_symbol(&self, proto: Avm2Object<'gc>) -> Option<(Arc, CharacterId)> { + match self.proto_map.get(&proto) { + Some(MovieSymbol(movie, symbol)) => Some((movie, symbol)), + None => None, + } } /// Associate an AVM2 prototype with a given library symbol. - pub fn set_proto_symbol(&mut self, proto: Avm2Object<'gc>, symbol: CharacterId) { - self.proto_map.insert(proto, symbol); + pub fn set_proto_symbol( + &mut self, + proto: Avm2Object<'gc>, + movie: Arc, + symbol: CharacterId, + ) { + self.proto_map.insert(proto, MovieSymbol(movie, symbol)); } }