avm2: Ensure global object always has correct vtable

This commit is contained in:
Lord-McSweeney 2024-08-25 17:17:56 -07:00 committed by Lord-McSweeney
parent ea85dd6dfc
commit 52e8534aba
23 changed files with 300 additions and 337 deletions

View File

@ -4,7 +4,7 @@ use std::rc::Rc;
use crate::avm2::class::AllocatorFn;
use crate::avm2::error::make_error_1107;
use crate::avm2::globals::SystemClasses;
use crate::avm2::globals::{SystemClassDefs, SystemClasses};
use crate::avm2::method::{Method, NativeMethodImpl};
use crate::avm2::scope::ScopeChain;
use crate::avm2::script::{Script, TranslationUnit};
@ -126,6 +126,9 @@ pub struct Avm2<'gc> {
/// System classes.
system_classes: Option<SystemClasses<'gc>>,
/// System class definitions.
system_class_defs: Option<SystemClassDefs<'gc>>,
/// Top-level global object. It contains most top-level types (Object, Class) and functions.
/// However, it's not strictly defined which items end up there.
toplevel_global_object: Option<Object<'gc>>,
@ -221,6 +224,7 @@ impl<'gc> Avm2<'gc> {
playerglobals_domain,
stage_domain,
system_classes: None,
system_class_defs: None,
toplevel_global_object: None,
public_namespace_base_version: Namespace::package("", ApiVersion::AllVersions, context),
@ -289,6 +293,13 @@ impl<'gc> Avm2<'gc> {
self.system_classes.as_ref().unwrap()
}
/// Return the current set of system class definitions.
///
/// This function panics if the interpreter has not yet been initialized.
pub fn class_defs(&self) -> &SystemClassDefs<'gc> {
self.system_class_defs.as_ref().unwrap()
}
pub fn toplevel_global_object(&self) -> Option<Object<'gc>> {
self.toplevel_global_object
}

View File

@ -118,8 +118,7 @@ pub fn serialize_value<'gc>(
})
.collect();
let val_type = val_type
.unwrap_or(activation.avm2().classes().object.inner_class_definition());
let val_type = val_type.unwrap_or(activation.avm2().class_defs().object);
let name = class_to_alias(activation, val_type);
Some(AmfValue::VectorObject(obj_vec, name, vec.is_fixed()))

View File

