avm2: Refactor Vector.<T> class creation
This commit is contained in:
parent
8be0a36b1a
commit
f504e9d0b4
|
@ -78,7 +78,7 @@ pub struct Class<'gc> {
|
|||
name: QName<'gc>,
|
||||
|
||||
/// 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.
|
||||
super_class: Option<Multiname<'gc>>,
|
||||
|
@ -139,12 +139,6 @@ pub struct Class<'gc> {
|
|||
/// If None, a simple coercion is done.
|
||||
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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
|
@ -220,11 +214,6 @@ impl<'gc> Class<'gc> {
|
|||
class_initializer_called: false,
|
||||
call_handler: None,
|
||||
class_traits: Vec::new(),
|
||||
specialized_class_init: Method::from_builtin(
|
||||
|_, _, _| Ok(Value::Undefined),
|
||||
"<Null specialization constructor>",
|
||||
mc,
|
||||
),
|
||||
traits_loaded: true,
|
||||
is_system: true,
|
||||
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.
|
||||
///
|
||||
/// This is used to parameterize a generic type. The returned class will no
|
||||
/// longer be generic.
|
||||
pub fn with_type_param(
|
||||
this: GcCell<'gc, Class<'gc>>,
|
||||
param: GcCell<'gc, Class<'gc>>,
|
||||
param: Option<GcCell<'gc, Class<'gc>>>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> GcCell<'gc, Class<'gc>> {
|
||||
let read = this.read();
|
||||
let key = ClassKey(param);
|
||||
let key = param.map(ClassKey);
|
||||
|
||||
if let Some(application) = read.applications.get(&key) {
|
||||
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);
|
||||
new_class.attributes.remove(ClassAttributes::GENERIC);
|
||||
new_class.class_init = new_class.specialized_class_init.clone();
|
||||
new_class.class_initializer_called = false;
|
||||
let object_vector_cls = read
|
||||
.applications
|
||||
.get(&None)
|
||||
.expect("Vector.<*> not initialized?");
|
||||
|
||||
// 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_with_params = format!(
|
||||
"{}.<{}>",
|
||||
new_class.name.local_name(),
|
||||
param.read().name().to_qualified_name(mc)
|
||||
let param = param.expect("Trying to create Vector<*>, which shouldn't happen here");
|
||||
let name = format!("Vector.<{}>", param.read().name().to_qualified_name(mc));
|
||||
|
||||
let new_class = Self::new(
|
||||
// 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
|
||||
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);
|
||||
|
||||
this.write(mc).applications.insert(key, new_class);
|
||||
new_class
|
||||
}
|
||||
|
@ -395,11 +394,6 @@ impl<'gc> Class<'gc> {
|
|||
class_initializer_called: false,
|
||||
call_handler: native_call_handler,
|
||||
class_traits: Vec::new(),
|
||||
specialized_class_init: Method::from_builtin(
|
||||
|_, _, _| Ok(Value::Undefined),
|
||||
"<Null specialization constructor>",
|
||||
activation.context.gc_context,
|
||||
),
|
||||
traits_loaded: false,
|
||||
is_system: false,
|
||||
applications: Default::default(),
|
||||
|
@ -564,11 +558,6 @@ impl<'gc> Class<'gc> {
|
|||
"<Activation object class constructor>",
|
||||
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,
|
||||
call_handler: None,
|
||||
class_traits: Vec::new(),
|
||||
|
@ -587,6 +576,10 @@ impl<'gc> Class<'gc> {
|
|||
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>> {
|
||||
&self.super_class
|
||||
}
|
||||
|
@ -815,11 +808,6 @@ impl<'gc> Class<'gc> {
|
|||
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>] {
|
||||
&self.direct_interfaces
|
||||
}
|
||||
|
@ -847,10 +835,6 @@ impl<'gc> Class<'gc> {
|
|||
pub fn is_generic(&self) -> bool {
|
||||
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>>);
|
||||
|
|
|
@ -183,9 +183,15 @@ impl<'gc> Domain<'gc> {
|
|||
if let Some(param) = multiname.param() {
|
||||
if !param.is_any_name() {
|
||||
if let Some(resolved_param) = self.get_class(¶m, 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);
|
||||
} else {
|
||||
return Ok(Some(Class::with_type_param(class, None, mc)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,8 @@ pub struct SystemClasses<'gc> {
|
|||
pub sprite: ClassObject<'gc>,
|
||||
pub simplebutton: 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 soundchannel: ClassObject<'gc>,
|
||||
pub bitmap: ClassObject<'gc>,
|
||||
|
@ -203,7 +204,8 @@ impl<'gc> SystemClasses<'gc> {
|
|||
sprite: object,
|
||||
simplebutton: object,
|
||||
regexp: object,
|
||||
vector: object,
|
||||
generic_vector: object,
|
||||
object_vector: object,
|
||||
soundtransform: object,
|
||||
soundchannel: object,
|
||||
bitmap: object,
|
||||
|
@ -364,6 +366,41 @@ fn class<'gc>(
|
|||
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 {
|
||||
($field:ident, $activation:ident, $class:expr, $script:expr) => {
|
||||
let class_object = class($class, $script, $activation)?;
|
||||
|
@ -569,7 +606,38 @@ pub fn load_player_globals<'gc>(
|
|||
)?;
|
||||
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);
|
||||
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::error::type_error;
|
||||
use crate::avm2::globals::array::{
|
||||
compare_numeric, compare_string_case_insensitive, compare_string_case_sensitive, ArrayIter,
|
||||
SortOptions,
|
||||
};
|
||||
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::vector::VectorStorage;
|
||||
use crate::avm2::Error;
|
||||
|
@ -17,6 +20,17 @@ use crate::string::AvmString;
|
|||
use gc_arena::GcCell;
|
||||
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.
|
||||
pub fn instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
|
@ -56,7 +70,7 @@ fn class_call<'gc>(
|
|||
let this_class = activation.subclass_object().unwrap();
|
||||
let value_type = this_class
|
||||
.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);
|
||||
|
||||
let arg = args.get(0).cloned().unwrap();
|
||||
|
@ -84,96 +98,15 @@ fn class_call<'gc>(
|
|||
Ok(VectorObject::from_vector(new_storage, activation)?.into())
|
||||
}
|
||||
|
||||
/// Implements `Vector`'s class constructor.
|
||||
pub fn class_init<'gc>(
|
||||
pub fn generic_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let mut globals = activation.global_scope().unwrap();
|
||||
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)
|
||||
activation.super_init(this, args)
|
||||
}
|
||||
|
||||
/// Implements `Vector`'s specialized-class constructor.
|
||||
pub fn specialized_class_init<'gc>(
|
||||
fn class_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
|
@ -307,9 +240,6 @@ pub fn concat<'gc>(
|
|||
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();
|
||||
|
||||
for arg in args {
|
||||
|
@ -319,11 +249,16 @@ pub fn concat<'gc>(
|
|||
let arg_class = arg_obj
|
||||
.instance_of_class_definition()
|
||||
.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!(
|
||||
"TypeError: Cannot coerce argument of type {:?} to argument of type {:?}",
|
||||
arg_class.read().name(),
|
||||
my_class.inner_class_definition().read().name()
|
||||
my_base_vector_class.inner_class_definition().read().name()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
@ -945,28 +880,61 @@ pub fn splice<'gc>(
|
|||
}
|
||||
|
||||
/// 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 class = Class::new(
|
||||
QName::new(activation.avm2().vector_public_namespace, "Vector"),
|
||||
Some(Multiname::new(activation.avm2().public_namespace, "Object")),
|
||||
Method::from_builtin(instance_init, "<Vector instance initializer>", mc),
|
||||
Method::from_builtin(class_init, "<Vector class initializer>", mc),
|
||||
Method::from_builtin(generic_init, "<Vector instance 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,
|
||||
);
|
||||
|
||||
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_specialized_init(Method::from_builtin(
|
||||
specialized_class_init,
|
||||
"<Vector specialized class initializer>",
|
||||
mc,
|
||||
));
|
||||
write.set_call_handler(Method::from_builtin(
|
||||
class_call,
|
||||
"<Vector call handler>",
|
||||
"<Vector.<T> call handler>",
|
||||
mc,
|
||||
));
|
||||
|
||||
|
|
|
@ -465,15 +465,6 @@ impl<'gc> ClassObject<'gc> {
|
|||
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()
|
||||
}
|
||||
|
||||
|
@ -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>> {
|
||||
if let Method::Bytecode(bc) = self.0.read().constructor {
|
||||
Some(bc.txunit)
|
||||
|
@ -780,6 +783,14 @@ impl<'gc> ClassObject<'gc> {
|
|||
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>>> {
|
||||
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());
|
||||
}
|
||||
|
||||
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
|
||||
//parameters instead of objects. We coerce them to objects now.
|
||||
let object_param = match nullable_param {
|
||||
|
@ -933,75 +940,23 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
return Ok(*application);
|
||||
}
|
||||
|
||||
let class_param = object_param
|
||||
.unwrap_or(activation.avm2().classes().object)
|
||||
.inner_class_definition();
|
||||
// if it's not a known application, then it's not int/uint/Number/*,
|
||||
// so it must be a simple Vector.<*>-derived class.
|
||||
|
||||
let class_param = object_param.map(|c| c.inner_class_definition());
|
||||
|
||||
let parameterized_class: GcCell<'_, Class<'_>> =
|
||||
Class::with_type_param(self_class, class_param, activation.context.gc_context);
|
||||
|
||||
let class_scope = self.0.read().class_scope;
|
||||
let instance_scope = self.0.read().instance_scope;
|
||||
let instance_allocator = self.0.read().instance_allocator.clone();
|
||||
let superclass_object = self.0.read().superclass_object;
|
||||
// NOTE: this isn't fully accurate, but much simpler.
|
||||
// FP's Vector is more of special case that literally copies some parent class's properties
|
||||
// main example: Vector.<Object>.prototype === Vector.<*>.prototype
|
||||
|
||||
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;
|
||||
|
||||
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)?;
|
||||
class_object.0.write(activation.context.gc_context).params = Some(object_param);
|
||||
|
||||
self.0
|
||||
.write(activation.context.gc_context)
|
||||
|
|
|
@ -71,7 +71,7 @@ impl<'gc> VectorObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
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())?;
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::avm2::script::TranslationUnit;
|
|||
use crate::avm2::Error;
|
||||
use crate::avm2::Multiname;
|
||||
use crate::avm2::Namespace;
|
||||
use crate::avm2::QName;
|
||||
use crate::ecma_conversions::{f64_to_wrapping_i32, f64_to_wrapping_u32};
|
||||
use crate::string::{AvmAtom, AvmString, WStr};
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
|
@ -1029,24 +1028,6 @@ impl<'gc> Value<'gc> {
|
|||
if object.is_of_type(class, &mut activation.context) {
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue