avm2: Allow parsing generic typenames into a multiname
This commit is contained in:
parent
47e3b2229a
commit
86616b748f
|
@ -6,7 +6,8 @@ use crate::avm2::string::AvmString;
|
|||
use crate::avm2::Error;
|
||||
use gc_arena::{Collect, MutationContext};
|
||||
use swf::avm2::types::{
|
||||
Index, Multiname as AbcMultiname, Namespace as AbcNamespace, NamespaceSet as AbcNamespaceSet,
|
||||
AbcFile, Index, Multiname as AbcMultiname, Namespace as AbcNamespace,
|
||||
NamespaceSet as AbcNamespaceSet,
|
||||
};
|
||||
|
||||
/// Represents the name of a namespace.
|
||||
|
@ -225,8 +226,16 @@ impl<'gc> QName<'gc> {
|
|||
#[derive(Clone, Debug, Collect)]
|
||||
#[collect(no_drop)]
|
||||
pub struct Multiname<'gc> {
|
||||
/// The list of namespaces that satisfy this multiname.
|
||||
ns: Vec<Namespace<'gc>>,
|
||||
|
||||
/// The local name that satisfies this multiname. If `None`, then this
|
||||
/// multiname is satisfied by any name in the namespace.
|
||||
name: Option<AvmString<'gc>>,
|
||||
|
||||
/// The type parameters required to satisfy this multiname. If empty, then
|
||||
/// this multiname is satisfied by any type parameters in any amount.
|
||||
params: Vec<Multiname<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> Multiname<'gc> {
|
||||
|
@ -264,28 +273,20 @@ impl<'gc> Multiname<'gc> {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
/// Read a multiname from the ABC constant pool, copying it into the most
|
||||
/// general form of multiname.
|
||||
/// Resolve an ABC multiname's parameters and yields an AVM multiname with
|
||||
/// those parameters filled in.
|
||||
///
|
||||
/// Multiname index zero is also treated as an error, you must check for it
|
||||
/// and substitute it with whatever default is called for by AVM2.
|
||||
pub fn from_abc_multiname(
|
||||
/// This function deliberately errors out if handed a `TypeName`, as it
|
||||
/// assumes that this is an attempt to construct a recursive generic type.
|
||||
/// Type parameters may themselves be typenames, but not the base type.
|
||||
/// This is valid: `Vector.<Vector.<int>>`, but this is not:
|
||||
/// `Vector.<int>.<int>`
|
||||
fn resolve_multiname_params(
|
||||
translation_unit: TranslationUnit<'gc>,
|
||||
multiname_index: Index<AbcMultiname>,
|
||||
abc_multiname: &AbcMultiname,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Self, Error> {
|
||||
let actual_index: Result<usize, Error> = (multiname_index.0 as usize)
|
||||
.checked_sub(1)
|
||||
.ok_or_else(|| "Attempted to resolve a multiname at index zero. This is a bug.".into());
|
||||
let actual_index = actual_index?;
|
||||
let abc = translation_unit.abc();
|
||||
let abc_multiname: Result<_, Error> = abc
|
||||
.constant_pool
|
||||
.multinames
|
||||
.get(actual_index)
|
||||
.ok_or_else(|| format!("Unknown multiname constant {}", multiname_index.0).into());
|
||||
|
||||
Ok(match abc_multiname? {
|
||||
Ok(match abc_multiname {
|
||||
AbcMultiname::QName { namespace, name } | AbcMultiname::QNameA { namespace, name } => {
|
||||
Self {
|
||||
ns: vec![Namespace::from_abc_namespace(
|
||||
|
@ -295,6 +296,7 @@ impl<'gc> Multiname<'gc> {
|
|||
)?],
|
||||
name: translation_unit
|
||||
.pool_string_option(name.0, activation.context.gc_context)?,
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
AbcMultiname::RTQName { name } | AbcMultiname::RTQNameA { name } => {
|
||||
|
@ -303,6 +305,7 @@ impl<'gc> Multiname<'gc> {
|
|||
ns: vec![ns],
|
||||
name: translation_unit
|
||||
.pool_string_option(name.0, activation.context.gc_context)?,
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
AbcMultiname::RTQNameL | AbcMultiname::RTQNameLA => {
|
||||
|
@ -311,6 +314,7 @@ impl<'gc> Multiname<'gc> {
|
|||
Self {
|
||||
ns: vec![ns],
|
||||
name: Some(name),
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
AbcMultiname::Multiname {
|
||||
|
@ -327,6 +331,7 @@ impl<'gc> Multiname<'gc> {
|
|||
activation.context.gc_context,
|
||||
)?,
|
||||
name: translation_unit.pool_string_option(name.0, activation.context.gc_context)?,
|
||||
params: Vec::new(),
|
||||
},
|
||||
AbcMultiname::MultinameL { namespace_set }
|
||||
| AbcMultiname::MultinameLA { namespace_set } => {
|
||||
|
@ -338,12 +343,68 @@ impl<'gc> Multiname<'gc> {
|
|||
activation.context.gc_context,
|
||||
)?,
|
||||
name: Some(name),
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
AbcMultiname::TypeName { .. } => return Err("TypeName not implemented".into()),
|
||||
AbcMultiname::TypeName { .. } => {
|
||||
return Err("Recursive TypeNames are not supported!".into())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve a given multiname index from the ABC file, yielding an error
|
||||
/// if the multiname index is zero.
|
||||
pub fn resolve_multiname_index(
|
||||
abc: &AbcFile,
|
||||
multiname_index: Index<AbcMultiname>,
|
||||
) -> Result<&AbcMultiname, Error> {
|
||||
let actual_index: Result<usize, Error> = (multiname_index.0 as usize)
|
||||
.checked_sub(1)
|
||||
.ok_or_else(|| "Attempted to resolve a multiname at index zero. This is a bug.".into());
|
||||
let actual_index = actual_index?;
|
||||
let abc_multiname: Result<_, Error> = abc
|
||||
.constant_pool
|
||||
.multinames
|
||||
.get(actual_index)
|
||||
.ok_or_else(|| format!("Unknown multiname constant {}", multiname_index.0).into());
|
||||
|
||||
abc_multiname
|
||||
}
|
||||
|
||||
/// Read a multiname from the ABC constant pool, copying it into the most
|
||||
/// general form of multiname.
|
||||
pub fn from_abc_multiname(
|
||||
translation_unit: TranslationUnit<'gc>,
|
||||
multiname_index: Index<AbcMultiname>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Self, Error> {
|
||||
let abc = translation_unit.abc();
|
||||
let abc_multiname = Self::resolve_multiname_index(&abc, multiname_index)?;
|
||||
|
||||
match abc_multiname {
|
||||
AbcMultiname::TypeName {
|
||||
base_type,
|
||||
parameters,
|
||||
} => {
|
||||
let base_multiname = Self::resolve_multiname_index(&abc, base_type.clone())?;
|
||||
let mut base =
|
||||
Self::resolve_multiname_params(translation_unit, base_multiname, activation)?;
|
||||
|
||||
for param_type in parameters {
|
||||
let param_multiname =
|
||||
Self::from_abc_multiname(translation_unit, param_type.clone(), activation)?;
|
||||
|
||||
base.params.push(param_multiname);
|
||||
}
|
||||
|
||||
Ok(base)
|
||||
}
|
||||
abc_multiname => {
|
||||
Self::resolve_multiname_params(translation_unit, abc_multiname, activation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a static multiname from the ABC constant pool
|
||||
///
|
||||
/// This function prohibits the use of runtime-qualified and late-bound
|
||||
|
@ -377,6 +438,7 @@ impl<'gc> Multiname<'gc> {
|
|||
mc,
|
||||
)?],
|
||||
name: translation_unit.pool_string_option(name.0, mc)?,
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
AbcMultiname::Multiname {
|
||||
|
@ -389,6 +451,7 @@ impl<'gc> Multiname<'gc> {
|
|||
} => Self {
|
||||
ns: Self::abc_namespace_set(translation_unit, namespace_set.clone(), mc)?,
|
||||
name: translation_unit.pool_string_option(name.0, mc)?,
|
||||
params: Vec::new(),
|
||||
},
|
||||
_ => return Err(format!("Multiname {} is not static", multiname_index.0).into()),
|
||||
})
|
||||
|
@ -399,6 +462,7 @@ impl<'gc> Multiname<'gc> {
|
|||
Self {
|
||||
ns: vec![Namespace::Any],
|
||||
name: None,
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,6 +506,7 @@ impl<'gc> From<QName<'gc>> for Multiname<'gc> {
|
|||
Self {
|
||||
ns: vec![q.ns],
|
||||
name: Some(q.name),
|
||||
params: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue