avm2: Use `Gc` instead of `GcCell` in `ClassObject`
This commit is contained in:
parent
42e0380e2e
commit
4773591c42
|
@ -1390,7 +1390,7 @@ impl<'gc> Object<'gc> {
|
||||||
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(GcCell::downgrade(o.0))),
|
||||||
Self::ClassObject(o) => WeakObject::ClassObject(ClassObjectWeak(GcCell::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(GcCell::downgrade(o.0))),
|
Self::SoundObject(o) => WeakObject::SoundObject(SoundObjectWeak(GcCell::downgrade(o.0))),
|
||||||
Self::SoundChannelObject(o) => WeakObject::SoundChannelObject(SoundChannelObjectWeak(GcCell::downgrade(o.0))),
|
Self::SoundChannelObject(o) => WeakObject::SoundChannelObject(SoundChannelObjectWeak(GcCell::downgrade(o.0))),
|
||||||
|
|
|
@ -18,38 +18,42 @@ use crate::avm2::QName;
|
||||||
use crate::avm2::TranslationUnit;
|
use crate::avm2::TranslationUnit;
|
||||||
use crate::string::AvmString;
|
use crate::string::AvmString;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gc_arena::{Collect, GcCell, GcWeakCell, Mutation};
|
use gc_arena::barrier::unlock;
|
||||||
use std::cell::{BorrowError, Ref, RefMut};
|
use gc_arena::{
|
||||||
|
lock::{Lock, RefLock},
|
||||||
|
Collect, Gc, GcWeak, Mutation,
|
||||||
|
};
|
||||||
|
use std::cell::{Ref, RefMut};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// An Object which can be called to execute its function code.
|
/// An Object which can be called to execute its function code.
|
||||||
#[derive(Collect, Clone, Copy)]
|
#[derive(Collect, Clone, Copy)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct ClassObject<'gc>(pub GcCell<'gc, ClassObjectData<'gc>>);
|
pub struct ClassObject<'gc>(pub Gc<'gc, ClassObjectData<'gc>>);
|
||||||
|
|
||||||
#[derive(Collect, Clone, Copy, Debug)]
|
#[derive(Collect, Clone, Copy, Debug)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct ClassObjectWeak<'gc>(pub GcWeakCell<'gc, ClassObjectData<'gc>>);
|
pub struct ClassObjectWeak<'gc>(pub GcWeak<'gc, ClassObjectData<'gc>>);
|
||||||
|
|
||||||
#[derive(Collect, Clone)]
|
#[derive(Collect, Clone)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub struct ClassObjectData<'gc> {
|
pub struct ClassObjectData<'gc> {
|
||||||
/// Base script object
|
/// Base script object
|
||||||
base: ScriptObjectData<'gc>,
|
base: RefLock<ScriptObjectData<'gc>>,
|
||||||
|
|
||||||
/// The class associated with this class object.
|
/// The class associated with this class object.
|
||||||
class: Class<'gc>,
|
class: Class<'gc>,
|
||||||
|
|
||||||
/// The associated prototype.
|
/// The associated prototype.
|
||||||
/// Should always be non-None after initialization.
|
/// Should always be non-None after initialization.
|
||||||
prototype: Option<Object<'gc>>,
|
prototype: Lock<Option<Object<'gc>>>,
|
||||||
|
|
||||||
/// The captured scope that all class traits will use.
|
/// The captured scope that all class traits will use.
|
||||||
class_scope: ScopeChain<'gc>,
|
class_scope: ScopeChain<'gc>,
|
||||||
|
|
||||||
/// The captured scope that all instance traits will use.
|
/// The captured scope that all instance traits will use.
|
||||||
instance_scope: ScopeChain<'gc>,
|
instance_scope: Lock<ScopeChain<'gc>>,
|
||||||
|
|
||||||
/// The base class of this one.
|
/// The base class of this one.
|
||||||
///
|
///
|
||||||
|
@ -65,7 +69,7 @@ pub struct ClassObjectData<'gc> {
|
||||||
/// as `None` here. AVM2 considers both applications to be separate
|
/// as `None` here. AVM2 considers both applications to be separate
|
||||||
/// classes, though we consider the parameter to be the class `Object` when
|
/// classes, though we consider the parameter to be the class `Object` when
|
||||||
/// we get a param of `null`.
|
/// we get a param of `null`.
|
||||||
applications: FnvHashMap<Option<Class<'gc>>, ClassObject<'gc>>,
|
applications: RefLock<FnvHashMap<Option<Class<'gc>>, ClassObject<'gc>>>,
|
||||||
|
|
||||||
/// VTable used for instances of this class.
|
/// VTable used for instances of this class.
|
||||||
instance_vtable: VTable<'gc>,
|
instance_vtable: VTable<'gc>,
|
||||||
|
@ -160,30 +164,31 @@ impl<'gc> ClassObject<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let class_object = ClassObject(GcCell::new(
|
let mc = activation.context.gc_context;
|
||||||
|
|
||||||
|
let class_object = ClassObject(Gc::new(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
ClassObjectData {
|
ClassObjectData {
|
||||||
base: ScriptObjectData::custom_new(c_class, None, None),
|
base: RefLock::new(ScriptObjectData::custom_new(c_class, None, None)),
|
||||||
class,
|
class,
|
||||||
prototype: None,
|
prototype: Lock::new(None),
|
||||||
class_scope: scope,
|
class_scope: scope,
|
||||||
instance_scope: scope,
|
instance_scope: Lock::new(scope),
|
||||||
superclass_object,
|
superclass_object,
|
||||||
applications: Default::default(),
|
applications: RefLock::new(Default::default()),
|
||||||
instance_vtable: VTable::empty(activation.context.gc_context),
|
instance_vtable: VTable::empty(mc),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
// instance scope = [..., class object]
|
// instance scope = [..., class object]
|
||||||
let instance_scope = scope.chain(
|
let instance_scope = scope.chain(mc, &[Scope::new(class_object.into())]);
|
||||||
activation.context.gc_context,
|
|
||||||
&[Scope::new(class_object.into())],
|
|
||||||
);
|
|
||||||
|
|
||||||
class_object
|
unlock!(
|
||||||
.0
|
Gc::write(mc, class_object.0),
|
||||||
.write(activation.context.gc_context)
|
ClassObjectData,
|
||||||
.instance_scope = instance_scope;
|
instance_scope
|
||||||
|
)
|
||||||
|
.set(instance_scope);
|
||||||
class_object.init_instance_vtable(activation)?;
|
class_object.init_instance_vtable(activation)?;
|
||||||
|
|
||||||
class.add_class_object(activation.context.gc_context, class_object);
|
class.add_class_object(activation.context.gc_context, class_object);
|
||||||
|
@ -261,13 +266,11 @@ impl<'gc> ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
class_proto: Object<'gc>,
|
class_proto: Object<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
self.0.write(activation.context.gc_context).prototype = Some(class_proto);
|
let mc = activation.context.gc_context;
|
||||||
|
|
||||||
|
unlock!(Gc::write(mc, self.0), ClassObjectData, prototype).set(Some(class_proto));
|
||||||
class_proto.set_string_property_local("constructor", self.into(), activation)?;
|
class_proto.set_string_property_local("constructor", self.into(), activation)?;
|
||||||
class_proto.set_local_property_is_enumerable(
|
class_proto.set_local_property_is_enumerable(mc, "constructor".into(), false);
|
||||||
activation.context.gc_context,
|
|
||||||
"constructor".into(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -309,9 +312,9 @@ impl<'gc> ClassObject<'gc> {
|
||||||
/// `Class` and `Object`. All other types should pull `Class`'s prototype
|
/// `Class` and `Object`. All other types should pull `Class`'s prototype
|
||||||
/// and type object from the `Avm2` instance.
|
/// and type object from the `Avm2` instance.
|
||||||
pub fn link_type(self, gc_context: &Mutation<'gc>, proto: Object<'gc>) {
|
pub fn link_type(self, gc_context: &Mutation<'gc>, proto: Object<'gc>) {
|
||||||
let mut write = self.0.write(gc_context);
|
unlock!(Gc::write(gc_context, self.0), ClassObjectData, base)
|
||||||
|
.borrow_mut()
|
||||||
write.base.set_proto(proto);
|
.set_proto(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the class's initializer method.
|
/// Run the class's initializer method.
|
||||||
|
@ -321,7 +324,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
let object: Object<'gc> = self.into();
|
let object: Object<'gc> = self.into();
|
||||||
|
|
||||||
let scope = self.0.read().class_scope;
|
let scope = self.0.class_scope;
|
||||||
let c_class = self
|
let c_class = self
|
||||||
.inner_class_definition()
|
.inner_class_definition()
|
||||||
.c_class()
|
.c_class()
|
||||||
|
@ -348,7 +351,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scope = self.0.read().instance_scope;
|
let scope = self.0.instance_scope.get();
|
||||||
let method = self.constructor();
|
let method = self.constructor();
|
||||||
exec(
|
exec(
|
||||||
method,
|
method,
|
||||||
|
@ -372,7 +375,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
arguments: &[Value<'gc>],
|
arguments: &[Value<'gc>],
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let scope = self.0.read().instance_scope;
|
let scope = self.0.instance_scope.get();
|
||||||
let method = self.native_constructor();
|
let method = self.native_constructor();
|
||||||
exec(
|
exec(
|
||||||
method,
|
method,
|
||||||
|
@ -603,11 +606,13 @@ impl<'gc> ClassObject<'gc> {
|
||||||
|
|
||||||
pub fn add_application(
|
pub fn add_application(
|
||||||
&self,
|
&self,
|
||||||
gc_context: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
param: Option<Class<'gc>>,
|
param: Option<Class<'gc>>,
|
||||||
cls: ClassObject<'gc>,
|
cls: ClassObject<'gc>,
|
||||||
) {
|
) {
|
||||||
self.0.write(gc_context).applications.insert(param, cls);
|
unlock!(Gc::write(mc, self.0), ClassObjectData, applications)
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(param, cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parametrize this class. This does not check to ensure that this class is generic.
|
/// Parametrize this class. This does not check to ensure that this class is generic.
|
||||||
|
@ -618,7 +623,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
) -> Result<ClassObject<'gc>, Error<'gc>> {
|
) -> Result<ClassObject<'gc>, Error<'gc>> {
|
||||||
let self_class = self.inner_class_definition();
|
let self_class = self.inner_class_definition();
|
||||||
|
|
||||||
if let Some(application) = self.0.read().applications.get(&class_param) {
|
if let Some(application) = self.0.applications.borrow().get(&class_param) {
|
||||||
return Ok(*application);
|
return Ok(*application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,10 +641,13 @@ impl<'gc> ClassObject<'gc> {
|
||||||
let class_object =
|
let class_object =
|
||||||
Self::from_class(activation, parameterized_class, Some(vector_star_cls))?;
|
Self::from_class(activation, parameterized_class, Some(vector_star_cls))?;
|
||||||
|
|
||||||
self.0
|
unlock!(
|
||||||
.write(activation.context.gc_context)
|
Gc::write(activation.context.gc_context, self.0),
|
||||||
.applications
|
ClassObjectData,
|
||||||
.insert(class_param, class_object);
|
applications
|
||||||
|
)
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(class_param, class_object);
|
||||||
|
|
||||||
Ok(class_object)
|
Ok(class_object)
|
||||||
}
|
}
|
||||||
|
@ -665,36 +673,27 @@ impl<'gc> ClassObject<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instance_vtable(self) -> VTable<'gc> {
|
pub fn instance_vtable(self) -> VTable<'gc> {
|
||||||
self.0.read().instance_vtable
|
self.0.instance_vtable
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `inner_class_definition`, but returns an `Err(BorrowError)` instead of panicking
|
|
||||||
/// if our `GcCell` is already mutably borrowed. This is useful
|
|
||||||
/// in contexts where panicking would be extremely undesirable,
|
|
||||||
/// and there's a fallback if we cannot obtain the `Class`
|
|
||||||
/// (such as `Debug` impls),
|
|
||||||
pub fn try_inner_class_definition(&self) -> Result<Class<'gc>, BorrowError> {
|
|
||||||
self.0.try_read().map(|c| c.class)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_class_definition(self) -> Class<'gc> {
|
pub fn inner_class_definition(self) -> Class<'gc> {
|
||||||
self.0.read().class
|
self.0.class
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prototype(self) -> Object<'gc> {
|
pub fn prototype(self) -> Object<'gc> {
|
||||||
self.0.read().prototype.unwrap()
|
self.0.prototype.get().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn class_scope(self) -> ScopeChain<'gc> {
|
pub fn class_scope(self) -> ScopeChain<'gc> {
|
||||||
self.0.read().class_scope
|
self.0.class_scope
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instance_scope(self) -> ScopeChain<'gc> {
|
pub fn instance_scope(self) -> ScopeChain<'gc> {
|
||||||
self.0.read().instance_scope
|
self.0.instance_scope.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn superclass_object(self) -> Option<ClassObject<'gc>> {
|
pub fn superclass_object(self) -> Option<ClassObject<'gc>> {
|
||||||
self.0.read().superclass_object
|
self.0.superclass_object
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instance_allocator(self) -> AllocatorFn {
|
fn instance_allocator(self) -> AllocatorFn {
|
||||||
|
@ -709,9 +708,7 @@ impl<'gc> ClassObject<'gc> {
|
||||||
/// we need infallible access to *something* to print
|
/// we need infallible access to *something* to print
|
||||||
/// out.
|
/// out.
|
||||||
pub fn debug_class_name(&self) -> Box<dyn Debug + 'gc> {
|
pub fn debug_class_name(&self) -> Box<dyn Debug + 'gc> {
|
||||||
let class_name = self
|
let class_name = self.inner_class_definition().try_name();
|
||||||
.try_inner_class_definition()
|
|
||||||
.and_then(|class| class.try_name());
|
|
||||||
|
|
||||||
match class_name {
|
match class_name {
|
||||||
Ok(class_name) => Box::new(class_name),
|
Ok(class_name) => Box::new(class_name),
|
||||||
|
@ -722,21 +719,21 @@ impl<'gc> ClassObject<'gc> {
|
||||||
|
|
||||||
impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
impl<'gc> TObject<'gc> for ClassObject<'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), ClassObjectData, 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 to_string(&self, activation: &mut Activation<'_, 'gc>) -> Result<Value<'gc>, Error<'gc>> {
|
fn to_string(&self, activation: &mut Activation<'_, 'gc>) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
Ok(AvmString::new_utf8(
|
Ok(AvmString::new_utf8(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
format!("[class {}]", self.0.read().class.name().local_name()),
|
format!("[class {}]", self.0.class.name().local_name()),
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
@ -759,7 +756,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(call_handler) = self.call_handler() {
|
if let Some(call_handler) = self.call_handler() {
|
||||||
let scope = self.0.read().class_scope;
|
let scope = self.0.class_scope;
|
||||||
exec(
|
exec(
|
||||||
call_handler,
|
call_handler,
|
||||||
scope,
|
scope,
|
||||||
|
@ -803,18 +800,6 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
Some(*self)
|
Some(*self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_local_property_is_enumerable(
|
|
||||||
&self,
|
|
||||||
mc: &Mutation<'gc>,
|
|
||||||
name: AvmString<'gc>,
|
|
||||||
is_enumerable: bool,
|
|
||||||
) {
|
|
||||||
self.0
|
|
||||||
.write(mc)
|
|
||||||
.base
|
|
||||||
.set_local_property_is_enumerable(name, is_enumerable);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(
|
fn apply(
|
||||||
&self,
|
&self,
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
@ -887,7 +872,7 @@ impl<'gc> Debug for ClassObject<'gc> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("ClassObject")
|
f.debug_struct("ClassObject")
|
||||||
.field("name", &self.debug_class_name())
|
.field("name", &self.debug_class_name())
|
||||||
.field("ptr", &self.0.as_ptr())
|
.field("ptr", &Gc::as_ptr(self.0))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2208,9 +2208,9 @@ impl<'gc> MovieClip<'gc> {
|
||||||
"Got \"{:?}\" when constructing AVM2 side of movie clip of type {}",
|
"Got \"{:?}\" when constructing AVM2 side of movie clip of type {}",
|
||||||
e,
|
e,
|
||||||
class_object
|
class_object
|
||||||
.try_inner_class_definition()
|
.inner_class_definition()
|
||||||
.map(|c| c.name().to_qualified_name(context.gc_context))
|
.name()
|
||||||
.unwrap_or_else(|_| "[BorrowError!]".into())
|
.to_qualified_name(context.gc_context)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue