avm2: Correctly differentiate between Any and null Multiname

This commit is contained in:
Lord-McSweeney 2024-09-12 23:18:20 -07:00 committed by Lord-McSweeney
parent 6a9f4b665a
commit ed9b250d0a
16 changed files with 184 additions and 181 deletions

View File

@ -518,7 +518,7 @@ impl<'gc> Class<'gc> {
// A 'callable' class doesn't have a signature - let the // A 'callable' class doesn't have a signature - let the
// method do any needed coercions // method do any needed coercions
vec![], vec![],
activation.avm2().multinames.any, None,
true, true,
activation.context.gc_context, activation.context.gc_context,
); );
@ -1022,8 +1022,12 @@ impl<'gc> Class<'gc> {
value: Value<'gc>, value: Value<'gc>,
) { ) {
self.define_instance_trait( self.define_instance_trait(
activation.context.gc_context, activation.gc(),
Trait::from_const(name, activation.avm2().multinames.function, Some(value)), Trait::from_const(
name,
Some(activation.avm2().multinames.function),
Some(value),
),
); );
} }
@ -1036,10 +1040,10 @@ impl<'gc> Class<'gc> {
) { ) {
for &(name, value) in items { for &(name, value) in items {
self.define_class_trait( self.define_class_trait(
activation.context.gc_context, activation.gc(),
Trait::from_const( Trait::from_const(
QName::new(namespace, name), QName::new(namespace, name),
activation.avm2().multinames.number, Some(activation.avm2().multinames.number),
Some(value.into()), Some(value.into()),
), ),
); );
@ -1055,10 +1059,10 @@ impl<'gc> Class<'gc> {
) { ) {
for &(name, value) in items { for &(name, value) in items {
self.define_class_trait( self.define_class_trait(
activation.context.gc_context, activation.gc(),
Trait::from_const( Trait::from_const(
QName::new(namespace, name), QName::new(namespace, name),
activation.avm2().multinames.uint, Some(activation.avm2().multinames.uint),
Some(value.into()), Some(value.into()),
), ),
); );
@ -1077,7 +1081,7 @@ impl<'gc> Class<'gc> {
activation.context.gc_context, activation.context.gc_context,
Trait::from_const( Trait::from_const(
QName::new(namespace, name), QName::new(namespace, name),
activation.avm2().multinames.int, Some(activation.avm2().multinames.int),
Some(value.into()), Some(value.into()),
), ),
); );
@ -1111,7 +1115,7 @@ impl<'gc> Class<'gc> {
&'static str, &'static str,
NativeMethodImpl, NativeMethodImpl,
Vec<ParamConfig<'gc>>, Vec<ParamConfig<'gc>>,
Gc<'gc, Multiname<'gc>>, Option<Gc<'gc, Multiname<'gc>>>,
)>, )>,
) { ) {
for (name, value, params, return_type) in items { for (name, value, params, return_type) in items {
@ -1188,7 +1192,7 @@ impl<'gc> Class<'gc> {
activation.context.gc_context, activation.context.gc_context,
Trait::from_const( Trait::from_const(
QName::new(namespace, name), QName::new(namespace, name),
activation.avm2().multinames.int, Some(activation.avm2().multinames.int),
Some(value.into()), Some(value.into()),
), ),
); );

View File

@ -221,7 +221,7 @@ impl<'gc> Domain<'gc> {
if let Some(class) = class { if let Some(class) = class {
if let Some(param) = multiname.param() { if let Some(param) = multiname.param() {
if !param.is_any_name() { if let Some(param) = param {
if let Some(resolved_param) = self.get_class(context, &param) { if let Some(resolved_param) = self.get_class(context, &param) {
return Some(Class::with_type_param(context, class, Some(resolved_param))); return Some(Class::with_type_param(context, class, Some(resolved_param)));
} }

View File

@ -118,10 +118,10 @@ impl<'gc> BoundMethod<'gc> {
} }
} }
pub fn return_type(&self) -> &Multiname<'gc> { pub fn return_type(&self) -> Option<Gc<'gc, Multiname<'gc>>> {
match &self.method { match &self.method {
Method::Native(method) => &method.return_type, Method::Native(method) => method.return_type,
Method::Bytecode(method) => &method.return_type, Method::Bytecode(method) => method.return_type,
} }
} }
} }

View File

@ -612,7 +612,7 @@ pub fn load_player_globals<'gc>(
// right now. // right now.
global_traits.push(Trait::from_const( global_traits.push(Trait::from_const(
qname, qname,
activation.avm2().multinames.function, Some(activation.avm2().multinames.function),
Some(Value::Null), Some(Value::Null),
)); ));
} }

View File

@ -5,10 +5,14 @@ use crate::avm2::method::Method;
use crate::avm2::object::{ArrayObject, TObject}; use crate::avm2::object::{ArrayObject, TObject};
use crate::avm2::parameters::ParametersExt; use crate::avm2::parameters::ParametersExt;
use crate::avm2::property::Property; use crate::avm2::property::Property;
use crate::avm2::{Activation, Error, Multiname, Namespace, Object, Value}; use crate::avm2::{Activation, Error, Multiname, Namespace, Object, Value};
use crate::context::GcContext;
use crate::string::AvmString;
use crate::avm2_stub_method; use crate::avm2_stub_method;
use gc_arena::Gc;
// Implements `avmplus.describeTypeJSON` // Implements `avmplus.describeTypeJSON`
pub fn describe_type_json<'gc>( pub fn describe_type_json<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -357,10 +361,8 @@ fn describe_internal_body<'gc>(
continue; continue;
} }
let return_type_name = method let return_type_name =
.method display_name(&mut activation.borrow_gc(), method.method.return_type());
.return_type()
.to_qualified_name_or_star(&mut activation.borrow_gc());
let declared_by = method.class; let declared_by = method.class;
if flags.contains(DescribeTypeFlags::HIDE_OBJECT) if flags.contains(DescribeTypeFlags::HIDE_OBJECT)
@ -457,8 +459,7 @@ fn describe_internal_body<'gc>(
Some(ns.as_uri()) Some(ns.as_uri())
}; };
let accessor_type = let accessor_type = display_name(&mut activation.borrow_gc(), method_type);
method_type.to_qualified_name_or_star(&mut activation.borrow_gc());
let declared_by = defining_class.dollar_removed_name(mc).to_qualified_name(mc); let declared_by = defining_class.dollar_removed_name(mc).to_qualified_name(mc);
let accessor_obj = activation let accessor_obj = activation
@ -535,6 +536,17 @@ fn describe_internal_body<'gc>(
Ok(traits) Ok(traits)
} }
fn display_name<'gc>(
context: &mut GcContext<'_, 'gc>,
name: Option<Gc<'gc, Multiname<'gc>>>,
) -> AvmString<'gc> {
if let Some(name) = name {
name.to_qualified_name_or_star(context)
} else {
context.interner.get_ascii_char('*')
}
}
fn write_params<'gc>( fn write_params<'gc>(
method: &Method<'gc>, method: &Method<'gc>,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
@ -544,9 +556,7 @@ fn write_params<'gc>(
.as_array_storage_mut(activation.context.gc_context) .as_array_storage_mut(activation.context.gc_context)
.unwrap(); .unwrap();
for param in method.signature() { for param in method.signature() {
let param_type_name = param let param_type_name = display_name(&mut activation.borrow_gc(), param.param_type_name);
.param_type_name
.to_qualified_name_or_star(&mut activation.borrow_gc());
let optional = param.default_value.is_some(); let optional = param.default_value.is_some();
let param_obj = activation let param_obj = activation
.avm2() .avm2()

View File

@ -227,10 +227,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
"<int instance initializer>", "<int instance initializer>",
vec![ParamConfig { vec![ParamConfig {
param_name: AvmString::new_utf8(activation.context.gc_context, "value"), param_name: AvmString::new_utf8(activation.context.gc_context, "value"),
param_type_name: activation.avm2().multinames.any, param_type_name: None,
default_value: Some(Value::Integer(0)), default_value: Some(Value::Integer(0)),
}], }],
activation.avm2().multinames.any, None,
true, true,
mc, mc,
), ),

View File

@ -276,32 +276,20 @@ pub fn create_i_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
( (
"hasOwnProperty", "hasOwnProperty",
has_own_property, has_own_property,
vec![ParamConfig::optional( vec![ParamConfig::optional("name", None, Value::Undefined)],
"name", Some(activation.avm2().multinames.boolean),
activation.avm2().multinames.any,
Value::Undefined,
)],
activation.avm2().multinames.boolean,
), ),
( (
"isPrototypeOf", "isPrototypeOf",
is_prototype_of, is_prototype_of,
vec![ParamConfig::optional( vec![ParamConfig::optional("theClass", None, Value::Undefined)],
"theClass", Some(activation.avm2().multinames.boolean),
activation.avm2().multinames.any,
Value::Undefined,
)],
activation.avm2().multinames.boolean,
), ),
( (
"propertyIsEnumerable", "propertyIsEnumerable",
property_is_enumerable, property_is_enumerable,
vec![ParamConfig::optional( vec![ParamConfig::optional("name", None, Value::Undefined)],
"name", Some(activation.avm2().multinames.boolean),
activation.avm2().multinames.any,
Value::Undefined,
)],
activation.avm2().multinames.boolean,
), ),
]; ];
object_i_class.define_builtin_instance_methods_with_sig( object_i_class.define_builtin_instance_methods_with_sig(
@ -336,7 +324,7 @@ pub fn create_c_class<'gc>(
gc_context, gc_context,
Trait::from_const( Trait::from_const(
QName::new(activation.avm2().public_namespace_base_version, "length"), QName::new(activation.avm2().public_namespace_base_version, "length"),
activation.avm2().multinames.int, Some(activation.avm2().multinames.int),
Some(1.into()), Some(1.into()),
), ),
); );