@ -367,7 +367,7 @@ impl<'gc> Class<'gc> {
),
object_vector_i_class.instance_init(),
object_vector_c_class.instance_init(),
context.avm2.classes().class.inner_class_definition(),
context.avm2.class_defs().class,
mc,
);
@ -573,7 +573,7 @@ impl<'gc> Class<'gc> {
ClassData {
name: c_name,
param: None,
super_class: Some(activation.avm2().classes().class.inner_class_definition()),
super_class: Some(activation.avm2().class_defs().class),
attributes: ClassAttributes::FINAL,
protected_namespace,
direct_interfaces: Vec::new(),
@ -871,7 +871,7 @@ impl<'gc> Class<'gc> {
ClassData {
name: c_name,
param: None,
super_class: Some(activation.avm2().classes().class.inner_class_definition()),
super_class: Some(activation.avm2().class_defs().class),
attributes: ClassAttributes::FINAL,
protected_namespace: None,
direct_interfaces: Vec::new(),
@ -1228,6 +1228,10 @@ impl<'gc> Class<'gc> {
Ref::map(self.0.read(), |c| &c.traits)
}
pub fn set_traits(&self, mc: &Mutation<'gc>, traits: Vec<Trait<'gc>>) {
self.0.write(mc).traits = traits;
}
/// Get this class's instance allocator.
///
/// If `None`, then you should use the instance allocator of the superclass
@ -1311,6 +1315,10 @@ impl<'gc> Class<'gc> {
self.0.write(mc).linked_class = ClassLink::LinkToClass(c_class);
}
pub fn is_c_class(self) -> bool {
matches!(self.0.read().linked_class, ClassLink::LinkToInstance(_))
}
pub fn i_class(self) -> Option<Class<'gc>> {
if let ClassLink::LinkToInstance(i_class) = self.0.read().linked_class {
Some(i_class)
@ -1324,4 +1332,8 @@ impl<'gc> Class<'gc> {
self.0.write(mc).linked_class = ClassLink::LinkToInstance(i_class);
}
pub fn is_i_class(self) -> bool {
matches!(self.0.read().linked_class, ClassLink::LinkToClass(_))
}
}

View File

@ -268,9 +268,9 @@ pub fn display_function<'gc>(
.map(|b| Gc::ptr_eq(b, *method))
.unwrap_or(false)
{
if bound_class.c_class().is_none() {
// If the associated class has no c_class, it is a c_class,
// and the instance initializer is the class initializer.
if bound_class.is_c_class() {
// If the associated class is a c_class, its initializer
// method is a class initializer.
output.push_utf8("cinit");
}
// We purposely do nothing for instance initializers

View File

@ -2,14 +2,17 @@ use crate::avm2::activation::Activation;
use crate::avm2::api_version::ApiVersion;
use crate::avm2::class::Class;
use crate::avm2::domain::Domain;
use crate::avm2::multiname::Multiname;
use crate::avm2::object::{ClassObject, ScriptObject, TObject};
use crate::avm2::scope::{Scope, ScopeChain};
use crate::avm2::script::Script;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::vtable::VTable;
use crate::avm2::Avm2;
use crate::avm2::Error;
use crate::avm2::Namespace;
use crate::avm2::QName;
use crate::string::AvmString;
use crate::tag_utils::{self, ControlFlow, SwfMovie, SwfSlice, SwfStream};
use gc_arena::Collect;
use std::sync::Arc;
@ -66,7 +69,6 @@ pub struct SystemClasses<'gc> {
pub number: ClassObject<'gc>,
pub int: ClassObject<'gc>,
pub uint: ClassObject<'gc>,
pub void_def: Class<'gc>,
pub namespace: ClassObject<'gc>,
pub array: ClassObject<'gc>,
pub movieclip: ClassObject<'gc>,
@ -179,6 +181,15 @@ pub struct SystemClasses<'gc> {
pub textrun: ClassObject<'gc>,
}
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct SystemClassDefs<'gc> {
pub object: Class<'gc>,
pub class: Class<'gc>,
pub function: Class<'gc>,
pub void: Class<'gc>,
}
impl<'gc> SystemClasses<'gc> {
/// Construct a minimal set of system classes necessary for bootstrapping
/// player globals.
@ -198,7 +209,6 @@ impl<'gc> SystemClasses<'gc> {
number: object,
int: object,
uint: object,
void_def: object.inner_class_definition(),
namespace: object,
array: object,
movieclip: object,
@ -311,41 +321,37 @@ impl<'gc> SystemClasses<'gc> {
}
}
impl<'gc> SystemClassDefs<'gc> {
fn new(object: Class<'gc>, class: Class<'gc>, function: Class<'gc>, void: Class<'gc>) -> Self {
SystemClassDefs {
object,
class,
function,
void,
}
}
}
/// Looks up a function defined in the script domain, and defines it on the global object.
///
/// This expects the looked-up value to be a function.
fn define_fn_on_global<'gc>(
activation: &mut Activation<'_, 'gc>,
package: impl Into<AvmString<'gc>>,
name: &'static str,
script: Script<'gc>,
) {
let (_, global, domain) = script.init();
let qname = QName::new(
Namespace::package(
package,
ApiVersion::AllVersions,
&mut activation.borrow_gc(),
),
Namespace::package("", ApiVersion::AllVersions, &mut activation.borrow_gc()),
name,
);
let func = domain
.get_defined_value(activation, qname)
.expect("Function being defined on global should be defined in domain!");
global.install_const_late(
activation.context.gc_context,
qname,
func,
activation
.avm2()
.classes()
.function
.inner_class_definition(),
);
script
.global_class()
.define_constant_function_instance_trait(activation, qname, func);
global
.init_property(&qname.into(), func, activation)
.expect("Should set property");
}
/// Add a fully-formed class object builtin to the global scope.
@ -361,15 +367,10 @@ fn dynamic_class<'gc>(
let class = class_object.inner_class_definition();
let name = class.name();
global.install_const_late(
activation.context.gc_context,
name,
class_object.into(),
class_object.instance_class(),
);
script
.global_class()
.define_constant_class_instance_trait(activation, name, class_object);
global
.init_property(&name.into(), class_object.into(), activation)
.expect("Should set property");
domain.export_definition(name, script, activation.context.gc_context)
}
@ -398,24 +399,19 @@ fn class<'gc>(
let class_name = class_def.name();
let class_object = ClassObject::from_class(activation, class_def, super_class)?;
global.install_const_late(
mc,
class_name,
class_object.into(),
class_object.instance_class(),
);
script.global_class().define_constant_class_instance_trait(
activation,
class_name,
class_object,
);
global
.init_property(&class_name.into(), class_object.into(), activation)
.expect("Should set property");
domain.export_definition(class_name, script, mc);
domain.export_class(class_name, class_def, mc);
Ok(class_object)
}
fn vector_class<'gc>(
param_class: Option<ClassObject<'gc>>,
param_class: Option<Class<'gc>>,
class_def: Class<'gc>,
legacy_name: &'static str,
script: Script<'gc>,
activation: &mut Activation<'_, 'gc>,
@ -423,12 +419,9 @@ fn vector_class<'gc>(
let mc = activation.context.gc_context;
let (_, global, mut domain) = script.init();
let param_class = param_class.map(|c| c.inner_class_definition());
let vector_cls = class(
vector::create_builtin_class(activation, param_class),
script,
activation,
)?;
let object_class = activation.avm2().classes().object;
let vector_cls = ClassObject::from_class(activation, class_def, Some(object_class))?;
let generic_vector = activation.avm2().classes().generic_vector;
generic_vector.add_application(mc, param_class, vector_cls);
@ -436,15 +429,11 @@ fn vector_class<'gc>(
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(
mc,
legacy_name,
vector_cls.into(),
vector_cls.instance_class(),
);
script
.global_class()
.define_constant_class_instance_trait(activation, legacy_name, vector_cls);
global
.init_property(&legacy_name.into(), vector_cls.into(), activation)
.expect("Should set property");
domain.export_definition(legacy_name, script, mc);
Ok(vector_cls)
}
@ -477,7 +466,7 @@ pub fn load_player_globals<'gc>(
// This part of global initialization is very complicated, because
// everything has to circularly reference everything else:
//
// - Object is an instance of itself, as well as it's prototype
// - Object is an instance of itself, as well as its prototype
// - All other types are instances of Class, which is an instance of
// itself
// - Function's prototype is an instance of itself
@ -506,21 +495,110 @@ pub fn load_player_globals<'gc>(
// Function is more of a "normal" class than the other two, so we can create it normally.
let fn_classdef = function::create_class(activation, object_i_class, class_i_class);
// Do the same for the global class
let global_classdef = global_scope::create_class(activation, object_i_class, class_i_class);
// void doesn't have a ClassObject
let void_def = void::create_class(activation);
// Register the classes in the domain, now (except for the global class)
domain.export_class(object_i_class.name(), object_i_class, mc);
domain.export_class(class_i_class.name(), class_i_class, mc);
domain.export_class(fn_classdef.name(), fn_classdef, mc);
domain.export_class(void_def.name(), void_def, mc);
activation.context.avm2.system_class_defs = Some(SystemClassDefs::new(
object_i_class,
class_i_class,
fn_classdef,
void_def,
));
let string_class = string::create_class(activation);
let boolean_class = boolean::create_class(activation);
let number_class = number::create_class(activation);
let int_class = int::create_class(activation);
let uint_class = uint::create_class(activation);
let namespace_class = namespace::create_class(activation);
let array_class = array::create_class(activation);
let vector_generic_class = vector::create_generic_class(activation);
let date_class = date::create_class(activation);
let vector_int_class = vector::create_builtin_class(activation, Some(int_class));
let vector_uint_class = vector::create_builtin_class(activation, Some(uint_class));
let vector_number_class = vector::create_builtin_class(activation, Some(number_class));
let vector_object_class = vector::create_builtin_class(activation, None);
let public_ns = activation.avm2().public_namespace_base_version;
let vector_public_ns = activation.avm2().vector_public_namespace;
let vector_internal_ns = activation.avm2().vector_internal_namespace;
// Unfortunately we need to specify the global traits manually, at least until
// all the builtin classes are defined in AS.
let mut global_traits = Vec::new();
let class_trait_list = &[
(public_ns, "Object", object_i_class),
(public_ns, "Class", class_i_class),
(public_ns, "Function", fn_classdef),
(public_ns, "String", string_class),
(public_ns, "Boolean", boolean_class),
(public_ns, "Number", number_class),
(public_ns, "int", int_class),
(public_ns, "uint", uint_class),
(public_ns, "Namespace", namespace_class),
(public_ns, "Array", array_class),
(public_ns, "Date", date_class),
(vector_public_ns, "Vector", vector_generic_class),
(vector_internal_ns, "Vector$int", vector_int_class),
(vector_internal_ns, "Vector$uint", vector_uint_class),
(vector_internal_ns, "Vector$double", vector_number_class),
(vector_internal_ns, "Vector$object", vector_object_class),
];
// "trace" is the only builtin function not defined on the toplevel global object
let function_trait_list = &[
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"escape",
"unescape",
"isXMLName",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
];
for (namespace, name, class) in class_trait_list {
let qname = QName::new(*namespace, *name);
global_traits.push(Trait::from_class(qname, *class));
}
for function_name in function_trait_list {
let qname = QName::new(public_ns, *function_name);
// FIXME: These should be TraitKind::Methods, to match how they are when
// defined on the AS global object, but we don't have the actual Methods
// right now.
global_traits.push(Trait::from_const(
qname,
Multiname::new(public_ns, "Function"),
Some(Value::Null),
));
}
// Create the builtin globals' classdef
let global_classdef = global_scope::create_class(activation, global_traits);
// Initialize the global object. This gives it a temporary vtable until the
// global ClassObject is constructed and we have the true vtable.
// global ClassObject is constructed and we have the true vtable; nothing actually
// operates on the global object until it gets its true vtable, so this should
// be fine.
let globals = ScriptObject::custom_object(mc, global_classdef, None, global_classdef.vtable());
// Initialize the script
let script = Script::empty_script(mc, globals, domain);
globals.install_instance_slots(mc);
let gs = ScopeChain::new(domain).chain(mc, &[Scope::new(globals)]);
let scope = ScopeChain::new(domain);
let gs = scope.chain(mc, &[Scope::new(globals)]);
activation.set_outer(gs);
let object_class = ClassObject::from_class_partial(activation, object_i_class, None)?;
@ -577,16 +655,26 @@ pub fn load_player_globals<'gc>(
let fn_proto = fn_class.construct(activation, &[])?;
fn_class.link_prototype(activation, fn_proto)?;
// Construct the global class.
let global_class = ClassObject::from_class(activation, global_classdef, Some(object_class))?;
// Object prototype is enough
globals.set_proto(mc, object_class.prototype());
globals.set_proto(mc, global_class.prototype());
globals.set_vtable(mc, global_class.instance_vtable());
// Create a new, full vtable for globals.
let global_obj_vtable = VTable::empty(mc);
global_obj_vtable.init_vtable(
global_classdef,
Some(object_class),
&global_classdef.traits(),
Some(scope),
Some(object_class.instance_vtable()),
mc,
);
globals.set_vtable(mc, global_obj_vtable);
activation.context.avm2.toplevel_global_object = Some(globals);
script.set_global_class(mc, global_classdef);
script.set_global_class_obj(mc, global_class);
// Initialize the script
let script = Script::empty_script(mc, globals, domain);
// From this point, `globals` is safe to be modified
@ -597,55 +685,44 @@ pub fn load_player_globals<'gc>(
// After this point, it is safe to initialize any other classes.
// Make sure to initialize superclasses *before* their subclasses!
avm2_system_class!(string, activation, string::create_class(activation), script);
avm2_system_class!(
boolean,
activation,
boolean::create_class(activation),
script
);
avm2_system_class!(number, activation, number::create_class(activation), script);
avm2_system_class!(int, activation, int::create_class(activation), script);
avm2_system_class!(uint, activation, uint::create_class(activation), script);
avm2_system_class!(
namespace,
activation,
namespace::create_class(activation),
script
);
avm2_system_class!(array, activation, array::create_class(activation), script);
avm2_system_class!(string, activation, string_class, script);
avm2_system_class!(boolean, activation, boolean_class, script);
avm2_system_class!(number, activation, number_class, script);
avm2_system_class!(int, activation, int_class, script);
avm2_system_class!(uint, activation, uint_class, script);
avm2_system_class!(namespace, activation, namespace_class, script);
avm2_system_class!(array, activation, array_class, script);
// void doesn't have a ClassObject
let void_def = void::create_class(activation);
activation.avm2().system_classes.as_mut().unwrap().void_def = void_def;
domain.export_class(void_def.name(), void_def, mc);
avm2_system_class!(
generic_vector,
activation,
vector::create_generic_class(activation),
script
);
avm2_system_class!(generic_vector, activation, vector_generic_class, script);
vector_class(
Some(activation.avm2().classes().int),
Some(int_class),
vector_int_class,
"Vector$int",
script,
activation,
)?;
vector_class(
Some(activation.avm2().classes().uint),
Some(uint_class),
vector_uint_class,
"Vector$uint",
script,
activation,
)?;
vector_class(
Some(activation.avm2().classes().number),
Some(number_class),
vector_number_class,
"Vector$double",
script,
activation,
)?;
let object_vector = vector_class(None, "Vector$object", script, activation)?;
let object_vector = vector_class(
None,
vector_object_class,
"Vector$object",
script,
activation,
)?;
activation
.avm2()
.system_classes
@ -653,7 +730,7 @@ pub fn load_player_globals<'gc>(
.unwrap()
.object_vector = object_vector;
avm2_system_class!(date, activation, date::create_class(activation), script);
avm2_system_class!(date, activation, date_class, script);
// Inside this call, the macro `avm2_system_classes_playerglobal`
// triggers classloading. Therefore, we run `load_playerglobal`
@ -661,22 +738,10 @@ pub fn load_player_globals<'gc>(
// this call.
load_playerglobal(activation, domain)?;
// Except for `trace`, top-level builtin functions are defined
// on the `global` object.
define_fn_on_global(activation, "", "decodeURI", script);
define_fn_on_global(activation, "", "decodeURIComponent", script);
define_fn_on_global(activation, "", "encodeURI", script);
define_fn_on_global(activation, "", "encodeURIComponent", script);
define_fn_on_global(activation, "", "escape", script);
define_fn_on_global(activation, "", "unescape", script);
define_fn_on_global(activation, "", "isXMLName", script);
define_fn_on_global(activation, "", "isFinite", script);
define_fn_on_global(activation, "", "isNaN", script);
define_fn_on_global(activation, "", "parseFloat", script);
define_fn_on_global(activation, "", "parseInt", script);
global_classdef.mark_traits_loaded(mc);
global_classdef.init_vtable(activation.context)?;
for function_name in function_trait_list {
// Now copy those functions to the global object.
define_fn_on_global(activation, function_name, script);
}
Ok(())
}
@ -731,7 +796,7 @@ fn load_playerglobal<'gc>(
let _ = tag_utils::decode_tags(&mut reader, tag_callback);
macro_rules! avm2_system_classes_playerglobal {
($activation:expr, $script:expr, [$(($package:expr, $class_name:expr, $field:ident)),* $(,)?]) => {
($activation:expr, [$(($package:expr, $class_name:expr, $field:ident)),* $(,)?]) => {
let activation = $activation;
$(
// Lookup with the highest version, so we we see all defined classes here
@ -750,7 +815,6 @@ fn load_playerglobal<'gc>(
// and are stored in 'avm2().system_classes'
avm2_system_classes_playerglobal!(
&mut *activation,
script,
[
("", "Error", error),
("", "ArgumentError", argumenterror),

View File

@ -1247,10 +1247,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "Array"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Array instance initializer>", mc),
Method::from_builtin(class_init, "<Array class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -377,7 +377,7 @@ fn describe_internal_body<'gc>(
let declared_by = method.class;
if flags.contains(DescribeTypeFlags::HIDE_OBJECT)
&& declared_by == activation.avm2().classes().object.inner_class_definition()
&& declared_by == activation.avm2().class_defs().object
{
continue;
}

View File

@ -136,10 +136,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "Boolean"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Boolean instance initializer>", mc),
Method::from_builtin(class_init, "<Boolean class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -1326,10 +1326,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "Date"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Date instance initializer>", mc),
Method::from_builtin(class_init, "<Date class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -8,6 +8,7 @@ use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::object::Object;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::avm2::QName;
@ -21,42 +22,24 @@ pub fn instance_init<'gc>(
Ok(Value::Undefined)
}
/// Implements `global`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Undefined)
}
/// Construct `global`'s class.
pub fn create_class<'gc>(
activation: &mut Activation<'_, 'gc>,
object_classdef: Class<'gc>,
class_classdef: Class<'gc>,
traits: Vec<Trait<'gc>>,
) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
let class = Class::custom_new(
QName::new(activation.avm2().public_namespace_base_version, "global"),
Some(object_classdef),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<global instance initializer>", mc),
Method::from_builtin(class_init, "<global class initializer>", mc),
class_classdef,
mc,
);
class.mark_traits_loaded(activation.context.gc_context);
class.set_traits(mc, traits);
class.mark_traits_loaded(mc);
class
.init_vtable(activation.context)
.expect("Native class's vtable should initialize");
let c_class = class.c_class().expect("Class::new returns an i_class");
c_class.mark_traits_loaded(activation.context.gc_context);
c_class
.init_vtable(activation.context)
.expect("Native class's vtable should initialize");
class
}

View File

@ -221,7 +221,7 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "int"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin_and_params(
instance_init,
"<int instance initializer>",
@ -235,7 +235,7 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
mc,
),
Method::from_builtin(class_init, "<int class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -174,10 +174,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "Namespace"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Namespace instance initializer>", mc),
Method::from_builtin(class_init, "<Namespace class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -376,10 +376,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "Number"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Number instance initializer>", mc),
Method::from_builtin(class_init, "<Number class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -689,10 +689,10 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "String"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<String instance initializer>", mc),
Method::from_builtin(class_init, "<String class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -222,7 +222,7 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().public_namespace_base_version, "uint"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin_and_params(
instance_init,
"<uint instance initializer>",
@ -236,7 +236,7 @@ pub fn create_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
mc,
),
Method::from_builtin(class_init, "<uint class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -901,10 +901,10 @@ pub fn create_generic_class<'gc>(activation: &mut Activation<'_, 'gc>) -> Class<
let mc = activation.context.gc_context;
let class = Class::new(
QName::new(activation.avm2().vector_public_namespace, "Vector"),
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(generic_init, "<Vector instance initializer>", mc),
Method::from_builtin(generic_init, "<Vector class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);
@ -947,10 +947,10 @@ pub fn create_builtin_class<'gc>(
let class = Class::new(
name,
Some(activation.avm2().classes().object.inner_class_definition()),
Some(activation.avm2().class_defs().object),
Method::from_builtin(instance_init, "<Vector.<T> instance initializer>", mc),
Method::from_builtin(class_init, "<Vector.<T> class initializer>", mc),
activation.avm2().classes().class.inner_class_definition(),
activation.avm2().class_defs().class,
mc,
);

View File

@ -16,7 +16,6 @@ use crate::avm2::vtable::{ClassBoundMethod, VTable};
use crate::avm2::Error;
use crate::avm2::Multiname;
use crate::avm2::Namespace;
use crate::avm2::QName;
use crate::bitmap::bitmap_data::BitmapDataWrapper;
use crate::display_object::DisplayObject;
use crate::html::TextFormat;
@ -860,23 +859,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
base.install_bound_method(mc, disp_id, function)
}
/// Install a const trait on the global object.
/// This should only ever be called on the `global` object, during initialization.
#[no_dynamic]
fn install_const_late(
&self,
mc: &Mutation<'gc>,
name: QName<'gc>,
value: Value<'gc>,
class: Class<'gc>,
) {
let new_slot_id = self
.vtable()
.install_const_trait_late(mc, name, value, class);
self.base().install_const_slot_late(mc, new_slot_id, value);
}
#[no_dynamic]
fn install_instance_slots(&self, mc: &Mutation<'gc>) {
self.base().install_instance_slots(mc);

View File

@ -95,7 +95,6 @@ impl<'gc> ScriptObject<'gc> {
/// This should *not* be used unless you really need
/// to do something low-level, weird or lazily initialize the object.
/// You shouldn't let scripts observe this weirdness.
/// Another exception is ES3 class-less objects, which we don't really understand well :)
///
/// The "everyday" way to create a normal empty ScriptObject (AS "Object") is to call
/// `avm2.classes().object.construct(self, &[])`.
@ -355,19 +354,6 @@ impl<'gc> ScriptObjectWrapper<'gc> {
}
}
/// Set a slot by its index. This does extend the array if needed.
/// This should only be used during AVM initialization, not at runtime.
pub fn install_const_slot_late(&self, mc: &Mutation<'gc>, id: u32, value: Value<'gc>) {
let mut slots = self.slots_mut(mc);
if slots.len() < id as usize + 1 {
slots.resize(id as usize + 1, Value::Undefined);
}
if let Some(slot) = slots.get_mut(id as usize) {
*slot = value;
}
}
/// Retrieve a bound method from the method table.
pub fn get_bound_method(&self, id: u32) -> Option<FunctionObject<'gc>> {
self.bound_methods().get(id as usize).and_then(|v| *v)

View File

@ -100,7 +100,7 @@ impl<'gc> OptValue<'gc> {
|| self.class == Some(classes.uint.inner_class_definition())
|| self.class == Some(classes.number.inner_class_definition())
|| self.class == Some(classes.boolean.inner_class_definition())
|| self.class == Some(classes.void_def)
|| self.class == Some(activation.avm2().class_defs().void)
}
pub fn merged_with(self, other: OptValue<'gc>) -> OptValue<'gc> {
@ -344,7 +344,7 @@ pub fn optimize<'gc>(
.classes()
.function
.inner_class_definition(),
void: activation.avm2().classes().void_def,
void: activation.avm2().class_defs().void,
namespace: activation
.avm2()
.classes()

View File

@ -6,10 +6,11 @@ use crate::avm2::class::Class;
use crate::avm2::domain::Domain;
use crate::avm2::globals::global_scope;
use crate::avm2::method::{BytecodeMethod, Method};
use crate::avm2::object::{ClassObject, Object, TObject};
use crate::avm2::object::{Object, ScriptObject, TObject};
use crate::avm2::scope::ScopeChain;
use crate::avm2::traits::{Trait, TraitKind};
use crate::avm2::value::Value;
use crate::avm2::vtable::VTable;
use crate::avm2::Multiname;
use crate::avm2::Namespace;
use crate::avm2::{Avm2, Error};
@ -18,7 +19,6 @@ use crate::string::{AvmAtom, AvmString};
use crate::tag_utils::SwfMovie;
use crate::PlayerRuntime;
use gc_arena::{Collect, Gc, GcCell, Mutation};
use std::cell::Ref;
use std::fmt::Debug;
use std::rc::Rc;
use std::sync::Arc;
@ -254,28 +254,7 @@ impl<'gc> TranslationUnit<'gc> {
drop(read);
let object_class = activation.avm2().classes().object;
let class_classdef = activation.avm2().classes().class.inner_class_definition();
let global_classdef = global_scope::create_class(
activation,
object_class.inner_class_definition(),
class_classdef,
);
let global_class =
ClassObject::from_class(activation, global_classdef, Some(object_class))?;
let global_obj = global_class.construct(activation, &[])?;
let script = Script::from_abc_index(
self,
script_index,
global_obj,
global_class,
domain,
activation,
)?;
let script = Script::from_abc_index(self, script_index, domain, activation)?;
self.0.write(activation.context.gc_context).scripts[script_index as usize] = Some(script);
script.load_traits(self, script_index, activation)?;
@ -430,13 +409,7 @@ pub struct Script<'gc>(pub GcCell<'gc, ScriptData<'gc>>);
#[collect(no_drop)]
pub struct ScriptData<'gc> {
/// The global object for the script.
globals: Object<'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>>,
globals: Option<Object<'gc>>,
/// The domain associated with this script.
domain: Domain<'gc>,
@ -444,9 +417,6 @@ pub struct ScriptData<'gc> {
/// The initializer method to run for the script.
init: Method<'gc>,
/// Traits that this script uses.
traits: Vec<Trait<'gc>>,
/// Whether or not we loaded our traits.
traits_loaded: bool,
@ -474,16 +444,13 @@ impl<'gc> Script<'gc> {
Self(GcCell::new(
mc,
ScriptData {
globals,
global_class: None,
global_class_obj: None,
globals: Some(globals),
domain,
init: Method::from_builtin(
|_, _, _| Ok(Value::Undefined),
"<Built-in script initializer>",
mc,
),
traits: Vec::new(),
traits_loaded: true,
initialized: false,
translation_unit: None,
@ -504,8 +471,6 @@ impl<'gc> Script<'gc> {
pub fn from_abc_index(
unit: TranslationUnit<'gc>,
script_index: u32,
globals: Object<'gc>,
global_class_obj: ClassObject<'gc>,
domain: Domain<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> Result<Self, Error<'gc>> {
@ -521,12 +486,9 @@ impl<'gc> Script<'gc> {
Ok(Self(GcCell::new(
activation.context.gc_context,
ScriptData {
globals,
global_class: Some(global_class_obj.inner_class_definition()),
global_class_obj: Some(global_class_obj),
globals: None,
domain,
init,
traits: Vec::new(),
traits_loaded: false,
initialized: false,
translation_unit: Some(unit),
@ -547,7 +509,9 @@ impl<'gc> Script<'gc> {
script_index: u32,
activation: &mut Activation<'_, 'gc>,
) -> Result<(), Error<'gc>> {
let mut write = self.0.write(activation.context.gc_context);
let mc = activation.gc();
let mut write = self.0.write(mc);
if write.traits_loaded {
return Ok(());
@ -561,30 +525,49 @@ impl<'gc> Script<'gc> {
.get(script_index as usize)
.ok_or_else(|| "LoadError: Script index not valid".into());
let script = script?;
let mut domain = write.domain;
let mut traits = Vec::new();
for abc_trait in script.traits.iter() {
let newtrait = Trait::from_abc_trait(unit, abc_trait, activation)?;
write
.domain
.export_definition(newtrait.name(), self, activation.context.gc_context);
domain.export_definition(newtrait.name(), self, mc);
if let TraitKind::Class { class, .. } = newtrait.kind() {
write
.domain
.export_class(newtrait.name(), *class, activation.context.gc_context);
domain.export_class(newtrait.name(), *class, mc);
}
write.traits.push(newtrait.clone());
write
.global_class
.expect("Global class should be initialized")
.define_instance_trait(activation.context.gc_context, newtrait);
traits.push(newtrait);
}
drop(write);
self.global_class()
.mark_traits_loaded(activation.context.gc_context);
self.global_class().init_vtable(activation.context)?;
// Now that we have the traits, create the global class for this script
// and use it to initialize a vtable and global object.
let global_class = global_scope::create_class(activation, traits);
let scope = ScopeChain::new(domain);
let object_class = activation.avm2().classes().object;
let global_obj_vtable = VTable::empty(mc);
global_obj_vtable.init_vtable(
global_class,
Some(object_class),
&global_class.traits(),
Some(scope),
Some(object_class.instance_vtable()),
mc,
);
let global_object = ScriptObject::custom_object(
mc,
global_class,
object_class.proto(), // Just use Object's prototype
global_obj_vtable,
);
global_object.install_instance_slots(mc);
self.0.write(mc).globals = Some(global_object);
Ok(())
}
@ -592,7 +575,9 @@ impl<'gc> Script<'gc> {
/// Return the entrypoint for the script and the scope it should run in.
pub fn init(self) -> (Method<'gc>, Object<'gc>, Domain<'gc>) {
let read = self.0.read();
(read.init, read.globals, read.domain)
let globals = read.globals.expect("Global object should be initialized");
(read.init, globals, read.domain)
}
pub fn domain(self) -> Domain<'gc> {
@ -610,16 +595,9 @@ impl<'gc> Script<'gc> {
pub fn global_class(self) -> Class<'gc> {
self.0
.read()
.global_class
.expect("Global class should be initialized if it is accessed")
}
pub fn set_global_class(self, mc: &Mutation<'gc>, global_class: Class<'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);
.globals
.expect("Global object should be initialized")
.instance_class()
}
/// Return the global scope for the script.
@ -629,46 +607,16 @@ impl<'gc> Script<'gc> {
pub fn globals(self, context: &mut UpdateContext<'gc>) -> Result<Object<'gc>, Error<'gc>> {
let mut write = self.0.write(context.gc_context);
let globals = write.globals.expect("Global object should be initialized");
if !write.initialized {
write.initialized = true;
let globals = write.globals;
let domain = write.domain;
drop(write);
let scope = ScopeChain::new(domain);
globals.vtable().init_vtable(
globals.instance_class(),
Some(context.avm2.classes().object),
&self.traits()?,
Some(scope),
None,
context.gc_context,
);
globals.install_instance_slots(context.gc_context);
Avm2::run_script_initializer(self, context)?;
Ok(globals)
} else {
Ok(write.globals)
}
}
/// Return traits for this script.
///
/// This function will return an error if it is incorrectly called before
/// traits are loaded.
pub fn traits<'a>(&'a self) -> Result<Ref<'a, [Trait<'gc>]>, Error<'gc>> {
let read = self.0.read();
if !read.traits_loaded {
return Err("LoadError: Script traits accessed before they were loaded!".into());
}
Ok(Ref::map(read, |read| &read.traits[..]))
Ok(globals)
}
}

View File

@ -1007,7 +1007,7 @@ impl<'gc> Value<'gc> {
}
if matches!(self, Value::Undefined) || matches!(self, Value::Null) {
if class == activation.avm2().classes().void_def {
if class == activation.avm2().class_defs().void {
return Ok(Value::Undefined);
}
return Ok(Value::Null);
@ -1104,7 +1104,7 @@ impl<'gc> Value<'gc> {
}
if let Value::Undefined = self {
if type_object == activation.avm2().classes().void_def {
if type_object == activation.avm2().class_defs().void {
return true;
}
}

View File

@ -140,7 +140,7 @@ impl<'gc> VectorStorage<'gc> {
/// Get the value type this vector coerces things to.
pub fn value_type_for_coercion(&self, activation: &mut Activation<'_, 'gc>) -> Class<'gc> {
self.value_type
.unwrap_or_else(|| activation.avm2().classes().object.inner_class_definition())
.unwrap_or_else(|| activation.avm2().class_defs().object)
}
/// Check if a vector index is in bounds.

View File

@ -534,28 +534,6 @@ impl<'gc> VTable<'gc> {
)
}
/// Install a const trait on the global object.
/// This should only ever be called via `Object::install_const_late`,
/// on the `global` object.
pub fn install_const_trait_late(
self,
mc: &Mutation<'gc>,
name: QName<'gc>,
value: Value<'gc>,
class: Class<'gc>,
) -> u32 {
let mut write = self.0.write(mc);
write.default_slots.push(Some(value));
let new_slot_id = write.default_slots.len() as u32 - 1;
write
.resolved_traits
.insert(name, Property::new_const_slot(new_slot_id));
write.slot_classes.push(PropertyClass::Class(class));
new_slot_id
}
/// Install an existing trait under a new name, provided by interface.
/// This should only ever be called by `link_interfaces`.
pub fn copy_property_for_interface(