avm2: Make `coerce_to_type` take a class object instead of a multiname.
This commit is contained in:
parent
bd2f758976
commit
2c927f2b6b
|
@ -199,6 +199,30 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Resolve a type name to a class.
|
||||
///
|
||||
/// This returns an error if a type is named but does not exist; or if the
|
||||
/// typed named is not a class object.
|
||||
fn resolve_type(&mut self, type_name: Multiname<'gc>) -> Result<Option<Object<'gc>>, Error> {
|
||||
if type_name.is_any() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let class = self
|
||||
.scope()
|
||||
.ok_or("Cannot resolve parameter types without a scope stack")?
|
||||
.write(self.context.gc_context)
|
||||
.resolve(&type_name, self)?
|
||||
.ok_or_else(|| format!("Could not resolve parameter type {:?}", type_name))?
|
||||
.coerce_to_object(self)?;
|
||||
|
||||
if class.as_class().is_none() {
|
||||
return Err(format!("Resolved parameter type {:?} is not a class", type_name).into());
|
||||
}
|
||||
|
||||
Ok(Some(class))
|
||||
}
|
||||
|
||||
/// Resolve a single parameter value.
|
||||
///
|
||||
/// Given an individual parameter value and the associated parameter's
|
||||
|
@ -226,7 +250,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
.into());
|
||||
};
|
||||
|
||||
arg.coerce_to_type(self, param_config.param_type_name.clone())
|
||||
let type_name = param_config.param_type_name.clone();
|
||||
let param_type = self.resolve_type(type_name)?;
|
||||
|
||||
if let Some(param_type) = param_type {
|
||||
arg.coerce_to_type(self, param_type)
|
||||
} else {
|
||||
Ok(arg.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Statically resolve all of the parameters for a given method.
|
||||
|
@ -2606,8 +2637,13 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
|||
) -> Result<FrameControl<'gc>, Error> {
|
||||
let val = self.context.avm2.pop();
|
||||
let type_name = self.pool_multiname_static_any(method, index)?;
|
||||
let param_type = self.resolve_type(type_name)?;
|
||||
|
||||
let x = val.coerce_to_type(self, type_name)?;
|
||||
let x = if let Some(param_type) = param_type {
|
||||
val.coerce_to_type(self, param_type)?
|
||||
} else {
|
||||
val
|
||||
};
|
||||
|
||||
self.context.avm2.push(x);
|
||||
Ok(FrameControl::Continue)
|
||||
|
|
|
@ -295,20 +295,11 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
_superclass_object: Option<Object<'gc>>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
let class_name = self
|
||||
.as_class()
|
||||
.ok_or("Attempted to cast to class object that is missing a class!")?
|
||||
.read()
|
||||
.name()
|
||||
.clone()
|
||||
.into();
|
||||
|
||||
log::error!("{:?}", class_name);
|
||||
arguments
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
.coerce_to_type(activation, class_name)
|
||||
.coerce_to_type(activation, self.into())
|
||||
}
|
||||
|
||||
fn call_init(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! AVM2 values
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::names::Multiname;
|
||||
use crate::avm2::names::Namespace;
|
||||
use crate::avm2::names::QName;
|
||||
use crate::avm2::object::{NamespaceObject, Object, PrimitiveObject, TObject};
|
||||
|
@ -572,33 +571,29 @@ impl<'gc> Value<'gc> {
|
|||
/// Coerce the value to another value by type name.
|
||||
///
|
||||
/// This function implements a handful of coercion rules that appear to be
|
||||
/// in use when parameters are typechecked. I suspect `op_coerce` also uses
|
||||
/// these, but I cannot typecheck this for certain.
|
||||
///
|
||||
/// If `type_name` matches a primitive type, additional primitive coercions
|
||||
/// will apply to the resulting value. This relies on the fact that you
|
||||
/// cannot redefine default types, so `int` always refers to an int.
|
||||
/// in use when parameters are typechecked. `op_coerce` appears to use
|
||||
/// these as well. If `class` is the class corresponding to a primitive
|
||||
/// type, then this function will coerce the given value to that type.
|
||||
///
|
||||
/// If the type is not coercible to the given type, an error is thrown.
|
||||
pub fn coerce_to_type(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
type_name: Multiname<'gc>,
|
||||
class: Object<'gc>,
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if !type_name.is_any() {
|
||||
if type_name.contains_name(&QName::new(Namespace::public(), "int")) {
|
||||
if Object::ptr_eq(class, activation.avm2().classes().int) {
|
||||
return Ok(self.coerce_to_i32(activation)?.into());
|
||||
}
|
||||
|
||||
if type_name.contains_name(&QName::new(Namespace::public(), "uint")) {
|
||||
if Object::ptr_eq(class, activation.avm2().classes().uint) {
|
||||
return Ok(self.coerce_to_u32(activation)?.into());
|
||||
}
|
||||
|
||||
if type_name.contains_name(&QName::new(Namespace::public(), "Number")) {
|
||||
if Object::ptr_eq(class, activation.avm2().classes().number) {
|
||||
return Ok(self.coerce_to_number(activation)?.into());
|
||||
}
|
||||
|
||||
if type_name.contains_name(&QName::new(Namespace::public(), "Boolean")) {
|
||||
if Object::ptr_eq(class, activation.avm2().classes().boolean) {
|
||||
return Ok(self.coerce_to_boolean().into());
|
||||
}
|
||||
|
||||
|
@ -606,29 +601,26 @@ impl<'gc> Value<'gc> {
|
|||
return Ok(Value::Null);
|
||||
}
|
||||
|
||||
if type_name.contains_name(&QName::new(Namespace::public(), "String")) {
|
||||
if Object::ptr_eq(class, activation.avm2().classes().string) {
|
||||
return Ok(self.coerce_to_string(activation)?.into());
|
||||
}
|
||||
|
||||
if let Ok(object) = self.coerce_to_object(activation) {
|
||||
let param_type = activation
|
||||
.scope()
|
||||
.ok_or("Cannot resolve parameter types without a scope stack")?
|
||||
.write(activation.context.gc_context)
|
||||
.resolve(&type_name, activation)?
|
||||
.ok_or_else(|| format!("Could not resolve parameter type {:?}", type_name))?
|
||||
.coerce_to_object(activation)?;
|
||||
|
||||
if object.is_of_type(param_type)? {
|
||||
if object.is_of_type(class)? {
|
||||
return Ok(object.into());
|
||||
}
|
||||
|
||||
return Err(format!("Cannot coerce {:?} to an {:?}", self, type_name).into());
|
||||
}
|
||||
}
|
||||
|
||||
//type is unconstrained
|
||||
Ok(self.clone())
|
||||
if let Some(static_class) = class.as_class() {
|
||||
return Err(format!(
|
||||
"Cannot coerce {:?} to an {:?}",
|
||||
self,
|
||||
static_class.read().name()
|
||||
)
|
||||
.into());
|
||||
} else {
|
||||
return Err(format!("Cannot coerce {:?} to {:?}", self, class).into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if this value is any kind of number.
|
||||
|
|
Loading…
Reference in New Issue