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.
|
/// Resolve a single parameter value.
|
||||||
///
|
///
|
||||||
/// Given an individual parameter value and the associated parameter's
|
/// 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());
|
.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.
|
/// 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> {
|
) -> Result<FrameControl<'gc>, Error> {
|
||||||
let val = self.context.avm2.pop();
|
let val = self.context.avm2.pop();
|
||||||
let type_name = self.pool_multiname_static_any(method, index)?;
|
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);
|
self.context.avm2.push(x);
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
|
|
|
@ -295,20 +295,11 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
_superclass_object: Option<Object<'gc>>,
|
_superclass_object: Option<Object<'gc>>,
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> 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
|
arguments
|
||||||
.get(0)
|
.get(0)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Undefined)
|
.unwrap_or(Value::Undefined)
|
||||||
.coerce_to_type(activation, class_name)
|
.coerce_to_type(activation, self.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_init(
|
fn call_init(
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! AVM2 values
|
//! AVM2 values
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::names::Multiname;
|
|
||||||
use crate::avm2::names::Namespace;
|
use crate::avm2::names::Namespace;
|
||||||
use crate::avm2::names::QName;
|
use crate::avm2::names::QName;
|
||||||
use crate::avm2::object::{NamespaceObject, Object, PrimitiveObject, TObject};
|
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.
|
/// Coerce the value to another value by type name.
|
||||||
///
|
///
|
||||||
/// This function implements a handful of coercion rules that appear to be
|
/// This function implements a handful of coercion rules that appear to be
|
||||||
/// in use when parameters are typechecked. I suspect `op_coerce` also uses
|
/// in use when parameters are typechecked. `op_coerce` appears to use
|
||||||
/// these, but I cannot typecheck this for certain.
|
/// 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 `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.
|
|
||||||
///
|
///
|
||||||
/// If the type is not coercible to the given type, an error is thrown.
|
/// If the type is not coercible to the given type, an error is thrown.
|
||||||
pub fn coerce_to_type(
|
pub fn coerce_to_type(
|
||||||
&self,
|
&self,
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
type_name: Multiname<'gc>,
|
class: Object<'gc>,
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if !type_name.is_any() {
|
if Object::ptr_eq(class, activation.avm2().classes().int) {
|
||||||
if type_name.contains_name(&QName::new(Namespace::public(), "int")) {
|
|
||||||
return Ok(self.coerce_to_i32(activation)?.into());
|
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());
|
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());
|
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());
|
return Ok(self.coerce_to_boolean().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,29 +601,26 @@ impl<'gc> Value<'gc> {
|
||||||
return Ok(Value::Null);
|
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());
|
return Ok(self.coerce_to_string(activation)?.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(object) = self.coerce_to_object(activation) {
|
if let Ok(object) = self.coerce_to_object(activation) {
|
||||||
let param_type = activation
|
if object.is_of_type(class)? {
|
||||||
.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)? {
|
|
||||||
return Ok(object.into());
|
return Ok(object.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(format!("Cannot coerce {:?} to an {:?}", self, type_name).into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//type is unconstrained
|
if let Some(static_class) = class.as_class() {
|
||||||
Ok(self.clone())
|
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.
|
/// Determine if this value is any kind of number.
|
||||||
|
|
Loading…
Reference in New Issue