avm2: Remove SystemPrototypes.

This commit is contained in:
Adrian Wielgosik 2022-06-29 16:28:42 +02:00 committed by Mike Welsh
parent 1c5312d6c8
commit f31d4c2498
16 changed files with 40 additions and 190 deletions

View File

@ -1,6 +1,6 @@
//! ActionScript Virtual Machine 2 (AS3) support //! ActionScript Virtual Machine 2 (AS3) support
use crate::avm2::globals::{SystemClasses, SystemPrototypes}; use crate::avm2::globals::SystemClasses;
use crate::avm2::method::Method; use crate::avm2::method::Method;
use crate::avm2::object::EventObject; use crate::avm2::object::EventObject;
use crate::avm2::script::{Script, TranslationUnit}; use crate::avm2::script::{Script, TranslationUnit};
@ -72,9 +72,6 @@ pub struct Avm2<'gc> {
/// Global scope object. /// Global scope object.
globals: Domain<'gc>, globals: Domain<'gc>,
/// System prototypes.
system_prototypes: Option<SystemPrototypes<'gc>>,
/// System classes. /// System classes.
system_classes: Option<SystemClasses<'gc>>, system_classes: Option<SystemClasses<'gc>>,
@ -100,7 +97,6 @@ impl<'gc> Avm2<'gc> {
Self { Self {
stack: Vec::new(), stack: Vec::new(),
globals, globals,
system_prototypes: None,
system_classes: None, system_classes: None,
broadcast_list: Default::default(), broadcast_list: Default::default(),
@ -115,13 +111,6 @@ impl<'gc> Avm2<'gc> {
globals::load_player_globals(&mut activation, globals) globals::load_player_globals(&mut activation, globals)
} }
/// Return the current set of system prototypes.
///
/// This function panics if the interpreter has not yet been initialized.
pub fn prototypes(&self) -> &SystemPrototypes<'gc> {
self.system_prototypes.as_ref().unwrap()
}
/// Return the current set of system classes. /// Return the current set of system classes.
/// ///
/// This function panics if the interpreter has not yet been initialized. /// This function panics if the interpreter has not yet been initialized.

View File

@ -44,124 +44,6 @@ const NS_VECTOR: &str = "__AS3__.vec";
pub use flash::utils::NS_FLASH_PROXY; pub use flash::utils::NS_FLASH_PROXY;
/// This structure represents all system builtins' prototypes.
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct SystemPrototypes<'gc> {
pub object: Object<'gc>,
pub function: Object<'gc>,
pub class: Object<'gc>,
pub global: Object<'gc>,
pub string: Object<'gc>,
pub boolean: Object<'gc>,
pub number: Object<'gc>,
pub int: Object<'gc>,
pub uint: Object<'gc>,
pub namespace: Object<'gc>,
pub array: Object<'gc>,
pub movieclip: Object<'gc>,
pub framelabel: Object<'gc>,
pub scene: Object<'gc>,
pub application_domain: Object<'gc>,
pub event: Object<'gc>,
pub fullscreenevent: Object<'gc>,
pub video: Object<'gc>,
pub xml: Object<'gc>,
pub xml_list: Object<'gc>,
pub display_object: Object<'gc>,
pub shape: Object<'gc>,
pub textfield: Object<'gc>,
pub textformat: Object<'gc>,
pub graphics: Object<'gc>,
pub loaderinfo: Object<'gc>,
pub bytearray: Object<'gc>,
pub stage: Object<'gc>,
pub sprite: Object<'gc>,
pub simplebutton: Object<'gc>,
pub regexp: Object<'gc>,
pub vector: Object<'gc>,
pub soundtransform: Object<'gc>,
pub soundchannel: Object<'gc>,
pub bitmap: Object<'gc>,
pub bitmapdata: Object<'gc>,
pub date: Object<'gc>,
pub qname: Object<'gc>,
pub sharedobject: Object<'gc>,
pub nativemenu: Object<'gc>,
pub contextmenu: Object<'gc>,
pub mouseevent: Object<'gc>,
pub textevent: Object<'gc>,
pub errorevent: Object<'gc>,
pub ioerrorevent: Object<'gc>,
pub securityerrorevent: Object<'gc>,
}
impl<'gc> SystemPrototypes<'gc> {
/// Construct a minimal set of system prototypes necessary for
/// bootstrapping player globals.
///
/// All other system prototypes aside from the three given here will be set
/// to the empty object also handed to this function. It is the caller's
/// responsibility to instantiate each class and replace the empty object
/// with that.
fn new(
object: Object<'gc>,
function: Object<'gc>,
class: Object<'gc>,
global: Object<'gc>,
empty: Object<'gc>,
) -> Self {
SystemPrototypes {
object,
function,
class,
global,
string: empty,
boolean: empty,
number: empty,
int: empty,
uint: empty,
namespace: empty,
array: empty,
movieclip: empty,
framelabel: empty,
scene: empty,
application_domain: empty,
event: empty,
fullscreenevent: empty,
video: empty,
xml: empty,
xml_list: empty,
display_object: empty,
shape: empty,
textfield: empty,
textformat: empty,
graphics: empty,
loaderinfo: empty,
bytearray: empty,
stage: empty,
sprite: empty,
simplebutton: empty,
regexp: empty,
vector: empty,
soundtransform: empty,
soundchannel: empty,
bitmap: empty,
bitmapdata: empty,
date: empty,
qname: empty,
sharedobject: empty,
nativemenu: empty,
contextmenu: empty,
mouseevent: empty,
textevent: empty,
errorevent: empty,
ioerrorevent: empty,
securityerrorevent: empty,
}
}
}
/// This structure represents all system builtin classes. /// This structure represents all system builtin classes.
#[derive(Clone, Collect)] #[derive(Clone, Collect)]
#[collect(no_drop)] #[collect(no_drop)]
@ -319,13 +201,13 @@ fn dynamic_class<'gc>(
/// Add a class builtin to the global scope. /// Add a class builtin to the global scope.
/// ///
/// This function returns the class object and class prototype as a pair, which /// This function returns the class object and class prototype as a class, which
/// may be stored in `SystemClasses` and `SystemPrototypes`, respectively. /// may be stored in `SystemClasses`
fn class<'gc>( fn class<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
class_def: GcCell<'gc, Class<'gc>>, class_def: GcCell<'gc, Class<'gc>>,
script: Script<'gc>, script: Script<'gc>,
) -> Result<(ClassObject<'gc>, Object<'gc>), Error> { ) -> Result<ClassObject<'gc>, Error> {
let (_, mut global, mut domain) = script.init(); let (_, mut global, mut domain) = script.init();
let class_read = class_def.read(); let class_read = class_def.read();
@ -364,7 +246,7 @@ fn class<'gc>(
); );
domain.export_definition(class_name, script, activation.context.gc_context)?; domain.export_definition(class_name, script, activation.context.gc_context)?;
Ok((class_object, class_object.prototype())) Ok(class_object)
} }
/// Add a builtin constant to the global scope. /// Add a builtin constant to the global scope.
@ -402,13 +284,10 @@ fn namespace<'gc>(
macro_rules! avm2_system_class { macro_rules! avm2_system_class {
($field:ident, $activation:ident, $class:expr, $script:expr) => { ($field:ident, $activation:ident, $class:expr, $script:expr) => {
let (class_object, proto) = class($activation, $class, $script)?; let class_object = class($activation, $class, $script)?;
let sc = $activation.avm2().system_classes.as_mut().unwrap(); let sc = $activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object; sc.$field = class_object;
let sp = $activation.avm2().system_prototypes.as_mut().unwrap();
sp.$field = proto;
}; };
} }
@ -478,16 +357,9 @@ pub fn load_player_globals<'gc>(
global_class.link_prototype(activation, global_proto)?; global_class.link_prototype(activation, global_proto)?;
global_class.link_type(activation, class_proto, class_class); global_class.link_type(activation, class_proto, class_class);
// At this point, we need at least a partial set of system prototypes in // At this point, we need at least a partial set of system classes in
// order to continue initializing the player. The rest of the prototypes // order to continue initializing the player. The rest of the classes
// are set to a bare object until we have a chance to initialize them. // are set to a temporary class until we have a chance to initialize them.
activation.context.avm2.system_prototypes = Some(SystemPrototypes::new(
object_proto,
fn_proto,
class_proto,
global_proto,
ScriptObject::bare_object(mc),
));
activation.context.avm2.system_classes = Some(SystemClasses::new( activation.context.avm2.system_classes = Some(SystemClasses::new(
object_class, object_class,
@ -505,8 +377,8 @@ pub fn load_player_globals<'gc>(
let object_class = object_class.into_finished_class(activation)?; let object_class = object_class.into_finished_class(activation)?;
let _global_class = global_class.into_finished_class(activation)?; let _global_class = global_class.into_finished_class(activation)?;
globals.set_proto(mc, activation.avm2().prototypes().global); globals.set_proto(mc, global_proto);
globals.set_instance_of(mc, activation.avm2().classes().global); globals.set_instance_of(mc, global_class);
globals.fork_vtable(activation.context.gc_context); globals.fork_vtable(activation.context.gc_context);
// From this point, `globals` is safe to be modified // From this point, `globals` is safe to be modified
@ -951,16 +823,13 @@ fn load_playerglobal<'gc>(
let class_object = activation.resolve_class(&qname.into())?; let class_object = activation.resolve_class(&qname.into())?;
let sc = $activation.avm2().system_classes.as_mut().unwrap(); let sc = $activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object; sc.$field = class_object;
let sp = $activation.avm2().system_prototypes.as_mut().unwrap();
sp.$field = class_object.prototype();
)* )*
} }
} }
// This acts the same way as 'avm2_system_class', but for classes // This acts the same way as 'avm2_system_class', but for classes
// declared in 'playerglobal'. Classes are declared as ("package", "class", field_name), // declared in 'playerglobal'. Classes are declared as ("package", "class", field_name),
// and are stored in 'avm2().system_classes' and 'avm2().system_prototypes' // and are stored in 'avm2().system_classes'
avm2_system_classes_playerglobal!(activation, script, [("flash.display", "Scene", scene)]); avm2_system_classes_playerglobal!(activation, script, [("flash.display", "Scene", scene)]);
Ok(()) Ok(())

