avm2: Use `Gc` instead of `GcCell` in `LoaderInfoObject`
This commit is contained in:
parent
fa759a06a4
commit
49188bd5d7
|
@ -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))),
|
||||||
|
|
|
@ -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>> {
|
||||||
|
|
Loading…
Reference in New Issue