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>,
|
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>>);
|
||||||
|
|
|
@ -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(¶m, mc)? {
|
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);
|
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 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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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())?;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue