avm2: Actually apply class refactor to ClassObject and describeType
This requires changing the AVM2 ClassObject symbol registry to use Classes
This commit is contained in:
parent
bb50a6f926
commit
9c872860e9
|
@ -564,7 +564,7 @@ impl<'gc> Class<'gc> {
|
|||
param: None,
|
||||
super_class: Some(activation.avm2().classes().class.inner_class_definition()),
|
||||
attributes: ClassAttributes::FINAL,
|
||||
protected_namespace: None,
|
||||
protected_namespace,
|
||||
direct_interfaces: Vec::new(),
|
||||
all_interfaces: Vec::new(),
|
||||
instance_allocator: Allocator(scriptobject_allocator),
|
||||
|
@ -944,7 +944,7 @@ impl<'gc> Class<'gc> {
|
|||
|
||||
// Matching avmplus, this doesn't check whether the class is a
|
||||
// c_class; it strips the suffix even for i_classes
|
||||
if let Some(stripped) = local_name_wstr.strip_suffix('$' as u8) {
|
||||
if let Some(stripped) = local_name_wstr.strip_suffix(b'$') {
|
||||
let new_local_name = AvmString::new(mc, stripped);
|
||||
|
||||
QName::new(namespace, new_local_name)
|
||||
|
|
|
@ -519,15 +519,19 @@ pub fn load_player_globals<'gc>(
|
|||
let fn_proto = ScriptObject::custom_object(mc, Some(fn_class), Some(object_proto));
|
||||
domain.export_class(fn_classdef.name(), fn_classdef, mc);
|
||||
|
||||
let fn_c_class = fn_classdef
|
||||
.c_class()
|
||||
.expect("function::create_class returns an i_class");
|
||||
|
||||
// Now to weave the Gordian knot...
|
||||
object_class.link_prototype(activation, object_proto)?;
|
||||
object_class.link_type(mc, class_proto, class_class);
|
||||
object_class.link_type(mc, class_proto, object_c_class);
|
||||
|
||||
fn_class.link_prototype(activation, fn_proto)?;
|
||||
fn_class.link_type(mc, class_proto, class_class);
|
||||
fn_class.link_type(mc, class_proto, fn_c_class);
|
||||
|
||||
class_class.link_prototype(activation, class_proto)?;
|
||||
class_class.link_type(mc, class_proto, class_class);
|
||||
class_class.link_type(mc, class_proto, class_c_class);
|
||||
|
||||
// At this point, we need at least a partial set of system classes in
|
||||
// order to continue initializing the player. The rest of the classes
|
||||
|
@ -561,11 +565,13 @@ pub fn load_player_globals<'gc>(
|
|||
let global_class = ClassObject::from_class(activation, global_classdef, Some(object_class))?;
|
||||
|
||||
globals.set_proto(mc, global_class.prototype());
|
||||
globals.set_instance_of(mc, global_class);
|
||||
globals.set_instance_class(mc, global_classdef);
|
||||
globals.set_vtable(mc, global_class.instance_vtable());
|
||||
|
||||
activation.context.avm2.toplevel_global_object = Some(globals);
|
||||
|
||||
script.set_global_class(mc, global_classdef);
|
||||
script.set_global_class_obj(mc, global_class);
|
||||
|
||||
// From this point, `globals` is safe to be modified
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::avm2::class::Class;
|
||||
pub use crate::avm2::globals::flash::utils::get_qualified_class_name;
|
||||
use crate::avm2::metadata::Metadata;
|
||||
use crate::avm2::method::Method;
|
||||
use crate::avm2::object::{ArrayObject, TObject};
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::property::Property;
|
||||
use crate::avm2::{ClassObject, Namespace};
|
||||
|
||||
use crate::avm2::{Activation, Error, Object, Value};
|
||||
use crate::avm2::{Activation, Error, Namespace, Object, Value};
|
||||
use crate::avm2_stub_method;
|
||||
|
||||
// Implements `avmplus.describeTypeJSON`
|
||||
|
@ -21,42 +21,44 @@ pub fn describe_type_json<'gc>(
|
|||
}
|
||||
|
||||
let value = args[0].coerce_to_object(activation)?;
|
||||
let class_obj = value.as_class_object().or_else(|| value.instance_of());
|
||||
let class_def = value.instance_class();
|
||||
let object = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.object
|
||||
.construct(activation, &[])?;
|
||||
let Some(class_obj) = class_obj else {
|
||||
let Some(class_def) = class_def else {
|
||||
return Ok(Value::Null);
|
||||
};
|
||||
|
||||
let is_static = value.as_class_object().is_some();
|
||||
if !is_static && flags.contains(DescribeTypeFlags::USE_ITRAITS) {
|
||||
return Ok(Value::Null);
|
||||
let mut used_class_def = class_def;
|
||||
if flags.contains(DescribeTypeFlags::USE_ITRAITS) {
|
||||
if let Some(i_class) = used_class_def.i_class() {
|
||||
used_class_def = i_class;
|
||||
} else {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
}
|
||||
|
||||
let class = class_obj.inner_class_definition();
|
||||
|
||||
let qualified_name = class
|
||||
.name()
|
||||
let qualified_name = used_class_def
|
||||
.dollar_removed_name(activation.context.gc_context)
|
||||
.to_qualified_name(activation.context.gc_context);
|
||||
|
||||
object.set_public_property("name", qualified_name.into(), activation)?;
|
||||
|
||||
object.set_public_property(
|
||||
"isDynamic",
|
||||
(is_static || !class.is_sealed()).into(),
|
||||
(!used_class_def.is_sealed()).into(),
|
||||
activation,
|
||||
)?;
|
||||
object.set_public_property("isFinal", used_class_def.is_final().into(), activation)?;
|
||||
object.set_public_property(
|
||||
"isFinal",
|
||||
(is_static || class.is_final()).into(),
|
||||
"isStatic",
|
||||
value.as_class_object().is_some().into(),
|
||||
activation,
|
||||
)?;
|
||||
object.set_public_property("isStatic", is_static.into(), activation)?;
|
||||
|
||||
let traits = describe_internal_body(activation, class_obj, is_static, flags)?;
|
||||
let traits = describe_internal_body(activation, used_class_def, flags)?;
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_TRAITS) {
|
||||
object.set_public_property("traits", traits.into(), activation)?;
|
||||
} else {
|
||||
|
@ -181,14 +183,9 @@ fn describe_type_json_null<'gc>(
|
|||
|
||||
fn describe_internal_body<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
class_obj: ClassObject<'gc>,
|
||||
is_static: bool,
|
||||
class_def: Class<'gc>,
|
||||
flags: DescribeTypeFlags,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
// If we were passed a non-ClassObject, or the caller specifically requested it, then
|
||||
// look at the instance "traits" (our implementation is different than avmplus)
|
||||
|
||||
let use_instance_traits = !is_static || flags.contains(DescribeTypeFlags::USE_ITRAITS);
|
||||
let traits = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
|
@ -247,39 +244,24 @@ fn describe_internal_body<'gc>(
|
|||
.as_array_storage_mut(activation.context.gc_context)
|
||||
.unwrap();
|
||||
|
||||
let superclass = if use_instance_traits {
|
||||
class_obj.superclass_object()
|
||||
} else {
|
||||
Some(activation.avm2().classes().class)
|
||||
};
|
||||
let superclass = class_def.super_class();
|
||||
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_BASES) {
|
||||
let mut current_super_obj = superclass;
|
||||
while let Some(super_obj) = current_super_obj {
|
||||
let super_name = super_obj
|
||||
.inner_class_definition()
|
||||
let mut current_super_class = superclass;
|
||||
while let Some(super_class) = current_super_class {
|
||||
let super_name = super_class
|
||||
.name()
|
||||
.to_qualified_name(activation.context.gc_context);
|
||||
bases_array.push(super_name.into());
|
||||
current_super_obj = super_obj.superclass_object();
|
||||
current_super_class = super_class.super_class();
|
||||
}
|
||||
}
|
||||
|
||||
// When we're describing a Class object, we use the class vtable (which hides instance properties)
|
||||
let vtable = if use_instance_traits {
|
||||
class_obj.instance_vtable()
|
||||
} else {
|
||||
class_obj.class_vtable()
|
||||
};
|
||||
let vtable = class_def.instance_vtable();
|
||||
let super_vtable = class_def.super_class().map(|c| c.instance_vtable());
|
||||
|
||||
let super_vtable = if use_instance_traits {
|
||||
class_obj.superclass_object().map(|c| c.instance_vtable())
|
||||
} else {
|
||||
class_obj.instance_class().map(|c| c.instance_vtable())
|
||||
};
|
||||
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_INTERFACES) && use_instance_traits {
|
||||
for interface in &*class_obj.inner_class_definition().all_interfaces() {
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_INTERFACES) {
|
||||
for interface in &*class_def.all_interfaces() {
|
||||
let interface_name = interface
|
||||
.name()
|
||||
.to_qualified_name(activation.context.gc_context);
|
||||
|
@ -475,7 +457,7 @@ fn describe_internal_body<'gc>(
|
|||
let accessor_type =
|
||||
method_type.to_qualified_name_or_star(activation.context.gc_context);
|
||||
let declared_by = defining_class
|
||||
.name()
|
||||
.dollar_removed_name(activation.context.gc_context)
|
||||
.to_qualified_name(activation.context.gc_context);
|
||||
|
||||
let accessor_obj = activation
|
||||
|
@ -524,11 +506,9 @@ fn describe_internal_body<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
let constructor = class_obj.inner_class_definition().instance_init();
|
||||
let constructor = class_def.instance_init();
|
||||
// Flash only shows a <constructor> element if it has at least one parameter
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_CONSTRUCTOR)
|
||||
&& use_instance_traits
|
||||
&& !constructor.signature().is_empty()
|
||||
if flags.contains(DescribeTypeFlags::INCLUDE_CONSTRUCTOR) && !constructor.signature().is_empty()
|
||||
{
|
||||
let params = write_params(&constructor, activation)?;
|
||||
traits.set_public_property("constructor", params.into(), activation)?;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! `Class` builtin/prototype
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
use crate::avm2::object::{Object, TObject};
|
||||
use crate::avm2::value::Value;
|
||||
|
@ -85,6 +85,7 @@ pub fn create_c_class<'gc>(
|
|||
Method::from_builtin(class_init, "<Class class initializer>", gc_context),
|
||||
gc_context,
|
||||
);
|
||||
class_c_class.set_attributes(gc_context, ClassAttributes::FINAL);
|
||||
|
||||
// 'length' is a weird undocumented constant in Class.
|
||||
// We need to define it, since it shows up in 'describeType'
|
||||
|
|
|
@ -19,12 +19,12 @@ pub fn bitmap_allocator<'gc>(
|
|||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let bitmap_cls = activation.avm2().classes().bitmap;
|
||||
let bitmap_cls = activation.avm2().classes().bitmap.inner_class_definition();
|
||||
let bitmapdata_cls = activation.context.avm2.classes().bitmapdata;
|
||||
|
||||
let mut class_object = Some(class);
|
||||
let mut class_def = Some(class.inner_class_definition());
|
||||
let orig_class = class;
|
||||
while let Some(class) = class_object {
|
||||
while let Some(class) = class_def {
|
||||
if class == bitmap_cls {
|
||||
let bitmap_data = BitmapDataWrapper::dummy(activation.context.gc_context);
|
||||
let display_object = Bitmap::new_with_bitmap_data(
|
||||
|
@ -77,7 +77,7 @@ pub fn bitmap_allocator<'gc>(
|
|||
return Ok(obj);
|
||||
}
|
||||
}
|
||||
class_object = class.superclass_object();
|
||||
class_def = class.super_class();
|
||||
}
|
||||
unreachable!("A Bitmap subclass should have Bitmap in superclass chain");
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ pub fn init<'gc>(
|
|||
// which makes custom classes see a disposed BitmapData before they call super()
|
||||
let name = this.instance_class().map(|c| c.name());
|
||||
let character = this
|
||||
.instance_of()
|
||||
.instance_class()
|
||||
.and_then(|t| {
|
||||
activation
|
||||
.context
|
||||
|
|
|
@ -12,11 +12,11 @@ pub fn shape_allocator<'gc>(
|
|||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let shape_cls = activation.avm2().classes().shape;
|
||||
let shape_cls = activation.avm2().classes().shape.inner_class_definition();
|
||||
|
||||
let mut class_object = Some(class);
|
||||
let mut class_def = Some(class.inner_class_definition());
|
||||
let orig_class = class;
|
||||
while let Some(class) = class_object {
|
||||
while let Some(class) = class_def {
|
||||
if class == shape_cls {
|
||||
let display_object = Graphic::empty(&mut activation.context).into();
|
||||
return initialize_for_allocator(activation, display_object, orig_class);
|
||||
|
@ -36,7 +36,7 @@ pub fn shape_allocator<'gc>(
|
|||
|
||||
return initialize_for_allocator(activation, child, orig_class);
|
||||
}
|
||||
class_object = class.superclass_object();
|
||||
class_def = class.super_class();
|
||||
}
|
||||
unreachable!("A Shape subclass should have Shape in superclass chain");
|
||||
}
|
||||
|
|
|
@ -19,11 +19,15 @@ pub fn simple_button_allocator<'gc>(
|
|||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
use crate::vminterface::Instantiator;
|
||||
|
||||
let simplebutton_cls = activation.avm2().classes().simplebutton;
|
||||
let simplebutton_cls = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.simplebutton
|
||||
.inner_class_definition();
|
||||
|
||||
let mut class_object = Some(class);
|
||||
let mut class_def = Some(class.inner_class_definition());
|
||||
let orig_class = class;
|
||||
while let Some(class) = class_object {
|
||||
while let Some(class) = class_def {
|
||||
if class == simplebutton_cls {
|
||||
let button = Avm2Button::empty_button(&mut activation.context);
|
||||
// [NA] Buttons specifically need to PO'd
|
||||
|
@ -48,7 +52,7 @@ pub fn simple_button_allocator<'gc>(
|
|||
|
||||
return initialize_for_allocator(activation, child, orig_class);
|
||||
}
|
||||
class_object = class.superclass_object();
|
||||
class_def = class.super_class();
|
||||
}
|
||||
unreachable!("A SimpleButton subclass should have SimpleButton in superclass chain");
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ pub fn sprite_allocator<'gc>(
|
|||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let sprite_cls = activation.avm2().classes().sprite;
|
||||
let sprite_cls = activation.avm2().classes().sprite.inner_class_definition();
|
||||
|
||||
let mut class_object = Some(class);
|
||||
let mut class_def = Some(class.inner_class_definition());
|
||||
let orig_class = class;
|
||||
while let Some(class) = class_object {
|
||||
while let Some(class) = class_def {
|
||||
if class == sprite_cls {
|
||||
let movie = activation.caller_movie_or_root();
|
||||
let display_object = MovieClip::new(movie, activation.context.gc_context).into();
|
||||
|
@ -39,7 +39,7 @@ pub fn sprite_allocator<'gc>(
|
|||
|
||||
return initialize_for_allocator(activation, child, orig_class);
|
||||
}
|
||||
class_object = class.superclass_object();
|
||||
class_def = class.super_class();
|
||||
}
|
||||
unreachable!("A Sprite subclass should have Sprite in superclass chain");
|
||||
}
|
||||
|
|
|
@ -22,15 +22,15 @@ pub fn init<'gc>(
|
|||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(sound_object) = this.as_sound_object() {
|
||||
let class_object = this
|
||||
.instance_of()
|
||||
let class_def = this
|
||||
.instance_class()
|
||||
.ok_or("Attempted to construct Sound on a bare object.")?;
|
||||
|
||||
if let Some((movie, symbol)) = activation
|
||||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(class_object)
|
||||
.class_symbol(class_def)
|
||||
{
|
||||
if let Some(Character::Sound(sound)) = activation
|
||||
.context
|
||||
|
@ -41,7 +41,7 @@ pub fn init<'gc>(
|
|||
let sound = *sound;
|
||||
sound_object.set_sound(&mut activation.context, sound)?;
|
||||
} else {
|
||||
tracing::warn!("Attempted to construct subclass of Sound, {}, which is associated with non-Sound character {}", class_object.inner_class_definition().name().local_name(), symbol);
|
||||
tracing::warn!("Attempted to construct subclass of Sound, {}, which is associated with non-Sound character {}", class_def.name().local_name(), symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ pub fn video_allocator<'gc>(
|
|||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let video_class = activation.avm2().classes().video;
|
||||
let video_class = activation.avm2().classes().video.inner_class_definition();
|
||||
|
||||
let mut target_class = Some(class);
|
||||
let mut target_class = Some(class.inner_class_definition());
|
||||
while let Some(target) = target_class {
|
||||
if target == video_class {
|
||||
let movie = activation.caller_movie_or_root();
|
||||
|
@ -32,7 +32,7 @@ pub fn video_allocator<'gc>(
|
|||
return initialize_for_allocator(activation, child, class);
|
||||
}
|
||||
|
||||
target_class = target.superclass_object();
|
||||
target_class = target.super_class();
|
||||
}
|
||||
|
||||
unreachable!("A Video subclass should have Video in superclass chain");
|
||||
|
|
|
@ -133,7 +133,7 @@ pub fn register_font<'gc>(
|
|||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(class)
|
||||
.class_symbol(class.inner_class_definition())
|
||||
{
|
||||
if let Some(lib) = activation.context.library.library_for_movie(movie) {
|
||||
if let Some(Character::Font(font)) = lib.character_by_id(id) {
|
||||
|
|
|
@ -17,11 +17,15 @@ pub fn text_field_allocator<'gc>(
|
|||
class: ClassObject<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let textfield_cls = activation.avm2().classes().textfield;
|
||||
let textfield_cls = activation
|
||||
.avm2()
|
||||
.classes()
|
||||
.textfield
|
||||
.inner_class_definition();
|
||||
|
||||
let mut class_object = Some(class);
|
||||
let mut class_def = Some(class.inner_class_definition());
|
||||
let orig_class = class;
|
||||
while let Some(class) = class_object {
|
||||
while let Some(class) = class_def {
|
||||
if class == textfield_cls {
|
||||
let movie = activation.caller_movie_or_root();
|
||||
let display_object =
|
||||
|
@ -43,7 +47,7 @@ pub fn text_field_allocator<'gc>(
|
|||
|
||||
return initialize_for_allocator(activation, child, orig_class);
|
||||
}
|
||||
class_object = class.superclass_object();
|
||||
class_def = class.super_class();
|
||||
}
|
||||
unreachable!("A TextField subclass should have TextField in superclass chain");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Function builtin and prototype
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::error::eval_error;
|
||||
use crate::avm2::globals::array::resolve_array_hole;
|
||||
use crate::avm2::method::{Method, NativeMethodImpl};
|
||||
|
@ -239,6 +239,7 @@ pub fn create_class<'gc>(
|
|||
Method::from_builtin(class_init, "<Function class initializer>", gc_context),
|
||||
gc_context,
|
||||
);
|
||||
function_c_class.set_attributes(gc_context, ClassAttributes::FINAL);
|
||||
|
||||
function_i_class.set_c_class(gc_context, function_c_class);
|
||||
function_c_class.set_i_class(gc_context, function_i_class);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Object builtin and prototype
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::class::Class;
|
||||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::method::{Method, NativeMethodImpl, ParamConfig};
|
||||
use crate::avm2::object::{FunctionObject, Object, TObject};
|
||||
use crate::avm2::traits::Trait;
|
||||
|
@ -324,6 +324,7 @@ pub fn create_c_class<'gc>(
|
|||
Method::from_builtin(class_init, "<Object class initializer>", gc_context),
|
||||
gc_context,
|
||||
);
|
||||
object_c_class.set_attributes(gc_context, ClassAttributes::FINAL);
|
||||
|
||||
object_c_class.define_instance_trait(
|
||||
gc_context,
|
||||
|
|
|
@ -1107,12 +1107,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Get a raw pointer value for this object.
|
||||
fn as_ptr(&self) -> *const ObjectPtr;
|
||||
|
||||
/// Get this object's class, if it has one.
|
||||
fn instance_of(&self) -> Option<ClassObject<'gc>> {
|
||||
let base = self.base();
|
||||
base.instance_of()
|
||||
}
|
||||
|
||||
/// Get this object's vtable, if it has one.
|
||||
/// Every object with class should have a vtable
|
||||
fn vtable(&self) -> Option<VTable<'gc>> {
|
||||
|
@ -1127,7 +1121,8 @@ 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_class(&self) -> Option<Class<'gc>> {
|
||||
self.instance_of().map(|cls| cls.inner_class_definition())
|
||||
let base = self.base();
|
||||
base.instance_class()
|
||||
}
|
||||
|
||||
/// Get this object's class's name, formatted for debug output.
|
||||
|
@ -1137,11 +1132,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
.unwrap_or_else(|| "<Unknown type>".into())
|
||||
}
|
||||
|
||||
fn set_instance_of(&self, mc: &Mutation<'gc>, instance_of: ClassObject<'gc>) {
|
||||
let instance_vtable = instance_of.instance_vtable();
|
||||
|
||||
fn set_instance_class(&self, mc: &Mutation<'gc>, instance_class: Class<'gc>) {
|
||||
let mut base = self.base_mut(mc);
|
||||
base.set_instance_of(instance_of, instance_vtable);
|
||||
base.set_instance_class(instance_class);
|
||||
}
|
||||
|
||||
// Sets a different vtable for object, without changing instance_of.
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn byte_array_allocator<'gc>(
|
|||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(class)
|
||||
.class_symbol(class.inner_class_definition())
|
||||
{
|
||||
if let Some(lib) = activation.context.library.library_for_movie(movie) {
|
||||
if let Some(Character::BinaryData(binary_data)) = lib.character_by_id(id) {
|
||||
|
|
|
@ -112,19 +112,18 @@ impl<'gc> ClassObject<'gc> {
|
|||
class: Class<'gc>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
) -> Result<Self, Error<'gc>> {
|
||||
let c_class = class
|
||||
.c_class()
|
||||
.expect("Can only call ClassObject::from_class on i_classes");
|
||||
|
||||
let class_object = Self::from_class_partial(activation, class, superclass_object)?;
|
||||
let class_proto = class_object.allocate_prototype(activation, superclass_object)?;
|
||||
|
||||
class_object.link_prototype(activation, class_proto)?;
|
||||
|
||||
let class_class = activation.avm2().classes().class;
|
||||
let class_class_proto = class_class.prototype();
|
||||
let class_class_proto = activation.avm2().classes().class.prototype();
|
||||
|
||||
class_object.link_type(
|
||||
activation.context.gc_context,
|
||||
class_class_proto,
|
||||
class_class,
|
||||
);
|
||||
class_object.link_type(activation.context.gc_context, class_class_proto, c_class);
|
||||
class_object.init_instance_vtable(activation)?;
|
||||
class_object.into_finished_class(activation)
|
||||
}
|
||||
|
@ -146,10 +145,6 @@ impl<'gc> ClassObject<'gc> {
|
|||
class: Class<'gc>,
|
||||
superclass_object: Option<ClassObject<'gc>>,
|
||||
) -> Result<Self, Error<'gc>> {
|
||||
// We should only be able to construct `i_class` Classes
|
||||
// (if i_class() is None it means that the Class is an i_class)
|
||||
assert!(class.i_class().is_none());
|
||||
|
||||
let scope = activation.create_scopechain();
|
||||
if let Some(base_class) = superclass_object.map(|b| b.inner_class_definition()) {
|
||||
if base_class.is_final() {
|
||||
|
@ -324,17 +319,10 @@ impl<'gc> ClassObject<'gc> {
|
|||
/// This is intended to support initialization of early types such as
|
||||
/// `Class` and `Object`. All other types should pull `Class`'s prototype
|
||||
/// and type object from the `Avm2` instance.
|
||||
pub fn link_type(
|
||||
self,
|
||||
gc_context: &Mutation<'gc>,
|
||||
proto: Object<'gc>,
|
||||
instance_of: ClassObject<'gc>,
|
||||
) {
|
||||
let instance_vtable = instance_of.instance_vtable();
|
||||
|
||||
pub fn link_type(self, gc_context: &Mutation<'gc>, proto: Object<'gc>, c_class: Class<'gc>) {
|
||||
let mut write = self.0.write(gc_context);
|
||||
|
||||
write.base.set_instance_of(instance_of, instance_vtable);
|
||||
write.base.set_instance_class(c_class);
|
||||
write.base.set_proto(proto);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn font_allocator<'gc>(
|
|||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(class)
|
||||
.class_symbol(class.inner_class_definition())
|
||||
{
|
||||
if let Some(lib) = activation.context.library.library_for_movie(movie) {
|
||||
if let Some(Character::Font(font)) = lib.character_by_id(id) {
|
||||
|
|
|
@ -53,9 +53,9 @@ pub struct ScriptObjectData<'gc> {
|
|||
/// Implicit prototype of this script object.
|
||||
proto: Option<Object<'gc>>,
|
||||
|
||||
/// The class object that this is an instance of.
|
||||
/// The `Class` that this is an instance of.
|
||||
/// If `none`, this is not an ES4 object at all.
|
||||
instance_of: Option<ClassObject<'gc>>,
|
||||
instance_class: Option<Class<'gc>>,
|
||||
|
||||
/// The table used for non-dynamic property lookups.
|
||||
vtable: Option<VTable<'gc>>,
|
||||
|
@ -143,7 +143,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
slots: Vec::new(),
|
||||
bound_methods: Vec::new(),
|
||||
proto,
|
||||
instance_of,
|
||||
instance_class: instance_of.map(|cls| cls.inner_class_definition()),
|
||||
vtable: instance_of.map(|cls| cls.instance_vtable()),
|
||||
}
|
||||
}
|
||||
|
@ -390,13 +390,9 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
*self.bound_methods.get_mut(disp_id as usize).unwrap() = Some(function);
|
||||
}
|
||||
|
||||
/// Get the class object for this object, if it has one.
|
||||
pub fn instance_of(&self) -> Option<ClassObject<'gc>> {
|
||||
self.instance_of
|
||||
}
|
||||
|
||||
/// Get the `Class` for this object, if it has one.
|
||||
pub fn instance_class(&self) -> Option<Class<'gc>> {
|
||||
self.instance_of.map(|cls| cls.inner_class_definition())
|
||||
self.instance_class
|
||||
}
|
||||
|
||||
/// Get the vtable for this object, if it has one.
|
||||
|
@ -411,9 +407,8 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
|
||||
/// Set the class object for this object.
|
||||
pub fn set_instance_of(&mut self, instance_of: ClassObject<'gc>, vtable: VTable<'gc>) {
|
||||
self.instance_of = Some(instance_of);
|
||||
self.vtable = Some(vtable);
|
||||
pub fn set_instance_class(&mut self, instance_class: Class<'gc>) {
|
||||
self.instance_class = Some(instance_class);
|
||||
}
|
||||
|
||||
pub fn set_vtable(&mut self, vtable: VTable<'gc>) {
|
||||
|
|
|
@ -280,33 +280,30 @@ pub fn optimize<'gc>(
|
|||
// but this works since it's guaranteed to be set in `Activation::from_method`.
|
||||
let this_value = activation.local_register(0);
|
||||
|
||||
let (this_class, this_vtable) = if let Some(this_class) = activation.subclass_object() {
|
||||
let this_class = if let Some(this_class) = activation.subclass_object() {
|
||||
if this_value.is_of_type(activation, this_class.inner_class_definition()) {
|
||||
(
|
||||
Some(this_class),
|
||||
Some(this_class.inner_class_definition().instance_vtable()),
|
||||
)
|
||||
} else if this_value
|
||||
.as_object()
|
||||
.and_then(|o| o.as_class_object())
|
||||
.map(|c| c.inner_class_definition() == this_class.inner_class_definition())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Static method
|
||||
(
|
||||
Some(activation.avm2().classes().class),
|
||||
Some(this_class.class_vtable()),
|
||||
)
|
||||
Some(this_class.inner_class_definition())
|
||||
} else if let Some(this_object) = this_value.as_object() {
|
||||
if this_object
|
||||
.as_class_object()
|
||||
.map(|c| c.inner_class_definition() == this_class.inner_class_definition())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Static method
|
||||
this_object.instance_class()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
None
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
None
|
||||
};
|
||||
|
||||
let this_value = OptValue {
|
||||
class: this_class.map(|c| c.inner_class_definition()),
|
||||
vtable: this_vtable,
|
||||
class: this_class,
|
||||
vtable: this_class.map(|cls| cls.instance_vtable()),
|
||||
contains_valid_integer: false,
|
||||
contains_valid_unsigned: false,
|
||||
null_state: NullState::NotNull,
|
||||
|
@ -861,7 +858,7 @@ pub fn optimize<'gc>(
|
|||
if !multiname.has_lazy_component() && has_simple_scoping {
|
||||
let outer_scope = activation.outer();
|
||||
if !outer_scope.is_empty() {
|
||||
if let Some(this_vtable) = this_vtable {
|
||||
if let Some(this_vtable) = this_class.map(|cls| cls.instance_vtable()) {
|
||||
if this_vtable.has_trait(&multiname) {
|
||||
*op = Op::GetScopeObject { index: 0 };
|
||||
|
||||
|
|
|
@ -267,7 +267,7 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
self,
|
||||
script_index,
|
||||
global_obj,
|
||||
global_classdef,
|
||||
global_class,
|
||||
domain,
|
||||
activation,
|
||||
)?;
|
||||
|
@ -430,6 +430,9 @@ pub struct ScriptData<'gc> {
|
|||
/// The class of this script's global object.
|
||||
global_class: Option<Class<'gc>>,
|
||||
|
||||
/// The ClassObject of this script's global object.
|
||||
global_class_obj: Option<ClassObject<'gc>>,
|
||||
|
||||
/// The domain associated with this script.
|
||||
domain: Domain<'gc>,
|
||||
|
||||
|
@ -468,6 +471,7 @@ impl<'gc> Script<'gc> {
|
|||
ScriptData {
|
||||
globals,
|
||||
global_class: None,
|
||||
global_class_obj: None,
|
||||
domain,
|
||||
init: Method::from_builtin(
|
||||
|_, _, _| Ok(Value::Undefined),
|
||||
|
@ -496,7 +500,7 @@ impl<'gc> Script<'gc> {
|
|||
unit: TranslationUnit<'gc>,
|
||||
script_index: u32,
|
||||
globals: Object<'gc>,
|
||||
global_class: Class<'gc>,
|
||||
global_class_obj: ClassObject<'gc>,
|
||||
domain: Domain<'gc>,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
) -> Result<Self, Error<'gc>> {
|
||||
|
@ -513,7 +517,8 @@ impl<'gc> Script<'gc> {
|
|||
activation.context.gc_context,
|
||||
ScriptData {
|
||||
globals,
|
||||
global_class: Some(global_class),
|
||||
global_class: Some(global_class_obj.inner_class_definition()),
|
||||
global_class_obj: Some(global_class_obj),
|
||||
domain,
|
||||
init,
|
||||
traits: Vec::new(),
|
||||
|
@ -608,6 +613,10 @@ impl<'gc> Script<'gc> {
|
|||
self.0.write(mc).global_class = Some(global_class);
|
||||
}
|
||||
|
||||
pub fn set_global_class_obj(self, mc: &Mutation<'gc>, global_class_obj: ClassObject<'gc>) {
|
||||
self.0.write(mc).global_class_obj = Some(global_class_obj);
|
||||
}
|
||||
|
||||
/// Return the global scope for the script.
|
||||
///
|
||||
/// If the script has not yet been initialized, this will initialize it on
|
||||
|
@ -628,7 +637,7 @@ impl<'gc> Script<'gc> {
|
|||
|
||||
globals.vtable().unwrap().init_vtable(
|
||||
globals.instance_class().unwrap(),
|
||||
globals.instance_of(),
|
||||
self.0.read().global_class_obj,
|
||||
&self.traits()?,
|
||||
Some(scope),
|
||||
None,
|
||||
|
|
|
@ -87,7 +87,7 @@ impl Avm2ObjectWindow {
|
|||
.num_columns(2)
|
||||
.striped(true)
|
||||
.show(ui, |ui| {
|
||||
if let Some(class) = object.instance_of() {
|
||||
if let Some(class) = object.instance_class().and_then(|o| o.class_object()) {
|
||||
ui.label("Instance Of");
|
||||
show_avm2_value(ui, &mut activation.context, class.into(), messages);
|
||||
ui.end_row();
|
||||
|
|
|
@ -4548,7 +4548,11 @@ impl<'gc, 'a> MovieClip<'gc> {
|
|||
.context
|
||||
.library
|
||||
.avm2_class_registry_mut()
|
||||
.set_class_symbol(class_object, movie.clone(), id);
|
||||
.set_class_symbol(
|
||||
class_object.inner_class_definition(),
|
||||
movie.clone(),
|
||||
id,
|
||||
);
|
||||
|
||||
let library = activation
|
||||
.context
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::avm1::{PropertyMap as Avm1PropertyMap, PropertyMap};
|
||||
use crate::avm2::{ClassObject as Avm2ClassObject, Domain as Avm2Domain};
|
||||
use crate::avm2::{Class as Avm2Class, Domain as Avm2Domain};
|
||||
use crate::backend::audio::SoundHandle;
|
||||
use crate::character::Character;
|
||||
use std::borrow::Cow;
|
||||
|
@ -48,7 +48,7 @@ impl WeakElement for WeakMovieSymbol {
|
|||
pub struct Avm2ClassRegistry<'gc> {
|
||||
/// A list of AVM2 class objects and the character IDs they are expected to
|
||||
/// instantiate.
|
||||
class_map: WeakValueHashMap<Avm2ClassObject<'gc>, WeakMovieSymbol>,
|
||||
class_map: WeakValueHashMap<Avm2Class<'gc>, WeakMovieSymbol>,
|
||||
}
|
||||
|
||||
unsafe impl Collect for Avm2ClassRegistry<'_> {
|
||||
|
@ -76,24 +76,21 @@ impl<'gc> Avm2ClassRegistry<'gc> {
|
|||
///
|
||||
/// A value of `None` indicates that this AVM2 class is not associated with
|
||||
/// a library symbol.
|
||||
pub fn class_symbol(
|
||||
&self,
|
||||
class_object: Avm2ClassObject<'gc>,
|
||||
) -> Option<(Arc<SwfMovie>, CharacterId)> {
|
||||
match self.class_map.get(&class_object) {
|
||||
pub fn class_symbol(&self, class_def: Avm2Class<'gc>) -> Option<(Arc<SwfMovie>, CharacterId)> {
|
||||
match self.class_map.get(&class_def) {
|
||||
Some(MovieSymbol(movie, symbol)) => Some((movie, symbol)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Associate an AVM2 class object with a given library symbol.
|
||||
/// Associate an AVM2 class definition with a given library symbol.
|
||||
pub fn set_class_symbol(
|
||||
&mut self,
|
||||
class_object: Avm2ClassObject<'gc>,
|
||||
class_def: Avm2Class<'gc>,
|
||||
movie: Arc<SwfMovie>,
|
||||
symbol: CharacterId,
|
||||
) {
|
||||
if let Some(old) = self.class_map.get(&class_object) {
|
||||
if let Some(old) = self.class_map.get(&class_def) {
|
||||
if Arc::ptr_eq(&movie, &old.0) && symbol != old.1 {
|
||||
// Flash player actually allows using the same class in multiple SymbolClass
|
||||
// entries in the same swf, with *different* symbol ids. Whichever one
|
||||
|
@ -102,7 +99,7 @@ impl<'gc> Avm2ClassRegistry<'gc> {
|
|||
// of deliberately crafted SWFs.
|
||||
tracing::warn!(
|
||||
"Tried to overwrite class {:?} id={:?} with symbol id={:?} from same movie",
|
||||
class_object,
|
||||
class_def,
|
||||
old.1,
|
||||
symbol,
|
||||
);
|
||||
|
@ -114,8 +111,7 @@ impl<'gc> Avm2ClassRegistry<'gc> {
|
|||
// instantiates the clip on the timeline.
|
||||
return;
|
||||
}
|
||||
self.class_map
|
||||
.insert(class_object, MovieSymbol(movie, symbol));
|
||||
self.class_map.insert(class_def, MovieSymbol(movie, symbol));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue