diff --git a/core/src/avm2.rs b/core/src/avm2.rs index e694ecd9c..c180f147f 100644 --- a/core/src/avm2.rs +++ b/core/src/avm2.rs @@ -2,11 +2,10 @@ use crate::avm2::activation::Activation; 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::script::Script; use crate::avm2::script::TranslationUnit; -use crate::avm2::script_object::ScriptObject; use crate::avm2::value::Value; use crate::context::UpdateContext; use crate::tag_utils::SwfSlice; diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 85ec0d9c8..5b8914ed8 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -51,6 +51,34 @@ pub struct SystemPrototypes<'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. fn function<'gc>( mc: MutationContext<'gc, '_>, @@ -69,39 +97,47 @@ fn function<'gc>( .unwrap() } -/// Add an ES3-style builtin to the global scope. -fn oldstyle_class<'gc>( +/// Add a class builtin with prototype methods to the global scope. +/// +/// 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, '_>, mut global_scope: Object<'gc>, - package: impl Into>, - name: impl Into>, - constr: NativeMethod<'gc>, - proto: Object<'gc>, - fn_proto: Object<'gc>, + constr: Object<'gc>, ) { - global_scope - .install_dynamic_property( - mc, - QName::new(Namespace::package(package), name), - FunctionObject::from_builtin_constr(mc, constr, proto, fn_proto) - .unwrap() - .into(), - ) - .unwrap(); + let name = constr + .as_class() + .expect("constrs have classes in them") + .read() + .name() + .clone(); + + global_scope.install_const(mc, name, 0, constr.into()); } /// Add a class builtin to the global scope. +/// +/// This function returns a prototype which may be stored in `SystemPrototypes`. fn class<'gc>( activation: &mut Activation<'_, 'gc, '_>, mut global: Object<'gc>, class_def: GcCell<'gc, Class<'gc>>, -) -> Result<(), Error> { +) -> Result, Error> { let class_trait = Trait::from_class(class_def); 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)?; - - Ok(()) + constr + .get_property( + constr, + &QName::new(Namespace::public_namespace(), "prototype"), + activation, + )? + .coerce_to_object(activation) } /// 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) } -/// 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. /// -/// 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 latter phase. +/// This should be called only once, to construct the global scope of the +/// player. It will return a list of prototypes it has created, which should be +/// stored on the AVM. pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Result<(), Error> { 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` class( activation, diff --git a/core/src/avm2/globals/boolean.rs b/core/src/avm2/globals/boolean.rs index f0519e235..781709d18 100644 --- a/core/src/avm2/globals/boolean.rs +++ b/core/src/avm2/globals/boolean.rs @@ -1,13 +1,16 @@ //! `Boolean` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `Boolean` -pub fn constructor<'gc>( +/// Implements `Boolean`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("Boolean constructor is a stub.".into()) } -/// Construct `Boolean.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `Boolean`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) } diff --git a/core/src/avm2/globals/class.rs b/core/src/avm2/globals/class.rs index 9eb0f5027..e7429ac50 100644 --- a/core/src/avm2/globals/class.rs +++ b/core/src/avm2/globals/class.rs @@ -1,16 +1,19 @@ //! `Class` builtin/prototype 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::Error; -use gc_arena::MutationContext; -/// Implements `Class` +/// Implements `Class`'s instance initializer. /// /// Notably, you cannot construct new classes this way, so this returns an /// error. -pub fn constructor<'gc>( +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -18,11 +21,45 @@ pub fn constructor<'gc>( Err("Classes cannot be constructed.".into()) } -/// Construct `Class.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implement's `Class`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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) } diff --git a/core/src/avm2/globals/function.rs b/core/src/avm2/globals/function.rs index cbf22e9ca..f611d4989 100644 --- a/core/src/avm2/globals/function.rs +++ b/core/src/avm2/globals/function.rs @@ -1,14 +1,25 @@ //! Function builtin and prototype use crate::avm2::activation::Activation; +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::Error; -use gc_arena::MutationContext; -/// Implements `Function` -pub fn constructor<'gc>( +/// Implements `Function`'s instance initializer. +pub fn instance_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + Ok(Value::Undefined) +} + +/// Implements `Function`'s class initializer. +pub fn class_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -38,22 +49,42 @@ fn call<'gc>( } } -/// Partially construct `Function.prototype`. -/// -/// `__proto__` and other cross-linked properties of this object will *not* -/// be defined here. The caller of this function is responsible for linking -/// them in order to obtain a valid ECMAScript `Function` prototype. The -/// returned object is also a bare object, which will need to be linked into -/// the prototype of `Object`. -pub fn create_proto<'gc>(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> { - let mut function_proto = ScriptObject::object(gc_context, proto); - - function_proto.install_method( - gc_context, - QName::new(Namespace::as3_namespace(), "call"), - 0, - FunctionObject::from_builtin(gc_context, call, function_proto), +/// Construct `Function` and `Function.prototype`, respectively. +pub fn create_class<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + proto: Object<'gc>, +) -> (Object<'gc>, Object<'gc>) { + let function_class = Class::new( + QName::new(Namespace::public_namespace(), "Function"), + Some(QName::new(Namespace::public_namespace(), "Object").into()), + Method::from_builtin(instance_init), + Method::from_builtin(class_init), + activation.context.gc_context, ); - 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) } diff --git a/core/src/avm2/globals/int.rs b/core/src/avm2/globals/int.rs index f73f3c326..136becca8 100644 --- a/core/src/avm2/globals/int.rs +++ b/core/src/avm2/globals/int.rs @@ -1,13 +1,16 @@ //! `int` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `int` -pub fn constructor<'gc>( +/// Implements `int`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("int constructor is a stub.".into()) } -/// Construct `int.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `int`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) } diff --git a/core/src/avm2/globals/namespace.rs b/core/src/avm2/globals/namespace.rs index 61798a607..954618781 100644 --- a/core/src/avm2/globals/namespace.rs +++ b/core/src/avm2/globals/namespace.rs @@ -1,13 +1,16 @@ //! `Namespace` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `Namespace` -pub fn constructor<'gc>( +/// Implements `Namespace`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("Namespace constructor is a stub.".into()) } -/// Construct `Namespace.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `Namespace`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) } diff --git a/core/src/avm2/globals/number.rs b/core/src/avm2/globals/number.rs index dfaccc4ab..cb3937d1c 100644 --- a/core/src/avm2/globals/number.rs +++ b/core/src/avm2/globals/number.rs @@ -1,13 +1,16 @@ //! `Number` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `Number` -pub fn constructor<'gc>( +/// Implements `Number`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("Number constructor is a stub.".into()) } -/// Construct `Number.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `Number`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) } diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index 730399e80..db0c1e4e3 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -1,14 +1,26 @@ //! Object builtin and prototype use crate::avm2::activation::Activation; +use crate::avm2::class::Class; +use crate::avm2::method::Method; 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::Error; use gc_arena::MutationContext; -/// Implements `Object` -pub fn constructor<'gc>( +/// Implements `Object`'s instance initializer. +pub fn instance_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + Ok(Value::Undefined) +} + +/// Implements `Object`'s class initializer +pub fn class_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -140,7 +152,26 @@ pub fn set_property_is_enumerable<'gc>( 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* /// 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, '_>, mut object_proto: Object<'gc>, fn_proto: Object<'gc>, -) { +) -> Object<'gc> { object_proto.install_method( gc_context, QName::new(Namespace::public_namespace(), "toString"), @@ -196,4 +227,6 @@ pub fn fill_proto<'gc>( 0, FunctionObject::from_builtin(gc_context, set_property_is_enumerable, fn_proto), ); + + FunctionObject::from_builtin_constr(gc_context, instance_init, object_proto, fn_proto).unwrap() } diff --git a/core/src/avm2/globals/string.rs b/core/src/avm2/globals/string.rs index 9b822d3a3..44ef55b4a 100644 --- a/core/src/avm2/globals/string.rs +++ b/core/src/avm2/globals/string.rs @@ -1,13 +1,16 @@ //! `String` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `String` -pub fn constructor<'gc>( +/// Implements `String`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("String constructor is a stub.".into()) } -/// Construct `String.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `String`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) } diff --git a/core/src/avm2/globals/uint.rs b/core/src/avm2/globals/uint.rs index 5755d448b..f437b19a9 100644 --- a/core/src/avm2/globals/uint.rs +++ b/core/src/avm2/globals/uint.rs @@ -1,13 +1,16 @@ //! `uint` impl 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::Error; -use gc_arena::MutationContext; +use gc_arena::{GcCell, MutationContext}; -/// Implements `uint` -pub fn constructor<'gc>( +/// Implements `uint`'s instance initializer. +pub fn instance_init<'gc>( _activation: &mut Activation<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], @@ -15,11 +18,22 @@ pub fn constructor<'gc>( Err("uint constructor is a stub.".into()) } -/// Construct `uint.prototype`. -pub fn create_proto<'gc>( - mc: MutationContext<'gc, '_>, - super_proto: Object<'gc>, - _fn_proto: Object<'gc>, -) -> Object<'gc> { - ScriptObject::object(mc, super_proto) +/// Implements `uint`'s class initializer. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + 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, + ) }