View File

@ -228,10 +228,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
"<uint instance initializer>", "<uint instance initializer>",
vec![ParamConfig { vec![ParamConfig {
param_name: AvmString::new_utf8(activation.context.gc_context, "value"), param_name: AvmString::new_utf8(activation.context.gc_context, "value"),
param_type_name: activation.avm2().multinames.any, param_type_name: None,
default_value: Some(Value::Integer(0)), default_value: Some(Value::Integer(0)),
}], }],
activation.avm2().multinames.any, None,
true, true,
mc, mc,
), ),

View File

@ -60,7 +60,7 @@ pub struct ParamConfig<'gc> {
pub param_name: AvmString<'gc>, pub param_name: AvmString<'gc>,
/// The name of the type of the parameter. /// The name of the type of the parameter.
pub param_type_name: Gc<'gc, Multiname<'gc>>, pub param_type_name: Option<Gc<'gc, Multiname<'gc>>>,
/// The default value for this parameter. /// The default value for this parameter.
pub default_value: Option<Value<'gc>>, pub default_value: Option<Value<'gc>>,
@ -96,7 +96,7 @@ impl<'gc> ParamConfig<'gc> {
pub fn of_type( pub fn of_type(
name: impl Into<AvmString<'gc>>, name: impl Into<AvmString<'gc>>,
param_type_name: Gc<'gc, Multiname<'gc>>, param_type_name: Option<Gc<'gc, Multiname<'gc>>>,
) -> Self { ) -> Self {
Self { Self {
param_name: name.into(), param_name: name.into(),
@ -107,7 +107,7 @@ impl<'gc> ParamConfig<'gc> {
pub fn optional( pub fn optional(
name: impl Into<AvmString<'gc>>, name: impl Into<AvmString<'gc>>,
param_type_name: Gc<'gc, Multiname<'gc>>, param_type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: impl Into<Value<'gc>>, default_value: impl Into<Value<'gc>>,
) -> Self { ) -> Self {
Self { Self {
@ -140,8 +140,9 @@ pub struct BytecodeMethod<'gc> {
/// The parameter signature of this method. /// The parameter signature of this method.
pub signature: Vec<ParamConfig<'gc>>, pub signature: Vec<ParamConfig<'gc>>,
/// The return type of this method. /// The return type of this method, or None if the method does not coerce
pub return_type: Gc<'gc, Multiname<'gc>>, /// its return value.
pub return_type: Option<Gc<'gc, Multiname<'gc>>>,
/// The associated activation class. Initialized lazily, and only /// The associated activation class. Initialized lazily, and only
/// if the method requires it. /// if the method requires it.
@ -164,7 +165,7 @@ impl<'gc> BytecodeMethod<'gc> {
) -> Result<Self, Error<'gc>> { ) -> Result<Self, Error<'gc>> {
let abc = txunit.abc(); let abc = txunit.abc();
let mut signature = Vec::new(); let mut signature = Vec::new();
let mut return_type = activation.avm2().multinames.any; let mut return_type = None;
let mut abc_method_body = None; let mut abc_method_body = None;
if abc.methods.get(abc_method.0 as usize).is_some() { if abc.methods.get(abc_method.0 as usize).is_some() {
@ -289,7 +290,7 @@ impl<'gc> BytecodeMethod<'gc> {
} }
for param in self.signature() { for param in self.signature() {
if !param.param_type_name.is_any_name() || param.default_value.is_some() { if param.param_type_name.is_some() || param.default_value.is_some() {
return false; return false;
} }
} }
@ -336,8 +337,9 @@ pub struct NativeMethod<'gc> {
/// The resolved parameter signature of the method. /// The resolved parameter signature of the method.
pub resolved_signature: GcCell<'gc, Option<Vec<ResolvedParamConfig<'gc>>>>, pub resolved_signature: GcCell<'gc, Option<Vec<ResolvedParamConfig<'gc>>>>,
/// The return type of this method. /// The return type of this method, or None if the method does not coerce
pub return_type: Gc<'gc, Multiname<'gc>>, /// its return value.
pub return_type: Option<Gc<'gc, Multiname<'gc>>>,
/// Whether or not this method accepts parameters beyond those /// Whether or not this method accepts parameters beyond those
/// mentioned in the parameter list. /// mentioned in the parameter list.
@ -391,7 +393,7 @@ impl<'gc> Method<'gc> {
method: NativeMethodImpl, method: NativeMethodImpl,
name: &'static str, name: &'static str,
signature: Vec<ParamConfig<'gc>>, signature: Vec<ParamConfig<'gc>>,
return_type: Gc<'gc, Multiname<'gc>>, return_type: Option<Gc<'gc, Multiname<'gc>>>,
is_variadic: bool, is_variadic: bool,
mc: &Mutation<'gc>, mc: &Mutation<'gc>,
) -> Self { ) -> Self {
@ -418,7 +420,7 @@ impl<'gc> Method<'gc> {
signature: Vec::new(), signature: Vec::new(),
resolved_signature: GcCell::new(mc, None), resolved_signature: GcCell::new(mc, None),
// FIXME - take in the real return type. This is needed for 'describeType' // FIXME - take in the real return type. This is needed for 'describeType'
return_type: Gc::new(mc, Multiname::any()), return_type: None,
is_variadic: true, is_variadic: true,
}, },
)) ))
@ -436,7 +438,7 @@ impl<'gc> Method<'gc> {
} }
} }
pub fn return_type(&self) -> Gc<'gc, Multiname<'gc>> { pub fn return_type(&self) -> Option<Gc<'gc, Multiname<'gc>>> {
match self { match self {
Method::Native(nm) => nm.return_type, Method::Native(nm) => nm.return_type,
Method::Bytecode(bm) => bm.return_type, Method::Bytecode(bm) => bm.return_type,

View File

@ -96,8 +96,9 @@ pub struct Multiname<'gc> {
name: Option<AvmString<'gc>>, name: Option<AvmString<'gc>>,
/// The type parameter required to satisfy this multiname. If None, then /// The type parameter required to satisfy this multiname. If None, then
/// this multiname is satisfied by any type parameter or no type parameter /// this multiname does not have a type parameter. If Some(None), then
param: Option<Gc<'gc, Multiname<'gc>>>, /// this multiname uses the Any type parameter (`*`).
param: Option<Option<Gc<'gc, Multiname<'gc>>>>,
#[collect(require_static)] #[collect(require_static)]
flags: MultinameFlags, flags: MultinameFlags,
@ -287,6 +288,7 @@ impl<'gc> Multiname<'gc> {
) { ) {
multiname.flags |= MultinameFlags::ATTRIBUTE; multiname.flags |= MultinameFlags::ATTRIBUTE;
} }
Ok(multiname) Ok(multiname)
} }
@ -436,7 +438,7 @@ impl<'gc> Multiname<'gc> {
} }
/// List the parameters that the selected class must match. /// List the parameters that the selected class must match.
pub fn param(&self) -> Option<Gc<'gc, Multiname<'gc>>> { pub fn param(&self) -> Option<Option<Gc<'gc, Multiname<'gc>>>> {
self.param self.param
} }
@ -461,7 +463,11 @@ impl<'gc> Multiname<'gc> {
if let Some(param) = self.param { if let Some(param) = self.param {
uri.push_str(WStr::from_units(b".<")); uri.push_str(WStr::from_units(b".<"));
uri.push_str(&param.to_qualified_name(mc)); if let Some(param) = param {
uri.push_str(&param.to_qualified_name(mc));
} else {
uri.push_str(WStr::from_units(b"*"));
}
uri.push_str(WStr::from_units(b">")); uri.push_str(WStr::from_units(b">"));
} }
@ -529,8 +535,6 @@ impl<'gc> From<QName<'gc>> for Multiname<'gc> {
#[derive(Collect)] #[derive(Collect)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct CommonMultinames<'gc> { pub struct CommonMultinames<'gc> {
pub any: Gc<'gc, Multiname<'gc>>,
pub boolean: Gc<'gc, Multiname<'gc>>, pub boolean: Gc<'gc, Multiname<'gc>>,
pub function: Gc<'gc, Multiname<'gc>>, pub function: Gc<'gc, Multiname<'gc>>,
pub int: Gc<'gc, Multiname<'gc>>, pub int: Gc<'gc, Multiname<'gc>>,
@ -558,7 +562,6 @@ impl<'gc> CommonMultinames<'gc> {
}; };
Self { Self {
any: Gc::new(mc, Multiname::any()),
boolean: create_pub_multiname(b"Boolean"), boolean: create_pub_multiname(b"Boolean"),
function: create_pub_multiname(b"Function"), function: create_pub_multiname(b"Function"),
int: create_pub_multiname(b"int"), int: create_pub_multiname(b"int"),

View File

@ -39,7 +39,7 @@ pub fn function_allocator<'gc>(
name: "<Empty Function>", name: "<Empty Function>",
signature: vec![], signature: vec![],
resolved_signature: GcCell::new(mc, None), resolved_signature: GcCell::new(mc, None),
return_type: activation.avm2().multinames.any, return_type: None,
is_variadic: true, is_variadic: true,
}, },
); );

View File

@ -47,10 +47,14 @@ pub enum PropertyClass<'gc> {
impl<'gc> PropertyClass<'gc> { impl<'gc> PropertyClass<'gc> {
pub fn name( pub fn name(
mc: &Mutation<'gc>, mc: &Mutation<'gc>,
name: Gc<'gc, Multiname<'gc>>, name: Option<Gc<'gc, Multiname<'gc>>>,
unit: Option<TranslationUnit<'gc>>, unit: Option<TranslationUnit<'gc>>,
) -> Self { ) -> Self {
PropertyClass::Name(Gc::new(mc, (name, unit))) if let Some(name) = name {
PropertyClass::Name(Gc::new(mc, (name, unit)))
} else {
PropertyClass::Any
}
} }
/// Returns `value` coerced to the type of this `PropertyClass`. /// Returns `value` coerced to the type of this `PropertyClass`.
@ -65,27 +69,21 @@ impl<'gc> PropertyClass<'gc> {
PropertyClass::Class(class) => (Some(*class), false), PropertyClass::Class(class) => (Some(*class), false),
PropertyClass::Name(gc) => { PropertyClass::Name(gc) => {
let (name, unit) = &**gc; let (name, unit) = &**gc;
if name.is_any_namespace() && name.is_any_name() {
*self = PropertyClass::Any;
(None, true)
} else {
// Note - we look up the class in the domain by name, which allows us to look up private classes.
// This also has the advantage of letting us coerce to a class while the `ClassObject`
// is still being constructed (since the `Class` will already exist in the domain).
// We should only be missing a translation unit when performing a lookup from playerglobals, // Note - we look up the class in the domain by name, which allows us to look up private classes.
// so use that domain if we don't have a translation unit. // This also has the advantage of letting us coerce to a class while the `ClassObject`
let domain = // is still being constructed (since the `Class` will already exist in the domain).
unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain());
if let Some(class) = domain.get_class(activation.context, name) { // We should only be missing a translation unit when performing a lookup from playerglobals,
*self = PropertyClass::Class(class); // so use that domain if we don't have a translation unit.
(Some(class), true) let domain = unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain());
} else { if let Some(class) = domain.get_class(activation.context, name) {
return Err(format!( *self = PropertyClass::Class(class);
"Could not resolve class {name:?} for property coercion" (Some(class), true)
) } else {
.into()); return Err(
} format!("Could not resolve class {name:?} for property coercion").into(),
);
} }
} }
PropertyClass::Any => (None, false), PropertyClass::Any => (None, false),
@ -108,21 +106,16 @@ impl<'gc> PropertyClass<'gc> {
PropertyClass::Class(class) => Ok(Some(*class)), PropertyClass::Class(class) => Ok(Some(*class)),
PropertyClass::Name(gc) => { PropertyClass::Name(gc) => {
let (name, unit) = &**gc; let (name, unit) = &**gc;
if name.is_any_namespace() && name.is_any_name() {
*self = PropertyClass::Any; let domain = unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain());
Ok(None) if let Some(class) = domain.get_class(activation.context, name) {
*self = PropertyClass::Class(class);
Ok(Some(class))
} else { } else {
let domain = Err(
unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain()); format!("Could not resolve class {name:?} for property class lookup")
if let Some(class) = domain.get_class(activation.context, name) { .into(),
*self = PropertyClass::Class(class); )
Ok(Some(class))
} else {
Err(
format!("Could not resolve class {name:?} for property class lookup")
.into(),
)
}
} }
} }
PropertyClass::Any => Ok(None), PropertyClass::Any => Ok(None),

View File

@ -386,16 +386,17 @@ impl<'gc> TranslationUnit<'gc> {
/// Retrieve a static, or non-runtime, multiname from the current constant /// Retrieve a static, or non-runtime, multiname from the current constant
/// pool. /// pool.
/// ///
/// This version of the function treats index 0 as the any-type `*`. /// This version of the function returns None for index 0.
pub fn pool_multiname_static_any( pub fn pool_multiname_static_any(
self, self,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
multiname_index: Index<AbcMultiname>, multiname_index: Index<AbcMultiname>,
) -> Result<Gc<'gc, Multiname<'gc>>, Error<'gc>> { ) -> Result<Option<Gc<'gc, Multiname<'gc>>>, Error<'gc>> {
if multiname_index.0 == 0 { if multiname_index.0 == 0 {
Ok(activation.avm2().multinames.any) Ok(None)
} else { } else {
self.pool_multiname_static(activation, multiname_index) self.pool_multiname_static(activation, multiname_index)
.map(Some)
} }
} }
} }

View File

@ -64,7 +64,7 @@ fn format_signature(params: &[ParamConfig], is_variadic: bool) -> Vec<ParamInfo>
result.push(ParamInfo { result.push(ParamInfo {
type_info: param type_info: param
.param_type_name .param_type_name
.local_name() .and_then(|m| m.local_name())
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or_else(|| "*".to_string()), .unwrap_or_else(|| "*".to_string()),
value: param.default_value.and_then(|v| format_value(&v)), value: param.default_value.and_then(|v| format_value(&v)),
@ -158,7 +158,7 @@ impl FunctionInfo {
Self { Self {
returns: method returns: method
.return_type() .return_type()
.local_name() .and_then(|m| m.local_name())
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or_else(|| "void".to_string()), .unwrap_or_else(|| "void".to_string()),
args: format_signature(method.signature(), method.is_variadic()), args: format_signature(method.signature(), method.is_variadic()),
@ -170,7 +170,7 @@ impl FunctionInfo {
Self { Self {
returns: executable returns: executable
.return_type() .return_type()
.local_name() .and_then(|m| m.local_name())
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or_else(|| "void".to_string()), .unwrap_or_else(|| "void".to_string()),
args: format_signature(executable.signature(), executable.is_variadic()), args: format_signature(executable.signature(), executable.is_variadic()),
@ -390,7 +390,9 @@ impl Definition {
.insert( .insert(
trait_name, trait_name,
VariableInfo { VariableInfo {
type_info: type_name.local_name().map(|n| n.to_string()), type_info: type_name
.and_then(|m| m.local_name())
.map(|n| n.to_string()),
value: format_value(default_value), value: format_value(default_value),
stubbed: false, stubbed: false,
}, },
@ -411,7 +413,7 @@ impl Definition {
type_info: Some( type_info: Some(
method method
.return_type() .return_type()
.local_name() .and_then(|m| m.local_name())
.map(|n| n.to_string()) .map(|n| n.to_string())
.unwrap_or_else(|| "*".to_string()), .unwrap_or_else(|| "*".to_string()),
), ),
@ -429,7 +431,8 @@ impl Definition {
method method
.signature() .signature()
.first() .first()
.and_then(|p| p.param_type_name.local_name()) .and_then(|p| p.param_type_name)
.and_then(|m| m.local_name())
.map(|t| t.to_string()) .map(|t| t.to_string())
.unwrap_or_else(|| "*".to_string()), .unwrap_or_else(|| "*".to_string()),
), ),
@ -451,7 +454,9 @@ impl Definition {
.insert( .insert(
trait_name, trait_name,
VariableInfo { VariableInfo {
type_info: type_name.local_name().map(|n| n.to_string()), type_info: type_name
.and_then(|m| m.local_name())
.map(|n| n.to_string()),
value: format_value(default_value), value: format_value(default_value),
stubbed: false, stubbed: false,
}, },

View File

@ -74,7 +74,7 @@ pub enum TraitKind<'gc> {
/// to. /// to.
Slot { Slot {
slot_id: u32, slot_id: u32,
type_name: Gc<'gc, Multiname<'gc>>, type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Value<'gc>, default_value: Value<'gc>,
unit: Option<TranslationUnit<'gc>>, unit: Option<TranslationUnit<'gc>>,
}, },
@ -99,7 +99,7 @@ pub enum TraitKind<'gc> {
/// be overridden. /// be overridden.
Const { Const {
slot_id: u32, slot_id: u32,
type_name: Gc<'gc, Multiname<'gc>>, type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Value<'gc>, default_value: Value<'gc>,
unit: Option<TranslationUnit<'gc>>, unit: Option<TranslationUnit<'gc>>,
}, },
@ -156,7 +156,7 @@ impl<'gc> Trait<'gc> {
pub fn from_slot( pub fn from_slot(
name: QName<'gc>, name: QName<'gc>,
type_name: Gc<'gc, Multiname<'gc>>, type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Option<Value<'gc>>, default_value: Option<Value<'gc>>,
) -> Self { ) -> Self {
Trait { Trait {
@ -164,7 +164,7 @@ impl<'gc> Trait<'gc> {
attributes: TraitAttributes::empty(), attributes: TraitAttributes::empty(),
kind: TraitKind::Slot { kind: TraitKind::Slot {
slot_id: 0, slot_id: 0,
default_value: default_value.unwrap_or_else(|| default_value_for_type(&type_name)), default_value: default_value.unwrap_or_else(|| default_value_for_type(type_name)),
type_name, type_name,
unit: None, unit: None,
}, },
@ -174,7 +174,7 @@ impl<'gc> Trait<'gc> {
pub fn from_const( pub fn from_const(
name: QName<'gc>, name: QName<'gc>,
type_name: Gc<'gc, Multiname<'gc>>, type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Option<Value<'gc>>, default_value: Option<Value<'gc>>,
) -> Self { ) -> Self {
Trait { Trait {
@ -182,7 +182,7 @@ impl<'gc> Trait<'gc> {
attributes: TraitAttributes::empty(), attributes: TraitAttributes::empty(),
kind: TraitKind::Const { kind: TraitKind::Const {
slot_id: 0, slot_id: 0,
default_value: default_value.unwrap_or_else(|| default_value_for_type(&type_name)), default_value: default_value.unwrap_or_else(|| default_value_for_type(type_name)),
type_name, type_name,
unit: None, unit: None,
}, },
@ -205,7 +205,7 @@ impl<'gc> Trait<'gc> {
value, value,
} => { } => {
let type_name = unit.pool_multiname_static_any(activation, *type_name)?; let type_name = unit.pool_multiname_static_any(activation, *type_name)?;
let default_value = slot_default_value(unit, value, &type_name, activation)?; let default_value = slot_default_value(unit, value, type_name, activation)?;
Trait { Trait {
name, name,
attributes: trait_attribs_from_abc_traits(abc_trait), attributes: trait_attribs_from_abc_traits(abc_trait),
@ -269,7 +269,7 @@ impl<'gc> Trait<'gc> {
value, value,
} => { } => {
let type_name = unit.pool_multiname_static_any(activation, *type_name)?; let type_name = unit.pool_multiname_static_any(activation, *type_name)?;
let default_value = slot_default_value(unit, value, &type_name, activation)?; let default_value = slot_default_value(unit, value, type_name, activation)?;
Trait { Trait {
name, name,
attributes: trait_attribs_from_abc_traits(abc_trait), attributes: trait_attribs_from_abc_traits(abc_trait),
@ -386,7 +386,7 @@ impl<'gc> Trait<'gc> {
fn slot_default_value<'gc>( fn slot_default_value<'gc>(
translation_unit: TranslationUnit<'gc>, translation_unit: TranslationUnit<'gc>,
value: &Option<AbcDefaultValue>, value: &Option<AbcDefaultValue>,
type_name: &Multiname<'gc>, type_name: Option<Gc<'gc, Multiname<'gc>>>,
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(value) = value { if let Some(value) = value {
@ -399,29 +399,31 @@ fn slot_default_value<'gc>(
/// Returns the default "null" value for the given type. /// Returns the default "null" value for the given type.
/// (`0` for ints, `null` for objects, etc.) /// (`0` for ints, `null` for objects, etc.)
fn default_value_for_type<'gc>(type_name: &Multiname<'gc>) -> Value<'gc> { fn default_value_for_type<'gc>(type_name: Option<Gc<'gc, Multiname<'gc>>>) -> Value<'gc> {
// TODO: It's technically possible to have a multiname in here, so this should go through something if let Some(type_name) = type_name {
// like `Activation::resolve_type` to get an actual `Class` object, and then check something like `Class::built_in_type`. // TODO: It's technically possible to have a multiname in here, so this should go through something
// The Multiname is guaranteed to be static by `pool.pool_multiname_static` earlier. // like `Activation::resolve_type` to get an actual `Class` object, and then check something like `Class::built_in_type`.
if type_name.is_any_name() { // The Multiname is guaranteed to be static by `pool.pool_multiname_static` earlier.
Value::Undefined if type_name.contains_public_namespace() {
} else if type_name.contains_public_namespace() { let name = type_name.local_name().unwrap_or_default();
let name = type_name.local_name().unwrap_or_default(); if &name == b"Boolean" {
if &name == b"Boolean" { false.into()
false.into() } else if &name == b"Number" {
} else if &name == b"Number" { f64::NAN.into()
f64::NAN.into() } else if &name == b"int" {
} else if &name == b"int" { 0.into()
0.into() } else if &name == b"String" {
} else if &name == b"String" { Value::Null
Value::Null } else if &name == b"uint" {
} else if &name == b"uint" { 0.into()
0.into() } else {
Value::Null // Object type
}
} else { } else {
Value::Null // Object type // Object type
Value::Null
} }
} else { } else {
// Object type Value::Undefined
Value::Null
} }
} }

View File

@ -98,7 +98,7 @@ pub fn verify_method<'gc>(
} }
let resolved_param_config = resolve_param_config(activation, method.signature())?; let resolved_param_config = resolve_param_config(activation, method.signature())?;
let resolved_return_type = resolve_return_type(activation, &method.return_type)?; let resolved_return_type = resolve_return_type(activation, method.return_type)?;
let mut seen_exception_indices = HashSet::new(); let mut seen_exception_indices = HashSet::new();
@ -671,26 +671,24 @@ pub fn resolve_param_config<'gc>(
let mut resolved_param_config = Vec::new(); let mut resolved_param_config = Vec::new();
for param in param_config { for param in param_config {
if param.param_type_name.has_lazy_component() { let resolved_class = if let Some(param_type_name) = param.param_type_name {
return Err(make_error_1014(activation, "[]".into())); if param_type_name.has_lazy_component() {
} return Err(make_error_1014(activation, "[]".into()));
}
let resolved_class = if param.param_type_name.is_any_name() { Some(
None activation
.domain()
.get_class(activation.context, &param_type_name)
.ok_or_else(|| {
make_error_1014(
activation,
param_type_name.to_qualified_name(activation.gc()),
)
})?,
)
} else { } else {
let lookedup_class = activation None
.domain()
.get_class(activation.context, &param.param_type_name)
.ok_or_else(|| {
make_error_1014(
activation,
param
.param_type_name
.to_qualified_name(activation.context.gc_context),
)
})?;
Some(lookedup_class)
}; };
resolved_param_config.push(ResolvedParamConfig { resolved_param_config.push(ResolvedParamConfig {
@ -705,27 +703,24 @@ pub fn resolve_param_config<'gc>(
fn resolve_return_type<'gc>( fn resolve_return_type<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
return_type: &Multiname<'gc>, return_type: Option<Gc<'gc, Multiname<'gc>>>,
) -> Result<Option<Class<'gc>>, Error<'gc>> { ) -> Result<Option<Class<'gc>>, Error<'gc>> {
if return_type.has_lazy_component() { if let Some(return_type) = return_type {
return Err(make_error_1014(activation, "[]".into())); if return_type.has_lazy_component() {
} return Err(make_error_1014(activation, "[]".into()));
}
if return_type.is_any_name() { Ok(Some(
return Ok(None); activation
.domain()
.get_class(activation.context, &return_type)
.ok_or_else(|| {
make_error_1014(activation, return_type.to_qualified_name(activation.gc()))
})?,
))
} else {
Ok(None)
} }
Ok(Some(
activation
.domain()
.get_class(activation.context, return_type)
.ok_or_else(|| {
make_error_1014(
activation,
return_type.to_qualified_name(activation.context.gc_context),
)
})?,
))
} }
// Taken from avmplus's opcodes.tbl // Taken from avmplus's opcodes.tbl