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