ES4 classes, while superficially similar to functions, are not functions and should not inherit from the `Function` prototype.

We still reuse the `FunctionObject` machinery internally. If necessary, we may want to split this into a separate `ClassObject` if some internal `TObject` method needs replacing for classes.
This commit is contained in:
David Wendt 2020-03-08 21:53:33 -04:00
parent 8b36751fbb
commit 4b66af8dc3
3 changed files with 46 additions and 1 deletions

View File

@ -308,6 +308,7 @@ impl<'gc> FunctionObject<'gc> {
}); });
let mut class_proto = super_proto?.derive(avm, context, class.clone(), scope)?; let mut class_proto = super_proto?.derive(avm, context, class.clone(), scope)?;
let fn_proto = avm.prototypes().function; let fn_proto = avm.prototypes().function;
let class_constr_proto = avm.prototypes().class;
let initializer_index = class.instance().init_method.clone(); let initializer_index = class.instance().init_method.clone();
let initializer: Result<Avm2MethodEntry, Error> = let initializer: Result<Avm2MethodEntry, Error> =
@ -358,7 +359,7 @@ impl<'gc> FunctionObject<'gc> {
context.gc_context, context.gc_context,
class_initializer?, class_initializer?,
scope, scope,
fn_proto, class_constr_proto,
None, None,
); );

View File

@ -10,6 +10,7 @@ use crate::avm2::{Avm2, Error};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
mod class;
mod flash; mod flash;
mod function; mod function;
mod object; mod object;
@ -33,6 +34,7 @@ fn trace<'gc>(
pub struct SystemPrototypes<'gc> { pub struct SystemPrototypes<'gc> {
pub object: Object<'gc>, pub object: Object<'gc>,
pub function: Object<'gc>, pub function: Object<'gc>,
pub class: Object<'gc>,
} }
/// Add a free-function builtin to the global scope. /// Add a free-function builtin to the global scope.
@ -86,6 +88,7 @@ pub fn construct_global_scope<'gc>(
// public / root package // public / root package
let object_proto = ScriptObject::bare_object(mc); let object_proto = ScriptObject::bare_object(mc);
let fn_proto = function::create_proto(mc, object_proto); let fn_proto = function::create_proto(mc, object_proto);
let class_proto = class::create_proto(mc, object_proto, fn_proto);
object::fill_proto(mc, object_proto, fn_proto); object::fill_proto(mc, object_proto, fn_proto);
@ -107,6 +110,15 @@ pub fn construct_global_scope<'gc>(
fn_proto, fn_proto,
fn_proto, fn_proto,
); );
class(
mc,
gs,
"",
"Class",
class::constructor,
class_proto,
fn_proto,
);
function(mc, gs, "", "trace", trace, fn_proto); function(mc, gs, "", "trace", trace, fn_proto);
// package `flash.events` // package `flash.events`
@ -183,6 +195,7 @@ pub fn construct_global_scope<'gc>(
let system_prototypes = SystemPrototypes { let system_prototypes = SystemPrototypes {
object: object_proto, object: object_proto,
function: fn_proto, function: fn_proto,
class: class_proto,
}; };
(gs, system_prototypes) (gs, system_prototypes)

View File

@ -0,0 +1,31 @@
//! `Class` builtin/prototype
use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error};
use crate::context::UpdateContext;
use gc_arena::MutationContext;
/// Implements `Class`
///
/// Notably, you cannot construct new classes this way, so this returns an
/// error.
pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
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)
}