avm2: Replace most uses of `instance_of` with `instance_class`
Also store a Class -> alias mapping instead of a ClassObject -> alias mapping for AMF.
This commit is contained in:
parent
2b17b6ce20
commit
2c9028d2f9
|
@ -72,6 +72,7 @@ mod vtable;
|
|||
pub use crate::avm2::activation::Activation;
|
||||
pub use crate::avm2::array::ArrayStorage;
|
||||
pub use crate::avm2::call_stack::{CallNode, CallStack};
|
||||
pub use crate::avm2::class::Class;
|
||||
#[allow(unused)] // For debug_ui
|
||||
pub use crate::avm2::domain::{Domain, DomainPtr};
|
||||
pub use crate::avm2::error::Error;
|
||||
|
@ -179,7 +180,7 @@ pub struct Avm2<'gc> {
|
|||
orphan_objects: Rc<Vec<DisplayObjectWeak<'gc>>>,
|
||||
|
||||
alias_to_class_map: FnvHashMap<AvmString<'gc>, ClassObject<'gc>>,
|
||||
class_to_alias_map: FnvHashMap<ClassObject<'gc>, AvmString<'gc>>,
|
||||
class_to_alias_map: FnvHashMap<Class<'gc>, AvmString<'gc>>,
|
||||
|
||||
/// The api version of our root movie clip. Note - this is used as the
|
||||
/// api version for swfs loaded via `Loader`, overriding the api version
|
||||
|
@ -293,14 +294,15 @@ impl<'gc> Avm2<'gc> {
|
|||
|
||||
pub fn register_class_alias(&mut self, name: AvmString<'gc>, class_object: ClassObject<'gc>) {
|
||||
self.alias_to_class_map.insert(name, class_object);
|
||||
self.class_to_alias_map.insert(class_object, name);
|
||||
self.class_to_alias_map
|
||||
.insert(class_object.inner_class_definition(), name);
|
||||
}
|
||||
|
||||
pub fn get_class_by_alias(&self, name: AvmString<'gc>) -> Option<ClassObject<'gc>> {
|
||||
self.alias_to_class_map.get(&name).copied()
|
||||
}
|
||||
|
||||
pub fn get_alias_by_class(&self, cls: ClassObject<'gc>) -> Option<AvmString<'gc>> {
|
||||
pub fn get_alias_by_class(&self, cls: Class<'gc>) -> Option<AvmString<'gc>> {
|
||||
self.class_to_alias_map.get(&cls).copied()
|
||||
}
|
||||
|
||||
|
|
|
@ -1632,6 +1632,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
|
||||
fn op_get_outer_scope(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||
// Verifier ensures that this points to a valid outer scope
|
||||
|
||||
let scope = self.outer.get_unchecked(index as usize);
|
||||
|
||||
self.push_stack(scope.values());
|
||||
|
@ -1730,10 +1731,9 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
} else {
|
||||
// Even if it's an object with the "descendants" property, we won't support it.
|
||||
let class_name = object
|
||||
.instance_of()
|
||||
.instance_class()
|
||||
.map(|cls| {
|
||||
cls.inner_class_definition()
|
||||
.name()
|
||||
cls.name()
|
||||
.to_qualified_name_err_message(self.context.gc_context)
|
||||
})
|
||||
.unwrap_or_else(|| AvmString::from("<UNKNOWN>"));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::avm2::bytearray::ByteArrayStorage;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::object::{ByteArrayObject, ClassObject, TObject, VectorObject};
|
||||
use crate::avm2::vector::VectorStorage;
|
||||
use crate::avm2::ArrayObject;
|
||||
|
@ -72,7 +73,7 @@ pub fn serialize_value<'gc>(
|
|||
Some(AmfValue::ECMAArray(dense, sparse, len))
|
||||
} else if let Some(vec) = o.as_vector_storage() {
|
||||
let val_type = vec.value_type();
|
||||
if val_type == Some(activation.avm2().classes().int) {
|
||||
if val_type == Some(activation.avm2().classes().int.inner_class_definition()) {
|
||||
let int_vec: Vec<_> = vec
|
||||
.iter()
|
||||
.map(|v| {
|
||||
|
@ -81,7 +82,9 @@ pub fn serialize_value<'gc>(
|
|||
})
|
||||
.collect();
|
||||
Some(AmfValue::VectorInt(int_vec, vec.is_fixed()))
|
||||
} else if val_type == Some(activation.avm2().classes().uint) {
|
||||
} else if val_type
|
||||
== Some(activation.avm2().classes().uint.inner_class_definition())
|
||||
{
|
||||
let uint_vec: Vec<_> = vec
|
||||
.iter()
|
||||
.map(|v| {
|
||||
|
@ -90,7 +93,9 @@ pub fn serialize_value<'gc>(
|
|||
})
|
||||
.collect();
|
||||
Some(AmfValue::VectorUInt(uint_vec, vec.is_fixed()))
|
||||
} else if val_type == Some(activation.avm2().classes().number) {
|
||||
} else if val_type
|
||||
== Some(activation.avm2().classes().number.inner_class_definition())
|
||||
{
|
||||
let num_vec: Vec<_> = vec
|
||||
.iter()
|
||||
.map(|v| {
|
||||
|
@ -108,7 +113,8 @@ pub fn serialize_value<'gc>(
|
|||
})
|
||||
.collect();
|
||||
|
||||
let val_type = val_type.unwrap_or(activation.avm2().classes().object);
|
||||
let val_type = val_type
|
||||
.unwrap_or(activation.avm2().classes().object.inner_class_definition());
|
||||
|
||||
let name = class_to_alias(activation, val_type);
|
||||
Some(AmfValue::VectorObject(obj_vec, name, vec.is_fixed()))
|
||||
|
@ -125,11 +131,11 @@ pub fn serialize_value<'gc>(
|
|||
} else if let Some(bytearray) = o.as_bytearray() {
|
||||
Some(AmfValue::ByteArray(bytearray.bytes().to_vec()))
|
||||
} else {
|
||||
let class = o.instance_of().expect("Missing ClassObject");
|
||||
let class = o.instance_class().expect("Missing Class");
|
||||
let name = class_to_alias(activation, class);
|
||||
|
||||
let mut attributes = EnumSet::empty();
|
||||
if !class.inner_class_definition().is_sealed() {
|
||||
if !class.is_sealed() {
|
||||
attributes.insert(Attribute::Dynamic);
|
||||
}
|
||||
|
||||
|
@ -173,7 +179,7 @@ fn alias_to_class<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
fn class_to_alias<'gc>(activation: &mut Activation<'_, 'gc>, class: ClassObject<'gc>) -> String {
|
||||
fn class_to_alias<'gc>(activation: &mut Activation<'_, 'gc>, class: Class<'gc>) -> String {
|
||||
if let Some(alias) = activation.avm2().get_alias_by_class(class) {
|
||||
alias.to_string()
|
||||
} else {
|
||||
|
@ -359,7 +365,7 @@ pub fn deserialize_value<'gc>(
|
|||
let storage = VectorStorage::from_values(
|
||||
vec.iter().map(|v| (*v).into()).collect(),
|
||||
*is_fixed,
|
||||
Some(activation.avm2().classes().number),
|
||||
Some(activation.avm2().classes().number.inner_class_definition()),
|
||||
);
|
||||
VectorObject::from_vector(storage, activation)?.into()
|
||||
}
|
||||
|
@ -367,7 +373,7 @@ pub fn deserialize_value<'gc>(
|
|||
let storage = VectorStorage::from_values(
|
||||
vec.iter().map(|v| (*v).into()).collect(),
|
||||
*is_fixed,
|
||||
Some(activation.avm2().classes().uint),
|
||||
Some(activation.avm2().classes().uint.inner_class_definition()),
|
||||
);
|
||||
VectorObject::from_vector(storage, activation)?.into()
|
||||
}
|
||||
|
@ -375,7 +381,7 @@ pub fn deserialize_value<'gc>(
|
|||
let storage = VectorStorage::from_values(
|
||||
vec.iter().map(|v| (*v).into()).collect(),
|
||||
*is_fixed,
|
||||
Some(activation.avm2().classes().int),
|
||||
Some(activation.avm2().classes().int.inner_class_definition()),
|
||||
);
|
||||
VectorObject::from_vector(storage, activation)?.into()
|
||||
}
|
||||
|
@ -397,7 +403,7 @@ pub fn deserialize_value<'gc>(
|
|||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
*is_fixed,
|
||||
Some(class),
|
||||
Some(class.inner_class_definition()),
|
||||
);
|
||||
VectorObject::from_vector(storage, activation)?.into()
|
||||
}
|
||||
|
|
|
@ -104,6 +104,9 @@ pub struct ClassData<'gc> {
|
|||
/// superinterfaces, nor interfaces implemented by the superclass.
|
||||
direct_interfaces: Vec<Class<'gc>>,
|
||||
|
||||
/// The list of all interfaces implemented by this class.
|
||||
all_interfaces: Vec<Class<'gc>>,
|
||||
|
||||
/// The instance allocator for this class.
|
||||
///
|
||||
/// If `None`, then instances of this object will be allocated the same way
|
||||
|
@ -163,7 +166,7 @@ pub struct ClassData<'gc> {
|
|||
/// Maps a type parameter to the application of this class with that parameter.
|
||||
///
|
||||
/// Only applicable if this class is generic.
|
||||
applications: FnvHashMap<Option<ClassKey<'gc>>, Class<'gc>>,
|
||||
applications: FnvHashMap<Option<Class<'gc>>, Class<'gc>>,
|
||||
|
||||
/// Whether or not this is a system-defined class.
|
||||
///
|
||||
|
@ -184,32 +187,20 @@ impl PartialEq for Class<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for Class<'_> {}
|
||||
|
||||
impl Hash for Class<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.as_ptr().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> core::fmt::Debug for Class<'gc> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
f.debug_struct("Class").field("name", &self.name()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows using a `Class<'gc>` as a HashMap key,
|
||||
/// using the pointer address for hashing/equality.
|
||||
#[derive(Collect, Copy, Clone)]
|
||||
#[collect(no_drop)]
|
||||
struct ClassKey<'gc>(Class<'gc>);
|
||||
|
||||
impl PartialEq for ClassKey<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ClassKey<'_> {}
|
||||
|
||||
impl Hash for ClassKey<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0 .0.as_ptr().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Class<'gc> {
|
||||
/// Create a new class.
|
||||
///
|
||||
|
@ -237,6 +228,7 @@ impl<'gc> Class<'gc> {
|
|||
attributes: ClassAttributes::empty(),
|
||||
protected_namespace: None,
|
||||
direct_interfaces: Vec::new(),
|
||||
all_interfaces: Vec::new(),
|
||||
instance_allocator: None,
|
||||
instance_init,
|
||||
native_instance_init,
|
||||
|
@ -255,8 +247,7 @@ impl<'gc> Class<'gc> {
|
|||
}
|
||||
|
||||
pub fn add_application(self, mc: &Mutation<'gc>, param: Option<Class<'gc>>, cls: Class<'gc>) {
|
||||
let key = param.map(ClassKey);
|
||||
self.0.write(mc).applications.insert(key, cls);
|
||||
self.0.write(mc).applications.insert(param, cls);
|
||||
}
|
||||
|
||||
/// Apply type parameters to an existing class.
|
||||
|
@ -271,9 +262,7 @@ impl<'gc> Class<'gc> {
|
|||
let mc = context.gc_context;
|
||||
let this_read = this.0.read();
|
||||
|
||||
let key = param.map(ClassKey);
|
||||
|
||||
if let Some(application) = this_read.applications.get(&key) {
|
||||
if let Some(application) = this_read.applications.get(¶m) {
|
||||
return *application;
|
||||
}
|
||||
|
||||
|
@ -312,7 +301,7 @@ impl<'gc> Class<'gc> {
|
|||
|
||||
drop(this_read);
|
||||
|
||||
this.0.write(mc).applications.insert(key, new_class);
|
||||
this.0.write(mc).applications.insert(Some(param), new_class);
|
||||
new_class
|
||||
}
|
||||
|
||||
|
@ -464,6 +453,7 @@ impl<'gc> Class<'gc> {
|
|||
attributes,
|
||||
protected_namespace,
|
||||
direct_interfaces: interfaces,
|
||||
all_interfaces: Vec::new(),
|
||||
instance_allocator,
|
||||
instance_init,
|
||||
native_instance_init,
|
||||
|
@ -658,7 +648,7 @@ impl<'gc> Class<'gc> {
|
|||
// interfaces (i.e. those that were not already implemented by the superclass)
|
||||
// Otherwise, our behavior diverges from Flash Player in certain cases.
|
||||
// See the ignored test 'tests/tests/swfs/avm2/weird_superinterface_properties/'
|
||||
for interface in interfaces {
|
||||
for interface in &interfaces {
|
||||
for interface_trait in &*interface.instance_traits() {
|
||||
if !interface_trait.name().namespace().is_public() {
|
||||
let public_name = QName::new(
|
||||
|
@ -674,6 +664,8 @@ impl<'gc> Class<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
self.0.write(context.gc_context).all_interfaces = interfaces;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -704,6 +696,7 @@ impl<'gc> Class<'gc> {
|
|||
attributes: ClassAttributes::empty(),
|
||||
protected_namespace: None,
|
||||
direct_interfaces: Vec::new(),
|
||||
all_interfaces: Vec::new(),
|
||||
instance_allocator: None,
|
||||
instance_init: Method::from_builtin(
|
||||
|_, _, _| Ok(Value::Undefined),
|
||||
|
@ -737,6 +730,36 @@ impl<'gc> Class<'gc> {
|
|||
Ok(class)
|
||||
}
|
||||
|
||||
/// Determine if this class has a given type in its superclass chain.
|
||||
///
|
||||
/// The given class `test_class` should be either a superclass or
|
||||
/// interface we are checking against this class.
|
||||
///
|
||||
/// To test if a class *instance* is of a given type, see `Object::is_of_type`.
|
||||
pub fn has_class_in_chain(self, test_class: Class<'gc>) -> bool {
|
||||
let mut my_class = Some(self);
|
||||
|
||||
while let Some(class) = my_class {
|
||||
if class == test_class {
|
||||
return true;
|
||||
}
|
||||
|
||||
my_class = class.super_class()
|
||||
}
|
||||
|
||||
// A `Class` stores all of the interfaces it implements, including
|
||||
// those from superinterfaces and superclasses (recursively).
|
||||
if test_class.is_interface() {
|
||||
for interface in &*self.all_interfaces() {
|
||||
if *interface == test_class {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn instance_vtable(self) -> VTable<'gc> {
|
||||
self.0.read().instance_vtable
|
||||
}
|
||||
|
@ -749,6 +772,26 @@ impl<'gc> Class<'gc> {
|
|||
self.0.try_read().map(|r| r.name)
|
||||
}
|
||||
|
||||
/// Attempts to obtain the name of this class.
|
||||
/// If we are unable to read from the necessary `GcCell`,
|
||||
/// the returned value will be some kind of error message.
|
||||
///
|
||||
/// This should only be used in a debug context, where
|
||||
/// we need infallible access to *something* to print
|
||||
/// out.
|
||||
pub fn debug_name(self) -> Box<dyn fmt::Debug + 'gc> {
|
||||
let class_name = self.try_name();
|
||||
|
||||
match class_name {
|
||||
Ok(class_name) => Box::new(class_name),
|
||||
Err(err) => Box::new(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn param(self) -> Option<Option<Class<'gc>>> {
|
||||
self.0.read().param
|
||||
}
|
||||
|
||||
pub fn set_param(self, mc: &Mutation<'gc>, param: Option<Option<Class<'gc>>>) {
|
||||
self.0.write(mc).param = param;
|
||||
}
|
||||
|
@ -1079,6 +1122,10 @@ impl<'gc> Class<'gc> {
|
|||
Ref::map(self.0.read(), |c| &c.direct_interfaces)
|
||||
}
|
||||
|
||||
pub fn all_interfaces(&self) -> Ref<Vec<Class<'gc>>> {
|
||||
Ref::map(self.0.read(), |c| &c.all_interfaces)
|
||||
}
|
||||
|
||||
/// Determine if this class is sealed (no dynamic properties)
|
||||
pub fn is_sealed(self) -> bool {
|
||||
self.0.read().attributes.contains(ClassAttributes::SEALED)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use ruffle_wstr::WString;
|
||||
|
||||
use crate::avm2::object::TObject;
|
||||
use crate::avm2::Activation;
|
||||
use crate::avm2::AvmString;
|
||||
use crate::avm2::Multiname;
|
||||
use crate::avm2::Value;
|
||||
use crate::avm2::{Activation, AvmString, Class, Multiname, Value};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
use std::mem::size_of;
|
||||
|
@ -94,13 +91,12 @@ pub fn make_reference_error<'gc>(
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
code: ReferenceErrorCode,
|
||||
multiname: &Multiname<'gc>,
|
||||
object_class: Option<ClassObject<'gc>>,
|
||||
object_class: Option<Class<'gc>>,
|
||||
) -> Error<'gc> {
|
||||
let qualified_name = multiname.as_uri(activation.context.gc_context);
|
||||
let class_name = object_class
|
||||
.map(|cls| {
|
||||
cls.inner_class_definition()
|
||||
.name()
|
||||
cls.name()
|
||||
.to_qualified_name_err_message(activation.context.gc_context)
|
||||
})
|
||||
.unwrap_or_else(|| AvmString::from("<UNKNOWN>"));
|
||||
|
|
|
@ -419,18 +419,17 @@ fn vector_class<'gc>(
|
|||
let mc = activation.context.gc_context;
|
||||
let (_, global, mut domain) = script.init();
|
||||
|
||||
let cls = param_class.map(|c| c.inner_class_definition());
|
||||
let param_class = param_class.map(|c| c.inner_class_definition());
|
||||
let vector_cls = class(
|
||||
vector::create_builtin_class(activation, cls),
|
||||
vector::create_builtin_class(activation, param_class),
|
||||
script,
|
||||
activation,
|
||||
)?;
|
||||
vector_cls.set_param(mc, Some(param_class));
|
||||
|
||||
let generic_vector = activation.avm2().classes().generic_vector;
|
||||
generic_vector.add_application(mc, param_class, vector_cls);
|
||||
let generic_cls = generic_vector.inner_class_definition();
|
||||
generic_cls.add_application(mc, cls, vector_cls.inner_class_definition());
|
||||
generic_cls.add_application(mc, param_class, vector_cls.inner_class_definition());
|
||||
|
||||
let legacy_name = QName::new(activation.avm2().vector_internal_namespace, legacy_name);
|
||||
global.install_const_late(
|
||||
|
|
|
@ -92,7 +92,7 @@ pub fn init<'gc>(
|
|||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
// We set the underlying BitmapData instance - we start out with a dummy BitmapDataWrapper,
|
||||
// which makes custom classes see a disposed BitmapData before they call super()
|
||||
let name = this.instance_of_class_definition().map(|c| c.name());
|
||||
let name = this.instance_class().map(|c| c.name());
|
||||
let character = this
|
||||
.instance_of()
|
||||
.and_then(|t| {
|
||||
|
@ -374,7 +374,7 @@ pub fn get_vector<'gc>(
|
|||
height,
|
||||
);
|
||||
|
||||
let value_type = activation.avm2().classes().uint;
|
||||
let value_type = activation.avm2().classes().uint.inner_class_definition();
|
||||
let new_storage = VectorStorage::from_values(pixels, false, Some(value_type));
|
||||
|
||||
return Ok(VectorObject::from_vector(new_storage, activation)?.into());
|
||||
|
|
|
@ -1166,7 +1166,11 @@ pub fn read_graphics_data<'gc>(
|
|||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
avm2_stub_method!(activation, "flash.display.Graphics", "readGraphicsData");
|
||||
let value_type = activation.avm2().classes().igraphicsdata;
|
||||
let value_type = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.igraphicsdata
|
||||
.inner_class_definition();
|
||||
let new_storage = VectorStorage::new(0, false, Some(value_type), activation);
|
||||
Ok(VectorObject::from_vector(new_storage, activation)?.into())
|
||||
}
|
||||
|
@ -1298,17 +1302,41 @@ fn handle_igraphics_data<'gc>(
|
|||
drawing: &mut Drawing,
|
||||
obj: &Object<'gc>,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let class = obj.instance_of().expect("No class");
|
||||
let class = obj.instance_class().expect("No class");
|
||||
|
||||
if class == activation.avm2().classes().graphicsbitmapfill {
|
||||
if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsbitmapfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_bitmap_fill(activation, drawing, obj)?;
|
||||
drawing.set_fill_style(Some(style));
|
||||
} else if class == activation.avm2().classes().graphicsendfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsendfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
drawing.set_fill_style(None);
|
||||
} else if class == activation.avm2().classes().graphicsgradientfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsgradientfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_gradient_fill(activation, obj)?;
|
||||
drawing.set_fill_style(Some(style));
|
||||
} else if class == activation.avm2().classes().graphicspath {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicspath
|
||||
.inner_class_definition()
|
||||
{
|
||||
let commands = obj
|
||||
.get_public_property("commands", activation)?
|
||||
.coerce_to_object(activation)?;
|
||||
|
@ -1330,13 +1358,31 @@ fn handle_igraphics_data<'gc>(
|
|||
.expect("commands is not a Vector"),
|
||||
&data.as_vector_storage().expect("data is not a Vector"),
|
||||
)?;
|
||||
} else if class == activation.avm2().classes().graphicssolidfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicssolidfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_solid_fill(activation, obj)?;
|
||||
drawing.set_fill_style(Some(style));
|
||||
} else if class == activation.avm2().classes().graphicsshaderfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsshaderfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
tracing::warn!("Graphics shader fill unimplemented {:?}", class);
|
||||
drawing.set_fill_style(None);
|
||||
} else if class == activation.avm2().classes().graphicsstroke {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsstroke
|
||||
.inner_class_definition()
|
||||
{
|
||||
let thickness = obj
|
||||
.get_public_property("thickness", activation)?
|
||||
.coerce_to_number(activation)?;
|
||||
|
@ -1392,7 +1438,13 @@ fn handle_igraphics_data<'gc>(
|
|||
|
||||
drawing.set_line_style(Some(line_style));
|
||||
}
|
||||
} else if class == activation.avm2().classes().graphicstrianglepath {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicstrianglepath
|
||||
.inner_class_definition()
|
||||
{
|
||||
handle_graphics_triangle_path(activation, drawing, obj)?;
|
||||
} else {
|
||||
panic!("Unknown graphics data class {:?}", class);
|
||||
|
@ -1528,20 +1580,50 @@ fn handle_igraphics_fill<'gc>(
|
|||
drawing: &mut Drawing,
|
||||
obj: &Object<'gc>,
|
||||
) -> Result<Option<FillStyle>, Error<'gc>> {
|
||||
let class = obj.instance_of().expect("No class");
|
||||
let class = obj.instance_class().expect("No class");
|
||||
|
||||
if class == activation.avm2().classes().graphicsbitmapfill {
|
||||
if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsbitmapfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_bitmap_fill(activation, drawing, obj)?;
|
||||
Ok(Some(style))
|
||||
} else if class == activation.avm2().classes().graphicsendfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsendfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
Ok(None)
|
||||
} else if class == activation.avm2().classes().graphicsgradientfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsgradientfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_gradient_fill(activation, obj)?;
|
||||
Ok(Some(style))
|
||||
} else if class == activation.avm2().classes().graphicssolidfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicssolidfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
let style = handle_solid_fill(activation, obj)?;
|
||||
Ok(Some(style))
|
||||
} else if class == activation.avm2().classes().graphicsshaderfill {
|
||||
} else if class
|
||||
== activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.graphicsshaderfill
|
||||
.inner_class_definition()
|
||||
{
|
||||
tracing::warn!("Graphics shader fill unimplemented {:?}", class);
|
||||
Ok(None)
|
||||
} else {
|
||||
|
|
|
@ -423,7 +423,7 @@ pub fn get_stage3ds<'gc>(
|
|||
.map(|obj| Value::Object(*obj))
|
||||
.collect(),
|
||||
false,
|
||||
Some(activation.avm2().classes().stage3d),
|
||||
Some(activation.avm2().classes().stage3d.inner_class_definition()),
|
||||
);
|
||||
let stage3ds = VectorObject::from_vector(storage, activation)?;
|
||||
return Ok(stage3ds.into());
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn attach_net_stream<'gc>(
|
|||
return Err(format!(
|
||||
"Cannot use value of type {:?} as video source",
|
||||
source
|
||||
.and_then(|o| o.instance_of_class_definition())
|
||||
.and_then(|o| o.instance_class())
|
||||
.map(|c| c.name().local_name())
|
||||
.unwrap_or_else(|| "Object".into())
|
||||
)
|
||||
|
|
|
@ -123,7 +123,7 @@ pub fn get_qualified_definition_names<'gc>(
|
|||
.map(|name| Value::String(name.to_qualified_name(activation.context.gc_context)))
|
||||
.collect(),
|
||||
false,
|
||||
Some(activation.avm2().classes().string),
|
||||
Some(activation.avm2().classes().string.inner_class_definition()),
|
||||
);
|
||||
|
||||
let name_vector = VectorObject::from_vector(storage, activation)?;
|
||||
|
|
|
@ -199,15 +199,14 @@ pub fn get_qualified_class_name<'gc>(
|
|||
let obj = val.coerce_to_object(activation)?;
|
||||
|
||||
let class = match obj.as_class_object() {
|
||||
Some(class) => class,
|
||||
None => match obj.instance_of() {
|
||||
Some(class) => class.inner_class_definition(),
|
||||
None => match obj.instance_class() {
|
||||
Some(cls) => cls,
|
||||
None => return Ok(Value::Null),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(class
|
||||
.inner_class_definition()
|
||||
.name()
|
||||
.to_qualified_name(activation.context.gc_context)
|
||||
.into())
|
||||
|
@ -225,16 +224,15 @@ pub fn get_qualified_superclass_name<'gc>(
|
|||
.coerce_to_object(activation)?;
|
||||
|
||||
let class = match obj.as_class_object() {
|
||||
Some(class) => class,
|
||||
None => match obj.instance_of() {
|
||||
Some(class) => class.inner_class_definition(),
|
||||
None => match obj.instance_class() {
|
||||
Some(cls) => cls,
|
||||
None => return Ok(Value::Null),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(super_class) = class.superclass_object() {
|
||||
if let Some(super_class) = class.super_class() {
|
||||
Ok(super_class
|
||||
.inner_class_definition()
|
||||
.name()
|
||||
.to_qualified_name(activation.context.gc_context)
|
||||
.into())
|
||||
|
|
|
@ -74,13 +74,14 @@ fn class_call<'gc>(
|
|||
|
||||
let this_class = activation.subclass_object().unwrap();
|
||||
let value_type = this_class
|
||||
.as_class_params()
|
||||
.inner_class_definition()
|
||||
.param()
|
||||
.ok_or("Cannot convert to unparametrized Vector")?; // technically unreachable
|
||||
|
||||
let arg = args.get(0).cloned().unwrap();
|
||||
let arg = arg.as_object().ok_or("Cannot convert to Vector")?;
|
||||
|
||||
if arg.instance_of() == Some(this_class) {
|
||||
if arg.instance_class() == Some(this_class.inner_class_definition()) {
|
||||
return Ok(arg.into());
|
||||
}
|
||||
|
||||
|
@ -91,9 +92,7 @@ fn class_call<'gc>(
|
|||
let mut new_storage = VectorStorage::new(0, false, value_type, activation);
|
||||
new_storage.reserve_exact(length as usize);
|
||||
|
||||
let value_type_for_coercion = new_storage
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let value_type_for_coercion = new_storage.value_type_for_coercion(activation);
|
||||
|
||||
let mut iter = ArrayIter::new(activation, arg)?;
|
||||
|
||||
|
@ -248,9 +247,7 @@ pub fn concat<'gc>(
|
|||
return Err("Not a vector-structured object".into());
|
||||
};
|
||||
|
||||
let val_class = new_vector_storage
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let val_class = new_vector_storage.value_type_for_coercion(activation);
|
||||
|
||||
for arg in args {
|
||||
let arg_obj = arg
|
||||
|
@ -260,10 +257,10 @@ pub fn concat<'gc>(
|
|||
// this is Vector.<int/uint/Number/*>
|
||||
let my_base_vector_class = activation
|
||||
.subclass_object()
|
||||
.expect("Method call without bound class?");
|
||||
if !arg.is_of_type(activation, my_base_vector_class.inner_class_definition()) {
|
||||
.expect("Method call without bound class?")
|
||||
.inner_class_definition();
|
||||
if !arg.is_of_type(activation, my_base_vector_class) {
|
||||
let base_vector_name = my_base_vector_class
|
||||
.inner_class_definition()
|
||||
.name()
|
||||
.to_qualified_name_err_message(activation.context.gc_context);
|
||||
|
||||
|
@ -289,7 +286,7 @@ pub fn concat<'gc>(
|
|||
if let Ok(val_obj) = val.coerce_to_object(activation) {
|
||||
if !val.is_of_type(activation, val_class) {
|
||||
let other_val_class = val_obj
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.ok_or("TypeError: Tried to concat a bare object into a Vector")?;
|
||||
return Err(format!(
|
||||
"TypeError: Cannot coerce Vector value of type {:?} to type {:?}",
|
||||
|
@ -449,9 +446,9 @@ pub fn filter<'gc>(
|
|||
let receiver = args.get(1).cloned().unwrap_or(Value::Null);
|
||||
|
||||
let value_type = this
|
||||
.instance_of()
|
||||
.instance_class()
|
||||
.unwrap()
|
||||
.as_class_params()
|
||||
.param()
|
||||
.ok_or("Cannot filter unparameterized vector")?; // technically unreachable
|
||||
let mut new_storage = VectorStorage::new(0, false, value_type, activation);
|
||||
let mut iter = ArrayIter::new(activation, this)?;
|
||||
|
@ -578,14 +575,12 @@ pub fn map<'gc>(
|
|||
let receiver = args.get(1).cloned().unwrap_or(Value::Null);
|
||||
|
||||
let value_type = this
|
||||
.instance_of()
|
||||
.instance_class()
|
||||
.unwrap()
|
||||
.as_class_params()
|
||||
.param()
|
||||
.ok_or("Cannot filter unparameterized vector")?; // technically unreachable
|
||||
let mut new_storage = VectorStorage::new(0, false, value_type, activation);
|
||||
let value_type_for_coercion = new_storage
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let value_type_for_coercion = new_storage.value_type_for_coercion(activation);
|
||||
let mut iter = ArrayIter::new(activation, this)?;
|
||||
|
||||
while let Some(r) = iter.next(activation) {
|
||||
|
@ -620,9 +615,7 @@ pub fn push<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(mut vs) = this.as_vector_storage_mut(activation.context.gc_context) {
|
||||
let value_type = vs
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let value_type = vs.value_type_for_coercion(activation);
|
||||
|
||||
// Pushing nothing will still throw if the Vector is fixed.
|
||||
vs.check_fixed(activation)?;
|
||||
|
@ -659,9 +652,7 @@ pub fn unshift<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(mut vs) = this.as_vector_storage_mut(activation.context.gc_context) {
|
||||
let value_type = vs
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let value_type = vs.value_type_for_coercion(activation);
|
||||
|
||||
for arg in args.iter().rev() {
|
||||
let coerced_arg = arg.coerce_to_type(activation, value_type)?;
|
||||
|
@ -687,9 +678,9 @@ pub fn insert_at<'gc>(
|
|||
.cloned()
|
||||
.unwrap_or(Value::Undefined)
|
||||
.coerce_to_i32(activation)?;
|
||||
let value_type = vs
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
|
||||
let value_type = vs.value_type_for_coercion(activation);
|
||||
|
||||
let value = args
|
||||
.get(1)
|
||||
.cloned()
|
||||
|
@ -877,9 +868,7 @@ pub fn splice<'gc>(
|
|||
.unwrap_or(Value::Undefined)
|
||||
.coerce_to_i32(activation)?;
|
||||
let value_type = vs.value_type();
|
||||
let value_type_for_coercion = vs
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let value_type_for_coercion = vs.value_type_for_coercion(activation);
|
||||
|
||||
let start = vs.clamp_parameter_index(start_len);
|
||||
let end = max(
|
||||
|
|
|
@ -268,7 +268,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::ReadFromWriteOnly,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
None => self.get_property_local(multiname, activation),
|
||||
|
@ -359,7 +359,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::AssignToMethod,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
Some(Property::Virtual { set: Some(set), .. }) => {
|
||||
|
@ -370,7 +370,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::WriteToReadOnly,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
None => self.set_property_local(multiname, value, activation),
|
||||
|
@ -437,7 +437,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::AssignToMethod,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
Some(Property::Virtual { set: Some(set), .. }) => {
|
||||
|
@ -448,7 +448,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::WriteToReadOnly,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
None => self.init_property_local(multiname, value, activation),
|
||||
|
@ -518,7 +518,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::ReadFromWriteOnly,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
None => self.call_property_local(multiname, arguments, activation),
|
||||
|
@ -705,14 +705,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidDelete,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
|
||||
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
|
||||
None => {
|
||||
if self
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.map(|c| c.is_sealed())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
@ -968,7 +968,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// coercions.
|
||||
fn to_string(&self, activation: &mut Activation<'_, 'gc>) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let class_name = self
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.map(|c| c.name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
@ -988,7 +988,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let class_name = self
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.map(|c| c.name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
@ -1086,7 +1086,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// The given object should be the class object for the given type we are
|
||||
/// checking against this object.
|
||||
fn is_of_type(&self, test_class: Class<'gc>, context: &mut UpdateContext<'_, 'gc>) -> bool {
|
||||
let my_class = self.instance_of();
|
||||
let my_class = self.instance_class();
|
||||
|
||||
// ES3 objects are not class instances but are still treated as
|
||||
// instances of Object, which is an ES4 class.
|
||||
|
@ -1123,13 +1123,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
}
|
||||
|
||||
/// Get this object's class's `Class`, if it has one.
|
||||
fn instance_of_class_definition(&self) -> Option<Class<'gc>> {
|
||||
fn instance_class(&self) -> Option<Class<'gc>> {
|
||||
self.instance_of().map(|cls| cls.inner_class_definition())
|
||||
}
|
||||
|
||||
/// Get this object's class's name, formatted for debug output.
|
||||
fn instance_of_class_name(&self, mc: &Mutation<'gc>) -> AvmString<'gc> {
|
||||
self.instance_of_class_definition()
|
||||
self.instance_class()
|
||||
.map(|r| r.name().to_qualified_name(mc))
|
||||
.unwrap_or_else(|| "<Unknown type>".into())
|
||||
}
|
||||
|
|
|
@ -72,14 +72,6 @@ pub struct ClassObjectData<'gc> {
|
|||
/// If None, a simple coercion is done.
|
||||
call_handler: Option<Method<'gc>>,
|
||||
|
||||
/// The parameters of this specialized class.
|
||||
///
|
||||
/// None flags that this class has not been specialized.
|
||||
///
|
||||
/// An individual parameter of `None` signifies the parameter `*`, which is
|
||||
/// represented in AVM2 as `null` with regards to type application.
|
||||
params: Option<Option<ClassObject<'gc>>>,
|
||||
|
||||
/// List of all applications of this class.
|
||||
///
|
||||
/// Only applicable if this class is generic.
|
||||
|
@ -88,7 +80,7 @@ pub struct ClassObjectData<'gc> {
|
|||
/// as `None` here. AVM2 considers both applications to be separate
|
||||
/// classes, though we consider the parameter to be the class `Object` when
|
||||
/// we get a param of `null`.
|
||||
applications: FnvHashMap<Option<ClassObject<'gc>>, ClassObject<'gc>>,
|
||||
applications: FnvHashMap<Option<Class<'gc>>, ClassObject<'gc>>,
|
||||
|
||||
/// Interfaces implemented by this class, including interfaces
|
||||
/// from parent classes and superinterfaces (recursively).
|
||||
|
@ -212,7 +204,6 @@ impl<'gc> ClassObject<'gc> {
|
|||
constructor: class.instance_init(),
|
||||
native_constructor: class.native_instance_init(),
|
||||
call_handler: class.call_handler(),
|
||||
params: None,
|
||||
applications: Default::default(),
|
||||
interfaces: Vec::new(),
|
||||
instance_vtable: VTable::empty(activation.context.gc_context),
|
||||
|
@ -429,39 +420,6 @@ impl<'gc> ClassObject<'gc> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine if this class has a given type in its superclass chain.
|
||||
///
|
||||
/// The given object `test_class` should be either a superclass or
|
||||
/// interface we are checking against this class.
|
||||
///
|
||||
/// To test if a class *instance* is of a given type, see is_of_type.
|
||||
pub fn has_class_in_chain(self, test_class: Class<'gc>) -> bool {
|
||||
let mut my_class = Some(self);
|
||||
|
||||
while let Some(class) = my_class {
|
||||
if class.inner_class_definition() == test_class {
|
||||
return true;
|
||||
}
|
||||
|
||||
my_class = class.superclass_object()
|
||||
}
|
||||
|
||||
// A `ClassObject` stores all of the interfaces it implements,
|
||||
// including those from superinterfaces and superclasses (recursively).
|
||||
// Therefore, we only need to check interfaces once, and we can skip
|
||||
// checking them when we processing superclasses in the `while`
|
||||
// further down in this method.
|
||||
if test_class.is_interface() {
|
||||
for interface in self.interfaces() {
|
||||
if interface == test_class {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Call the instance initializer.
|
||||
pub fn call_init(
|
||||
self,
|
||||
|
@ -722,12 +680,46 @@ impl<'gc> ClassObject<'gc> {
|
|||
pub fn add_application(
|
||||
&self,
|
||||
gc_context: &Mutation<'gc>,
|
||||
param: Option<ClassObject<'gc>>,
|
||||
param: Option<Class<'gc>>,
|
||||
cls: ClassObject<'gc>,
|
||||
) {
|
||||
self.0.write(gc_context).applications.insert(param, cls);
|
||||
}
|
||||
|
||||
/// Parametrize this class. This does not check to ensure that this class is generic.
|
||||
pub fn parametrize(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
class_param: Option<Class<'gc>>,
|
||||
) -> Result<ClassObject<'gc>, Error<'gc>> {
|
||||
let self_class = self.inner_class_definition();
|
||||
|
||||
if let Some(application) = self.0.read().applications.get(&class_param) {
|
||||
return Ok(*application);
|
||||
}
|
||||
|
||||
// if it's not a known application, then it's not int/uint/Number/*,
|
||||
// so it must be a simple Vector.<*>-derived class.
|
||||
|
||||
let parameterized_class =
|
||||
Class::with_type_param(&mut activation.context, self_class, class_param);
|
||||
|
||||
// NOTE: this isn't fully accurate, but much simpler.
|
||||
// FP's Vector is more of special case that literally copies some parent class's properties
|
||||
// main example: Vector.<Object>.prototype === Vector.<*>.prototype
|
||||
|
||||
let vector_star_cls = activation.avm2().classes().object_vector;
|
||||
let class_object =
|
||||
Self::from_class(activation, parameterized_class, Some(vector_star_cls))?;
|
||||
|
||||
self.0
|
||||
.write(activation.context.gc_context)
|
||||
.applications
|
||||
.insert(class_param, class_object);
|
||||
|
||||
Ok(class_object)
|
||||
}
|
||||
|
||||
pub fn translation_unit(self) -> Option<TranslationUnit<'gc>> {
|
||||
if let Method::Bytecode(bc) = self.0.read().constructor {
|
||||
Some(bc.txunit)
|
||||
|
@ -781,14 +773,6 @@ impl<'gc> ClassObject<'gc> {
|
|||
self.0.read().superclass_object
|
||||
}
|
||||
|
||||
pub fn set_param(self, gc_context: &Mutation<'gc>, param: Option<Option<ClassObject<'gc>>>) {
|
||||
self.0.write(gc_context).params = param;
|
||||
}
|
||||
|
||||
pub fn as_class_params(self) -> Option<Option<ClassObject<'gc>>> {
|
||||
self.0.read().params
|
||||
}
|
||||
|
||||
fn instance_allocator(self) -> Option<AllocatorFn> {
|
||||
Some(self.0.read().instance_allocator.0)
|
||||
}
|
||||
|
@ -947,7 +931,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
Value::Null => None,
|
||||
v => Some(v),
|
||||
};
|
||||
let object_param = match object_param {
|
||||
let class_param = match object_param {
|
||||
None => None,
|
||||
Some(cls) => Some(
|
||||
cls.as_object()
|
||||
|
@ -958,38 +942,12 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
|
|||
"Cannot apply class {:?} with non-class parameter",
|
||||
self_class.name()
|
||||
)
|
||||
})?,
|
||||
})?
|
||||
.inner_class_definition(),
|
||||
),
|
||||
};
|
||||
|
||||
if let Some(application) = self.0.read().applications.get(&object_param) {
|
||||
return Ok(*application);
|
||||
}
|
||||
|
||||
// if it's not a known application, then it's not int/uint/Number/*,
|
||||
// so it must be a simple Vector.<*>-derived class.
|
||||
|
||||
let class_param = object_param.map(|c| c.inner_class_definition());
|
||||
|
||||
let parameterized_class =
|
||||
Class::with_type_param(&mut activation.context, self_class, class_param);
|
||||
|
||||
// NOTE: this isn't fully accurate, but much simpler.
|
||||
// FP's Vector is more of special case that literally copies some parent class's properties
|
||||
// main example: Vector.<Object>.prototype === Vector.<*>.prototype
|
||||
|
||||
let vector_star_cls = activation.avm2().classes().object_vector;
|
||||
let class_object =
|
||||
Self::from_class(activation, parameterized_class, Some(vector_star_cls))?;
|
||||
|
||||
class_object.0.write(activation.context.gc_context).params = Some(object_param);
|
||||
|
||||
self.0
|
||||
.write(activation.context.gc_context)
|
||||
.applications
|
||||
.insert(object_param, class_object);
|
||||
|
||||
Ok(class_object)
|
||||
self.parametrize(activation, class_param)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,8 @@ impl<'gc> ErrorObject<'gc> {
|
|||
.try_read()
|
||||
.map(|obj| {
|
||||
obj.base
|
||||
.instance_of()
|
||||
.map(|cls| cls.debug_class_name())
|
||||
.instance_class()
|
||||
.map(|cls| cls.debug_name())
|
||||
.unwrap_or_else(|| Box::new("None"))
|
||||
})
|
||||
.unwrap_or_else(|err| Box::new(err))
|
||||
|
|
|
@ -123,7 +123,7 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
|
|||
val @ Value::Integer(_) => Ok(val),
|
||||
_ => {
|
||||
let class_name = self
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.map(|c| c.name().local_name())
|
||||
.unwrap_or_else(|| "Object".into());
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Default AVM2 object impl
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::dynamic_map::{DynamicKey, DynamicMap};
|
||||
use crate::avm2::error;
|
||||
use crate::avm2::object::{ClassObject, FunctionObject, Object, ObjectPtr, TObject};
|
||||
|
@ -164,7 +165,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidRead,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -174,7 +175,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidRead,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
};
|
||||
|
||||
|
@ -205,7 +206,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidRead,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
} else {
|
||||
Ok(Value::Undefined)
|
||||
|
@ -223,7 +224,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidWrite,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -232,7 +233,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
activation,
|
||||
error::ReferenceErrorCode::InvalidWrite,
|
||||
multiname,
|
||||
self.instance_of(),
|
||||
self.instance_class(),
|
||||
));
|
||||
};
|
||||
|
||||
|
@ -394,14 +395,18 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
self.instance_of
|
||||
}
|
||||
|
||||
pub fn instance_class(&self) -> Option<Class<'gc>> {
|
||||
self.instance_of.map(|cls| cls.inner_class_definition())
|
||||
}
|
||||
|
||||
/// Get the vtable for this object, if it has one.
|
||||
pub fn vtable(&self) -> Option<VTable<'gc>> {
|
||||
self.vtable
|
||||
}
|
||||
|
||||
pub fn is_sealed(&self) -> bool {
|
||||
self.instance_of()
|
||||
.map(|cls| cls.inner_class_definition().is_sealed())
|
||||
self.instance_class()
|
||||
.map(|cls| cls.is_sealed())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
|
@ -416,9 +421,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
|
||||
pub fn debug_class_name(&self) -> Box<dyn std::fmt::Debug + 'gc> {
|
||||
let class_name = self
|
||||
.instance_of()
|
||||
.map(|class_obj| class_obj.debug_class_name());
|
||||
let class_name = self.instance_class().map(|cls| cls.debug_name());
|
||||
|
||||
match class_name {
|
||||
Some(class_name) => Box::new(class_name),
|
||||
|
|
|
@ -19,7 +19,8 @@ pub fn vector_allocator<'gc>(
|
|||
let base = ScriptObjectData::new(class);
|
||||
|
||||
let param_type = class
|
||||
.as_class_params()
|
||||
.inner_class_definition()
|
||||
.param()
|
||||
.ok_or("Cannot convert to unparametrized Vector")?;
|
||||
|
||||
Ok(VectorObject(GcCell::new(
|
||||
|
@ -65,10 +66,10 @@ impl<'gc> VectorObject<'gc> {
|
|||
vector: VectorStorage<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let value_type = vector.value_type().map(|o| o.into()).unwrap_or(Value::Null);
|
||||
let value_type = vector.value_type();
|
||||
let vector_class = activation.avm2().classes().generic_vector;
|
||||
|
||||
let applied_class = vector_class.apply(activation, &[value_type])?;
|
||||
let applied_class = vector_class.parametrize(activation, value_type)?;
|
||||
|
||||
let object: Object<'gc> = VectorObject(GcCell::new(
|
||||
activation.context.gc_context,
|
||||
|
@ -129,12 +130,7 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> {
|
|||
if name.contains_public_namespace() {
|
||||
if let Some(name) = name.local_name() {
|
||||
if let Ok(index) = name.parse::<usize>() {
|
||||
let type_of = self
|
||||
.0
|
||||
.read()
|
||||
.vector
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let type_of = self.0.read().vector.value_type_for_coercion(activation);
|
||||
let value = match value.coerce_to_type(activation, type_of)? {
|
||||
Value::Undefined => self.0.read().vector.default(activation),
|
||||
Value::Null => self.0.read().vector.default(activation),
|
||||
|
@ -165,12 +161,7 @@ impl<'gc> TObject<'gc> for VectorObject<'gc> {
|
|||
if name.contains_public_namespace() {
|
||||
if let Some(name) = name.local_name() {
|
||||
if let Ok(index) = name.parse::<usize>() {
|
||||
let type_of = self
|
||||
.0
|
||||
.read()
|
||||
.vector
|
||||
.value_type_for_coercion(activation)
|
||||
.inner_class_definition();
|
||||
let type_of = self.0.read().vector.value_type_for_coercion(activation);
|
||||
let value = match value.coerce_to_type(activation, type_of)? {
|
||||
Value::Undefined => self.0.read().vector.default(activation),
|
||||
Value::Null => self.0.read().vector.default(activation),
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::class::Class;
|
||||
use crate::avm2::method::{BytecodeMethod, ResolvedParamConfig};
|
||||
use crate::avm2::multiname::Multiname;
|
||||
use crate::avm2::object::{ClassObject, TObject};
|
||||
use crate::avm2::object::TObject;
|
||||
use crate::avm2::op::Op;
|
||||
use crate::avm2::property::Property;
|
||||
use crate::avm2::verify::JumpSources;
|
||||
|
@ -147,11 +147,6 @@ impl<'gc> Stack<'gc> {
|
|||
Self(Vec::new())
|
||||
}
|
||||
|
||||
fn push_class_object(&mut self, class: ClassObject<'gc>) {
|
||||
self.0
|
||||
.push(OptValue::of_type(class.inner_class_definition()));
|
||||
}
|
||||
|
||||
fn push_class(&mut self, class: Class<'gc>) {
|
||||
self.0.push(OptValue::of_type(class));
|
||||
}
|
||||
|
@ -867,7 +862,7 @@ pub fn optimize<'gc>(
|
|||
|
||||
stack_push_done = true;
|
||||
if let Some(class) = class {
|
||||
stack.push_class_object(class);
|
||||
stack.push_class(class);
|
||||
} else {
|
||||
stack.push_any();
|
||||
}
|
||||
|
@ -1292,8 +1287,8 @@ pub fn optimize<'gc>(
|
|||
let global_scope = outer_scope.get_unchecked(0);
|
||||
|
||||
stack_push_done = true;
|
||||
if let Some(class) = global_scope.values().instance_of() {
|
||||
stack.push_class_object(class);
|
||||
if let Some(class) = global_scope.values().instance_class() {
|
||||
stack.push_class(class);
|
||||
} else {
|
||||
stack.push_any();
|
||||
}
|
||||
|
@ -1310,7 +1305,7 @@ pub fn optimize<'gc>(
|
|||
if !outer_scope.is_empty() {
|
||||
let global_scope = outer_scope.get_unchecked(0);
|
||||
|
||||
if let Some(class) = global_scope.values().instance_of() {
|
||||
if let Some(class) = global_scope.values().instance_class() {
|
||||
let mut value_class =
|
||||
class.instance_vtable().slot_classes()[*slot_id as usize];
|
||||
let resolved_value_class = value_class.get_class(activation);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! Represents AVM2 scope chain resolution.
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::domain::Domain;
|
||||
use crate::avm2::object::{ClassObject, Object, TObject};
|
||||
use crate::avm2::object::{Object, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use crate::avm2::{Multiname, Namespace};
|
||||
|
@ -237,7 +238,7 @@ impl<'gc> ScopeChain<'gc> {
|
|||
pub fn get_entry_for_multiname(
|
||||
&self,
|
||||
multiname: &Multiname<'gc>,
|
||||
) -> Option<Option<(Option<ClassObject<'gc>>, u32)>> {
|
||||
) -> Option<Option<(Option<Class<'gc>>, u32)>> {
|
||||
if let Some(container) = self.container {
|
||||
for (index, scope) in container.scopes.iter().enumerate().skip(1).rev() {
|
||||
if scope.with() {
|
||||
|
@ -248,7 +249,7 @@ impl<'gc> ScopeChain<'gc> {
|
|||
|
||||
let values = scope.values();
|
||||
if values.has_trait(multiname) {
|
||||
return Some(Some((values.instance_of(), index as u32)));
|
||||
return Some(Some((values.instance_class(), index as u32)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -620,11 +620,7 @@ impl<'gc> Script<'gc> {
|
|||
|
||||
globals.vtable().unwrap().init_vtable(
|
||||
globals.instance_of(),
|
||||
globals
|
||||
.instance_of()
|
||||
.unwrap()
|
||||
.inner_class_definition()
|
||||
.protected_namespace(),
|
||||
globals.instance_class().unwrap().protected_namespace(),
|
||||
&self.traits()?,
|
||||
Some(scope),
|
||||
None,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Storage for AS3 Vectors
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::error::range_error;
|
||||
use crate::avm2::object::{ClassObject, Object};
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use gc_arena::Collect;
|
||||
|
@ -29,21 +29,21 @@ pub struct VectorStorage<'gc> {
|
|||
is_fixed: bool,
|
||||
|
||||
/// The allowed type of the contents of the vector, in the form of a class
|
||||
/// object. None represents a Vector.<*>.
|
||||
/// None represents a Vector.<*>.
|
||||
///
|
||||
/// Vector typing is enforced by one of two ways: either by generating
|
||||
/// exceptions on values that are not of the given type, or by coercing
|
||||
/// incorrectly typed values to the given type if possible. Values that do
|
||||
/// not coerce are replaced with the default value for the given value
|
||||
/// type.
|
||||
value_type: Option<ClassObject<'gc>>,
|
||||
value_type: Option<Class<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> VectorStorage<'gc> {
|
||||
pub fn new(
|
||||
length: usize,
|
||||
is_fixed: bool,
|
||||
value_type: Option<ClassObject<'gc>>,
|
||||
value_type: Option<Class<'gc>>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Self {
|
||||
let storage = Vec::new();
|
||||
|
@ -79,7 +79,7 @@ impl<'gc> VectorStorage<'gc> {
|
|||
pub fn from_values(
|
||||
storage: Vec<Value<'gc>>,
|
||||
is_fixed: bool,
|
||||
value_type: Option<ClassObject<'gc>>,
|
||||
value_type: Option<Class<'gc>>,
|
||||
) -> Self {
|
||||
VectorStorage {
|
||||
storage,
|
||||
|
@ -118,11 +118,11 @@ impl<'gc> VectorStorage<'gc> {
|
|||
/// Get the default value for this vector.
|
||||
pub fn default(&self, activation: &mut Activation<'_, 'gc>) -> Value<'gc> {
|
||||
if let Some(value_type) = self.value_type {
|
||||
if Object::ptr_eq(value_type, activation.avm2().classes().int)
|
||||
|| Object::ptr_eq(value_type, activation.avm2().classes().uint)
|
||||
if value_type == activation.avm2().classes().int.inner_class_definition()
|
||||
|| value_type == activation.avm2().classes().uint.inner_class_definition()
|
||||
{
|
||||
Value::Integer(0)
|
||||
} else if Object::ptr_eq(value_type, activation.avm2().classes().number) {
|
||||
} else if value_type == activation.avm2().classes().number.inner_class_definition() {
|
||||
Value::Number(0.0)
|
||||
} else {
|
||||
Value::Null
|
||||
|
@ -133,17 +133,14 @@ impl<'gc> VectorStorage<'gc> {
|
|||
}
|
||||
|
||||
/// Get the value type stored in this vector (same as the class <T> type).
|
||||
pub fn value_type(&self) -> Option<ClassObject<'gc>> {
|
||||
pub fn value_type(&self) -> Option<Class<'gc>> {
|
||||
self.value_type
|
||||
}
|
||||
|
||||
/// Get the value type this vector coerces things to.
|
||||
pub fn value_type_for_coercion(
|
||||
&self,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> ClassObject<'gc> {
|
||||
pub fn value_type_for_coercion(&self, activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
|
||||
self.value_type
|
||||
.unwrap_or_else(|| activation.avm2().classes().object)
|
||||
.unwrap_or_else(|| activation.avm2().classes().object.inner_class_definition())
|
||||
}
|
||||
|
||||
/// Check if a vector index is in bounds.
|
||||
|
@ -251,11 +248,11 @@ impl<'gc> VectorStorage<'gc> {
|
|||
if let Some(v) = self.storage.pop() {
|
||||
Ok(v)
|
||||
} else if let Some(value_type) = self.value_type() {
|
||||
if Object::ptr_eq(value_type, activation.avm2().classes().uint)
|
||||
|| Object::ptr_eq(value_type, activation.avm2().classes().int)
|
||||
if value_type == activation.avm2().classes().uint.inner_class_definition()
|
||||
|| value_type == activation.avm2().classes().int.inner_class_definition()
|
||||
{
|
||||
Ok(Value::Integer(0))
|
||||
} else if Object::ptr_eq(value_type, activation.avm2().classes().number) {
|
||||
} else if value_type == activation.avm2().classes().number.inner_class_definition() {
|
||||
Ok(Value::Number(0.0))
|
||||
} else {
|
||||
Ok(Value::Undefined)
|
||||
|
@ -297,11 +294,11 @@ impl<'gc> VectorStorage<'gc> {
|
|||
if !self.storage.is_empty() {
|
||||
Ok(self.storage.remove(0))
|
||||
} else if let Some(value_type) = self.value_type() {
|
||||
if Object::ptr_eq(value_type, activation.avm2().classes().uint)
|
||||
|| Object::ptr_eq(value_type, activation.avm2().classes().int)
|
||||
if value_type == activation.avm2().classes().uint.inner_class_definition()
|
||||
|| value_type == activation.avm2().classes().int.inner_class_definition()
|
||||
{
|
||||
Ok(Value::Integer(0))
|
||||
} else if Object::ptr_eq(value_type, activation.avm2().classes().number) {
|
||||
} else if value_type == activation.avm2().classes().number.inner_class_definition() {
|
||||
Ok(Value::Number(0.0))
|
||||
} else {
|
||||
Ok(Value::Undefined)
|
||||
|
|
|
@ -438,7 +438,7 @@ fn object_name<'gc>(mc: &Mutation<'gc>, object: Object<'gc>) -> String {
|
|||
.to_string()
|
||||
} else {
|
||||
let name = object
|
||||
.instance_of_class_definition()
|
||||
.instance_class()
|
||||
.map(|r| Cow::Owned(r.name().local_name().to_string()))
|
||||
.unwrap_or(Cow::Borrowed("Object"));
|
||||
format!("{} {:p}", name, object.as_ptr())
|
||||
|
|
|
@ -64,9 +64,12 @@ impl<'gc> BitmapClass<'gc> {
|
|||
class: Avm2ClassObject<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
) -> Option<Self> {
|
||||
if class.has_class_in_chain(context.avm2.classes().bitmap.inner_class_definition()) {
|
||||
let class_definition = class.inner_class_definition();
|
||||
if class_definition
|
||||
.has_class_in_chain(context.avm2.classes().bitmap.inner_class_definition())
|
||||
{
|
||||
Some(BitmapClass::Bitmap(class))
|
||||
} else if class
|
||||
} else if class_definition
|
||||
.has_class_in_chain(context.avm2.classes().bitmapdata.inner_class_definition())
|
||||
{
|
||||
Some(BitmapClass::BitmapData(class))
|
||||
|
|
Loading…
Reference in New Issue