Remove the two-step initialization process and construct an ES4 class for `Object`, `Function`, and `Class`.

This has some particularly annoying consequences for initialization order: notably, we can't actually create any ES4 classes using the standard machinery until after the three objects I just mentioned get created. Ergo, we have to create them through lower-level means, handing prototypes around, and then initialize AVM2's system prototypes list for it.

When we start adding more system prototypes, we'll also have to fill the extras with blank objects and then slot them in as we create them.
This commit is contained in:
David Wendt 2020-08-04 23:00:17 -04:00
parent 16d8c85d20
commit 11ddccfa6a
11 changed files with 431 additions and 243 deletions

View File

@ -2,11 +2,10 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::globals::SystemPrototypes; use crate::avm2::globals::SystemPrototypes;
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, ScriptObject, TObject};
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
use crate::avm2::script::Script; use crate::avm2::script::Script;
use crate::avm2::script::TranslationUnit; use crate::avm2::script::TranslationUnit;
use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::tag_utils::SwfSlice; use crate::tag_utils::SwfSlice;

View File

@ -51,6 +51,34 @@ pub struct SystemPrototypes<'gc> {
pub namespace: Object<'gc>, pub namespace: 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>,
empty: Object<'gc>,
) -> Self {
SystemPrototypes {
object,
function,
class,
string: empty,
boolean: empty,
number: empty,
int: empty,
uint: empty,
namespace: empty,
}
}
}
/// Add a free-function builtin to the global scope. /// Add a free-function builtin to the global scope.
fn function<'gc>( fn function<'gc>(
mc: MutationContext<'gc, '_>, mc: MutationContext<'gc, '_>,
@ -69,39 +97,47 @@ fn function<'gc>(
.unwrap() .unwrap()
} }
/// Add an ES3-style builtin to the global scope. /// Add a class builtin with prototype methods to the global scope.
fn oldstyle_class<'gc>( ///
/// Since the function has to return a normal prototype object in this case, we
/// have to construct a constructor to go along with it, as if we had called
/// `install_foreign_trait` with such a class.
fn dynamic_class<'gc>(
mc: MutationContext<'gc, '_>, mc: MutationContext<'gc, '_>,
mut global_scope: Object<'gc>, mut global_scope: Object<'gc>,
package: impl Into<AvmString<'gc>>, constr: Object<'gc>,
name: impl Into<AvmString<'gc>>,
constr: NativeMethod<'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) { ) {
global_scope let name = constr
.install_dynamic_property( .as_class()
mc, .expect("constrs have classes in them")
QName::new(Namespace::package(package), name), .read()
FunctionObject::from_builtin_constr(mc, constr, proto, fn_proto) .name()
.unwrap() .clone();
.into(),
) global_scope.install_const(mc, name, 0, constr.into());
.unwrap();
} }
/// Add a class builtin to the global scope. /// Add a class builtin to the global scope.
///
/// This function returns a prototype which may be stored in `SystemPrototypes`.
fn class<'gc>( fn class<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
mut global: Object<'gc>, mut global: Object<'gc>,
class_def: GcCell<'gc, Class<'gc>>, class_def: GcCell<'gc, Class<'gc>>,
) -> Result<(), Error> { ) -> Result<Object<'gc>, Error> {
let class_trait = Trait::from_class(class_def); let class_trait = Trait::from_class(class_def);
let global_scope = Scope::push_scope(global.get_scope(), global, activation.context.gc_context); let global_scope = Scope::push_scope(global.get_scope(), global, activation.context.gc_context);
let mut constr = global
.install_foreign_trait(activation, class_trait, Some(global_scope), global)?
.coerce_to_object(activation)?;
global.install_foreign_trait(activation, class_trait, Some(global_scope), global)?; constr
.get_property(
Ok(()) constr,
&QName::new(Namespace::public_namespace(), "prototype"),
activation,
)?
.coerce_to_object(activation)
} }
/// Add a builtin constant to the global scope. /// Add a builtin constant to the global scope.
@ -115,131 +151,99 @@ fn constant<'gc>(
global_scope.install_const(mc, QName::new(Namespace::package(package), name), 0, value) global_scope.install_const(mc, QName::new(Namespace::package(package), name), 0, value)
} }
/// Construct a new global scope.
///
/// This function returns both the global scope object, as well as all builtin
/// prototypes that other parts of the VM will need to use.
///
/// Due to a limitation of our type system and our garbage collector, the
/// player needs a valid `Avm2` but cannot provide us an `UpdateContext` yet.
/// As a result, global scope initialization is split into an "oldstyle phase"
/// and a "player-globals phase". This is the former phase, where we initialize
/// as much as we can without an `UpdateContext`. Note that not all
/// `SystemPrototypes` will be necessarily valid at this point in time, and
/// using them right away will result in objects of the wrong type.
pub fn construct_global_scope<'gc>(
mc: MutationContext<'gc, '_>,
) -> (Object<'gc>, SystemPrototypes<'gc>) {
let gs = ScriptObject::bare_object(mc);
// public / root package
let object_proto = ScriptObject::bare_object(mc);
let fn_proto = function::create_proto(mc, object_proto);
let class_proto = class::create_proto(mc, object_proto, fn_proto);
let string_proto = string::create_proto(mc, object_proto, fn_proto);
let boolean_proto = boolean::create_proto(mc, object_proto, fn_proto);
let number_proto = number::create_proto(mc, object_proto, fn_proto);
let int_proto = int::create_proto(mc, object_proto, fn_proto);
let uint_proto = uint::create_proto(mc, object_proto, fn_proto);
let namespace_proto = namespace::create_proto(mc, object_proto, fn_proto);
object::fill_proto(mc, object_proto, fn_proto);
oldstyle_class(
mc,
gs,
"",
"Object",
object::constructor,
object_proto,
fn_proto,
);
oldstyle_class(
mc,
gs,
"",
"Function",
function::constructor,
fn_proto,
fn_proto,
);
oldstyle_class(
mc,
gs,
"",
"Class",
class::constructor,
class_proto,
fn_proto,
);
oldstyle_class(
mc,
gs,
"",
"String",
string::constructor,
string_proto,
fn_proto,
);
oldstyle_class(
mc,
gs,
"",
"Boolean",
boolean::constructor,
boolean_proto,
fn_proto,
);
oldstyle_class(
mc,
gs,
"",
"Number",
number::constructor,
number_proto,
fn_proto,
);
oldstyle_class(mc, gs, "", "int", int::constructor, int_proto, fn_proto);
oldstyle_class(mc, gs, "", "uint", uint::constructor, uint_proto, fn_proto);
oldstyle_class(
mc,
gs,
"",
"Namespace",
namespace::constructor,
namespace_proto,
fn_proto,
);
function(mc, gs, "", "trace", trace, fn_proto);
constant(mc, gs, "", "undefined", Value::Undefined);
constant(mc, gs, "", "null", Value::Null);
constant(mc, gs, "", "NaN", NAN.into());
constant(mc, gs, "", "Infinity", f64::INFINITY.into());
let system_prototypes = SystemPrototypes {
object: object_proto,
function: fn_proto,
class: class_proto,
string: string_proto,
boolean: boolean_proto,
number: number_proto,
int: int_proto,
uint: uint_proto,
namespace: namespace_proto,
};
(gs, system_prototypes)
}
/// Initialize all remaining builtin classes. /// Initialize all remaining builtin classes.
/// ///
/// Due to a limitation of our type system and our garbage collector, the /// This should be called only once, to construct the global scope of the
/// player needs a valid `Avm2` but cannot provide us an `UpdateContext` yet. /// player. It will return a list of prototypes it has created, which should be
/// As a result, global scope initialization is split into an "oldstyle phase" /// stored on the AVM.
/// and a "player-globals phase". This is the latter phase.
pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Result<(), Error> { pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Result<(), Error> {
let gs = activation.avm2().globals(); let gs = activation.avm2().globals();
// public / root package
let object_proto = object::create_proto(activation);
let (function_constr, fn_proto) = function::create_class(activation, object_proto);
let (class_constr, class_proto) = class::create_class(activation, object_proto, fn_proto);
let object_constr = object::fill_proto(activation.context.gc_context, object_proto, fn_proto);
dynamic_class(activation.context.gc_context, gs, object_constr);
dynamic_class(activation.context.gc_context, gs, function_constr);
dynamic_class(activation.context.gc_context, gs, class_constr);
// At this point, we need at least a partial set of system prototypes in
// order to continue initializing the player. The rest of the prototypes
// are set to a bare object until we have a chance to initialize them.
activation.context.avm2.system_prototypes = Some(SystemPrototypes::new(
object_proto,
fn_proto,
class_proto,
ScriptObject::bare_object(activation.context.gc_context),
));
// Even sillier: for the sake of clarity and the borrow checker we need to
// clone the prototypes list and modify it outside of the activation. This
// also has the side effect that none of these classes can get at each
// other from the activation they're handed.
let mut sp = activation.context.avm2.system_prototypes.clone().unwrap();
sp.string = class(
activation,
gs,
string::create_class(activation.context.gc_context),
)?;
sp.boolean = class(
activation,
gs,
boolean::create_class(activation.context.gc_context),
)?;
sp.number = class(
activation,
gs,
number::create_class(activation.context.gc_context),
)?;
sp.int = class(
activation,
gs,
int::create_class(activation.context.gc_context),
)?;
sp.uint = class(
activation,
gs,
uint::create_class(activation.context.gc_context),
)?;
sp.namespace = class(
activation,
gs,
namespace::create_class(activation.context.gc_context),
)?;
activation.context.avm2.system_prototypes = Some(sp);
function(
activation.context.gc_context,
gs,
"",
"trace",
trace,
fn_proto,
);
constant(
activation.context.gc_context,
gs,
"",
"undefined",
Value::Undefined,
);
constant(activation.context.gc_context, gs, "", "null", Value::Null);
constant(activation.context.gc_context, gs, "", "NaN", NAN.into());
constant(
activation.context.gc_context,
gs,
"",
"Infinity",
f64::INFINITY.into(),
);
// package `flash.events` // package `flash.events`
class( class(
activation, activation,

View File

@ -1,13 +1,16 @@
//! `Boolean` impl //! `Boolean` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `Boolean` /// Implements `Boolean`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("Boolean constructor is a stub.".into()) Err("Boolean constructor is a stub.".into())
} }
/// Construct `Boolean.prototype`. /// Implements `Boolean`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `Boolean`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "Boolean"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }

View File

@ -1,16 +1,19 @@
//! `Class` builtin/prototype //! `Class` builtin/prototype
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject};
use crate::avm2::scope::Scope;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext;
/// Implements `Class` /// Implements `Class`'s instance initializer.
/// ///
/// Notably, you cannot construct new classes this way, so this returns an /// Notably, you cannot construct new classes this way, so this returns an
/// error. /// error.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -18,11 +21,45 @@ pub fn constructor<'gc>(
Err("Classes cannot be constructed.".into()) Err("Classes cannot be constructed.".into())
} }
/// Construct `Class.prototype`. /// Implement's `Class`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Err("Classes cannot be constructed.".into())
}
/// Construct `Class` and `Class.prototype`, respectively.
pub fn create_class<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> (Object<'gc>, Object<'gc>) {
let class_class = Class::new(
QName::new(Namespace::public_namespace(), "Class"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
activation.context.gc_context,
);
let globals = activation.avm2().globals();
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
let proto = ScriptObject::prototype(
activation.context.gc_context,
super_proto,
class_class,
Some(scope),
);
let constr = FunctionObject::from_builtin_constr(
activation.context.gc_context,
instance_init,
proto,
fn_proto,
)
.unwrap();
(constr, proto)
} }

