avm2: Refactor Vector.<T> class creation

This commit is contained in:
Adrian Wielgosik 2023-07-17 22:42:34 +02:00 committed by Adrian Wielgosik
parent 8be0a36b1a
commit f504e9d0b4
7 changed files with 216 additions and 254 deletions

View File

@ -78,7 +78,7 @@ pub struct Class<'gc> {
name: QName<'gc>, name: QName<'gc>,
/// The type parameter for this class (only supported for Vector) /// The type parameter for this class (only supported for Vector)
param: Option<GcCell<'gc, Class<'gc>>>, param: Option<Option<GcCell<'gc, Class<'gc>>>>,
/// The name of this class's superclass. /// The name of this class's superclass.
super_class: Option<Multiname<'gc>>, super_class: Option<Multiname<'gc>>,
@ -139,12 +139,6 @@ pub struct Class<'gc> {
/// If None, a simple coercion is done. /// If None, a simple coercion is done.
call_handler: Option<Method<'gc>>, call_handler: Option<Method<'gc>>,
/// The class initializer for specializations of this class.
///
/// Only applies for generic classes. Must be called once and only once
/// per specialization, prior to any use of the specialized class.
specialized_class_init: Method<'gc>,
/// Static traits for a given class. /// Static traits for a given class.
/// ///
/// These are accessed as class object properties. /// These are accessed as class object properties.
@ -156,7 +150,7 @@ pub struct Class<'gc> {
/// Maps a type parameter to the application of this class with that parameter. /// Maps a type parameter to the application of this class with that parameter.
/// ///
/// Only applicable if this class is generic. /// Only applicable if this class is generic.
applications: FnvHashMap<ClassKey<'gc>, GcCell<'gc, Class<'gc>>>, applications: FnvHashMap<Option<ClassKey<'gc>>, GcCell<'gc, Class<'gc>>>,
/// Whether or not this is a system-defined class. /// Whether or not this is a system-defined class.
/// ///
@ -220,11 +214,6 @@ impl<'gc> Class<'gc> {
class_initializer_called: false, class_initializer_called: false,
call_handler: None, call_handler: None,
class_traits: Vec::new(), class_traits: Vec::new(),
specialized_class_init: Method::from_builtin(
|_, _, _| Ok(Value::Undefined),
"<Null specialization constructor>",
mc,
),
traits_loaded: true, traits_loaded: true,
is_system: true, is_system: true,
applications: FnvHashMap::default(), applications: FnvHashMap::default(),
@ -232,45 +221,55 @@ impl<'gc> Class<'gc> {
) )
} }
pub fn add_application(
&mut self,
param: Option<GcCell<'gc, Class<'gc>>>,
cls: GcCell<'gc, Class<'gc>>,
) {
let key = param.map(ClassKey);
self.applications.insert(key, cls);
}
/// Apply type parameters to an existing class. /// Apply type parameters to an existing class.
/// ///
/// This is used to parameterize a generic type. The returned class will no /// This is used to parameterize a generic type. The returned class will no
/// longer be generic. /// longer be generic.
pub fn with_type_param( pub fn with_type_param(
this: GcCell<'gc, Class<'gc>>, this: GcCell<'gc, Class<'gc>>,
param: GcCell<'gc, Class<'gc>>, param: Option<GcCell<'gc, Class<'gc>>>,
mc: MutationContext<'gc, '_>, mc: MutationContext<'gc, '_>,
) -> GcCell<'gc, Class<'gc>> { ) -> GcCell<'gc, Class<'gc>> {
let read = this.read(); let read = this.read();
let key = ClassKey(param); let key = param.map(ClassKey);
if let Some(application) = read.applications.get(&key) { if let Some(application) = read.applications.get(&key) {
return *application; return *application;
} }
let mut new_class = (*read).clone(); // This can only happen for non-builtin Vector types,
// so let's create one here directly.
new_class.param = Some(param); let object_vector_cls = read
new_class.attributes.remove(ClassAttributes::GENERIC); .applications
new_class.class_init = new_class.specialized_class_init.clone(); .get(&None)
new_class.class_initializer_called = false; .expect("Vector.<*> not initialized?");
// FIXME - we should store a `Multiname` instead of a `QName`, and use the let param = param.expect("Trying to create Vector<*>, which shouldn't happen here");
// `params` field. For now, this is good enough to get tests passing let name = format!("Vector.<{}>", param.read().name().to_qualified_name(mc));
let name_with_params = format!(
"{}.<{}>", let new_class = Self::new(
new_class.name.local_name(), // FIXME - we should store a `Multiname` instead of a `QName`, and use the
param.read().name().to_qualified_name(mc) // `params` field. For now, this is good enough to get tests passing
QName::new(read.name.namespace(), AvmString::new_utf8(mc, name)),
Some(Multiname::new(read.name.namespace(), "Vector.<*>")),
object_vector_cls.read().instance_init(),
object_vector_cls.read().class_init(),
mc,
); );
new_class.write(mc).param = Some(Some(param));
new_class.write(mc).call_handler = object_vector_cls.read().call_handler();
new_class.name = QName::new(
new_class.name.namespace(),
AvmString::new_utf8(mc, name_with_params),
);
let new_class = GcCell::new(mc, new_class);
drop(read); drop(read);
this.write(mc).applications.insert(key, new_class); this.write(mc).applications.insert(key, new_class);
new_class new_class
} }
@ -395,11 +394,6 @@ impl<'gc> Class<'gc> {
class_initializer_called: false, class_initializer_called: false,
call_handler: native_call_handler, call_handler: native_call_handler,
class_traits: Vec::new(), class_traits: Vec::new(),
specialized_class_init: Method::from_builtin(
|_, _, _| Ok(Value::Undefined),
"<Null specialization constructor>",
activation.context.gc_context,
),
traits_loaded: false, traits_loaded: false,
is_system: false, is_system: false,
applications: Default::default(), applications: Default::default(),
@ -564,11 +558,6 @@ impl<'gc> Class<'gc> {
"<Activation object class constructor>", "<Activation object class constructor>",
activation.context.gc_context, activation.context.gc_context,
), ),
specialized_class_init: Method::from_builtin(
|_, _, _| Ok(Value::Undefined),
"<Activation object specialization constructor>",
activation.context.gc_context,
),
class_initializer_called: false, class_initializer_called: false,
call_handler: None, call_handler: None,
class_traits: Vec::new(), class_traits: Vec::new(),
@ -587,6 +576,10 @@ impl<'gc> Class<'gc> {
self.name = name; self.name = name;
} }
pub fn set_param(&mut self, param: Option<Option<GcCell<'gc, Class<'gc>>>>) {
self.param = param;
}
pub fn super_class_name(&self) -> &Option<Multiname<'gc>> { pub fn super_class_name(&self) -> &Option<Multiname<'gc>> {
&self.super_class &self.super_class
} }
@ -815,11 +808,6 @@ impl<'gc> Class<'gc> {
self.class_initializer_called = true; self.class_initializer_called = true;
} }
/// Set the class initializer for specializations of this class.
pub fn set_specialized_init(&mut self, specialized_init: Method<'gc>) {
self.specialized_class_init = specialized_init;
}
pub fn direct_interfaces(&self) -> &[Multiname<'gc>] { pub fn direct_interfaces(&self) -> &[Multiname<'gc>] {
&self.direct_interfaces &self.direct_interfaces
} }
@ -847,10 +835,6 @@ impl<'gc> Class<'gc> {
pub fn is_generic(&self) -> bool { pub fn is_generic(&self) -> bool {
self.attributes.contains(ClassAttributes::GENERIC) self.attributes.contains(ClassAttributes::GENERIC)
} }
pub fn param(&self) -> &Option<GcCell<'gc, Class<'gc>>> {
&self.param
}
} }
pub struct ClassHashWrapper<'gc>(pub GcCell<'gc, Class<'gc>>); pub struct ClassHashWrapper<'gc>(pub GcCell<'gc, Class<'gc>>);

