avm2: Use `Gc` instead of `GcCell` in `LoaderInfoObject`

This commit is contained in:
Lord-McSweeney 2024-07-31 11:01:44 -07:00 committed by Lord-McSweeney
parent fa759a06a4
commit 49188bd5d7
2 changed files with 74 additions and 75 deletions

View File

@ -1389,7 +1389,7 @@ impl<'gc> Object<'gc> {
Self::XmlListObject(o) => WeakObject::XmlListObject(XmlListObjectWeak(GcCell::downgrade(o.0))), Self::XmlListObject(o) => WeakObject::XmlListObject(XmlListObjectWeak(GcCell::downgrade(o.0))),
Self::RegExpObject(o) => WeakObject::RegExpObject(RegExpObjectWeak(Gc::downgrade(o.0))), Self::RegExpObject(o) => WeakObject::RegExpObject(RegExpObjectWeak(Gc::downgrade(o.0))),
Self::ByteArrayObject(o) => WeakObject::ByteArrayObject(ByteArrayObjectWeak(Gc::downgrade(o.0))), Self::ByteArrayObject(o) => WeakObject::ByteArrayObject(ByteArrayObjectWeak(Gc::downgrade(o.0))),
Self::LoaderInfoObject(o) => WeakObject::LoaderInfoObject(LoaderInfoObjectWeak(GcCell::downgrade(o.0))), Self::LoaderInfoObject(o) => WeakObject::LoaderInfoObject(LoaderInfoObjectWeak(Gc::downgrade(o.0))),
Self::ClassObject(o) => WeakObject::ClassObject(ClassObjectWeak(Gc::downgrade(o.0))), Self::ClassObject(o) => WeakObject::ClassObject(ClassObjectWeak(Gc::downgrade(o.0))),
Self::VectorObject(o) => WeakObject::VectorObject(VectorObjectWeak(Gc::downgrade(o.0))), Self::VectorObject(o) => WeakObject::VectorObject(VectorObjectWeak(Gc::downgrade(o.0))),
Self::SoundObject(o) => WeakObject::SoundObject(SoundObjectWeak(Gc::downgrade(o.0))), Self::SoundObject(o) => WeakObject::SoundObject(SoundObjectWeak(Gc::downgrade(o.0))),

View File

@ -13,8 +13,12 @@ use crate::display_object::{DisplayObject, TDisplayObject};
use crate::loader::ContentType; use crate::loader::ContentType;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use core::fmt; use core::fmt;
use gc_arena::{Collect, GcCell, GcWeakCell, Mutation}; use gc_arena::barrier::unlock;
use std::cell::{Ref, RefMut}; use gc_arena::{
lock::{Lock, RefLock},
Collect, Gc, GcWeak, Mutation,
};
use std::cell::{Cell, Ref, RefMut};
use std::sync::Arc; use std::sync::Arc;
/// ActionScript cannot construct a LoaderInfo. Note that LoaderInfo isn't a final class. /// ActionScript cannot construct a LoaderInfo. Note that LoaderInfo isn't a final class.
@ -70,16 +74,16 @@ impl<'gc> LoaderStream<'gc> {
/// resource. /// resource.
#[derive(Collect, Clone, Copy)] #[derive(Collect, Clone, Copy)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct LoaderInfoObject<'gc>(pub GcCell<'gc, LoaderInfoObjectData<'gc>>); pub struct LoaderInfoObject<'gc>(pub Gc<'gc, LoaderInfoObjectData<'gc>>);
#[derive(Collect, Clone, Copy, Debug)] #[derive(Collect, Clone, Copy, Debug)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct LoaderInfoObjectWeak<'gc>(pub GcWeakCell<'gc, LoaderInfoObjectData<'gc>>); pub struct LoaderInfoObjectWeak<'gc>(pub GcWeak<'gc, LoaderInfoObjectData<'gc>>);
impl fmt::Debug for LoaderInfoObject<'_> { impl fmt::Debug for LoaderInfoObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LoaderInfoObject") f.debug_struct("LoaderInfoObject")
.field("ptr", &self.0.as_ptr()) .field("ptr", &Gc::as_ptr(self.0))
.finish() .finish()
} }
} }
@ -88,18 +92,18 @@ impl fmt::Debug for LoaderInfoObject<'_> {
#[collect(no_drop)] #[collect(no_drop)]
pub struct LoaderInfoObjectData<'gc> { pub struct LoaderInfoObjectData<'gc> {
/// All normal script data. /// All normal script data.
base: ScriptObjectData<'gc>, base: RefLock<ScriptObjectData<'gc>>,
/// The loaded stream that this gets its info from. /// The loaded stream that this gets its info from.
loaded_stream: Option<LoaderStream<'gc>>, loaded_stream: RefLock<LoaderStream<'gc>>,
loader: Option<Object<'gc>>, loader: Option<Object<'gc>>,
/// Whether or not we've fired our 'init' event /// Whether or not we've fired our 'init' event
init_event_fired: bool, init_event_fired: Cell<bool>,
/// Whether or not we've fired our 'complete' event /// Whether or not we've fired our 'complete' event
complete_event_fired: bool, complete_event_fired: Cell<bool>,
/// The `EventDispatcher` used for `LoaderInfo.sharedEvents`. /// The `EventDispatcher` used for `LoaderInfo.sharedEvents`.
// FIXME: If we ever implement sandboxing, then ensure that we allow // FIXME: If we ever implement sandboxing, then ensure that we allow
@ -108,14 +112,13 @@ pub struct LoaderInfoObjectData<'gc> {
uncaught_error_events: Object<'gc>, uncaught_error_events: Object<'gc>,
cached_avm1movie: Option<Object<'gc>>, cached_avm1movie: Lock<Option<Object<'gc>>>,
#[collect(require_static)] content_type: Cell<ContentType>,
content_type: ContentType,
expose_content: bool, expose_content: Cell<bool>,
errored: bool, errored: Cell<bool>,
} }
impl<'gc> LoaderInfoObject<'gc> { impl<'gc> LoaderInfoObject<'gc> {
@ -127,17 +130,17 @@ impl<'gc> LoaderInfoObject<'gc> {
loader: Option<Object<'gc>>, loader: Option<Object<'gc>>,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let class = activation.avm2().classes().loaderinfo; let class = activation.avm2().classes().loaderinfo;
let base = ScriptObjectData::new(class); let base = ScriptObjectData::new(class).into();
let loaded_stream = Some(LoaderStream::Swf(movie, root)); let loaded_stream = LoaderStream::Swf(movie, root);
let this: Object<'gc> = LoaderInfoObject(GcCell::new( let this: Object<'gc> = LoaderInfoObject(Gc::new(
activation.context.gc_context, activation.context.gc_context,
LoaderInfoObjectData { LoaderInfoObjectData {
base, base,
loaded_stream, loaded_stream: RefLock::new(loaded_stream),
loader, loader,
init_event_fired: false, init_event_fired: Cell::new(false),
complete_event_fired: false, complete_event_fired: Cell::new(false),
shared_events: activation shared_events: activation
.context .context
.avm2 .avm2
@ -150,10 +153,10 @@ impl<'gc> LoaderInfoObject<'gc> {
.classes() .classes()
.uncaughterrorevents .uncaughterrorevents
.construct(activation, &[])?, .construct(activation, &[])?,
cached_avm1movie: None, cached_avm1movie: Lock::new(None),
content_type: ContentType::Swf, content_type: Cell::new(ContentType::Swf),
expose_content: false, expose_content: Cell::new(false),
errored: false, errored: Cell::new(false),
}, },
)) ))
.into(); .into();
@ -176,16 +179,16 @@ impl<'gc> LoaderInfoObject<'gc> {
is_stage: bool, is_stage: bool,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let class = activation.avm2().classes().loaderinfo; let class = activation.avm2().classes().loaderinfo;
let base = ScriptObjectData::new(class); let base = ScriptObjectData::new(class).into();
let this: Object<'gc> = LoaderInfoObject(GcCell::new( let this: Object<'gc> = LoaderInfoObject(Gc::new(
activation.context.gc_context, activation.context.gc_context,
LoaderInfoObjectData { LoaderInfoObjectData {
base, base,
loaded_stream: Some(LoaderStream::NotYetLoaded(movie, root_clip, is_stage)), loaded_stream: RefLock::new(LoaderStream::NotYetLoaded(movie, root_clip, is_stage)),
loader, loader,
init_event_fired: false, init_event_fired: Cell::new(false),
complete_event_fired: false, complete_event_fired: Cell::new(false),
shared_events: activation shared_events: activation
.context .context
.avm2 .avm2
@ -198,10 +201,10 @@ impl<'gc> LoaderInfoObject<'gc> {
.classes() .classes()
.uncaughterrorevents .uncaughterrorevents
.construct(activation, &[])?, .construct(activation, &[])?,
cached_avm1movie: None, cached_avm1movie: Lock::new(None),
content_type: ContentType::Unknown, content_type: Cell::new(ContentType::Unknown),
expose_content: false, expose_content: Cell::new(false),
errored: false, errored: Cell::new(false),
}, },
)) ))
.into(); .into();
@ -213,45 +216,43 @@ impl<'gc> LoaderInfoObject<'gc> {
} }
pub fn loader(&self) -> Option<Object<'gc>> { pub fn loader(&self) -> Option<Object<'gc>> {
return self.0.read().loader; self.0.loader
} }
pub fn shared_events(&self) -> Object<'gc> { pub fn shared_events(&self) -> Object<'gc> {
return self.0.read().shared_events; self.0.shared_events
} }
pub fn uncaught_error_events(&self) -> Object<'gc> { pub fn uncaught_error_events(&self) -> Object<'gc> {
return self.0.read().uncaught_error_events; self.0.uncaught_error_events
} }
/// Gets the `ContentType`, 'hiding' it by returning `ContentType::Unknown` /// Gets the `ContentType`, 'hiding' it by returning `ContentType::Unknown`
/// if we haven't yet fired the 'init' event. The real ContentType first becomes /// if we haven't yet fired the 'init' event. The real ContentType first becomes
/// visible to ActionScript in the 'init' event. /// visible to ActionScript in the 'init' event.
pub fn content_type_hide_before_init(&self) -> ContentType { pub fn content_type_hide_before_init(&self) -> ContentType {
if self.0.read().init_event_fired { if self.0.init_event_fired.get() {
self.0.read().content_type self.0.content_type.get()
} else { } else {
ContentType::Unknown ContentType::Unknown
} }
} }
pub fn set_errored(&self, val: bool, mc: &Mutation<'gc>) { pub fn set_errored(&self, val: bool, _mc: &Mutation<'gc>) {
self.0.write(mc).errored = val; self.0.errored.set(val);
} }
pub fn errored(&self) -> bool { pub fn errored(&self) -> bool {
self.0.read().errored self.0.errored.get()
} }
pub fn init_event_fired(&self) -> bool { pub fn init_event_fired(&self) -> bool {
self.0.read().init_event_fired self.0.init_event_fired.get()
} }
pub fn reset_init_and_complete_events(&self, mc: &Mutation<'gc>) { pub fn reset_init_and_complete_events(&self, _mc: &Mutation<'gc>) {
let mut write = self.0.write(mc); self.0.init_event_fired.set(false);
self.0.complete_event_fired.set(false);
write.init_event_fired = false;
write.complete_event_fired = false;
} }
pub fn fire_init_and_complete_events( pub fn fire_init_and_complete_events(
@ -260,9 +261,9 @@ impl<'gc> LoaderInfoObject<'gc> {
status: u16, status: u16,
redirected: bool, redirected: bool,
) { ) {
self.0.write(context.gc_context).expose_content = true; self.0.expose_content.set(true);
if !self.0.read().init_event_fired { if !self.0.init_event_fired.get() {
self.0.write(context.gc_context).init_event_fired = true; self.0.init_event_fired.set(true);
// TODO - 'init' should be fired earlier during the download. // TODO - 'init' should be fired earlier during the download.
// Right now, we fire it when downloading is fully completed. // Right now, we fire it when downloading is fully completed.
@ -270,11 +271,11 @@ impl<'gc> LoaderInfoObject<'gc> {
Avm2::dispatch_event(context, init_evt, (*self).into()); Avm2::dispatch_event(context, init_evt, (*self).into());
} }
if !self.0.read().complete_event_fired { if !self.0.complete_event_fired.get() {
// NOTE: We have to check load progress here because this function // NOTE: We have to check load progress here because this function
// is called unconditionally at the end of every frame. // is called unconditionally at the end of every frame.
let (should_complete, from_url) = match self.0.read().loaded_stream { let (should_complete, from_url) = match &*self.0.loaded_stream.borrow() {
Some(LoaderStream::Swf(ref movie, root)) => ( LoaderStream::Swf(ref movie, root) => (
root.as_movie_clip() root.as_movie_clip()
.map(|mc| mc.loaded_bytes() as i32 >= mc.total_bytes()) .map(|mc| mc.loaded_bytes() as i32 >= mc.total_bytes())
.unwrap_or(true), .unwrap_or(true),
@ -305,7 +306,7 @@ impl<'gc> LoaderInfoObject<'gc> {
Avm2::dispatch_event(context, http_status_evt, (*self).into()); Avm2::dispatch_event(context, http_status_evt, (*self).into());
} }
self.0.write(context.gc_context).complete_event_fired = true; self.0.complete_event_fired.set(true);
let complete_evt = EventObject::bare_default_event(context, "complete"); let complete_evt = EventObject::bare_default_event(context, "complete");
Avm2::dispatch_event(context, complete_evt, (*self).into()); Avm2::dispatch_event(context, complete_evt, (*self).into());
} }
@ -314,32 +315,26 @@ impl<'gc> LoaderInfoObject<'gc> {
/// Unwrap this object's loader stream /// Unwrap this object's loader stream
pub fn as_loader_stream(&self) -> Option<Ref<LoaderStream<'gc>>> { pub fn as_loader_stream(&self) -> Option<Ref<LoaderStream<'gc>>> {
if self.0.read().loaded_stream.is_some() { Some(self.0.loaded_stream.borrow())
Some(Ref::map(self.0.read(), |v: &LoaderInfoObjectData<'gc>| {
v.loaded_stream.as_ref().unwrap()
}))
} else {
None
}
} }
pub fn expose_content(&self) -> bool { pub fn expose_content(&self) -> bool {
self.0.read().expose_content self.0.expose_content.get()
} }
/// Makes the 'content' visible to ActionScript. /// Makes the 'content' visible to ActionScript.
/// This is used by certain special loaders (the stage and root movie), /// This is used by certain special loaders (the stage and root movie),
/// which expose the loaded content before the 'init' event is fired. /// which expose the loaded content before the 'init' event is fired.
pub fn set_expose_content(&self, mc: &Mutation<'gc>) { pub fn set_expose_content(&self, _mc: &Mutation<'gc>) {
self.0.write(mc).expose_content = true; self.0.expose_content.set(true);
} }
pub fn set_loader_stream(&self, stream: LoaderStream<'gc>, mc: &Mutation<'gc>) { pub fn set_loader_stream(&self, stream: LoaderStream<'gc>, mc: &Mutation<'gc>) {
self.0.write(mc).loaded_stream = Some(stream); *unlock!(Gc::write(mc, self.0), LoaderInfoObjectData, loaded_stream).borrow_mut() = stream;
} }
pub fn set_content_type(&self, content_type: ContentType, mc: &Mutation<'gc>) { pub fn set_content_type(&self, content_type: ContentType, _mc: &Mutation<'gc>) {
self.0.write(mc).content_type = content_type; self.0.content_type.set(content_type);
} }
/// Returns the AVM1Movie corresponding to the loaded movie- if /// Returns the AVM1Movie corresponding to the loaded movie- if
@ -349,7 +344,7 @@ impl<'gc> LoaderInfoObject<'gc> {
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
obj: DisplayObject<'gc>, obj: DisplayObject<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let cached_avm1movie = self.0.read().cached_avm1movie; let cached_avm1movie = self.0.cached_avm1movie.get();
if cached_avm1movie.is_none() { if cached_avm1movie.is_none() {
let class_object = activation.avm2().classes().avm1movie; let class_object = activation.avm2().classes().avm1movie;
let object = StageObject::for_display_object(activation, obj, class_object) let object = StageObject::for_display_object(activation, obj, class_object)
@ -359,10 +354,15 @@ impl<'gc> LoaderInfoObject<'gc> {
.call_native_init(object.into(), &[], activation) .call_native_init(object.into(), &[], activation)
.expect("Native init should succeed"); .expect("Native init should succeed");
self.0.write(activation.context.gc_context).cached_avm1movie = Some(object.into()); unlock!(
Gc::write(activation.context.gc_context, self.0),
LoaderInfoObjectData,
cached_avm1movie
)
.set(Some(object.into()));
} }
return self.0.read().cached_avm1movie.unwrap(); self.0.cached_avm1movie.get().unwrap()
} }
pub fn unload(&self, activation: &mut Activation<'_, 'gc>) { pub fn unload(&self, activation: &mut Activation<'_, 'gc>) {
@ -375,7 +375,6 @@ impl<'gc> LoaderInfoObject<'gc> {
let loader = self let loader = self
.0 .0
.read()
.loader .loader
.expect("LoaderInfo must have been created by Loader"); .expect("LoaderInfo must have been created by Loader");
@ -391,15 +390,15 @@ impl<'gc> LoaderInfoObject<'gc> {
impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> { impl<'gc> TObject<'gc> for LoaderInfoObject<'gc> {
fn base(&self) -> Ref<ScriptObjectData<'gc>> { fn base(&self) -> Ref<ScriptObjectData<'gc>> {
Ref::map(self.0.read(), |read| &read.base) self.0.base.borrow()
} }
fn base_mut(&self, mc: &Mutation<'gc>) -> RefMut<ScriptObjectData<'gc>> { fn base_mut(&self, mc: &Mutation<'gc>) -> RefMut<ScriptObjectData<'gc>> {
RefMut::map(self.0.write(mc), |write| &mut write.base) unlock!(Gc::write(mc, self.0), LoaderInfoObjectData, base).borrow_mut()
} }
fn as_ptr(&self) -> *const ObjectPtr { fn as_ptr(&self) -> *const ObjectPtr {
self.0.as_ptr() as *const ObjectPtr Gc::as_ptr(self.0) as *const ObjectPtr
} }
fn value_of(&self, _mc: &Mutation<'gc>) -> Result<Value<'gc>, Error<'gc>> { fn value_of(&self, _mc: &Mutation<'gc>) -> Result<Value<'gc>, Error<'gc>> {