View File

@ -1,14 +1,25 @@
//! Function builtin and prototype //! Function builtin and prototype
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject}; use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject};
use crate::avm2::scope::Scope;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext;
/// Implements `Function` /// Implements `Function`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Implements `Function`'s class initializer.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -38,22 +49,42 @@ fn call<'gc>(
} }
} }
/// Partially construct `Function.prototype`. /// Construct `Function` and `Function.prototype`, respectively.
/// pub fn create_class<'gc>(
/// `__proto__` and other cross-linked properties of this object will *not* activation: &mut Activation<'_, 'gc, '_>,
/// be defined here. The caller of this function is responsible for linking proto: Object<'gc>,
/// them in order to obtain a valid ECMAScript `Function` prototype. The ) -> (Object<'gc>, Object<'gc>) {
/// returned object is also a bare object, which will need to be linked into let function_class = Class::new(
/// the prototype of `Object`. QName::new(Namespace::public_namespace(), "Function"),
pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> { Some(QName::new(Namespace::public_namespace(), "Object").into()),
let mut function_proto = ScriptObject::object(gc_context, proto); Method::from_builtin(instance_init),
Method::from_builtin(class_init),
function_proto.install_method( activation.context.gc_context,
gc_context,
QName::new(Namespace::as3_namespace(), "call"),
0,
FunctionObject::from_builtin(gc_context, call, function_proto),
); );
function_proto let globals = activation.avm2().globals();
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
let mut function_proto = ScriptObject::prototype(
activation.context.gc_context,
proto,
function_class,
Some(scope),
);
function_proto.install_method(
activation.context.gc_context,
QName::new(Namespace::as3_namespace(), "call"),
0,
FunctionObject::from_builtin(activation.context.gc_context, call, function_proto),
);
let constr = FunctionObject::from_builtin_constr(
activation.context.gc_context,
instance_init,
proto,
function_proto,
)
.unwrap();
(constr, function_proto)
} }