View File

@ -183,9 +183,15 @@ impl<'gc> Domain<'gc> {
if let Some(param) = multiname.param() { if let Some(param) = multiname.param() {
if !param.is_any_name() { if !param.is_any_name() {
if let Some(resolved_param) = self.get_class(&param, mc)? { if let Some(resolved_param) = self.get_class(&param, mc)? {
return Ok(Some(Class::with_type_param(class, resolved_param, mc))); return Ok(Some(Class::with_type_param(
class,
Some(resolved_param),
mc,
)));
} }
return Ok(None); return Ok(None);
} else {
return Ok(Some(Class::with_type_param(class, None, mc)));
} }
} }
} }

View File

@ -83,7 +83,8 @@ pub struct SystemClasses<'gc> {
pub sprite: ClassObject<'gc>, pub sprite: ClassObject<'gc>,
pub simplebutton: ClassObject<'gc>, pub simplebutton: ClassObject<'gc>,
pub regexp: ClassObject<'gc>, pub regexp: ClassObject<'gc>,
pub vector: ClassObject<'gc>, pub generic_vector: ClassObject<'gc>,
pub object_vector: ClassObject<'gc>, // Vector.<*>
pub soundtransform: ClassObject<'gc>, pub soundtransform: ClassObject<'gc>,
pub soundchannel: ClassObject<'gc>, pub soundchannel: ClassObject<'gc>,
pub bitmap: ClassObject<'gc>, pub bitmap: ClassObject<'gc>,
@ -203,7 +204,8 @@ impl<'gc> SystemClasses<'gc> {
sprite: object, sprite: object,
simplebutton: object, simplebutton: object,
regexp: object, regexp: object,
vector: object, generic_vector: object,
object_vector: object,
soundtransform: object, soundtransform: object,
soundchannel: object, soundchannel: object,
bitmap: object, bitmap: object,
@ -364,6 +366,41 @@ fn class<'gc>(
Ok(class_object) Ok(class_object)
} }
fn vector_class<'gc>(
param_class: Option<ClassObject<'gc>>,
legacy_name: &'static str,
script: Script<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> Result<ClassObject<'gc>, Error<'gc>> {
let mc = activation.context.gc_context;
let (_, mut global, mut domain) = script.init();
let cls = param_class.map(|c| c.inner_class_definition());
let vector_cls = class(
vector::create_builtin_class(activation, cls),
script,
activation,
)?;
vector_cls.set_param(activation, Some(param_class));
let generic_vector = activation.avm2().classes().generic_vector;
generic_vector.add_application(activation, param_class, vector_cls);
let generic_cls = generic_vector.inner_class_definition();
generic_cls
.write(mc)
.add_application(cls, vector_cls.inner_class_definition());
let legacy_name = QName::new(activation.avm2().vector_internal_namespace, legacy_name);
global.install_const_late(
mc,
legacy_name,
vector_cls.into(),
activation.avm2().classes().class,
);
domain.export_definition(legacy_name, script, mc);
Ok(vector_cls)
}
macro_rules! avm2_system_class { macro_rules! avm2_system_class {
($field:ident, $activation:ident, $class:expr, $script:expr) => { ($field:ident, $activation:ident, $class:expr, $script:expr) => {
let class_object = class($class, $script, $activation)?; let class_object = class($class, $script, $activation)?;
@ -569,7 +606,38 @@ pub fn load_player_globals<'gc>(
)?; )?;
function(activation, "", "unescape", toplevel::unescape, script)?; function(activation, "", "unescape", toplevel::unescape, script)?;
avm2_system_class!(vector, activation, vector::create_class(activation), script); avm2_system_class!(
generic_vector,
activation,
vector::create_generic_class(activation),
script
);
vector_class(
Some(activation.avm2().classes().int),
"Vector$int",
script,
activation,
)?;
vector_class(
Some(activation.avm2().classes().uint),
"Vector$uint",
script,
activation,
)?;
vector_class(
Some(activation.avm2().classes().number),
"Vector$double",
script,
activation,
)?;
let object_vector = vector_class(None, "Vector$object", script, activation)?;
activation
.avm2()
.system_classes
.as_mut()
.unwrap()
.object_vector = object_vector;
avm2_system_class!(date, activation, date::create_class(activation), script); avm2_system_class!(date, activation, date::create_class(activation), script);

View File

@ -2,12 +2,15 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::class::{Class, ClassAttributes}; use crate::avm2::class::{Class, ClassAttributes};
use crate::avm2::error::type_error;
use crate::avm2::globals::array::{ use crate::avm2::globals::array::{
compare_numeric, compare_string_case_insensitive, compare_string_case_sensitive, ArrayIter, compare_numeric, compare_string_case_insensitive, compare_string_case_sensitive, ArrayIter,
SortOptions, SortOptions,
}; };
use crate::avm2::method::{Method, NativeMethodImpl}; use crate::avm2::method::{Method, NativeMethodImpl};
use crate::avm2::object::{vector_allocator, FunctionObject, Object, TObject, VectorObject}; use crate::avm2::object::{
vector_allocator, ClassObject, FunctionObject, Object, TObject, VectorObject,
};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::vector::VectorStorage; use crate::avm2::vector::VectorStorage;
use crate::avm2::Error; use crate::avm2::Error;
@ -17,6 +20,17 @@ use crate::string::AvmString;
use gc_arena::GcCell; use gc_arena::GcCell;
use std::cmp::{max, min, Ordering}; use std::cmp::{max, min, Ordering};
pub fn generic_vector_allocator<'gc>(
_class: ClassObject<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> Result<Object<'gc>, Error<'gc>> {
return Err(Error::AvmError(type_error(
activation,
"Error #1007: Instantiation attempted on a non-constructor.",
1007,
)?));
}
/// Implements `Vector`'s instance constructor. /// Implements `Vector`'s instance constructor.
pub fn instance_init<'gc>( pub fn instance_init<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -56,7 +70,7 @@ fn class_call<'gc>(
let this_class = activation.subclass_object().unwrap(); let this_class = activation.subclass_object().unwrap();
let value_type = this_class let value_type = this_class
.as_class_params() .as_class_params()
.ok_or("Cannot convert to Vector")? // note: ideally, an untyped Vector shouldn't have a call handler at all .ok_or("Cannot convert to Vector")? // technically unreachable
.unwrap_or(activation.avm2().classes().object); .unwrap_or(activation.avm2().classes().object);
let arg = args.get(0).cloned().unwrap(); let arg = args.get(0).cloned().unwrap();
@ -84,96 +98,15 @@ fn class_call<'gc>(
Ok(VectorObject::from_vector(new_storage, activation)?.into()) Ok(VectorObject::from_vector(new_storage, activation)?.into())
} }
/// Implements `Vector`'s class constructor. pub fn generic_init<'gc>(
pub fn class_init<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let mut globals = activation.global_scope().unwrap(); activation.super_init(this, args)
let mut domain = activation.domain();
let vector_internal_namespace = activation.avm2().vector_internal_namespace;
//We have to grab Object's defining script instead of our own, because
//at this point Vector hasn't actually been defined yet. It doesn't
//matter because we only have one script for our globals.
let (_, script) = domain
.get_defining_script(&Multiname::new(
activation.avm2().public_namespace,
"Object",
))?
.unwrap();
let class_class = activation.avm2().classes().class;
let int_class = activation.avm2().classes().int;
let int_vector_class = this.apply(activation, int_class.into())?;
let int_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$int");
globals.install_const_late(
activation.context.gc_context,
int_vector_name_legacy,
int_vector_class.into(),
class_class,
);
domain.export_definition(
int_vector_name_legacy,
script,
activation.context.gc_context,
);
let uint_class = activation.avm2().classes().uint;
let uint_vector_class = this.apply(activation, uint_class.into())?;
let uint_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$uint");
globals.install_const_late(
activation.context.gc_context,
uint_vector_name_legacy,
uint_vector_class.into(),
class_class,
);
domain.export_definition(
uint_vector_name_legacy,
script,
activation.context.gc_context,
);
let number_class = activation.avm2().classes().number;
let number_vector_class = this.apply(activation, number_class.into())?;
let number_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$double");
globals.install_const_late(
activation.context.gc_context,
number_vector_name_legacy,
number_vector_class.into(),
class_class,
);
domain.export_definition(
number_vector_name_legacy,
script,
activation.context.gc_context,
);
let plain_vector_class = this.apply(activation, Value::Null)?;
let object_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$object");
globals.install_const_late(
activation.context.gc_context,
object_vector_name_legacy,
plain_vector_class.into(),
class_class,
);
domain.export_definition(
object_vector_name_legacy,
script,
activation.context.gc_context,
);
Ok(Value::Undefined)
} }
/// Implements `Vector`'s specialized-class constructor. fn class_init<'gc>(
pub fn specialized_class_init<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -307,9 +240,6 @@ pub fn concat<'gc>(
return Err("Not a vector-structured object".into()); return Err("Not a vector-structured object".into());
}; };
let my_class = this
.instance_of()
.ok_or("TypeError: Tried to concat into a bare object")?;
let val_class = new_vector_storage.value_type().inner_class_definition(); let val_class = new_vector_storage.value_type().inner_class_definition();
for arg in args { for arg in args {
@ -319,11 +249,16 @@ pub fn concat<'gc>(
let arg_class = arg_obj let arg_class = arg_obj
.instance_of_class_definition() .instance_of_class_definition()
.ok_or("TypeError: Tried to concat from a bare object")?; .ok_or("TypeError: Tried to concat from a bare object")?;
if !arg.is_of_type(activation, my_class.inner_class_definition()) {
// this is Vector.<int/uint/Number/*>
let my_base_vector_class = activation
.subclass_object()
.expect("Method call without bound class?");
if !arg.is_of_type(activation, my_base_vector_class.inner_class_definition()) {
return Err(format!( return Err(format!(
"TypeError: Cannot coerce argument of type {:?} to argument of type {:?}", "TypeError: Cannot coerce argument of type {:?} to argument of type {:?}",
arg_class.read().name(), arg_class.read().name(),
my_class.inner_class_definition().read().name() my_base_vector_class.inner_class_definition().read().name()
) )
.into()); .into());
} }
@ -945,28 +880,61 @@ pub fn splice<'gc>(
} }
/// Construct `Vector`'s class. /// Construct `Vector`'s class.
pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> GcCell<'gc, Class<'gc>> { pub fn create_generic_class<'gc>(activation: &mut Activation<'_, 'gc>) -> GcCell<'gc, Class<'gc>> {
let mc = activation.context.gc_context; let mc = activation.context.gc_context;
let class = Class::new( let class = Class::new(
QName::new(activation.avm2().vector_public_namespace, "Vector"), QName::new(activation.avm2().vector_public_namespace, "Vector"),
Some(Multiname::new(activation.avm2().public_namespace, "Object")), Some(Multiname::new(activation.avm2().public_namespace, "Object")),
Method::from_builtin(instance_init, "<Vector instance initializer>", mc), Method::from_builtin(generic_init, "<Vector instance initializer>", mc),
Method::from_builtin(class_init, "<Vector class initializer>", mc), Method::from_builtin(generic_init, "<Vector class initializer>", mc),
mc,
);
let mut write = class.write(mc);
write.set_attributes(ClassAttributes::GENERIC | ClassAttributes::FINAL);
write.set_instance_allocator(generic_vector_allocator);
class
}
/// Construct `Vector.<int/uint/Number/*>`'s class.
pub fn create_builtin_class<'gc>(
activation: &mut Activation<'_, 'gc>,
param: Option<GcCell<'gc, Class<'gc>>>,
) -> GcCell<'gc, Class<'gc>> {
let mc = activation.context.gc_context;
// FIXME - we should store a `Multiname` instead of a `QName`, and use the
// `params` field. For now, this is good enough to get tests passing
let name = if let Some(param) = param {
let name = format!("Vector.<{}>", param.read().name().to_qualified_name(mc));
QName::new(
activation.avm2().vector_public_namespace,
AvmString::new_utf8(mc, name),
)
} else {
QName::new(activation.avm2().vector_public_namespace, "Vector.<*>")
};
let class = Class::new(
name,
Some(Multiname::new(activation.avm2().public_namespace, "Object")),
Method::from_builtin(instance_init, "<Vector.<T> instance initializer>", mc),
Method::from_builtin(class_init, "<Vector.<T> class initializer>", mc),
mc, mc,
); );
let mut write = class.write(mc); let mut write = class.write(mc);
write.set_attributes(ClassAttributes::GENERIC | ClassAttributes::FINAL); // TODO: Vector.<*> is also supposed to be final, but currently
// that'd make it impossible for us to create derived Vector.<MyType>.
if param.is_some() {
write.set_attributes(ClassAttributes::FINAL);
}
write.set_param(Some(param));
write.set_instance_allocator(vector_allocator); write.set_instance_allocator(vector_allocator);
write.set_specialized_init(Method::from_builtin(
specialized_class_init,
"<Vector specialized class initializer>",
mc,
));
write.set_call_handler(Method::from_builtin( write.set_call_handler(Method::from_builtin(
class_call, class_call,
"<Vector call handler>", "<Vector.<T> call handler>",
mc, mc,
)); ));

