avm2: Store multi-namespaces behind Gc And single namespaces directly

This commit is contained in:
Adrian Wielgosik 2022-08-24 21:49:55 +02:00 committed by Mike Welsh
parent dd2bc1ea78
commit c28549ed9e
3 changed files with 66 additions and 21 deletions

View File

@ -10,6 +10,36 @@ use std::fmt::Debug;
use swf::avm2::types::{
AbcFile, Index, Multiname as AbcMultiname, NamespaceSet as AbcNamespaceSet,
};
use gc_arena::Gc;
#[derive(Clone, Debug, Collect)]
#[collect(no_drop)]
pub enum MultinameNamespaceSet<'gc> {
Multiple(Gc<'gc, Vec<Namespace<'gc>>>),
Single(Namespace<'gc>),
}
impl<'gc> MultinameNamespaceSet<'gc> {
pub fn multiple(set: Vec<Namespace<'gc>>, mc: MutationContext<'gc, '_>) -> Self {
Self::Multiple(Gc::allocate(mc, set))
}
pub fn single(ns: Namespace<'gc>) -> Self {
Self::Single(ns)
}
pub fn len(&self) -> usize {
match self {
Self::Multiple(ns) => ns.len(),
Self::Single(_) => 1,
}
}
pub fn get(&self, index: usize) -> Option<Namespace<'gc>> {
match self {
Self::Multiple(ns) => ns.get(index).copied(),
Self::Single(ns) => if index == 0 { Some(*ns) } else { None },
}
}
}
/// A `Multiname` consists of a name which could be resolved in one or more
/// potential namespaces.
@ -22,7 +52,7 @@ use swf::avm2::types::{
#[collect(no_drop)]
pub struct Multiname<'gc> {
/// The list of namespaces that satisfy this multiname.
ns: Vec<Namespace<'gc>>,
ns: MultinameNamespaceSet<'gc>,
/// The local name that satisfies this multiname. If `None`, then this
/// multiname is satisfied by any name in the namespace.
@ -40,10 +70,11 @@ impl<'gc> Multiname<'gc> {
translation_unit: TranslationUnit<'gc>,
namespace_set_index: Index<AbcNamespaceSet>,
mc: MutationContext<'gc, '_>,
) -> Result<Vec<Namespace<'gc>>, Error> {
) -> Result<MultinameNamespaceSet<'gc>, Error> {
if namespace_set_index.0 == 0 {
//TODO: What is namespace set zero?
return Ok(vec![]);
let result = MultinameNamespaceSet::multiple(vec![], mc);
return Ok(result);
}
let actual_index = namespace_set_index.0 as usize - 1;
@ -61,7 +92,11 @@ impl<'gc> Multiname<'gc> {
result.push(Namespace::from_abc_namespace(translation_unit, *ns, mc)?)
}
Ok(result)
if result.len() == 1 {
Ok(MultinameNamespaceSet::single(result[0]))
} else {
Ok(MultinameNamespaceSet::multiple(result, mc))
}
}
/// Assemble a multiname from an ABC `MultinameL` and the late-bound name.
@ -105,11 +140,11 @@ impl<'gc> Multiname<'gc> {
Ok(match abc_multiname {
AbcMultiname::QName { namespace, name } | AbcMultiname::QNameA { namespace, name } => {
Self {
ns: vec![Namespace::from_abc_namespace(
ns: MultinameNamespaceSet::single(Namespace::from_abc_namespace(
translation_unit,
*namespace,
activation.context.gc_context,
)?],
)?),
name: translation_unit
.pool_string_option(name.0, activation.context.gc_context)?,
params: Vec::new(),
@ -119,7 +154,7 @@ impl<'gc> Multiname<'gc> {
let ns_value = activation.avm2().pop();
let ns = ns_value.as_namespace()?;
Self {
ns: vec![*ns],
ns: MultinameNamespaceSet::single(*ns),
name: translation_unit
.pool_string_option(name.0, activation.context.gc_context)?,
params: Vec::new(),
@ -130,7 +165,7 @@ impl<'gc> Multiname<'gc> {
let ns_value = activation.avm2().pop();
let ns = ns_value.as_namespace()?;
Self {
ns: vec![*ns],
ns: MultinameNamespaceSet::single(*ns),
name: Some(name),
params: Vec::new(),
}
@ -249,11 +284,11 @@ impl<'gc> Multiname<'gc> {
Ok(match abc_multiname? {
AbcMultiname::QName { namespace, name } | AbcMultiname::QNameA { namespace, name } => {
Self {
ns: vec![Namespace::from_abc_namespace(
ns: MultinameNamespaceSet::single(Namespace::from_abc_namespace(
translation_unit,
*namespace,
mc,
)?],
)?),
name: translation_unit.pool_string_option(name.0, mc)?,
params: Vec::new(),
}
@ -303,7 +338,7 @@ impl<'gc> Multiname<'gc> {
/// Indicates the any type (any name in any namespace).
pub fn any() -> Self {
Self {
ns: vec![Namespace::Any],
ns: MultinameNamespaceSet::single(Namespace::Any),
name: None,
params: Vec::new(),
}
@ -311,33 +346,43 @@ impl<'gc> Multiname<'gc> {
pub fn public(name: impl Into<AvmString<'gc>>) -> Self {
Self {
ns: vec![Namespace::public()],
ns: MultinameNamespaceSet::single(Namespace::public()),
name: Some(name.into()),
params: Vec::new(),
}
}
pub fn namespace_set(&self) -> impl Iterator<Item = &Namespace<'gc>> {
self.ns.iter()
pub fn namespace_set(&self) -> &[Namespace<'gc>] {
match &self.ns {
MultinameNamespaceSet::Single(ns) => std::slice::from_ref(ns),
MultinameNamespaceSet::Multiple(ns) => ns,
}
}
pub fn local_name(&self) -> Option<AvmString<'gc>> {
self.name
}
pub fn contains_public_namespace(&self) -> bool {
self.ns.iter().any(|ns| ns.is_public())
match self.ns {
MultinameNamespaceSet::Single(ns) => ns.is_public(),
MultinameNamespaceSet::Multiple(ns) => ns.iter().any(|ns| ns.is_public())
}
}
/// Indicates if this multiname matches any type in any namespace.
pub fn is_any(&self) -> bool {
self.ns.contains(&Namespace::Any) && self.name.is_none()
self.name.is_none() && match self.ns {
MultinameNamespaceSet::Single(ns) => ns == Namespace::Any,
MultinameNamespaceSet::Multiple(ns) => ns.contains(&Namespace::Any),
}
}
/// Determine if this multiname matches a given QName.
pub fn contains_name(&self, name: &QName<'gc>) -> bool {
let ns_match = self
.ns
.namespace_set()
.iter()
.any(|ns| *ns == Namespace::Any || *ns == name.namespace());
let name_match = self.name.map(|n| n == name.local_name()).unwrap_or(true);
@ -387,7 +432,7 @@ impl<'gc> Multiname<'gc> {
impl<'gc> From<QName<'gc>> for Multiname<'gc> {
fn from(q: QName<'gc>) -> Self {
Self {
ns: vec![q.namespace()],
ns: MultinameNamespaceSet::single(q.namespace()),
name: Some(q.local_name()),
params: Vec::new(),
}

View File

@ -69,7 +69,7 @@ impl<'gc, V> PropertyMap<'gc, V> {
if let Some(local_name) = name.local_name() {
self.0.get(&local_name).iter().find_map(|v| {
v.iter()
.filter(|(n, _)| name.namespace_set().any(|ns| *ns == *n))
.filter(|(n, _)| name.namespace_set().iter().any(|ns| *ns == *n))
.map(|(_, v)| v)
.next()
})
@ -82,7 +82,7 @@ impl<'gc, V> PropertyMap<'gc, V> {
if let Some(local_name) = name.local_name() {
self.0.get(&local_name).iter().find_map(|v| {
v.iter()
.filter(|(n, _)| name.namespace_set().any(|ns| *ns == *n))
.filter(|(n, _)| name.namespace_set().iter().any(|ns| *ns == *n))
.map(|(ns, v)| (*ns, v))
.next()
})

View File

@ -396,7 +396,7 @@ fn default_value_for_type<'gc>(type_name: &Multiname<'gc>) -> Value<'gc> {
// The Multiname is guaranteed to be static by `Multiname::from_abc_multiname_static` earlier.
if type_name.is_any() {
Value::Undefined
} else if type_name.namespace_set().any(|ns| ns.is_public()) {
} else if type_name.contains_public_namespace() {
let name = type_name.local_name().unwrap_or_default();
if &name == b"Boolean" {
false.into()