View File

@ -235,7 +235,7 @@ pub fn to_string<'gc>(
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let object_proto = activation.avm2().prototypes().object; let object_proto = activation.avm2().classes().object.prototype();
let name = QName::dynamic_name("toString").into(); let name = QName::dynamic_name("toString").into();
object_proto object_proto
.get_property(&name, activation)? .get_property(&name, activation)?

View File

@ -58,8 +58,7 @@ impl<'gc> ArrayObject<'gc> {
array: ArrayStorage<'gc>, array: ArrayStorage<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().array; let class = activation.avm2().classes().array;
let proto = activation.avm2().prototypes().array; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut instance: Object<'gc> = ArrayObject(GcCell::allocate( let mut instance: Object<'gc> = ArrayObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -45,8 +45,7 @@ impl<'gc> ByteArrayObject<'gc> {
bytes: ByteArrayStorage, bytes: ByteArrayStorage,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().bytearray; let class = activation.avm2().classes().bytearray;
let proto = activation.avm2().prototypes().bytearray; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut instance: Object<'gc> = ByteArrayObject(GcCell::allocate( let mut instance: Object<'gc> = ByteArrayObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -134,7 +134,7 @@ impl<'gc> ClassObject<'gc> {
class_object.link_prototype(activation, class_proto)?; class_object.link_prototype(activation, class_proto)?;
let class_class = activation.avm2().classes().class; let class_class = activation.avm2().classes().class;
let class_class_proto = activation.avm2().prototypes().class; let class_class_proto = class_class.prototype();
class_object.link_type(activation, class_class_proto, class_class); class_object.link_type(activation, class_class_proto, class_class);
class_object.into_finished_class(activation) class_object.into_finished_class(activation)
@ -877,7 +877,6 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
let class_proto = self.allocate_prototype(activation, superclass_object)?; let class_proto = self.allocate_prototype(activation, superclass_object)?;
let class_class = activation.avm2().classes().class; let class_class = activation.avm2().classes().class;
let class_class_proto = activation.avm2().prototypes().class;
let constructor = self.0.read().constructor.clone(); let constructor = self.0.read().constructor.clone();
let native_constructor = self.0.read().native_constructor.clone(); let native_constructor = self.0.read().native_constructor.clone();
@ -886,7 +885,7 @@ impl<'gc> TObject<'gc> for ClassObject<'gc> {
let mut class_object = ClassObject(GcCell::allocate( let mut class_object = ClassObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
ClassObjectData { ClassObjectData {
base: ScriptObjectData::base_new(Some(class_class_proto), Some(class_class)), base: ScriptObjectData::new(class_class),
class: parameterized_class, class: parameterized_class,
prototype: None, prototype: None,
class_scope, class_scope,

View File

@ -49,8 +49,7 @@ impl<'gc> DomainObject<'gc> {
domain: Domain<'gc>, domain: Domain<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().application_domain; let class = activation.avm2().classes().application_domain;
let proto = activation.avm2().prototypes().application_domain; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = DomainObject(GcCell::allocate( let mut this: Object<'gc> = DomainObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
DomainObjectData { base, domain }, DomainObjectData { base, domain },

View File

@ -42,7 +42,7 @@ impl<'gc> FunctionObject<'gc> {
let this = Self::from_method(activation, method, scope, None, None); let this = Self::from_method(activation, method, scope, None, None);
let es3_proto = ScriptObject::object( let es3_proto = ScriptObject::object(
activation.context.gc_context, activation.context.gc_context,
activation.avm2().prototypes().object, activation.avm2().classes().object.prototype(),
); );
this.0.write(activation.context.gc_context).prototype = Some(es3_proto); this.0.write(activation.context.gc_context).prototype = Some(es3_proto);
@ -61,14 +61,13 @@ impl<'gc> FunctionObject<'gc> {
receiver: Option<Object<'gc>>, receiver: Option<Object<'gc>>,
subclass_object: Option<ClassObject<'gc>>, subclass_object: Option<ClassObject<'gc>>,
) -> FunctionObject<'gc> { ) -> FunctionObject<'gc> {
let fn_proto = activation.avm2().prototypes().function;
let fn_class = activation.avm2().classes().function; let fn_class = activation.avm2().classes().function;
let exec = Executable::from_method(method, scope, receiver, subclass_object); let exec = Executable::from_method(method, scope, receiver, subclass_object);
FunctionObject(GcCell::allocate( FunctionObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
FunctionObjectData { FunctionObjectData {
base: ScriptObjectData::base_new(Some(fn_proto), Some(fn_class)), base: ScriptObjectData::new(fn_class),
exec, exec,
prototype: None, prototype: None,
}, },

View File

@ -71,8 +71,7 @@ impl<'gc> LoaderInfoObject<'gc> {
root: DisplayObject<'gc>, root: DisplayObject<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().loaderinfo; let class = activation.avm2().classes().loaderinfo;
let proto = activation.avm2().prototypes().loaderinfo; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let loaded_stream = Some(LoaderStream::Swf(movie, root)); let loaded_stream = Some(LoaderStream::Swf(movie, root));
let mut this: Object<'gc> = LoaderInfoObject(GcCell::allocate( let mut this: Object<'gc> = LoaderInfoObject(GcCell::allocate(
@ -93,8 +92,7 @@ impl<'gc> LoaderInfoObject<'gc> {
/// Create a loader info object for the stage. /// Create a loader info object for the stage.
pub fn from_stage(activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> { pub fn from_stage(activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().loaderinfo; let class = activation.avm2().classes().loaderinfo;
let proto = activation.avm2().prototypes().loaderinfo; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = LoaderInfoObject(GcCell::allocate( let mut this: Object<'gc> = LoaderInfoObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -49,8 +49,7 @@ impl<'gc> NamespaceObject<'gc> {
namespace: Namespace<'gc>, namespace: Namespace<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().namespace; let class = activation.avm2().classes().namespace;
let proto = activation.avm2().prototypes().namespace; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = NamespaceObject(GcCell::allocate( let mut this: Object<'gc> = NamespaceObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -65,14 +65,6 @@ impl<'gc> PrimitiveObject<'gc> {
return Err("Cannot box a null value".into()); return Err("Cannot box a null value".into());
} }
let proto = match primitive {
Value::Bool(_) => activation.avm2().prototypes().boolean,
Value::Number(_) => activation.avm2().prototypes().number,
Value::Unsigned(_) => activation.avm2().prototypes().uint,
Value::Integer(_) => activation.avm2().prototypes().int,
Value::String(_) => activation.avm2().prototypes().string,
_ => unreachable!(),
};
let class = match primitive { let class = match primitive {
Value::Bool(_) => activation.avm2().classes().boolean, Value::Bool(_) => activation.avm2().classes().boolean,
Value::Number(_) => activation.avm2().classes().number, Value::Number(_) => activation.avm2().classes().number,
@ -82,7 +74,7 @@ impl<'gc> PrimitiveObject<'gc> {
_ => unreachable!(), _ => unreachable!(),
}; };
let base = ScriptObjectData::base_new(Some(proto), Some(class)); let base = ScriptObjectData::new(class);
let mut this: Object<'gc> = PrimitiveObject(GcCell::allocate( let mut this: Object<'gc> = PrimitiveObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
PrimitiveObjectData { base, primitive }, PrimitiveObjectData { base, primitive },

View File

@ -46,8 +46,7 @@ impl<'gc> QNameObject<'gc> {
qname: QName<'gc>, qname: QName<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().qname; let class = activation.avm2().classes().qname;
let proto = activation.avm2().prototypes().qname; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = QNameObject(GcCell::allocate( let mut this: Object<'gc> = QNameObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -47,8 +47,7 @@ impl<'gc> RegExpObject<'gc> {
regexp: RegExp<'gc>, regexp: RegExp<'gc>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().regexp; let class = activation.avm2().classes().regexp;
let proto = activation.avm2().prototypes().regexp; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = RegExpObject(GcCell::allocate( let mut this: Object<'gc> = RegExpObject(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,

View File

@ -132,6 +132,18 @@ impl<'gc> ScriptObjectData<'gc> {
} }
} }
pub fn new(instance_of: ClassObject<'gc>) -> Self {
ScriptObjectData {
values: Default::default(),
slots: Vec::new(),
bound_methods: Vec::new(),
proto: Some(instance_of.prototype()),
instance_of: Some(instance_of),
vtable: Some(instance_of.instance_vtable()),
enumerants: Vec::new(),
}
}
pub fn get_property_local( pub fn get_property_local(
&self, &self,
multiname: &Multiname<'gc>, multiname: &Multiname<'gc>,

View File

@ -94,11 +94,10 @@ impl<'gc> StageObject<'gc> {
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let class = activation.avm2().classes().graphics; let class = activation.avm2().classes().graphics;
let proto = activation.avm2().prototypes().graphics;
let mut this = Self(GcCell::allocate( let mut this = Self(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,
StageObjectData { StageObjectData {
base: ScriptObjectData::base_new(Some(proto), Some(class)), base: ScriptObjectData::new(class),
display_object: Some(display_object), display_object: Some(display_object),
}, },
)); ));

View File

@ -46,8 +46,7 @@ impl<'gc> TextFormatObject<'gc> {
text_format: TextFormat, text_format: TextFormat,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let class = activation.avm2().classes().textformat; let class = activation.avm2().classes().textformat;
let proto = activation.avm2().prototypes().textformat; let base = ScriptObjectData::new(class);
let base = ScriptObjectData::base_new(Some(proto), Some(class));
let mut this: Object<'gc> = Self(GcCell::allocate( let mut this: Object<'gc> = Self(GcCell::allocate(
activation.context.gc_context, activation.context.gc_context,