View File

@ -465,15 +465,6 @@ impl<'gc> ClassObject<'gc> {
return true; return true;
} }
let test_class_read = test_class.read();
if let (Some(Some(my_param)), Some(other_single_param)) =
(class.as_class_params(), test_class_read.param())
{
if my_param.has_class_in_chain(*other_single_param) {
return true;
}
}
my_class = class.superclass_object() my_class = class.superclass_object()
} }
@ -727,6 +718,18 @@ impl<'gc> ClassObject<'gc> {
} }
} }
pub fn add_application(
&self,
activation: &mut Activation<'_, 'gc>,
param: Option<ClassObject<'gc>>,
cls: ClassObject<'gc>,
) {
self.0
.write(activation.context.gc_context)
.applications
.insert(param, cls);
}
pub fn translation_unit(self) -> Option<TranslationUnit<'gc>> { pub fn translation_unit(self) -> Option<TranslationUnit<'gc>> {
if let Method::Bytecode(bc) = self.0.read().constructor { if let Method::Bytecode(bc) = self.0.read().constructor {
Some(bc.txunit) Some(bc.txunit)
@ -780,6 +783,14 @@ impl<'gc> ClassObject<'gc> {
self.0.read().superclass_object self.0.read().superclass_object
} }
pub fn set_param(
self,
activation: &mut Activation<'_, 'gc>,
param: Option<Option<ClassObject<'gc>>>,
) {
self.0.write(activation.context.gc_context).params = param;
}
pub fn as_class_params(self) -> Option<Option<ClassObject<'gc>>> { pub fn as_class_params(self) -> Option<Option<ClassObject<'gc>>> {
self.0.read().params self.0.read().params
} }
@ -908,10 +919,6 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
return Err(format!("Class {:?} is not generic", self_class.read().name()).into()); return Err(format!("Class {:?} is not generic", self_class.read().name()).into());
} }
if !self_class.read().param().is_none() {
return Err(format!("Class {:?} was already applied", self_class.read().name()).into());
}
//Because `null` is a valid parameter, we have to accept values as //Because `null` is a valid parameter, we have to accept values as
//parameters instead of objects. We coerce them to objects now. //parameters instead of objects. We coerce them to objects now.
let object_param = match nullable_param { let object_param = match nullable_param {
@ -933,75 +940,23 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
return Ok(*application); return Ok(*application);
} }
let class_param = object_param // if it's not a known application, then it's not int/uint/Number/*,
.unwrap_or(activation.avm2().classes().object) // so it must be a simple Vector.<*>-derived class.
.inner_class_definition();
let class_param = object_param.map(|c| c.inner_class_definition());
let parameterized_class: GcCell<'_, Class<'_>> = let parameterized_class: GcCell<'_, Class<'_>> =
Class::with_type_param(self_class, class_param, activation.context.gc_context); Class::with_type_param(self_class, class_param, activation.context.gc_context);
let class_scope = self.0.read().class_scope; // NOTE: this isn't fully accurate, but much simpler.
let instance_scope = self.0.read().instance_scope; // FP's Vector is more of special case that literally copies some parent class's properties
let instance_allocator = self.0.read().instance_allocator.clone(); // main example: Vector.<Object>.prototype === Vector.<*>.prototype
let superclass_object = self.0.read().superclass_object;
let class_proto = self.allocate_prototype(activation, superclass_object)?; let vector_star_cls = activation.avm2().classes().object_vector;
let class_object =
Self::from_class(activation, parameterized_class, Some(vector_star_cls))?;
let class_class = activation.avm2().classes().class; class_object.0.write(activation.context.gc_context).params = Some(object_param);
let constructor = self.0.read().constructor.clone();
let native_constructor = self.0.read().native_constructor.clone();
let call_handler = self.0.read().call_handler.clone();
let mut class_object = ClassObject(GcCell::new(
activation.context.gc_context,
ClassObjectData {
base: ScriptObjectData::new(class_class),
class: parameterized_class,
prototype: None,
class_scope,
instance_scope,
superclass_object,
instance_allocator,
constructor,
native_constructor,
call_handler,
params: Some(object_param),
applications: Default::default(),
interfaces: Vec::new(),
instance_vtable: VTable::empty(activation.context.gc_context),
class_vtable: VTable::empty(activation.context.gc_context),
},
));
class_object
.inner_class_definition()
.read()
.validate_class(class_object.superclass_object())?;
class_object.instance_vtable().init_vtable(
class_object,
parameterized_class.read().instance_traits(),
class_object.instance_scope(),
class_object
.superclass_object()
.map(|cls| cls.instance_vtable()),
activation,
)?;
// class vtable == class traits + Class instance traits
class_object.class_vtable().init_vtable(
class_object,
parameterized_class.read().class_traits(),
class_object.class_scope(),
Some(class_object.instance_of().unwrap().instance_vtable()),
activation,
)?;
class_object.link_prototype(activation, class_proto)?;
class_object.link_interfaces(activation)?;
class_object.install_class_vtable_and_slots(activation.context.gc_context);
class_object.run_class_initializer(activation)?;
self.0 self.0
.write(activation.context.gc_context) .write(activation.context.gc_context)