View File

@ -1,13 +1,16 @@
//! `int` impl //! `int` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `int` /// Implements `int`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("int constructor is a stub.".into()) Err("int constructor is a stub.".into())
} }
/// Construct `int.prototype`. /// Implements `int`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `int`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "int"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }

View File

@ -1,13 +1,16 @@
//! `Namespace` impl //! `Namespace` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `Namespace` /// Implements `Namespace`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("Namespace constructor is a stub.".into()) Err("Namespace constructor is a stub.".into())
} }
/// Construct `Namespace.prototype`. /// Implements `Namespace`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `Namespace`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "Namespace"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }

View File

@ -1,13 +1,16 @@
//! `Number` impl //! `Number` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `Number` /// Implements `Number`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("Number constructor is a stub.".into()) Err("Number constructor is a stub.".into())
} }
/// Construct `Number.prototype`. /// Implements `Number`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `Number`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "Number"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }

View File

@ -1,14 +1,26 @@
//! Object builtin and prototype //! Object builtin and prototype
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{FunctionObject, Object, TObject}; use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject};
use crate::avm2::scope::Scope;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Object` /// Implements `Object`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Implements `Object`'s class initializer
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -140,7 +152,26 @@ pub fn set_property_is_enumerable<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
/// Partially construct `Object.prototype`. /// Create object prototype.
///
/// This function creates a suitable class and object prototype attached to it,
/// but does not actually fill it with methods. That requires a valid function
/// prototype, and is thus done by `fill_proto` below.
pub fn create_proto<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Object<'gc> {
let object_class = Class::new(
QName::new(Namespace::public_namespace(), "Object"),
None,
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
activation.context.gc_context,
);
let globals = activation.avm2().globals();
let scope = Scope::push_scope(globals.get_scope(), globals, activation.context.gc_context);
ScriptObject::bare_prototype(activation.context.gc_context, object_class, Some(scope))
}
/// Finish constructing `Object.prototype`, and also construct `Object`.
/// ///
/// `__proto__` and other cross-linked properties of this object will *not* /// `__proto__` and other cross-linked properties of this object will *not*
/// be defined here. The caller of this function is responsible for linking /// be defined here. The caller of this function is responsible for linking
@ -153,7 +184,7 @@ pub fn fill_proto<'gc>(
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
mut object_proto: Object<'gc>, mut object_proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) { ) -> Object<'gc> {
object_proto.install_method( object_proto.install_method(
gc_context, gc_context,
QName::new(Namespace::public_namespace(), "toString"), QName::new(Namespace::public_namespace(), "toString"),
@ -196,4 +227,6 @@ pub fn fill_proto<'gc>(
0, 0,
FunctionObject::from_builtin(gc_context, set_property_is_enumerable, fn_proto), FunctionObject::from_builtin(gc_context, set_property_is_enumerable, fn_proto),
); );
FunctionObject::from_builtin_constr(gc_context, instance_init, object_proto, fn_proto).unwrap()
} }

View File

@ -1,13 +1,16 @@
//! `String` impl //! `String` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `String` /// Implements `String`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("String constructor is a stub.".into()) Err("String constructor is a stub.".into())
} }
/// Construct `String.prototype`. /// Implements `String`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `String`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "String"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }

View File

@ -1,13 +1,16 @@
//! `uint` impl //! `uint` impl
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::object::{Object, ScriptObject}; use crate::avm2::class::Class;
use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::Object;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use gc_arena::MutationContext; use gc_arena::{GcCell, MutationContext};
/// Implements `uint` /// Implements `uint`'s instance initializer.
pub fn constructor<'gc>( pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -15,11 +18,22 @@ pub fn constructor<'gc>(
Err("uint constructor is a stub.".into()) Err("uint constructor is a stub.".into())
} }
/// Construct `uint.prototype`. /// Implements `uint`'s class initializer.
pub fn create_proto<'gc>( pub fn class_init<'gc>(
mc: MutationContext<'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
super_proto: Object<'gc>, _this: Option<Object<'gc>>,
_fn_proto: Object<'gc>, _args: &[Value<'gc>],
) -> Object<'gc> { ) -> Result<Value<'gc>, Error> {
ScriptObject::object(mc, super_proto) Ok(Value::Undefined)
}
/// Construct `uint`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package(""), "uint"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
} }