avm2: Export Vector classes in public __AS3__.vec namespace (#9879)
Previously, the Vector$ classes were only exported in the internal 'AS3.vec' namespace, which is used by older ActionScript code. However, newer ActionScript code can also access these classes through the public 'AS3.vec' namespace, via 'getDefintionByName'. We now export these classes in both namespaces. In the public 'AS3.vec' namespace, they are exported like 'Vector.' instead of 'Vector$uint'
This commit is contained in:
parent
2fca22bd4a
commit
b8f0de8171
|
@ -19,6 +19,8 @@ use swf::avm2::types::{
|
||||||
Class as AbcClass, Instance as AbcInstance, Method as AbcMethod, MethodBody as AbcMethodBody,
|
Class as AbcClass, Instance as AbcInstance, Method as AbcMethod, MethodBody as AbcMethodBody,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::string::AvmString;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// All possible attributes for a given class.
|
/// All possible attributes for a given class.
|
||||||
pub struct ClassAttributes: u8 {
|
pub struct ClassAttributes: u8 {
|
||||||
|
@ -218,6 +220,26 @@ impl<'gc> Class<'gc> {
|
||||||
new_class.class_init = new_class.specialized_class_init.clone();
|
new_class.class_init = new_class.specialized_class_init.clone();
|
||||||
new_class.class_initializer_called = false;
|
new_class.class_initializer_called = false;
|
||||||
|
|
||||||
|
if params.len() > 1 {
|
||||||
|
panic!(
|
||||||
|
"More than one type parameter is unsupported: {:?}",
|
||||||
|
self.name()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_with_params = format!(
|
||||||
|
"{}.<{}>",
|
||||||
|
new_class.name.local_name(),
|
||||||
|
params[0].read().name().to_qualified_name(mc)
|
||||||
|
);
|
||||||
|
|
||||||
|
new_class.name = QName::new(
|
||||||
|
new_class.name.namespace(),
|
||||||
|
AvmString::new_utf8(mc, name_with_params),
|
||||||
|
);
|
||||||
|
|
||||||
GcCell::allocate(mc, new_class)
|
GcCell::allocate(mc, new_class)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::avm2::QName;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
|
|
||||||
use super::class::Class;
|
use super::class::Class;
|
||||||
|
use super::string::AvmString;
|
||||||
|
|
||||||
/// Represents a set of scripts and movies that share traits across different
|
/// Represents a set of scripts and movies that share traits across different
|
||||||
/// script-global scopes.
|
/// script-global scopes.
|
||||||
|
@ -180,6 +181,43 @@ impl<'gc> Domain<'gc> {
|
||||||
globals.get_property(&name.into(), activation)
|
globals.get_property(&name.into(), activation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve a value from this domain, with special handling for 'Vector.<SomeType>'.
|
||||||
|
/// This is used by `getQualifiedClassName, ApplicationDomain.getDefinition, and ApplicationDomain.hasDefinition`.
|
||||||
|
pub fn get_defined_value_handling_vector(
|
||||||
|
self,
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
mut name: QName<'gc>,
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
// Special-case lookups of `Vector.<SomeType>` - these get internally converted
|
||||||
|
// to a lookup of `Vector,` a lookup of `SomeType`, and `vector_class.apply(some_type_class)`
|
||||||
|
let mut type_name = None;
|
||||||
|
if (name.namespace() == activation.avm2().vector_public_namespace
|
||||||
|
|| name.namespace() == activation.avm2().vector_internal_namespace)
|
||||||
|
&& (name.local_name().starts_with(b"Vector.<".as_slice())
|
||||||
|
&& name.local_name().ends_with(b">".as_slice()))
|
||||||
|
{
|
||||||
|
let local_name = name.local_name();
|
||||||
|
type_name = Some(AvmString::new(
|
||||||
|
activation.context.gc_context,
|
||||||
|
&local_name["Vector.<".len()..(local_name.len() - 1)],
|
||||||
|
));
|
||||||
|
name = QName::new(name.namespace(), "Vector");
|
||||||
|
}
|
||||||
|
let res = self.get_defined_value(activation, name);
|
||||||
|
|
||||||
|
if let Some(type_name) = type_name {
|
||||||
|
let type_qname = QName::from_qualified_name(type_name, activation);
|
||||||
|
let type_class = self.get_defined_value(activation, type_qname)?;
|
||||||
|
if let Ok(res) = res {
|
||||||
|
let class = res.as_object().ok_or_else(|| {
|
||||||
|
Error::RustError(format!("Vector type {:?} was not an object", res).into())
|
||||||
|
})?;
|
||||||
|
return class.apply(activation, &[type_class]).map(|obj| obj.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
/// Export a definition from a script into the current application domain.
|
/// Export a definition from a script into the current application domain.
|
||||||
///
|
///
|
||||||
/// This does nothing if the definition already exists.
|
/// This does nothing if the definition already exists.
|
||||||
|
|
|
@ -72,23 +72,7 @@ pub fn get_definition<'gc>(
|
||||||
.unwrap_or_else(|| "".into())
|
.unwrap_or_else(|| "".into())
|
||||||
.coerce_to_string(activation)?;
|
.coerce_to_string(activation)?;
|
||||||
let name = QName::from_qualified_name(name, activation);
|
let name = QName::from_qualified_name(name, activation);
|
||||||
let (qname, mut defined_script) = match appdomain.get_defining_script(&name.into())? {
|
return appdomain.get_defined_value_handling_vector(activation, name);
|
||||||
Some(data) => data,
|
|
||||||
None => {
|
|
||||||
return Err(Error::AvmError(crate::avm2::error::reference_error(
|
|
||||||
activation,
|
|
||||||
&format!(
|
|
||||||
"Error #1065: Variable {} is not defined.",
|
|
||||||
name.local_name()
|
|
||||||
),
|
|
||||||
1065,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let globals = defined_script.globals(&mut activation.context)?;
|
|
||||||
let definition = globals.get_property(&qname.into(), activation)?;
|
|
||||||
|
|
||||||
return Ok(definition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
|
@ -109,7 +93,10 @@ pub fn has_definition<'gc>(
|
||||||
|
|
||||||
let qname = QName::from_qualified_name(name, activation);
|
let qname = QName::from_qualified_name(name, activation);
|
||||||
|
|
||||||
return Ok(appdomain.has_definition(qname).into());
|
return Ok(appdomain
|
||||||
|
.get_defined_value_handling_vector(activation, qname)
|
||||||
|
.is_ok()
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
|
|
|
@ -260,7 +260,7 @@ pub fn get_definition_by_name<'gc>(
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?;
|
.coerce_to_string(activation)?;
|
||||||
let qname = QName::from_qualified_name(name, activation);
|
let qname = QName::from_qualified_name(name, activation);
|
||||||
appdomain.get_defined_value(activation, qname)
|
appdomain.get_defined_value_handling_vector(activation, qname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements `flash.utils.describeType`
|
// Implements `flash.utils.describeType`
|
||||||
|
|
|
@ -111,66 +111,66 @@ pub fn class_init<'gc>(
|
||||||
let class_class = activation.avm2().classes().class;
|
let class_class = activation.avm2().classes().class;
|
||||||
let int_class = activation.avm2().classes().int;
|
let int_class = activation.avm2().classes().int;
|
||||||
let int_vector_class = this.apply(activation, &[int_class.into()])?;
|
let int_vector_class = this.apply(activation, &[int_class.into()])?;
|
||||||
let int_vector_name = QName::new(vector_internal_namespace, "Vector$int");
|
let int_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$int");
|
||||||
int_vector_class
|
|
||||||
.inner_class_definition()
|
|
||||||
.write(activation.context.gc_context)
|
|
||||||
.set_name(int_vector_name);
|
|
||||||
|
|
||||||
globals.install_const_late(
|
globals.install_const_late(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
int_vector_name,
|
int_vector_name_legacy,
|
||||||
int_vector_class.into(),
|
int_vector_class.into(),
|
||||||
class_class,
|
class_class,
|
||||||
);
|
);
|
||||||
domain.export_definition(int_vector_name, script, activation.context.gc_context);
|
domain.export_definition(
|
||||||
|
int_vector_name_legacy,
|
||||||
|
script,
|
||||||
|
activation.context.gc_context,
|
||||||
|
);
|
||||||
|
|
||||||
let uint_class = activation.avm2().classes().uint;
|
let uint_class = activation.avm2().classes().uint;
|
||||||
let uint_vector_class = this.apply(activation, &[uint_class.into()])?;
|
let uint_vector_class = this.apply(activation, &[uint_class.into()])?;
|
||||||
let uint_vector_name = QName::new(vector_internal_namespace, "Vector$uint");
|
let uint_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$uint");
|
||||||
uint_vector_class
|
|
||||||
.inner_class_definition()
|
|
||||||
.write(activation.context.gc_context)
|
|
||||||
.set_name(uint_vector_name);
|
|
||||||
|
|
||||||
globals.install_const_late(
|
globals.install_const_late(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
uint_vector_name,
|
uint_vector_name_legacy,
|
||||||
uint_vector_class.into(),
|
uint_vector_class.into(),
|
||||||
class_class,
|
class_class,
|
||||||
);
|
);
|
||||||
domain.export_definition(uint_vector_name, script, activation.context.gc_context);
|
domain.export_definition(
|
||||||
|
uint_vector_name_legacy,
|
||||||
|
script,
|
||||||
|
activation.context.gc_context,
|
||||||
|
);
|
||||||
|
|
||||||
let number_class = activation.avm2().classes().number;
|
let number_class = activation.avm2().classes().number;
|
||||||
let number_vector_class = this.apply(activation, &[number_class.into()])?;
|
let number_vector_class = this.apply(activation, &[number_class.into()])?;
|
||||||
let number_vector_name = QName::new(vector_internal_namespace, "Vector$double");
|
let number_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$double");
|
||||||
number_vector_class
|
|
||||||
.inner_class_definition()
|
|
||||||
.write(activation.context.gc_context)
|
|
||||||
.set_name(number_vector_name);
|
|
||||||
|
|
||||||
globals.install_const_late(
|
globals.install_const_late(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
number_vector_name,
|
number_vector_name_legacy,
|
||||||
number_vector_class.into(),
|
number_vector_class.into(),
|
||||||
class_class,
|
class_class,
|
||||||
);
|
);
|
||||||
domain.export_definition(number_vector_name, script, activation.context.gc_context);
|
domain.export_definition(
|
||||||
|
number_vector_name_legacy,
|
||||||
|
script,
|
||||||
|
activation.context.gc_context,
|
||||||
|
);
|
||||||
|
|
||||||
let object_vector_class = this.apply(activation, &[Value::Null])?;
|
let plain_vector_class = this.apply(activation, &[Value::Null])?;
|
||||||
let object_vector_name = QName::new(vector_internal_namespace, "Vector$object");
|
let object_vector_name_legacy = QName::new(vector_internal_namespace, "Vector$object");
|
||||||
object_vector_class
|
|
||||||
.inner_class_definition()
|
|
||||||
.write(activation.context.gc_context)
|
|
||||||
.set_name(object_vector_name);
|
|
||||||
|
|
||||||
globals.install_const_late(
|
globals.install_const_late(
|
||||||
activation.context.gc_context,
|
activation.context.gc_context,
|
||||||
object_vector_name,
|
object_vector_name_legacy,
|
||||||
object_vector_class.into(),
|
plain_vector_class.into(),
|
||||||
class_class,
|
class_class,
|
||||||
);
|
);
|
||||||
domain.export_definition(object_vector_name, script, activation.context.gc_context);
|
domain.export_definition(
|
||||||
|
object_vector_name_legacy,
|
||||||
|
script,
|
||||||
|
activation.context.gc_context,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package {
|
||||||
|
import flash.utils.getDefinitionByName;
|
||||||
|
import flash.utils.getQualifiedClassName;
|
||||||
|
import flash.system.ApplicationDomain;
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
public function Test() {
|
||||||
|
var vec;
|
||||||
|
var name;
|
||||||
|
|
||||||
|
vec = new Vector.<int>([1, 2]);
|
||||||
|
name = getQualifiedClassName(vec);
|
||||||
|
trace("Vector.<int> name: " + name);
|
||||||
|
trace("Vector.<int>: " + getDefinitionByName(name));
|
||||||
|
trace("ApplicationDomain.hasDefinition Vector.<int>: " + ApplicationDomain.currentDomain.hasDefinition(name));
|
||||||
|
trace("ApplicationDomain.getDefinition Vector.<int>: " + ApplicationDomain.currentDomain.getDefinition(name));
|
||||||
|
|
||||||
|
vec = new Vector.<uint>([1, 2]);
|
||||||
|
name = getQualifiedClassName(vec);
|
||||||
|
trace("Vector.<uint> name: " + name);
|
||||||
|
trace("Vector.<uint>: " + getDefinitionByName(name));
|
||||||
|
trace("ApplicationDomain.hasDefinition Vector.<uint>: " + ApplicationDomain.currentDomain.hasDefinition(name));
|
||||||
|
trace("ApplicationDomain.getDefinition Vector.<uint>: " + ApplicationDomain.currentDomain.getDefinition(name));
|
||||||
|
|
||||||
|
vec = new Vector.<Number>([1, 2]);
|
||||||
|
name = getQualifiedClassName(vec);
|
||||||
|
trace("Vector.<Number> name: " + name);
|
||||||
|
trace("Vector.<Number>: " + getDefinitionByName(name));
|
||||||
|
trace("ApplicationDomain.hasDefinition Vector.<Number>: " + ApplicationDomain.currentDomain.hasDefinition(name));
|
||||||
|
trace("ApplicationDomain.getDefinition Vector.<Number>: " + ApplicationDomain.currentDomain.getDefinition(name));
|
||||||
|
|
||||||
|
trace("Early lookup: " + ApplicationDomain.currentDomain.hasDefinition("__AS3__.vec::Vector.<Test>"));
|
||||||
|
vec = new Vector.<Object>([1, 2]);
|
||||||
|
name = getQualifiedClassName(vec);
|
||||||
|
trace("Vector.<Object> name: " + name);
|
||||||
|
trace("Vector.<Object>: " + getDefinitionByName(name));
|
||||||
|
trace("ApplicationDomain.hasDefinition Vector.<Object>: " + ApplicationDomain.currentDomain.hasDefinition(name));
|
||||||
|
trace("ApplicationDomain.getDefinition Vector.<Object>: " + ApplicationDomain.currentDomain.getDefinition(name));
|
||||||
|
|
||||||
|
vec = new Vector.<Test>([]);
|
||||||
|
name = getQualifiedClassName(vec);
|
||||||
|
trace("Vector.<Test> name: " + name);
|
||||||
|
trace("Vector.<Test>: " + getDefinitionByName(name));
|
||||||
|
trace("ApplicationDomain.hasDefinition Vector.<Test>: " + ApplicationDomain.currentDomain.hasDefinition(name));
|
||||||
|
trace("ApplicationDomain.getDefinition Vector.<Test>: " + ApplicationDomain.currentDomain.getDefinition(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
Vector.<int> name: __AS3__.vec::Vector.<int>
|
||||||
|
Vector.<int>: [class Vector.<int>]
|
||||||
|
ApplicationDomain.hasDefinition Vector.<int>: true
|
||||||
|
ApplicationDomain.getDefinition Vector.<int>: [class Vector.<int>]
|
||||||
|
Vector.<uint> name: __AS3__.vec::Vector.<uint>
|
||||||
|
Vector.<uint>: [class Vector.<uint>]
|
||||||
|
ApplicationDomain.hasDefinition Vector.<uint>: true
|
||||||
|
ApplicationDomain.getDefinition Vector.<uint>: [class Vector.<uint>]
|
||||||
|
Vector.<Number> name: __AS3__.vec::Vector.<Number>
|
||||||
|
Vector.<Number>: [class Vector.<Number>]
|
||||||
|
ApplicationDomain.hasDefinition Vector.<Number>: true
|
||||||
|
ApplicationDomain.getDefinition Vector.<Number>: [class Vector.<Number>]
|
||||||
|
Early lookup: true
|
||||||
|
Vector.<Object> name: __AS3__.vec::Vector.<Object>
|
||||||
|
Vector.<Object>: [class Vector.<Object>]
|
||||||
|
ApplicationDomain.hasDefinition Vector.<Object>: true
|
||||||
|
ApplicationDomain.getDefinition Vector.<Object>: [class Vector.<Object>]
|
||||||
|
Vector.<Test> name: __AS3__.vec::Vector.<Test>
|
||||||
|
Vector.<Test>: [class Vector.<Test>]
|
||||||
|
ApplicationDomain.hasDefinition Vector.<Test>: true
|
||||||
|
ApplicationDomain.getDefinition Vector.<Test>: [class Vector.<Test>]
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
num_frames = 1
|
Loading…
Reference in New Issue