View File

@ -71,7 +71,7 @@ impl<'gc> VectorObject<'gc> {
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
) -> Result<Object<'gc>, Error<'gc>> { ) -> Result<Object<'gc>, Error<'gc>> {
let value_type = vector.value_type(); let value_type = vector.value_type();
let vector_class = activation.avm2().classes().vector; let vector_class = activation.avm2().classes().generic_vector;
let applied_class = vector_class.apply(activation, value_type.into())?; let applied_class = vector_class.apply(activation, value_type.into())?;

View File

@ -8,7 +8,6 @@ use crate::avm2::script::TranslationUnit;
use crate::avm2::Error; use crate::avm2::Error;
use crate::avm2::Multiname; use crate::avm2::Multiname;
use crate::avm2::Namespace; use crate::avm2::Namespace;
use crate::avm2::QName;
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32}; use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
use crate::string::{AvmAtom, AvmString, WStr}; use crate::string::{AvmAtom, AvmString, WStr};
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
@ -1029,24 +1028,6 @@ impl<'gc> Value<'gc> {
if object.is_of_type(class, &mut activation.context) { if object.is_of_type(class, &mut activation.context) {
return Ok(*self); return Ok(*self);
} }
if let Some(vector) = object.as_vector_storage() {
let name = class.read().name();
let vector_public_namespace = activation.avm2().vector_public_namespace;
let vector_internal_namespace = activation.avm2().vector_internal_namespace;
if name == QName::new(vector_public_namespace, "Vector")
|| (name == QName::new(vector_internal_namespace, "Vector$int")
&& vector.value_type() == activation.avm2().classes().int)
|| (name == QName::new(vector_internal_namespace, "Vector$uint")
&& vector.value_type() == activation.avm2().classes().uint)
|| (name == QName::new(vector_internal_namespace, "Vector$number")
&& vector.value_type() == activation.avm2().classes().number)
|| (name == QName::new(vector_internal_namespace, "Vector$object")
&& vector.value_type() == activation.avm2().classes().object)
{
return Ok(*self);
}
}
} }
let name = class let name = class