avm2: Intern namespaces
This makes `Namespace::exact_version_match` into a single pointer comparison.
This commit is contained in:
parent
cd4bfc8edf
commit
d1bae25f7f
|
@ -822,7 +822,7 @@ fn load_playerglobal<'gc>(
|
|||
let activation = $activation;
|
||||
$(
|
||||
// Lookup with the highest version, so we we see all defined classes here
|
||||
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, &mut activation.borrow_gc());
|
||||
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, activation.context);
|
||||
let name = QName::new(ns, $class_name);
|
||||
let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}"));
|
||||
let class_object = class_object.as_object().unwrap().as_class_object().unwrap();
|
||||
|
@ -837,7 +837,7 @@ fn load_playerglobal<'gc>(
|
|||
let activation = $activation;
|
||||
$(
|
||||
// Lookup with the highest version, so we we see all defined classes here
|
||||
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, &mut activation.borrow_gc());
|
||||
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, activation.context);
|
||||
let name = QName::new(ns, $class_name);
|
||||
let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}"));
|
||||
let class_def = class_object.as_object().unwrap().as_class_object().unwrap().inner_class_definition();
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
//! `flash.net.SharedObject` builtin/prototype
|
||||
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm2::error::error;
|
||||
use crate::avm2::object::TObject;
|
||||
use crate::avm2::Error::AvmError;
|
||||
use crate::avm2::Multiname;
|
||||
use crate::avm2::{Activation, Error, Namespace, Object, Value};
|
||||
use crate::avm2::{Activation, Error, Object, Value};
|
||||
use crate::string::AvmString;
|
||||
use crate::{avm2_stub_getter, avm2_stub_method, avm2_stub_setter};
|
||||
use flash_lso::types::{AMFVersion, Lso};
|
||||
|
|
|
@ -33,8 +33,7 @@ pub fn instance_init<'gc>(
|
|||
} else {
|
||||
uri.coerce_to_string(activation)?
|
||||
};
|
||||
let namespace =
|
||||
Namespace::package(namespace_uri, api_version, &mut activation.borrow_gc());
|
||||
let namespace = Namespace::package(namespace_uri, api_version, activation.context);
|
||||
let prefix_str = prefix.coerce_to_string(activation)?;
|
||||
|
||||
// The order is important here to match Flash
|
||||
|
@ -55,7 +54,7 @@ pub fn instance_init<'gc>(
|
|||
[Value::Object(Object::QNameObject(qname))] => {
|
||||
let ns = qname
|
||||
.uri()
|
||||
.map(|uri| Namespace::package(uri, api_version, &mut activation.borrow_gc()))
|
||||
.map(|uri| Namespace::package(uri, api_version, activation.context))
|
||||
.unwrap_or_else(Namespace::any);
|
||||
if ns.as_uri().is_empty() {
|
||||
(Some("".into()), ns)
|
||||
|
@ -68,7 +67,7 @@ pub fn instance_init<'gc>(
|
|||
let ns = Namespace::package(
|
||||
val.coerce_to_string(activation)?,
|
||||
api_version,
|
||||
&mut activation.borrow_gc(),
|
||||
activation.context,
|
||||
);
|
||||
if ns.as_uri().is_empty() {
|
||||
(Some("".into()), ns)
|
||||
|
|
|
@ -62,19 +62,15 @@ pub fn init<'gc>(
|
|||
|
||||
let namespace = match ns_arg {
|
||||
Value::Object(Object::NamespaceObject(ns)) => Some(ns.namespace()),
|
||||
Value::Object(Object::QNameObject(qname)) => qname.uri().map(|uri| {
|
||||
Namespace::package(uri, ApiVersion::AllVersions, &mut activation.borrow_gc())
|
||||
}),
|
||||
Value::Object(Object::QNameObject(qname)) => qname
|
||||
.uri()
|
||||
.map(|uri| Namespace::package(uri, ApiVersion::AllVersions, activation.context)),
|
||||
Value::Null => None,
|
||||
Value::Undefined => Some(Namespace::package(
|
||||
"",
|
||||
api_version,
|
||||
&mut activation.borrow_gc(),
|
||||
)),
|
||||
Value::Undefined => Some(activation.avm2().namespaces.public_for(api_version)),
|
||||
v => Some(Namespace::package(
|
||||
v.coerce_to_string(activation)?,
|
||||
api_version,
|
||||
&mut activation.borrow_gc(),
|
||||
activation.context,
|
||||
)),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::error::{make_error_1032, make_error_1080};
|
||||
use crate::avm2::namespace::{CommonNamespaces, Namespace};
|
||||
use crate::avm2::script::TranslationUnit;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::QName;
|
||||
use crate::avm2::{Object, Value};
|
||||
use crate::context::GcContext;
|
||||
use crate::string::{AvmString, WStr, WString};
|
||||
use crate::avm2::namespace::{CommonNamespaces, Namespace};
|
||||
use bitflags::bitflags;
|
||||
use gc_arena::Gc;
|
||||
use gc_arena::{Collect, Mutation};
|
||||
|
@ -543,10 +543,7 @@ pub struct CommonMultinames<'gc> {
|
|||
}
|
||||
|
||||
impl<'gc> CommonMultinames<'gc> {
|
||||
pub fn new(
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
namespaces: &CommonNamespaces<'gc>,
|
||||
) -> Self {
|
||||
pub fn new(context: &mut GcContext<'_, 'gc>, namespaces: &CommonNamespaces<'gc>) -> Self {
|
||||
let mc = context.gc_context;
|
||||
|
||||
let mut create_pub_multiname = |local_name: &'static [u8]| -> Gc<'gc, Multiname<'gc>> {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::Error;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::string::{AvmAtom, AvmString};
|
||||
use crate::utils::weak_set::WeakSet;
|
||||
use crate::{avm2::script::TranslationUnit, context::GcContext};
|
||||
use gc_arena::barrier::unlock;
|
||||
use gc_arena::lock::RefLock;
|
||||
use gc_arena::{Collect, Gc};
|
||||
use num_traits::FromPrimitive;
|
||||
use ruffle_wstr::WStr;
|
||||
|
@ -10,28 +14,62 @@ use swf::avm2::types::{Index, Namespace as AbcNamespace};
|
|||
|
||||
use super::api_version::ApiVersion;
|
||||
|
||||
#[derive(Clone, Copy, Collect, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Collect, Debug)]
|
||||
#[collect(no_drop)]
|
||||
pub struct Namespace<'gc>(
|
||||
// `None` represents the wildcard namespace `Namespace::any()`.
|
||||
Option<Gc<'gc, NamespaceData<'gc>>>,
|
||||
Option<Gc<'gc, NamespaceData<AvmAtom<'gc>>>>,
|
||||
);
|
||||
|
||||
/// Represents the name of a namespace.
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Copy, Clone, Collect, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Collect, Debug, PartialEq, Eq, Hash)]
|
||||
#[collect(no_drop)]
|
||||
enum NamespaceData<'gc> {
|
||||
enum NamespaceData<N> {
|
||||
// note: this is the default "public namespace", corresponding to both
|
||||
// ABC Namespace and PackageNamespace
|
||||
Namespace(AvmAtom<'gc>, #[collect(require_static)] ApiVersion),
|
||||
PackageInternal(AvmAtom<'gc>),
|
||||
Protected(AvmAtom<'gc>),
|
||||
Explicit(AvmAtom<'gc>),
|
||||
StaticProtected(AvmAtom<'gc>),
|
||||
// note: private namespaces are always compared by pointer identity
|
||||
// of the enclosing `Gc`.
|
||||
Private(AvmAtom<'gc>),
|
||||
Namespace(N, #[collect(require_static)] ApiVersion),
|
||||
PackageInternal(N),
|
||||
Protected(N),
|
||||
Explicit(N),
|
||||
StaticProtected(N),
|
||||
// note: private namespaces are the only kind that do not get interned,
|
||||
// so that each instance is considered separate.
|
||||
Private(N),
|
||||
}
|
||||
|
||||
type NamespaceInterner<'gc> = WeakSet<'gc, NamespaceData<AvmAtom<'gc>>>;
|
||||
|
||||
impl<N> NamespaceData<N> {
|
||||
fn map<M>(self, f: impl FnOnce(N) -> M) -> NamespaceData<M> {
|
||||
match self {
|
||||
Self::Namespace(n, v) => NamespaceData::Namespace(f(n), v),
|
||||
Self::PackageInternal(n) => NamespaceData::PackageInternal(f(n)),
|
||||
Self::Protected(n) => NamespaceData::Protected(f(n)),
|
||||
Self::Explicit(n) => NamespaceData::Explicit(f(n)),
|
||||
Self::StaticProtected(n) => NamespaceData::StaticProtected(f(n)),
|
||||
Self::Private(n) => NamespaceData::Private(f(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(moulins): allow passing an AvmAtom or a non-static `&WStr` directly
|
||||
impl<'gc, N: Into<AvmString<'gc>>> NamespaceData<N> {
|
||||
fn intern(
|
||||
self,
|
||||
interner: &mut NamespaceInterner<'gc>,
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
) -> Namespace<'gc> {
|
||||
debug_assert!(
|
||||
!matches!(self, NamespaceData::Private(_)),
|
||||
"private namespaces shouldn't be interned"
|
||||
);
|
||||
|
||||
let mc = context.gc();
|
||||
let raw = self.map(|name| context.interner.intern(mc, name.into()));
|
||||
let entry = interner.entry(mc, &raw).or_insert(|| Gc::new(mc, raw));
|
||||
Namespace(Some(entry.get()))
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_version_mark(url: &WStr, is_playerglobals: bool) -> Option<(&WStr, ApiVersion)> {
|
||||
|
@ -114,7 +152,7 @@ impl<'gc> Namespace<'gc> {
|
|||
let mut namespace_name =
|
||||
translation_unit.pool_string(index.0, &mut activation.borrow_gc())?;
|
||||
|
||||
// Private namespaces don't get any of the namespace version checks
|
||||
// Private namespaces don't get any of the namespace version checks, and shouldn't be interned.
|
||||
if let AbcNamespace::Private(_) = abc_namespace {
|
||||
return Ok(Self(Some(Gc::new(
|
||||
mc,
|
||||
|
@ -217,7 +255,8 @@ impl<'gc> Namespace<'gc> {
|
|||
AbcNamespace::StaticProtected(_) => NamespaceData::StaticProtected(namespace_name),
|
||||
AbcNamespace::Private(_) => unreachable!(),
|
||||
};
|
||||
Ok(Self(Some(Gc::new(mc, ns))))
|
||||
|
||||
Ok(Self::intern(ns, activation.context))
|
||||
}
|
||||
|
||||
pub fn any() -> Self {
|
||||
|
@ -228,29 +267,19 @@ impl<'gc> Namespace<'gc> {
|
|||
pub fn package(
|
||||
package_name: impl Into<AvmString<'gc>>,
|
||||
api_version: ApiVersion,
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
context: &mut UpdateContext<'gc>,
|
||||
) -> Self {
|
||||
let atom = context
|
||||
.interner
|
||||
.intern(context.gc_context, package_name.into());
|
||||
Self(Some(Gc::new(
|
||||
context.gc_context,
|
||||
NamespaceData::Namespace(atom, api_version),
|
||||
)))
|
||||
Self::intern(NamespaceData::Namespace(package_name, api_version), context)
|
||||
}
|
||||
|
||||
// TODO(moulins): allow passing an AvmAtom or a non-static `&WStr` directly
|
||||
pub fn internal(
|
||||
package_name: impl Into<AvmString<'gc>>,
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
) -> Self {
|
||||
let atom = context
|
||||
.interner
|
||||
.intern(context.gc_context, package_name.into());
|
||||
Self(Some(Gc::new(
|
||||
context.gc_context,
|
||||
NamespaceData::PackageInternal(atom),
|
||||
)))
|
||||
fn intern(
|
||||
raw: NamespaceData<impl Into<AvmString<'gc>>>,
|
||||
context: &mut UpdateContext<'gc>,
|
||||
) -> Namespace<'gc> {
|
||||
let namespaces = Gc::write(context.gc(), context.avm2.namespaces);
|
||||
let interner = unlock!(namespaces, CommonNamespaces, interner);
|
||||
raw.intern(&mut interner.borrow_mut(), &mut context.borrow_gc())
|
||||
}
|
||||
|
||||
pub fn is_public(&self) -> bool {
|
||||
|
@ -298,13 +327,9 @@ impl<'gc> Namespace<'gc> {
|
|||
/// Namespace does not implement `PartialEq`, so that each caller is required
|
||||
/// to explicitly choose either `exact_version_match` or `matches_ns`.
|
||||
pub fn exact_version_match(&self, other: Self) -> bool {
|
||||
if self.0.map(Gc::as_ptr) == other.0.map(Gc::as_ptr) {
|
||||
true
|
||||
} else if self.is_private() || other.is_private() {
|
||||
false
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
// Namespaces are interned (unless they are private), so comparing Gc pointers
|
||||
// is enough to determine equality.
|
||||
self.0.map(Gc::as_ptr) == other.0.map(Gc::as_ptr)
|
||||
}
|
||||
|
||||
/// Compares this namespace to another, considering them equal if this namespace's version
|
||||
|
@ -358,37 +383,54 @@ pub struct CommonNamespaces<'gc> {
|
|||
pub(super) flash_net_internal: Namespace<'gc>,
|
||||
|
||||
pub(super) __ruffle__: Namespace<'gc>,
|
||||
|
||||
interner: RefLock<NamespaceInterner<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> CommonNamespaces<'gc> {
|
||||
const PUBLIC_LEN: usize = ApiVersion::VM_INTERNAL as usize + 1;
|
||||
|
||||
pub fn new(context: &mut GcContext<'_, 'gc>) -> Self {
|
||||
let mut interner = WeakSet::new();
|
||||
let mut intern = |raw: NamespaceData<&'static [u8]>| {
|
||||
let raw = raw.map(WStr::from_units);
|
||||
raw.intern(&mut interner, context)
|
||||
};
|
||||
|
||||
Self {
|
||||
public_namespaces: std::array::from_fn(|val| {
|
||||
Namespace::package("", ApiVersion::from_usize(val).unwrap(), context)
|
||||
let version = ApiVersion::from_usize(val).unwrap();
|
||||
intern(NamespaceData::Namespace(b"", version))
|
||||
}),
|
||||
internal: Namespace::internal("", context),
|
||||
as3: Namespace::package(
|
||||
"http://adobe.com/AS3/2006/builtin",
|
||||
internal: intern(NamespaceData::PackageInternal(b"")),
|
||||
as3: intern(NamespaceData::Namespace(
|
||||
b"http://adobe.com/AS3/2006/builtin",
|
||||
ApiVersion::AllVersions,
|
||||
context,
|
||||
),
|
||||
vector_public: Namespace::package("__AS3__.vec", ApiVersion::AllVersions, context),
|
||||
vector_internal: Namespace::internal("__AS3__.vec", context),
|
||||
proxy: Namespace::package(
|
||||
"http://www.adobe.com/2006/actionscript/flash/proxy",
|
||||
)),
|
||||
vector_public: intern(NamespaceData::Namespace(
|
||||
b"__AS3__.vec",
|
||||
ApiVersion::AllVersions,
|
||||
context,
|
||||
),
|
||||
flash_display_internal: Namespace::internal("flash.display", context),
|
||||
flash_utils_internal: Namespace::internal("flash.utils", context),
|
||||
flash_geom_internal: Namespace::internal("flash.geom", context),
|
||||
flash_events_internal: Namespace::internal("flash.events", context),
|
||||
flash_text_engine_internal: Namespace::internal("flash.text.engine", context),
|
||||
flash_net_internal: Namespace::internal("flash.net", context),
|
||||
)),
|
||||
vector_internal: intern(NamespaceData::PackageInternal(b"__AS3__.vec")),
|
||||
proxy: intern(NamespaceData::Namespace(
|
||||
b"http://www.adobe.com/2006/actionscript/flash/proxy",
|
||||
ApiVersion::AllVersions,
|
||||
)),
|
||||
flash_display_internal: intern(NamespaceData::PackageInternal(b"flash.display")),
|
||||
flash_utils_internal: intern(NamespaceData::PackageInternal(b"flash.utils")),
|
||||
flash_geom_internal: intern(NamespaceData::PackageInternal(b"flash.geom")),
|
||||
flash_events_internal: intern(NamespaceData::PackageInternal(b"flash.events")),
|
||||
flash_text_engine_internal: intern(NamespaceData::PackageInternal(
|
||||
b"flash.text.engine",
|
||||
)),
|
||||
flash_net_internal: intern(NamespaceData::PackageInternal(b"flash.net")),
|
||||
|
||||
__ruffle__: Namespace::package("__ruffle__", ApiVersion::AllVersions, context),
|
||||
__ruffle__: intern(NamespaceData::Namespace(
|
||||
b"__ruffle__",
|
||||
ApiVersion::AllVersions,
|
||||
)),
|
||||
|
||||
interner: RefLock::new(interner),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -191,11 +191,9 @@ impl<'gc> XmlListObject<'gc> {
|
|||
if !matches!(*last_node.kind(), E4XNodeKind::ProcessingInstruction(_)) {
|
||||
if let Some(name) = last_node.local_name() {
|
||||
let ns = match last_node.namespace() {
|
||||
Some(ns) => Namespace::package(
|
||||
ns.uri,
|
||||
ApiVersion::AllVersions,
|
||||
&mut activation.context.borrow_gc(),
|
||||
),
|
||||
Some(ns) => {
|
||||
Namespace::package(ns.uri, ApiVersion::AllVersions, activation.context)
|
||||
}
|
||||
None => activation.avm2().namespaces.public_all(),
|
||||
};
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ impl<'gc> QName<'gc> {
|
|||
let package_name = context.interner.intern_wstr(context.gc(), package_name);
|
||||
|
||||
Self {
|
||||
ns: Namespace::package(package_name, api_version, &mut context.borrow_gc()),
|
||||
ns: Namespace::package(package_name, api_version, context),
|
||||
name: AvmString::new(context.gc(), local_name),
|
||||
}
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue