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
// 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<ParamConfig<'gc>>,
Gc<'gc, Multiname<'gc>>,
Option<Gc<'gc, Multiname<'gc>>>,
)>,
) {
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()),
),
);

View File

@ -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, &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 {
Method::Native(method) => &method.return_type,
Method::Bytecode(method) => &method.return_type,
Method::Native(method) => method.return_type,
Method::Bytecode(method) => method.return_type,
}
}
}

View File

@ -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),
));
}

View File

@ -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<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>(
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()

View File

@ -227,10 +227,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
"<int instance initializer>",
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,
),

View File

@ -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()),
),
);

View File

@ -228,10 +228,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
"<uint instance initializer>",
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,
),

View File

@ -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<Gc<'gc, Multiname<'gc>>>,
/// The default value for this parameter.
pub default_value: Option<Value<'gc>>,
@ -96,7 +96,7 @@ impl<'gc> ParamConfig<'gc> {
pub fn of_type(
name: impl Into<AvmString<'gc>>,
param_type_name: Gc<'gc, Multiname<'gc>>,
param_type_name: Option<Gc<'gc, Multiname<'gc>>>,
) -> Self {
Self {
param_name: name.into(),
@ -107,7 +107,7 @@ impl<'gc> ParamConfig<'gc> {
pub fn optional(
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>>,
) -> Self {
Self {
@ -140,8 +140,9 @@ pub struct BytecodeMethod<'gc> {
/// The parameter signature of this method.
pub signature: Vec<ParamConfig<'gc>>,
/// 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<Gc<'gc, Multiname<'gc>>>,
/// The associated activation class. Initialized lazily, and only
/// if the method requires it.
@ -164,7 +165,7 @@ impl<'gc> BytecodeMethod<'gc> {
) -> Result<Self, Error<'gc>> {
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<Vec<ResolvedParamConfig<'gc>>>>,
/// 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<Gc<'gc, Multiname<'gc>>>,
/// 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<ParamConfig<'gc>>,
return_type: Gc<'gc, Multiname<'gc>>,
return_type: Option<Gc<'gc, Multiname<'gc>>>,
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<Gc<'gc, Multiname<'gc>>> {
match self {
Method::Native(nm) => nm.return_type,
Method::Bytecode(bm) => bm.return_type,

View File

@ -96,8 +96,9 @@ pub struct Multiname<'gc> {
name: Option<AvmString<'gc>>,
/// 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<Gc<'gc, Multiname<'gc>>>,
/// this multiname does not have a type parameter. If Some(None), then
/// this multiname uses the Any type parameter (`*`).
param: Option<Option<Gc<'gc, Multiname<'gc>>>>,
#[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<Gc<'gc, Multiname<'gc>>> {
pub fn param(&self) -> Option<Option<Gc<'gc, Multiname<'gc>>>> {
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(&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">"));
}
@ -529,8 +535,6 @@ impl<'gc> From<QName<'gc>> 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"),

View File

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

View File

@ -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<Gc<'gc, Multiname<'gc>>>,
unit: Option<TranslationUnit<'gc>>,
) -> 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),

View File

@ -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<AbcMultiname>,
) -> Result<Gc<'gc, Multiname<'gc>>, Error<'gc>> {
) -> Result<Option<Gc<'gc, Multiname<'gc>>>, Error<'gc>> {
if multiname_index.0 == 0 {
Ok(activation.avm2().multinames.any)
Ok(None)
} else {
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 {
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,
},

View File

@ -74,7 +74,7 @@ pub enum TraitKind<'gc> {
/// to.
Slot {
slot_id: u32,
type_name: Gc<'gc, Multiname<'gc>>,
type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Value<'gc>,
unit: Option<TranslationUnit<'gc>>,
},
@ -99,7 +99,7 @@ pub enum TraitKind<'gc> {
/// be overridden.
Const {
slot_id: u32,
type_name: Gc<'gc, Multiname<'gc>>,
type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Value<'gc>,
unit: Option<TranslationUnit<'gc>>,
},
@ -156,7 +156,7 @@ impl<'gc> Trait<'gc> {
pub fn from_slot(
name: QName<'gc>,
type_name: Gc<'gc, Multiname<'gc>>,
type_name: Option<Gc<'gc, Multiname<'gc>>>,
default_value: Option<Value<'gc>>,
) -> 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<Gc<'gc, Multiname<'gc>>>,
default_value: Option<Value<'gc>>,
) -> 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<AbcDefaultValue>,
type_name: &Multiname<'gc>,
type_name: Option<Gc<'gc, Multiname<'gc>>>,
activation: &mut Activation<'_, 'gc>,
) -> Result<Value<'gc>, 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<Gc<'gc, Multiname<'gc>>>) -> 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
}
}

View File

@ -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, &param_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, &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)
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<Gc<'gc, Multiname<'gc>>>,
) -> Result<Option<Class<'gc>>, 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