From ed9b250d0a863277e8441914e4cb964147aa84fb Mon Sep 17 00:00:00 2001 From: Lord-McSweeney Date: Thu, 12 Sep 2024 23:18:20 -0700 Subject: [PATCH] avm2: Correctly differentiate between Any and null Multiname --- core/src/avm2/class.rs | 24 +++++---- core/src/avm2/domain.rs | 2 +- core/src/avm2/function.rs | 6 +-- core/src/avm2/globals.rs | 2 +- core/src/avm2/globals/avmplus.rs | 30 +++++++---- core/src/avm2/globals/int.rs | 4 +- core/src/avm2/globals/object.rs | 26 +++------- core/src/avm2/globals/uint.rs | 4 +- core/src/avm2/method.rs | 26 +++++----- core/src/avm2/multiname.rs | 17 +++--- core/src/avm2/object/function_object.rs | 2 +- core/src/avm2/property.rs | 65 +++++++++++------------ core/src/avm2/script.rs | 7 +-- core/src/avm2/specification.rs | 19 ++++--- core/src/avm2/traits.rs | 62 +++++++++++----------- core/src/avm2/verify.rs | 69 ++++++++++++------------- 16 files changed, 184 insertions(+), 181 deletions(-) diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index 46c58ec1b..80a6f809d 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -518,7 +518,7 @@ impl<'gc> Class<'gc> { // A 'callable' class doesn't have a signature - let the // method do any needed coercions vec![], - activation.avm2().multinames.any, + None, true, activation.context.gc_context, ); @@ -1022,8 +1022,12 @@ impl<'gc> Class<'gc> { value: Value<'gc>, ) { self.define_instance_trait( - activation.context.gc_context, - Trait::from_const(name, activation.avm2().multinames.function, Some(value)), + activation.gc(), + Trait::from_const( + name, + Some(activation.avm2().multinames.function), + Some(value), + ), ); } @@ -1036,10 +1040,10 @@ impl<'gc> Class<'gc> { ) { for &(name, value) in items { self.define_class_trait( - activation.context.gc_context, + activation.gc(), Trait::from_const( QName::new(namespace, name), - activation.avm2().multinames.number, + Some(activation.avm2().multinames.number), Some(value.into()), ), ); @@ -1055,10 +1059,10 @@ impl<'gc> Class<'gc> { ) { for &(name, value) in items { self.define_class_trait( - activation.context.gc_context, + activation.gc(), Trait::from_const( QName::new(namespace, name), - activation.avm2().multinames.uint, + Some(activation.avm2().multinames.uint), Some(value.into()), ), ); @@ -1077,7 +1081,7 @@ impl<'gc> Class<'gc> { activation.context.gc_context, Trait::from_const( QName::new(namespace, name), - activation.avm2().multinames.int, + Some(activation.avm2().multinames.int), Some(value.into()), ), ); @@ -1111,7 +1115,7 @@ impl<'gc> Class<'gc> { &'static str, NativeMethodImpl, Vec>, - Gc<'gc, Multiname<'gc>>, + Option>>, )>, ) { for (name, value, params, return_type) in items { @@ -1188,7 +1192,7 @@ impl<'gc> Class<'gc> { activation.context.gc_context, Trait::from_const( QName::new(namespace, name), - activation.avm2().multinames.int, + Some(activation.avm2().multinames.int), Some(value.into()), ), ); diff --git a/core/src/avm2/domain.rs b/core/src/avm2/domain.rs index 5037d0182..44828921e 100644 --- a/core/src/avm2/domain.rs +++ b/core/src/avm2/domain.rs @@ -221,7 +221,7 @@ impl<'gc> Domain<'gc> { if let Some(class) = class { 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, ¶m) { return Some(Class::with_type_param(context, class, Some(resolved_param))); } diff --git a/core/src/avm2/function.rs b/core/src/avm2/function.rs index d0ebb1fe5..cbf489e4c 100644 --- a/core/src/avm2/function.rs +++ b/core/src/avm2/function.rs @@ -118,10 +118,10 @@ impl<'gc> BoundMethod<'gc> { } } - pub fn return_type(&self) -> &Multiname<'gc> { + pub fn return_type(&self) -> Option>> { match &self.method { - Method::Native(method) => &method.return_type, - Method::Bytecode(method) => &method.return_type, + Method::Native(method) => method.return_type, + Method::Bytecode(method) => method.return_type, } } } diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 17fa0aaca..e8ec741f2 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -612,7 +612,7 @@ pub fn load_player_globals<'gc>( // right now. global_traits.push(Trait::from_const( qname, - activation.avm2().multinames.function, + Some(activation.avm2().multinames.function), Some(Value::Null), )); } diff --git a/core/src/avm2/globals/avmplus.rs b/core/src/avm2/globals/avmplus.rs index 912c79981..4520c8634 100644 --- a/core/src/avm2/globals/avmplus.rs +++ b/core/src/avm2/globals/avmplus.rs @@ -5,10 +5,14 @@ use crate::avm2::method::Method; use crate::avm2::object::{ArrayObject, TObject}; use crate::avm2::parameters::ParametersExt; use crate::avm2::property::Property; - use crate::avm2::{Activation, Error, Multiname, Namespace, Object, Value}; +use crate::context::GcContext; +use crate::string::AvmString; + use crate::avm2_stub_method; +use gc_arena::Gc; + // Implements `avmplus.describeTypeJSON` pub fn describe_type_json<'gc>( activation: &mut Activation<'_, 'gc>, @@ -357,10 +361,8 @@ fn describe_internal_body<'gc>( continue; } - let return_type_name = method - .method - .return_type() - .to_qualified_name_or_star(&mut activation.borrow_gc()); + let return_type_name = + display_name(&mut activation.borrow_gc(), method.method.return_type()); let declared_by = method.class; if flags.contains(DescribeTypeFlags::HIDE_OBJECT) @@ -457,8 +459,7 @@ fn describe_internal_body<'gc>( Some(ns.as_uri()) }; - let accessor_type = - method_type.to_qualified_name_or_star(&mut activation.borrow_gc()); + let accessor_type = display_name(&mut activation.borrow_gc(), method_type); let declared_by = defining_class.dollar_removed_name(mc).to_qualified_name(mc); let accessor_obj = activation @@ -535,6 +536,17 @@ fn describe_internal_body<'gc>( Ok(traits) } +fn display_name<'gc>( + context: &mut GcContext<'_, 'gc>, + name: Option>>, +) -> AvmString<'gc> { + if let Some(name) = name { + name.to_qualified_name_or_star(context) + } else { + context.interner.get_ascii_char('*') + } +} + fn write_params<'gc>( method: &Method<'gc>, activation: &mut Activation<'_, 'gc>, @@ -544,9 +556,7 @@ fn write_params<'gc>( .as_array_storage_mut(activation.context.gc_context) .unwrap(); for param in method.signature() { - let param_type_name = param - .param_type_name - .to_qualified_name_or_star(&mut activation.borrow_gc()); + let param_type_name = display_name(&mut activation.borrow_gc(), param.param_type_name); let optional = param.default_value.is_some(); let param_obj = activation .avm2() diff --git a/core/src/avm2/globals/int.rs b/core/src/avm2/globals/int.rs index 0a6ba605c..4c70fdcb7 100644 --- a/core/src/avm2/globals/int.rs +++ b/core/src/avm2/globals/int.rs @@ -227,10 +227,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { "", vec![ParamConfig { 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)), }], - activation.avm2().multinames.any, + None, true, mc, ), diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index 980f06d3c..5097a9bc3 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -276,32 +276,20 @@ pub fn create_i_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { ( "hasOwnProperty", has_own_property, - vec![ParamConfig::optional( - "name", - activation.avm2().multinames.any, - Value::Undefined, - )], - activation.avm2().multinames.boolean, + vec![ParamConfig::optional("name", None, Value::Undefined)], + Some(activation.avm2().multinames.boolean), ), ( "isPrototypeOf", is_prototype_of, - vec![ParamConfig::optional( - "theClass", - activation.avm2().multinames.any, - Value::Undefined, - )], - activation.avm2().multinames.boolean, + vec![ParamConfig::optional("theClass", None, Value::Undefined)], + Some(activation.avm2().multinames.boolean), ), ( "propertyIsEnumerable", property_is_enumerable, - vec![ParamConfig::optional( - "name", - activation.avm2().multinames.any, - Value::Undefined, - )], - activation.avm2().multinames.boolean, + vec![ParamConfig::optional("name", None, Value::Undefined)], + Some(activation.avm2().multinames.boolean), ), ]; object_i_class.define_builtin_instance_methods_with_sig( @@ -336,7 +324,7 @@ pub fn create_c_class<'gc>( gc_context, Trait::from_const( QName::new(activation.avm2().public_namespace_base_version, "length"), - activation.avm2().multinames.int, + Some(activation.avm2().multinames.int), Some(1.into()), ), ); diff --git a/core/src/avm2/globals/uint.rs b/core/src/avm2/globals/uint.rs index 10dfdff6a..ced651450 100644 --- a/core/src/avm2/globals/uint.rs +++ b/core/src/avm2/globals/uint.rs @@ -228,10 +228,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> { "", vec![ParamConfig { 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)), }], - activation.avm2().multinames.any, + None, true, mc, ), diff --git a/core/src/avm2/method.rs b/core/src/avm2/method.rs index b715a8728..bc11cadee 100644 --- a/core/src/avm2/method.rs +++ b/core/src/avm2/method.rs @@ -60,7 +60,7 @@ pub struct ParamConfig<'gc> { pub param_name: AvmString<'gc>, /// The name of the type of the parameter. - pub param_type_name: Gc<'gc, Multiname<'gc>>, + pub param_type_name: Option>>, /// The default value for this parameter. pub default_value: Option>, @@ -96,7 +96,7 @@ impl<'gc> ParamConfig<'gc> { pub fn of_type( name: impl Into>, - param_type_name: Gc<'gc, Multiname<'gc>>, + param_type_name: Option>>, ) -> Self { Self { param_name: name.into(), @@ -107,7 +107,7 @@ impl<'gc> ParamConfig<'gc> { pub fn optional( name: impl Into>, - param_type_name: Gc<'gc, Multiname<'gc>>, + param_type_name: Option>>, default_value: impl Into>, ) -> Self { Self { @@ -140,8 +140,9 @@ pub struct BytecodeMethod<'gc> { /// The parameter signature of this method. pub signature: Vec>, - /// The return type of this method. - pub return_type: Gc<'gc, Multiname<'gc>>, + /// The return type of this method, or None if the method does not coerce + /// its return value. + pub return_type: Option>>, /// The associated activation class. Initialized lazily, and only /// if the method requires it. @@ -164,7 +165,7 @@ impl<'gc> BytecodeMethod<'gc> { ) -> Result> { let abc = txunit.abc(); let mut signature = Vec::new(); - let mut return_type = activation.avm2().multinames.any; + let mut return_type = None; let mut abc_method_body = None; if abc.methods.get(abc_method.0 as usize).is_some() { @@ -289,7 +290,7 @@ impl<'gc> BytecodeMethod<'gc> { } 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; } } @@ -336,8 +337,9 @@ pub struct NativeMethod<'gc> { /// The resolved parameter signature of the method. pub resolved_signature: GcCell<'gc, Option>>>, - /// The return type of this method. - pub return_type: Gc<'gc, Multiname<'gc>>, + /// The return type of this method, or None if the method does not coerce + /// its return value. + pub return_type: Option>>, /// Whether or not this method accepts parameters beyond those /// mentioned in the parameter list. @@ -391,7 +393,7 @@ impl<'gc> Method<'gc> { method: NativeMethodImpl, name: &'static str, signature: Vec>, - return_type: Gc<'gc, Multiname<'gc>>, + return_type: Option>>, is_variadic: bool, mc: &Mutation<'gc>, ) -> Self { @@ -418,7 +420,7 @@ impl<'gc> Method<'gc> { signature: Vec::new(), resolved_signature: GcCell::new(mc, None), // 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, }, )) @@ -436,7 +438,7 @@ impl<'gc> Method<'gc> { } } - pub fn return_type(&self) -> Gc<'gc, Multiname<'gc>> { + pub fn return_type(&self) -> Option>> { match self { Method::Native(nm) => nm.return_type, Method::Bytecode(bm) => bm.return_type, diff --git a/core/src/avm2/multiname.rs b/core/src/avm2/multiname.rs index 3acfeb9ec..8c015ea31 100644 --- a/core/src/avm2/multiname.rs +++ b/core/src/avm2/multiname.rs @@ -96,8 +96,9 @@ pub struct Multiname<'gc> { name: Option>, /// The type parameter required to satisfy this multiname. If None, then - /// this multiname is satisfied by any type parameter or no type parameter - param: Option>>, + /// this multiname does not have a type parameter. If Some(None), then + /// this multiname uses the Any type parameter (`*`). + param: Option>>>, #[collect(require_static)] flags: MultinameFlags, @@ -287,6 +288,7 @@ impl<'gc> Multiname<'gc> { ) { multiname.flags |= MultinameFlags::ATTRIBUTE; } + Ok(multiname) } @@ -436,7 +438,7 @@ impl<'gc> Multiname<'gc> { } /// List the parameters that the selected class must match. - pub fn param(&self) -> Option>> { + pub fn param(&self) -> Option>>> { self.param } @@ -461,7 +463,11 @@ impl<'gc> Multiname<'gc> { if let Some(param) = self.param { uri.push_str(WStr::from_units(b".<")); - uri.push_str(¶m.to_qualified_name(mc)); + if let Some(param) = param { + uri.push_str(¶m.to_qualified_name(mc)); + } else { + uri.push_str(WStr::from_units(b"*")); + } uri.push_str(WStr::from_units(b">")); } @@ -529,8 +535,6 @@ impl<'gc> From> for Multiname<'gc> { #[derive(Collect)] #[collect(no_drop)] pub struct CommonMultinames<'gc> { - pub any: Gc<'gc, Multiname<'gc>>, - pub boolean: Gc<'gc, Multiname<'gc>>, pub function: Gc<'gc, Multiname<'gc>>, pub int: Gc<'gc, Multiname<'gc>>, @@ -558,7 +562,6 @@ impl<'gc> CommonMultinames<'gc> { }; Self { - any: Gc::new(mc, Multiname::any()), boolean: create_pub_multiname(b"Boolean"), function: create_pub_multiname(b"Function"), int: create_pub_multiname(b"int"), diff --git a/core/src/avm2/object/function_object.rs b/core/src/avm2/object/function_object.rs index d3ed3c1d2..f1b55d282 100644 --- a/core/src/avm2/object/function_object.rs +++ b/core/src/avm2/object/function_object.rs @@ -39,7 +39,7 @@ pub fn function_allocator<'gc>( name: "", signature: vec![], resolved_signature: GcCell::new(mc, None), - return_type: activation.avm2().multinames.any, + return_type: None, is_variadic: true, }, ); diff --git a/core/src/avm2/property.rs b/core/src/avm2/property.rs index 6d84d6ded..af4043812 100644 --- a/core/src/avm2/property.rs +++ b/core/src/avm2/property.rs @@ -47,10 +47,14 @@ pub enum PropertyClass<'gc> { impl<'gc> PropertyClass<'gc> { pub fn name( mc: &Mutation<'gc>, - name: Gc<'gc, Multiname<'gc>>, + name: Option>>, unit: Option>, ) -> 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`. @@ -65,27 +69,21 @@ impl<'gc> PropertyClass<'gc> { PropertyClass::Class(class) => (Some(*class), false), PropertyClass::Name(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, - // so use that domain if we don't have a translation unit. - let domain = - unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain()); - if let Some(class) = domain.get_class(activation.context, name) { - *self = PropertyClass::Class(class); - (Some(class), true) - } else { - return Err(format!( - "Could not resolve class {name:?} for property coercion" - ) - .into()); - } + // 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, + // so use that domain if we don't have a translation unit. + let domain = unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain()); + if let Some(class) = domain.get_class(activation.context, name) { + *self = PropertyClass::Class(class); + (Some(class), true) + } else { + return Err( + format!("Could not resolve class {name:?} for property coercion").into(), + ); } } PropertyClass::Any => (None, false), @@ -108,21 +106,16 @@ impl<'gc> PropertyClass<'gc> { PropertyClass::Class(class) => Ok(Some(*class)), PropertyClass::Name(gc) => { let (name, unit) = &**gc; - if name.is_any_namespace() && name.is_any_name() { - *self = PropertyClass::Any; - Ok(None) + + let domain = unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain()); + if let Some(class) = domain.get_class(activation.context, name) { + *self = PropertyClass::Class(class); + Ok(Some(class)) } else { - let domain = - unit.map_or(activation.avm2().playerglobals_domain, |u| u.domain()); - if let Some(class) = domain.get_class(activation.context, name) { - *self = PropertyClass::Class(class); - Ok(Some(class)) - } else { - Err( - format!("Could not resolve class {name:?} for property class lookup") - .into(), - ) - } + Err( + format!("Could not resolve class {name:?} for property class lookup") + .into(), + ) } } PropertyClass::Any => Ok(None), diff --git a/core/src/avm2/script.rs b/core/src/avm2/script.rs index 5f4eeb3ce..58fed84d4 100644 --- a/core/src/avm2/script.rs +++ b/core/src/avm2/script.rs @@ -386,16 +386,17 @@ impl<'gc> TranslationUnit<'gc> { /// Retrieve a static, or non-runtime, multiname from the current constant /// 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( self, activation: &mut Activation<'_, 'gc>, multiname_index: Index, - ) -> Result>, Error<'gc>> { + ) -> Result>>, Error<'gc>> { if multiname_index.0 == 0 { - Ok(activation.avm2().multinames.any) + Ok(None) } else { self.pool_multiname_static(activation, multiname_index) + .map(Some) } } } diff --git a/core/src/avm2/specification.rs b/core/src/avm2/specification.rs index 8278ee7ef..6375f614a 100644 --- a/core/src/avm2/specification.rs +++ b/core/src/avm2/specification.rs @@ -64,7 +64,7 @@ fn format_signature(params: &[ParamConfig], is_variadic: bool) -> Vec result.push(ParamInfo { type_info: param .param_type_name - .local_name() + .and_then(|m| m.local_name()) .map(|n| n.to_string()) .unwrap_or_else(|| "*".to_string()), value: param.default_value.and_then(|v| format_value(&v)), @@ -158,7 +158,7 @@ impl FunctionInfo { Self { returns: method .return_type() - .local_name() + .and_then(|m| m.local_name()) .map(|n| n.to_string()) .unwrap_or_else(|| "void".to_string()), args: format_signature(method.signature(), method.is_variadic()), @@ -170,7 +170,7 @@ impl FunctionInfo { Self { returns: executable .return_type() - .local_name() + .and_then(|m| m.local_name()) .map(|n| n.to_string()) .unwrap_or_else(|| "void".to_string()), args: format_signature(executable.signature(), executable.is_variadic()), @@ -390,7 +390,9 @@ impl Definition { .insert( trait_name, 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), stubbed: false, }, @@ -411,7 +413,7 @@ impl Definition { type_info: Some( method .return_type() - .local_name() + .and_then(|m| m.local_name()) .map(|n| n.to_string()) .unwrap_or_else(|| "*".to_string()), ), @@ -429,7 +431,8 @@ impl Definition { method .signature() .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()) .unwrap_or_else(|| "*".to_string()), ), @@ -451,7 +454,9 @@ impl Definition { .insert( trait_name, 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), stubbed: false, }, diff --git a/core/src/avm2/traits.rs b/core/src/avm2/traits.rs index 9b75abae1..ee4446554 100644 --- a/core/src/avm2/traits.rs +++ b/core/src/avm2/traits.rs @@ -74,7 +74,7 @@ pub enum TraitKind<'gc> { /// to. Slot { slot_id: u32, - type_name: Gc<'gc, Multiname<'gc>>, + type_name: Option>>, default_value: Value<'gc>, unit: Option>, }, @@ -99,7 +99,7 @@ pub enum TraitKind<'gc> { /// be overridden. Const { slot_id: u32, - type_name: Gc<'gc, Multiname<'gc>>, + type_name: Option>>, default_value: Value<'gc>, unit: Option>, }, @@ -156,7 +156,7 @@ impl<'gc> Trait<'gc> { pub fn from_slot( name: QName<'gc>, - type_name: Gc<'gc, Multiname<'gc>>, + type_name: Option>>, default_value: Option>, ) -> Self { Trait { @@ -164,7 +164,7 @@ impl<'gc> Trait<'gc> { attributes: TraitAttributes::empty(), kind: TraitKind::Slot { 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, unit: None, }, @@ -174,7 +174,7 @@ impl<'gc> Trait<'gc> { pub fn from_const( name: QName<'gc>, - type_name: Gc<'gc, Multiname<'gc>>, + type_name: Option>>, default_value: Option>, ) -> Self { Trait { @@ -182,7 +182,7 @@ impl<'gc> Trait<'gc> { attributes: TraitAttributes::empty(), kind: TraitKind::Const { 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, unit: None, }, @@ -205,7 +205,7 @@ impl<'gc> Trait<'gc> { value, } => { 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 { name, attributes: trait_attribs_from_abc_traits(abc_trait), @@ -269,7 +269,7 @@ impl<'gc> Trait<'gc> { value, } => { 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 { name, attributes: trait_attribs_from_abc_traits(abc_trait), @@ -386,7 +386,7 @@ impl<'gc> Trait<'gc> { fn slot_default_value<'gc>( translation_unit: TranslationUnit<'gc>, value: &Option, - type_name: &Multiname<'gc>, + type_name: Option>>, activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { if let Some(value) = value { @@ -399,29 +399,31 @@ fn slot_default_value<'gc>( /// Returns the default "null" value for the given type. /// (`0` for ints, `null` for objects, etc.) -fn default_value_for_type<'gc>(type_name: &Multiname<'gc>) -> Value<'gc> { - // TODO: It's technically possible to have a multiname in here, so this should go through something - // like `Activation::resolve_type` to get an actual `Class` object, and then check something like `Class::built_in_type`. - // The Multiname is guaranteed to be static by `pool.pool_multiname_static` earlier. - if type_name.is_any_name() { - Value::Undefined - } else if type_name.contains_public_namespace() { - let name = type_name.local_name().unwrap_or_default(); - if &name == b"Boolean" { - false.into() - } else if &name == b"Number" { - f64::NAN.into() - } else if &name == b"int" { - 0.into() - } else if &name == b"String" { - Value::Null - } else if &name == b"uint" { - 0.into() +fn default_value_for_type<'gc>(type_name: Option>>) -> Value<'gc> { + if let Some(type_name) = type_name { + // TODO: It's technically possible to have a multiname in here, so this should go through something + // like `Activation::resolve_type` to get an actual `Class` object, and then check something like `Class::built_in_type`. + // The Multiname is guaranteed to be static by `pool.pool_multiname_static` earlier. + if type_name.contains_public_namespace() { + let name = type_name.local_name().unwrap_or_default(); + if &name == b"Boolean" { + false.into() + } else if &name == b"Number" { + f64::NAN.into() + } else if &name == b"int" { + 0.into() + } else if &name == b"String" { + Value::Null + } else if &name == b"uint" { + 0.into() + } else { + Value::Null // Object type + } } else { - Value::Null // Object type + // Object type + Value::Null } } else { - // Object type - Value::Null + Value::Undefined } } diff --git a/core/src/avm2/verify.rs b/core/src/avm2/verify.rs index 099f2ad1a..77dcea39d 100644 --- a/core/src/avm2/verify.rs +++ b/core/src/avm2/verify.rs @@ -98,7 +98,7 @@ pub fn verify_method<'gc>( } 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(); @@ -671,26 +671,24 @@ pub fn resolve_param_config<'gc>( let mut resolved_param_config = Vec::new(); for param in param_config { - if param.param_type_name.has_lazy_component() { - return Err(make_error_1014(activation, "[]".into())); - } + let resolved_class = if let Some(param_type_name) = param.param_type_name { + 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() { - None + Some( + activation + .domain() + .get_class(activation.context, ¶m_type_name) + .ok_or_else(|| { + make_error_1014( + activation, + param_type_name.to_qualified_name(activation.gc()), + ) + })?, + ) } else { - let lookedup_class = activation - .domain() - .get_class(activation.context, ¶m.param_type_name) - .ok_or_else(|| { - make_error_1014( - activation, - param - .param_type_name - .to_qualified_name(activation.context.gc_context), - ) - })?; - - Some(lookedup_class) + None }; resolved_param_config.push(ResolvedParamConfig { @@ -705,27 +703,24 @@ pub fn resolve_param_config<'gc>( fn resolve_return_type<'gc>( activation: &mut Activation<'_, 'gc>, - return_type: &Multiname<'gc>, + return_type: Option>>, ) -> Result>, Error<'gc>> { - if return_type.has_lazy_component() { - return Err(make_error_1014(activation, "[]".into())); - } + if let Some(return_type) = return_type { + if return_type.has_lazy_component() { + return Err(make_error_1014(activation, "[]".into())); + } - if return_type.is_any_name() { - return 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.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