2020-02-05 18:52:11 +00:00
|
|
|
//! AVM2 names & namespacing
|
|
|
|
|
2020-02-09 21:46:29 +00:00
|
|
|
use crate::avm2::value::abc_string;
|
|
|
|
use crate::avm2::{Avm2, Error};
|
2020-02-05 18:52:11 +00:00
|
|
|
use gc_arena::Collect;
|
2020-02-09 21:46:29 +00:00
|
|
|
use swf::avm2::types::{
|
|
|
|
AbcFile, Index, Multiname as AbcMultiname, Namespace as AbcNamespace,
|
|
|
|
NamespaceSet as AbcNamespaceSet,
|
|
|
|
};
|
2020-02-05 18:52:11 +00:00
|
|
|
|
|
|
|
/// Represents the name of a namespace.
|
|
|
|
#[derive(Clone, Collect, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub enum Namespace {
|
|
|
|
Namespace(String),
|
|
|
|
Package(String),
|
|
|
|
PackageInternal(String),
|
|
|
|
Protected(String),
|
|
|
|
Explicit(String),
|
|
|
|
StaticProtected(String),
|
|
|
|
Private(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Namespace {
|
2020-02-08 23:08:30 +00:00
|
|
|
/// Read a namespace declaration from the ABC constant pool and copy it to
|
|
|
|
/// a namespace value.
|
|
|
|
pub fn from_abc_namespace(
|
|
|
|
file: &AbcFile,
|
|
|
|
namespace_index: Index<AbcNamespace>,
|
2020-02-09 21:46:29 +00:00
|
|
|
) -> Result<Self, Error> {
|
|
|
|
let abc_namespace: Result<&AbcNamespace, Error> = file
|
|
|
|
.constant_pool
|
|
|
|
.namespaces
|
|
|
|
.get(namespace_index.0 as usize)
|
|
|
|
.ok_or_else(|| format!("Unknown namespace constant {}", namespace_index.0).into());
|
|
|
|
|
|
|
|
Ok(match abc_namespace? {
|
|
|
|
AbcNamespace::Namespace(idx) => Self::Namespace(abc_string(file, idx.clone())?),
|
|
|
|
AbcNamespace::Package(idx) => Self::Package(abc_string(file, idx.clone())?),
|
|
|
|
AbcNamespace::PackageInternal(idx) => {
|
|
|
|
Self::PackageInternal(abc_string(file, idx.clone())?)
|
|
|
|
}
|
|
|
|
AbcNamespace::Protected(idx) => Self::Protected(abc_string(file, idx.clone())?),
|
|
|
|
AbcNamespace::Explicit(idx) => Self::Explicit(abc_string(file, idx.clone())?),
|
|
|
|
AbcNamespace::StaticProtected(idx) => {
|
|
|
|
Self::StaticProtected(abc_string(file, idx.clone())?)
|
|
|
|
}
|
|
|
|
AbcNamespace::Private(idx) => Self::Private(abc_string(file, idx.clone())?),
|
|
|
|
})
|
2020-02-05 18:52:11 +00:00
|
|
|
}
|
2020-02-11 19:33:30 +00:00
|
|
|
|
|
|
|
pub fn public_namespace() -> Self {
|
|
|
|
Namespace::Namespace("".to_string())
|
|
|
|
}
|
2020-02-05 18:52:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A `QName`, likely "qualified name", consists of a namespace and name string.
|
|
|
|
///
|
|
|
|
/// This is technically interchangeable with `xml::XMLName`, as they both
|
|
|
|
/// implement `QName`; however, AVM2 and XML have separate representations.
|
|
|
|
///
|
|
|
|
/// A property cannot be retrieved or set without first being resolved into a
|
|
|
|
/// `QName`. All other forms of names and multinames are either versions of
|
|
|
|
/// `QName` with unspecified parameters, or multiple names to be checked in
|
|
|
|
/// order.
|
|
|
|
#[derive(Clone, Collect, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct QName {
|
|
|
|
ns: Namespace,
|
|
|
|
name: String,
|
|
|
|
}
|
2020-02-08 23:09:03 +00:00
|
|
|
|
2020-02-11 19:33:30 +00:00
|
|
|
impl QName {
|
2020-02-11 23:58:25 +00:00
|
|
|
pub fn qualified(ns: &Namespace, name: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
ns: ns.clone(),
|
|
|
|
name: name.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-11 19:33:30 +00:00
|
|
|
pub fn dynamic_name(local_part: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
ns: Namespace::public_namespace(),
|
|
|
|
name: local_part.to_string(),
|
|
|
|
}
|
|
|
|
}
|
2020-02-18 00:43:23 +00:00
|
|
|
|
|
|
|
/// Pull a `QName` from the multiname pool.
|
|
|
|
///
|
|
|
|
/// This function returns an Err if the multiname does not exist or is not
|
|
|
|
/// a `QName`.
|
|
|
|
pub fn from_abc_multiname(
|
|
|
|
file: &AbcFile,
|
|
|
|
multiname_index: Index<AbcMultiname>,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
let abc_multiname: Result<&AbcMultiname, Error> = file
|
|
|
|
.constant_pool
|
|
|
|
.multinames
|
|
|
|
.get(multiname_index.0 as usize)
|
|
|
|
.ok_or_else(|| format!("Unknown multiname constant {}", multiname_index.0).into());
|
|
|
|
|
|
|
|
Ok(match abc_multiname? {
|
|
|
|
AbcMultiname::QName { namespace, name } => Self {
|
|
|
|
ns: Namespace::from_abc_namespace(file, namespace.clone())?,
|
|
|
|
name: abc_string(file, name.clone())?,
|
|
|
|
},
|
|
|
|
_ => return Err("Attempted to pull QName from non-QName multiname".into()),
|
|
|
|
})
|
|
|
|
}
|
2020-02-11 19:33:30 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 23:09:03 +00:00
|
|
|
/// A `Multiname` consists of a name which could be resolved in one or more
|
|
|
|
/// potential namespaces.
|
|
|
|
///
|
|
|
|
/// All unresolved names are of the form `Multiname`, and the name resolution
|
|
|
|
/// process consists of searching each name space for a given name.
|
|
|
|
pub struct Multiname {
|
|
|
|
ns: Vec<Namespace>,
|
|
|
|
name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Multiname {
|
2020-02-09 21:46:29 +00:00
|
|
|
/// Read a namespace set from the ABC constant pool, and return a list of
|
|
|
|
/// copied namespaces.
|
|
|
|
fn abc_namespace_set(
|
|
|
|
file: &AbcFile,
|
|
|
|
namespace_set_index: Index<AbcNamespaceSet>,
|
|
|
|
) -> Result<Vec<Namespace>, Error> {
|
|
|
|
let ns_set: Result<&AbcNamespaceSet, Error> = file
|
|
|
|
.constant_pool
|
|
|
|
.namespace_sets
|
|
|
|
.get(namespace_set_index.0 as usize)
|
|
|
|
.ok_or_else(|| {
|
|
|
|
format!("Unknown namespace set constant {}", namespace_set_index.0).into()
|
|
|
|
});
|
|
|
|
let mut result = vec![];
|
|
|
|
|
|
|
|
for ns in ns_set? {
|
|
|
|
result.push(Namespace::from_abc_namespace(file, ns.clone())?)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2020-02-08 23:09:03 +00:00
|
|
|
/// Read a multiname from the ABC constant pool, copying it into the most
|
|
|
|
/// general form of multiname.
|
|
|
|
pub fn from_abc_multiname(
|
|
|
|
file: &AbcFile,
|
2020-02-11 04:58:15 +00:00
|
|
|
multiname_index: Index<AbcMultiname>,
|
2020-02-08 23:09:03 +00:00
|
|
|
avm: &mut Avm2<'_>,
|
2020-02-09 21:46:29 +00:00
|
|
|
) -> Result<Self, Error> {
|
|
|
|
let abc_multiname: Result<&AbcMultiname, Error> = file
|
|
|
|
.constant_pool
|
|
|
|
.multinames
|
|
|
|
.get(multiname_index.0 as usize)
|
|
|
|
.ok_or_else(|| format!("Unknown multiname constant {}", multiname_index.0).into());
|
|
|
|
|
|
|
|
Ok(match abc_multiname? {
|
|
|
|
AbcMultiname::QName { namespace, name } | AbcMultiname::QNameA { namespace, name } => {
|
|
|
|
Self {
|
2020-02-08 23:09:03 +00:00
|
|
|
ns: vec![Namespace::from_abc_namespace(file, namespace.clone())?],
|
2020-02-09 21:46:29 +00:00
|
|
|
name: abc_string(file, name.clone())?,
|
2020-02-08 23:09:03 +00:00
|
|
|
}
|
2020-02-09 21:46:29 +00:00
|
|
|
}
|
|
|
|
AbcMultiname::RTQName { name } | AbcMultiname::RTQNameA { name } => {
|
|
|
|
let ns = avm.pop().as_namespace()?.clone();
|
|
|
|
Self {
|
|
|
|
ns: vec![ns],
|
|
|
|
name: abc_string(file, name.clone())?,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AbcMultiname::RTQNameL | AbcMultiname::RTQNameLA => {
|
|
|
|
let ns = avm.pop().as_namespace()?.clone();
|
|
|
|
let name = avm.pop().as_string()?.clone();
|
2020-02-11 04:29:00 +00:00
|
|
|
Self { ns: vec![ns], name }
|
2020-02-09 21:46:29 +00:00
|
|
|
}
|
|
|
|
AbcMultiname::Multiname {
|
|
|
|
namespace_set,
|
|
|
|
name,
|
|
|
|
}
|
|
|
|
| AbcMultiname::MultinameA {
|
|
|
|
namespace_set,
|
|
|
|
name,
|
|
|
|
} => Self {
|
|
|
|
ns: Self::abc_namespace_set(file, namespace_set.clone())?,
|
|
|
|
name: abc_string(file, name.clone())?,
|
2020-02-08 23:09:03 +00:00
|
|
|
},
|
2020-02-09 21:46:29 +00:00
|
|
|
AbcMultiname::MultinameL { namespace_set }
|
|
|
|
| AbcMultiname::MultinameLA { namespace_set } => {
|
|
|
|
let name = avm.pop().as_string()?.clone();
|
|
|
|
Self {
|
|
|
|
ns: Self::abc_namespace_set(file, namespace_set.clone())?,
|
|
|
|
name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2020-02-08 23:09:03 +00:00
|
|
|
}
|
2020-02-11 04:58:15 +00:00
|
|
|
|
2020-02-11 23:58:25 +00:00
|
|
|
pub fn namespace_set(&self) -> impl Iterator<Item = &Namespace> {
|
|
|
|
self.ns.iter()
|
|
|
|
}
|
|
|
|
|
2020-02-11 04:58:15 +00:00
|
|
|
pub fn local_name(&self) -> &str {
|
|
|
|
&self.name
|
|
|
|
}
|
2020-02-08 23:09:03 +00:00
|
|
|
}
|