avm2: AVM2 built-in class suport (merge #802)

Initial support for defining built-ins in AVM2
This commit is contained in:
Mike Welsh 2020-08-16 03:45:02 -07:00 committed by GitHub
commit f782ea8020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 948 additions and 667 deletions

View File

@ -2,7 +2,7 @@
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;
@ -36,7 +36,7 @@ mod scope;
mod script;
mod slot;
mod string;
mod r#trait;
mod traits;
mod value;
/// Boxed error alias.
@ -56,7 +56,7 @@ pub struct Avm2<'gc> {
globals: Object<'gc>,
/// System prototypes.
system_prototypes: SystemPrototypes<'gc>,
system_prototypes: Option<SystemPrototypes<'gc>>,
#[cfg(feature = "avm_debug")]
pub debug_output: bool,
@ -65,21 +65,28 @@ pub struct Avm2<'gc> {
impl<'gc> Avm2<'gc> {
/// Construct a new AVM interpreter.
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
let (globals, system_prototypes) = globals::construct_global_scope(mc);
let globals = ScriptObject::bare_object(mc);
Self {
stack: Vec::new(),
globals,
system_prototypes,
system_prototypes: None,
#[cfg(feature = "avm_debug")]
debug_output: false,
}
}
pub fn load_player_globals(context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
let mut activation = Activation::from_nothing(context.reborrow());
globals::load_player_globals(&mut activation)
}
/// Return the current set of system prototypes.
///
/// This function panics if the interpreter has not yet been initialized.
pub fn prototypes(&self) -> &SystemPrototypes<'gc> {
&self.system_prototypes
self.system_prototypes.as_ref().unwrap()
}
/// Run a script's initializer method.

View File

@ -2,13 +2,30 @@
use crate::avm2::method::Method;
use crate::avm2::names::{Multiname, Namespace, QName};
use crate::avm2::r#trait::{Trait, TraitKind};
use crate::avm2::script::TranslationUnit;
use crate::avm2::string::AvmString;
use crate::avm2::traits::{Trait, TraitKind};
use crate::avm2::{Avm2, Error};
use crate::collect::CollectWrapper;
use enumset::{EnumSet, EnumSetType};
use gc_arena::{Collect, GcCell, MutationContext};
use swf::avm2::types::{Class as AbcClass, Instance as AbcInstance};
/// All possible attributes for a given class.
#[derive(EnumSetType, Debug)]
pub enum ClassAttributes {
/// Class is sealed, attempts to set or init dynamic properties on an
/// object will generate a runtime error.
Sealed,
/// Class is final, attempts to construct child classes from it will
/// generate a verification error.
Final,
/// Class is an interface.
Interface,
}
/// A loaded ABC Class which can be used to construct objects with.
#[derive(Clone, Debug, Collect)]
#[collect(no_drop)]
@ -19,14 +36,8 @@ pub struct Class<'gc> {
/// The name of this class's superclass.
super_class: Option<Multiname<'gc>>,
/// If this class is sealed (dynamic property writes should fail)
is_sealed: bool,
/// If this class is final (subclassing should fail)
is_final: bool,
/// If this class is an interface
is_interface: bool,
/// Attributes of the given class.
attributes: CollectWrapper<EnumSet<ClassAttributes>>,
/// The namespace that protected traits of this class are stored into.
protected_namespace: Option<Namespace<'gc>>,
@ -97,6 +108,48 @@ fn do_trait_lookup<'gc>(
}
impl<'gc> Class<'gc> {
/// Create a new class.
///
/// This function is primarily intended for use by native code to define
/// builtin classes. The absolute minimum necessary to define a class is
/// required here; further methods allow further changes to the class.
///
/// Classes created in this way cannot have traits loaded from an ABC file
/// using `load_traits`.
pub fn new(
name: QName<'gc>,
super_class: Option<Multiname<'gc>>,
instance_init: Method<'gc>,
class_init: Method<'gc>,
mc: MutationContext<'gc, '_>,
) -> GcCell<'gc, Self> {
GcCell::allocate(
mc,
Self {
name,
super_class,
attributes: CollectWrapper(EnumSet::empty()),
protected_namespace: None,
interfaces: Vec::new(),
instance_init,
instance_traits: Vec::new(),
class_init,
class_traits: Vec::new(),
traits_loaded: true,
},
)
}
/// Set the attributes of the class (sealed/final/interface status).
pub fn set_attributes(&mut self, attributes: EnumSet<ClassAttributes>) {
self.attributes = CollectWrapper(attributes);
}
/// Add a protected namespace to this class.
pub fn set_protected_namespace(&mut self, ns: Namespace<'gc>) {
self.protected_namespace = Some(ns)
}
/// Construct a class from a `TranslationUnit` and it's class index.
///
/// The returned class will be allocated, but no traits will be loaded. The
@ -149,14 +202,25 @@ impl<'gc> Class<'gc> {
let instance_init = unit.load_method(abc_instance.init_method.0, mc)?;
let class_init = unit.load_method(abc_class.init_method.0, mc)?;
let mut attributes = EnumSet::new();
if abc_instance.is_sealed {
attributes |= ClassAttributes::Sealed;
}
if abc_instance.is_final {
attributes |= ClassAttributes::Final;
}
if abc_instance.is_interface {
attributes |= ClassAttributes::Interface;
}
Ok(GcCell::allocate(
mc,
Self {
name,
super_class,
is_sealed: abc_instance.is_sealed,
is_final: abc_instance.is_final,
is_interface: abc_instance.is_interface,
attributes: CollectWrapper(attributes),
protected_namespace,
interfaces,
instance_init,
@ -221,6 +285,14 @@ impl<'gc> Class<'gc> {
&self.super_class
}
/// Define a trait on the class.
///
/// Class traits will be accessible as properties on the class constructor
/// function.
pub fn define_class_trait(&mut self, my_trait: Trait<'gc>) {
self.class_traits.push(my_trait);
}
/// Given a name, append class traits matching the name to a list of known
/// traits.
///
@ -265,6 +337,15 @@ impl<'gc> Class<'gc> {
None
}
/// Define a trait on instances of the class.
///
/// Instance traits will be accessible as properties on instances of the
/// class. They will not be accessible on the class prototype, and any
/// properties defined on the prototype will be shadowed by these traits.
pub fn define_instance_trait(&mut self, my_trait: Trait<'gc>) {
self.class_traits.push(my_trait);
}
/// Given a name, append instance traits matching the name to a list of
/// known traits.
///
@ -322,4 +403,8 @@ impl<'gc> Class<'gc> {
pub fn interfaces(&self) -> &[Multiname<'gc>] {
&self.interfaces
}
pub fn implements(&mut self, iface: Multiname<'gc>) {
self.interfaces.push(iface)
}
}

View File

@ -1,14 +1,16 @@
//! Global scope built-ins
use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::method::NativeMethod;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{FunctionObject, ScriptObject};
use crate::avm2::object::{Object, TObject};
use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject};
use crate::avm2::scope::Scope;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{Collect, MutationContext};
use gc_arena::{Collect, GcCell, MutationContext};
use std::f64::NAN;
mod boolean;
@ -49,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, '_>,
@ -67,25 +97,47 @@ fn function<'gc>(
.unwrap()
}
/// Add a class builtin to the global scope.
fn 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<AvmString<'gc>>,
name: impl Into<AvmString<'gc>>,
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<Object<'gc>, 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)?;
constr
.get_property(
constr,
&QName::new(Namespace::public_namespace(), "prototype"),
activation,
)?
.coerce_to_object(activation)
}
/// Add a builtin constant to the global scope.
@ -99,181 +151,132 @@ fn constant<'gc>(
global_scope.install_const(mc, QName::new(Namespace::package(package), name), 0, value)
}
/// Construct a new global scope.
/// Initialize all remaining builtin classes.
///
/// This function returns both the global scope object, as well as all builtin
/// prototypes that other parts of the VM will need to use.
pub fn construct_global_scope<'gc>(
mc: MutationContext<'gc, '_>,
) -> (Object<'gc>, SystemPrototypes<'gc>) {
let gs = ScriptObject::bare_object(mc);
/// 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 = 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);
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);
object::fill_proto(mc, object_proto, fn_proto);
let object_constr = object::fill_proto(activation.context.gc_context, object_proto, fn_proto);
class(
mc,
gs,
"",
"Object",
object::constructor,
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(
mc,
gs,
"",
"Function",
function::constructor,
fn_proto,
fn_proto,
);
class(
mc,
gs,
"",
"Class",
class::constructor,
class_proto,
fn_proto,
);
class(
mc,
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,
"",
"String",
string::constructor,
string_proto,
"trace",
trace,
fn_proto,
);
class(
mc,
constant(
activation.context.gc_context,
gs,
"",
"Boolean",
boolean::constructor,
boolean_proto,
fn_proto,
"undefined",
Value::Undefined,
);
class(
mc,
constant(activation.context.gc_context, gs, "", "null", Value::Null);
constant(activation.context.gc_context, gs, "", "NaN", NAN.into());
constant(
activation.context.gc_context,
gs,
"",
"Number",
number::constructor,
number_proto,
fn_proto,
"Infinity",
f64::INFINITY.into(),
);
class(mc, gs, "", "int", int::constructor, int_proto, fn_proto);
class(mc, gs, "", "uint", uint::constructor, uint_proto, fn_proto);
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());
// package `flash.events`
let eventdispatcher_proto =
flash::events::eventdispatcher::create_proto(mc, object_proto, fn_proto);
class(
mc,
activation,
gs,
"flash.events",
"EventDispatcher",
flash::events::eventdispatcher::constructor,
eventdispatcher_proto,
fn_proto,
);
flash::events::eventdispatcher::create_class(activation.context.gc_context),
)?;
// package `flash.display`
let displayobject_proto =
flash::display::displayobject::create_proto(mc, eventdispatcher_proto, fn_proto);
let interactiveobject_proto =
flash::display::interactiveobject::create_proto(mc, displayobject_proto, fn_proto);
let displayobjectcontainer_proto =
flash::display::displayobjectcontainer::create_proto(mc, interactiveobject_proto, fn_proto);
let sprite_proto =
flash::display::sprite::create_proto(mc, displayobjectcontainer_proto, fn_proto);
let movieclip_proto = flash::display::movieclip::create_proto(mc, sprite_proto, fn_proto);
class(
activation,
gs,
flash::display::displayobject::create_class(activation.context.gc_context),
)?;
class(
activation,
gs,
flash::display::interactiveobject::create_class(activation.context.gc_context),
)?;
class(
activation,
gs,
flash::display::displayobjectcontainer::create_class(activation.context.gc_context),
)?;
class(
activation,
gs,
flash::display::sprite::create_class(activation.context.gc_context),
)?;
class(
activation,
gs,
flash::display::movieclip::create_class(activation.context.gc_context),
)?;
class(
mc,
gs,
"flash.display",
"DisplayObject",
flash::display::displayobject::constructor,
displayobject_proto,
fn_proto,
);
class(
mc,
gs,
"flash.display",
"InteractiveObject",
flash::display::interactiveobject::constructor,
interactiveobject_proto,
fn_proto,
);
class(
mc,
gs,
"flash.display",
"DisplayObjectContainer",
flash::display::displayobjectcontainer::constructor,
sprite_proto,
fn_proto,
);
class(
mc,
gs,
"flash.display",
"Sprite",
flash::display::sprite::constructor,
sprite_proto,
fn_proto,
);
class(
mc,
gs,
"flash.display",
"MovieClip",
flash::display::movieclip::constructor,
movieclip_proto,
fn_proto,
);
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)
Ok(())
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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)
}

View File

@ -1,13 +1,16 @@
//! `flash.display.DisplayObject` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.DisplayObject`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.display.DisplayObject`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,22 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `DisplayObject.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.display.DisplayObject`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `DisplayObject`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package("flash.display"), "DisplayObject"),
Some(QName::new(Namespace::package("flash.events"), "EventDispatcher").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -1,13 +1,16 @@
//! `flash.display.DisplayObjectContainer` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.DisplayObjectContainer`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,25 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `DisplayObjectContainer.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.display.DisplayObjectContainer`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `DisplayObjectContainer`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(
Namespace::package("flash.display"),
"DisplayObjectContainer",
),
Some(QName::new(Namespace::package("flash.display"), "InteractiveObject").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -1,13 +1,16 @@
//! `flash.display.InteractiveObject` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.InteractiveObject`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.display.InteractiveObject`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,22 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `InteractiveObject.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.display.InteractiveObject`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `InteractiveObject`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package("flash.display"), "InteractiveObject"),
Some(QName::new(Namespace::package("flash.display"), "DisplayObject").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -1,13 +1,16 @@
//! `flash.display.MovieClip` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.MovieClip`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.display.MovieClip`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,22 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `MovieClip.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.display.MovieClip`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `MovieClip`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package("flash.display"), "MovieClip"),
Some(QName::new(Namespace::package("flash.display"), "Sprite").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -1,13 +1,16 @@
//! `flash.display.Sprite` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.display.Sprite`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.display.Sprite`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,28 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `Sprite.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.display.Sprite`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `Sprite`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package("flash.display"), "Sprite"),
Some(
QName::new(
Namespace::package("flash.display"),
"DisplayObjectContainer",
)
.into(),
),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -1,13 +1,16 @@
//! `flash.events.EventDispatcher` 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::Object;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::MutationContext;
use gc_arena::{GcCell, MutationContext};
/// Implements `flash.events.EventDispatcher`'s constructor.
pub fn constructor<'gc>(
/// Implements `flash.events.EventDispatcher`'s instance constructor.
pub fn instance_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
@ -15,12 +18,22 @@ pub fn constructor<'gc>(
Ok(Value::Undefined)
}
/// Construct `EventDispatcher.prototype`.
pub fn create_proto<'gc>(
mc: MutationContext<'gc, '_>,
super_proto: Object<'gc>,
_fn_proto: Object<'gc>,
) -> Object<'gc> {
// TODO: Use `StageObject` here.
ScriptObject::object(mc, super_proto)
/// Implements `flash.events.EventDispatcher`'s class constructor.
pub fn class_init<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Construct `EventDispatcher`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new(
QName::new(Namespace::package("flash.events"), "EventDispatcher"),
Some(QName::new(Namespace::public_namespace(), "Object").into()),
Method::from_builtin(instance_init),
Method::from_builtin(class_init),
mc,
)
}

View File

@ -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<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, '_>,
_this: Option<Object<'gc>>,
_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)
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -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<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, '_>,
_this: Option<Object<'gc>>,
_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()
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -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<Object<'gc>>,
_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<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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,
)
}

View File

@ -5,15 +5,12 @@ use crate::avm2::object::Object;
use crate::avm2::script::TranslationUnit;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::collect::CollectWrapper;
use gc_arena::{Collect, CollectionContext, Gc, MutationContext};
use std::fmt;
use std::rc::Rc;
use swf::avm2::types::{AbcFile, Index, Method as AbcMethod, MethodBody as AbcMethodBody};
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct CollectWrapper<T>(T);
/// Represents a function defined in Ruffle's code.
///
/// Parameters are as follows:
@ -162,6 +159,12 @@ impl<'gc> From<Gc<'gc, BytecodeMethod<'gc>>> for Method<'gc> {
}
impl<'gc> Method<'gc> {
/// Builtin method constructor, because for some reason `nf.into()` just
/// causes odd lifetime mismatches.
pub fn from_builtin(nf: NativeMethod<'gc>) -> Self {
Self::Native(nf)
}
/// Access the bytecode of this method.
///
/// This function returns `Err` if there is no bytecode for this method.

View File

@ -357,3 +357,12 @@ impl<'gc> Multiname<'gc> {
self.name
}
}
impl<'gc> From<QName<'gc>> for Multiname<'gc> {
fn from(q: QName<'gc>) -> Self {
Self {
ns: vec![q.ns],
name: Some(q.name),
}
}
}

View File

@ -4,9 +4,9 @@ use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::function::Executable;
use crate::avm2::names::{Multiname, Namespace, QName};
use crate::avm2::r#trait::{Trait, TraitKind};
use crate::avm2::scope::Scope;
use crate::avm2::string::AvmString;
use crate::avm2::traits::{Trait, TraitKind};
use crate::avm2::value::{Hint, Value};
use crate::avm2::Error;
use gc_arena::{Collect, GcCell, MutationContext};
@ -415,23 +415,29 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// bound to. It should always be `self` except when doing things with
/// `super`, which needs to create bound methods pointing to a different
/// object.
///
/// The value of the trait at the time of installation will be returned
/// here.
fn install_trait(
&mut self,
activation: &mut Activation<'_, 'gc, '_>,
trait_entry: Trait<'gc>,
reciever: Object<'gc>,
) -> Result<(), Error> {
) -> Result<Value<'gc>, Error> {
self.install_foreign_trait(activation, trait_entry, self.get_scope(), reciever)
}
/// Install a trait from anywyere.
///
/// The value of the trait at the time of installation will be returned
/// here.
fn install_foreign_trait(
&mut self,
activation: &mut Activation<'_, 'gc, '_>,
trait_entry: Trait<'gc>,
scope: Option<GcCell<'gc, Scope<'gc>>>,
reciever: Object<'gc>,
) -> Result<(), Error> {
) -> Result<Value<'gc>, Error> {
let fn_proto = activation.avm2().prototypes().function;
let trait_name = trait_entry.name().clone();
avm_debug!(
@ -447,12 +453,15 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
default_value,
..
} => {
let value = default_value.clone().unwrap_or(Value::Undefined);
self.install_slot(
activation.context.gc_context,
trait_name,
*slot_id,
default_value.clone().unwrap_or(Value::Undefined),
value.clone(),
);
Ok(value)
}
TraitKind::Method {
disp_id, method, ..
@ -470,6 +479,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
*disp_id,
function,
);
Ok(function.into())
}
TraitKind::Getter {
disp_id, method, ..
@ -487,6 +498,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
*disp_id,
function,
)?;
Ok(function.into())
}
TraitKind::Setter {
disp_id, method, ..
@ -504,6 +517,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
*disp_id,
function,
)?;
Ok(function.into())
}
TraitKind::Class { slot_id, class } => {
let class_read = class.read();
@ -533,6 +548,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
*slot_id,
class_object.into(),
);
Ok(class_object.into())
}
TraitKind::Function {
slot_id, function, ..
@ -561,22 +578,25 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
*slot_id,
fobject.into(),
);
Ok(fobject.into())
}
TraitKind::Const {
slot_id,
default_value,
..
} => {
let value = default_value.clone().unwrap_or(Value::Undefined);
self.install_const(
activation.context.gc_context,
trait_name,
*slot_id,
default_value.clone().unwrap_or(Value::Undefined),
value.clone(),
);
Ok(value)
}
}
Ok(())
}
/// Call the object.
@ -703,6 +723,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// Get a raw pointer value for this object.
fn as_ptr(&self) -> *const ObjectPtr;
/// Get this object's `Class`, if it has one.
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>>;
/// Get this object's `Executable`, if it has one.
fn as_executable(&self) -> Option<Executable<'gc>> {
None

View File

@ -179,6 +179,10 @@ macro_rules! impl_avm2_custom_object {
self.0.as_ptr() as *const ObjectPtr
}
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
self.0.read().base.as_class()
}
fn install_method(
&mut self,
mc: MutationContext<'gc, '_>,

View File

@ -7,9 +7,9 @@ use crate::avm2::method::{Method, NativeMethod};
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::script_object::{ScriptObject, ScriptObjectClass, ScriptObjectData};
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::r#trait::Trait;
use crate::avm2::scope::Scope;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::impl_avm2_custom_object;
@ -195,10 +195,15 @@ impl<'gc> FunctionObject<'gc> {
mut prototype: Object<'gc>,
fn_proto: Object<'gc>,
) -> Result<Object<'gc>, Error> {
let scope = prototype.get_scope();
let class = prototype
.as_class()
.map(|c| ScriptObjectClass::ClassConstructor(c, scope))
.unwrap_or(ScriptObjectClass::NoClass);
let mut base: Object<'gc> = FunctionObject(GcCell::allocate(
mc,
FunctionObjectData {
base: ScriptObjectData::base_new(Some(fn_proto), ScriptObjectClass::NoClass),
base: ScriptObjectData::base_new(Some(fn_proto), class),
exec: Some(Executable::from_method(constr.into(), None, None, mc)),
},
))

View File

@ -6,8 +6,8 @@ use crate::avm2::class::Class;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::script_object::{ScriptObjectClass, ScriptObjectData};
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::r#trait::Trait;
use crate::avm2::scope::Scope;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::impl_avm2_custom_object;

View File

@ -2,15 +2,15 @@
use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::function::Executable;
use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::script_object::{ScriptObjectClass, ScriptObjectData};
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::r#trait::Trait;
use crate::avm2::scope::Scope;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use crate::impl_avm2_custom_object;
use gc_arena::{Collect, GcCell, MutationContext};
/// An Object which represents a primitive value of some other kind.
@ -50,181 +50,14 @@ impl<'gc> PrimitiveObject<'gc> {
}
impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
fn get_property_local(
self,
reciever: Object<'gc>,
name: &QName<'gc>,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
let read = self.0.read();
let rv = read.base.get_property_local(reciever, name, activation)?;
impl_avm2_custom_object!(base);
drop(read);
rv.resolve(activation)
fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
Ok(self.0.read().primitive.clone())
}
fn set_property_local(
self,
reciever: Object<'gc>,
name: &QName<'gc>,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> {
let mut write = self.0.write(activation.context.gc_context);
let rv = write
.base
.set_property_local(reciever, name, value, activation)?;
drop(write);
rv.resolve(activation)?;
Ok(())
}
fn init_property_local(
self,
reciever: Object<'gc>,
name: &QName<'gc>,
value: Value<'gc>,
activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> {
let mut write = self.0.write(activation.context.gc_context);
let rv = write
.base
.init_property_local(reciever, name, value, activation)?;
drop(write);
rv.resolve(activation)?;
Ok(())
}
fn is_property_overwritable(
self,
gc_context: MutationContext<'gc, '_>,
name: &QName<'gc>,
) -> bool {
self.0.write(gc_context).base.is_property_overwritable(name)
}
fn delete_property(
&self,
gc_context: MutationContext<'gc, '_>,
multiname: &QName<'gc>,
) -> bool {
self.0.write(gc_context).base.delete_property(multiname)
}
fn get_slot(self, id: u32) -> Result<Value<'gc>, Error> {
self.0.read().base.get_slot(id)
}
fn set_slot(
self,
id: u32,
value: Value<'gc>,
mc: MutationContext<'gc, '_>,
) -> Result<(), Error> {
self.0.write(mc).base.set_slot(id, value, mc)
}
fn init_slot(
self,
id: u32,
value: Value<'gc>,
mc: MutationContext<'gc, '_>,
) -> Result<(), Error> {
self.0.write(mc).base.init_slot(id, value, mc)
}
fn get_method(self, id: u32) -> Option<Object<'gc>> {
self.0.read().base.get_method(id)
}
fn get_trait(self, name: &QName<'gc>) -> Result<Vec<Trait<'gc>>, Error> {
self.0.read().base.get_trait(name)
}
fn get_provided_trait(
&self,
name: &QName<'gc>,
known_traits: &mut Vec<Trait<'gc>>,
) -> Result<(), Error> {
self.0.read().base.get_provided_trait(name, known_traits)
}
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
self.0.read().base.get_scope()
}
fn resolve_any(self, local_name: AvmString<'gc>) -> Result<Option<Namespace<'gc>>, Error> {
self.0.read().base.resolve_any(local_name)
}
fn resolve_any_trait(
self,
local_name: AvmString<'gc>,
) -> Result<Option<Namespace<'gc>>, Error> {
self.0.read().base.resolve_any_trait(local_name)
}
fn has_own_property(self, name: &QName<'gc>) -> Result<bool, Error> {
self.0.read().base.has_own_property(name)
}
fn has_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
self.0.read().base.has_trait(name)
}
fn provides_trait(self, name: &QName<'gc>) -> Result<bool, Error> {
self.0.read().base.provides_trait(name)
}
fn has_instantiated_property(self, name: &QName<'gc>) -> bool {
self.0.read().base.has_instantiated_property(name)
}
fn has_own_virtual_getter(self, name: &QName<'gc>) -> bool {
self.0.read().base.has_own_virtual_getter(name)
}
fn has_own_virtual_setter(self, name: &QName<'gc>) -> bool {
self.0.read().base.has_own_virtual_setter(name)
}
fn proto(&self) -> Option<Object<'gc>> {
self.0.read().base.proto()
}
fn get_enumerant_name(&self, index: u32) -> Option<QName<'gc>> {
self.0.read().base.get_enumerant_name(index)
}
fn property_is_enumerable(&self, name: &QName<'gc>) -> bool {
self.0.read().base.property_is_enumerable(name)
}
fn set_local_property_is_enumerable(
&self,
mc: MutationContext<'gc, '_>,
name: &QName<'gc>,
is_enumerable: bool,
) -> Result<(), Error> {
self.0
.write(mc)
.base
.set_local_property_is_enumerable(name, is_enumerable)
}
fn as_ptr(&self) -> *const ObjectPtr {
self.0.as_ptr() as *const ObjectPtr
}
fn as_executable(&self) -> Option<Executable<'gc>> {
None
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
Ok(self.0.read().primitive.clone())
}
fn construct(
@ -266,88 +99,4 @@ impl<'gc> TObject<'gc> for PrimitiveObject<'gc> {
))
.into())
}
fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
Ok(self.0.read().primitive.clone())
}
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
Ok(self.0.read().primitive.clone())
}
fn install_method(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
disp_id: u32,
function: Object<'gc>,
) {
self.0
.write(mc)
.base
.install_method(name, disp_id, function)
}
fn install_getter(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
disp_id: u32,
function: Object<'gc>,
) -> Result<(), Error> {
self.0
.write(mc)
.base
.install_getter(name, disp_id, function)
}
fn install_setter(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
disp_id: u32,
function: Object<'gc>,
) -> Result<(), Error> {
self.0
.write(mc)
.base
.install_setter(name, disp_id, function)
}
fn install_dynamic_property(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
value: Value<'gc>,
) -> Result<(), Error> {
self.0.write(mc).base.install_dynamic_property(name, value)
}
fn install_slot(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
id: u32,
value: Value<'gc>,
) {
self.0.write(mc).base.install_slot(name, id, value)
}
fn install_const(
&mut self,
mc: MutationContext<'gc, '_>,
name: QName<'gc>,
id: u32,
value: Value<'gc>,
) {
self.0.write(mc).base.install_const(name, id, value)
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.0.read().base.interfaces()
}
fn set_interfaces(&self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.0.write(context).base.set_interfaces(iface_list)
}
}

View File

@ -7,11 +7,11 @@ use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::property::Property;
use crate::avm2::property_map::PropertyMap;
use crate::avm2::r#trait::Trait;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::scope::Scope;
use crate::avm2::slot::Slot;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::value::Value;
use crate::avm2::Error;
use gc_arena::{Collect, GcCell, MutationContext};
@ -337,6 +337,10 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn set_interfaces(&self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.0.write(context).set_interfaces(iface_list)
}
fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
self.0.read().as_class()
}
}
impl<'gc> ScriptObject<'gc> {
@ -897,4 +901,13 @@ impl<'gc> ScriptObjectData<'gc> {
pub fn set_interfaces(&mut self, iface_list: Vec<Object<'gc>>) {
self.interfaces = iface_list;
}
/// Get the class for this object, if it has one.
pub fn as_class(&self) -> Option<GcCell<'gc, Class<'gc>>> {
match self.class {
ScriptObjectClass::ClassConstructor(class, _) => Some(class),
ScriptObjectClass::InstancePrototype(class, _) => Some(class),
ScriptObjectClass::NoClass => None,
}
}
}

View File

@ -2,19 +2,16 @@
use crate::avm2::class::Class;
use crate::avm2::method::{BytecodeMethod, Method};
use crate::avm2::r#trait::Trait;
use crate::avm2::string::AvmString;
use crate::avm2::traits::Trait;
use crate::avm2::{Avm2, Error};
use crate::collect::CollectWrapper;
use fnv::FnvHashMap;
use gc_arena::{Collect, Gc, GcCell, MutationContext};
use std::mem::drop;
use std::rc::Rc;
use swf::avm2::types::{AbcFile, Index, Script as AbcScript};
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct CollectWrapper<T>(T);
#[derive(Copy, Clone, Debug, Collect)]
#[collect(no_drop)]
pub struct TranslationUnit<'gc>(GcCell<'gc, TranslationUnitData<'gc>>);

View File

@ -6,9 +6,23 @@ use crate::avm2::names::{Multiname, QName};
use crate::avm2::script::TranslationUnit;
use crate::avm2::value::{abc_default_value, Value};
use crate::avm2::{Avm2, Error};
use crate::collect::CollectWrapper;
use enumset::{EnumSet, EnumSetType};
use gc_arena::{Collect, GcCell, MutationContext};
use swf::avm2::types::{Trait as AbcTrait, TraitKind as AbcTraitKind};
/// All attributes a trait can have.
#[derive(Debug, EnumSetType)]
pub enum TraitAttributes {
/// Whether or not traits in downstream classes are allowed to override
/// this trait.
Final,
/// Whether or not this trait is intended to override an upstream class's
/// trait.
Override,
}
/// Represents a trait as loaded into the VM.
///
/// A trait is an uninstantiated AVM2 property. Traits are used by objects to
@ -25,18 +39,22 @@ pub struct Trait<'gc> {
/// The name of this trait.
name: QName<'gc>,
/// Whether or not traits in downstream classes are allowed to override
/// this trait.
is_final: bool,
/// Whether or not this trait is intended to override an upstream class's
/// trait.
is_override: bool,
/// The attributes set on this trait.
attributes: CollectWrapper<EnumSet<TraitAttributes>>,
/// The kind of trait in use.
kind: TraitKind<'gc>,
}
fn trait_attribs_from_abc_traits(abc_trait: &AbcTrait) -> CollectWrapper<EnumSet<TraitAttributes>> {
CollectWrapper(match (abc_trait.is_final, abc_trait.is_override) {
(true, true) => TraitAttributes::Final | TraitAttributes::Override,
(true, false) => TraitAttributes::Final.into(),
(false, true) => TraitAttributes::Override.into(),
(false, false) => EnumSet::empty(),
})
}
/// The fields for a particular kind of trait.
///
/// The kind of a trait also determines how it's instantiated on the object.
@ -81,6 +99,83 @@ pub enum TraitKind<'gc> {
}
impl<'gc> Trait<'gc> {
pub fn from_class(class: GcCell<'gc, Class<'gc>>) -> Self {
let name = class.read().name().clone();
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Class { slot_id: 0, class },
}
}
pub fn from_method(name: QName<'gc>, method: Method<'gc>) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Method { disp_id: 0, method },
}
}
pub fn from_getter(name: QName<'gc>, method: Method<'gc>) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Getter { disp_id: 0, method },
}
}
pub fn from_setter(name: QName<'gc>, method: Method<'gc>) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Setter { disp_id: 0, method },
}
}
pub fn from_function(name: QName<'gc>, function: Method<'gc>) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Function {
slot_id: 0,
function,
},
}
}
pub fn from_slot(
name: QName<'gc>,
type_name: Multiname<'gc>,
default_value: Option<Value<'gc>>,
) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Slot {
slot_id: 0,
type_name,
default_value,
},
}
}
pub fn from_const(
name: QName<'gc>,
type_name: Multiname<'gc>,
default_value: Option<Value<'gc>>,
) -> Self {
Trait {
name,
attributes: CollectWrapper(EnumSet::empty()),
kind: TraitKind::Slot {
slot_id: 0,
type_name,
default_value,
},
}
}
/// Convert an ABC trait into a loaded trait.
pub fn from_abc_trait(
unit: TranslationUnit<'gc>,
@ -97,8 +192,7 @@ impl<'gc> Trait<'gc> {
value,
} => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Slot {
slot_id: *slot_id,
type_name: if type_name.0 == 0 {
@ -115,8 +209,7 @@ impl<'gc> Trait<'gc> {
},
AbcTraitKind::Method { disp_id, method } => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Method {
disp_id: *disp_id,
method: unit.load_method(method.0, mc)?,
@ -124,8 +217,7 @@ impl<'gc> Trait<'gc> {
},
AbcTraitKind::Getter { disp_id, method } => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Getter {
disp_id: *disp_id,
method: unit.load_method(method.0, mc)?,
@ -133,8 +225,7 @@ impl<'gc> Trait<'gc> {
},
AbcTraitKind::Setter { disp_id, method } => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Setter {
disp_id: *disp_id,
method: unit.load_method(method.0, mc)?,
@ -142,8 +233,7 @@ impl<'gc> Trait<'gc> {
},
AbcTraitKind::Class { slot_id, class } => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Class {
slot_id: *slot_id,
class: unit.load_class(class.0, avm2, mc)?,
@ -151,8 +241,7 @@ impl<'gc> Trait<'gc> {
},
AbcTraitKind::Function { slot_id, function } => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Function {
slot_id: *slot_id,
function: unit.load_method(function.0, mc)?,
@ -164,8 +253,7 @@ impl<'gc> Trait<'gc> {
value,
} => Trait {
name,
is_final: abc_trait.is_final,
is_override: abc_trait.is_override,
attributes: trait_attribs_from_abc_traits(abc_trait),
kind: TraitKind::Const {
slot_id: *slot_id,
type_name: if type_name.0 == 0 {
@ -192,10 +280,27 @@ impl<'gc> Trait<'gc> {
}
pub fn is_final(&self) -> bool {
self.is_final
self.attributes.0.contains(TraitAttributes::Final)
}
pub fn is_override(&self) -> bool {
self.is_override
self.attributes.0.contains(TraitAttributes::Override)
}
pub fn set_attributes(&mut self, attribs: EnumSet<TraitAttributes>) {
self.attributes.0 = attribs;
}
/// Set the slot or dispatch ID of this trait.
pub fn set_slot_id(&mut self, id: u32) {
match &mut self.kind {
TraitKind::Slot { slot_id, .. } => *slot_id = id,
TraitKind::Method { disp_id, .. } => *disp_id = id,
TraitKind::Getter { disp_id, .. } => *disp_id = id,
TraitKind::Setter { disp_id, .. } => *disp_id = id,
TraitKind::Class { slot_id, .. } => *slot_id = id,
TraitKind::Function { slot_id, .. } => *slot_id = id,
TraitKind::Const { slot_id, .. } => *slot_id = id,
}
}
}

7
core/src/collect.rs Normal file
View File

@ -0,0 +1,7 @@
//! Collect Wrapper Utility
use gc_arena::Collect;
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct CollectWrapper<T>(pub T);

View File

@ -1,5 +1,6 @@
//! Layout box structure
use crate::collect::CollectWrapper;
use crate::context::UpdateContext;
use crate::drawing::Drawing;
use crate::font::{EvalParameters, Font};
@ -552,10 +553,6 @@ pub struct LayoutBox<'gc> {
content: LayoutContent<'gc>,
}
#[derive(Clone, Debug, Collect)]
#[collect(require_static)]
pub struct CollectWrapper<T>(T);
/// Represents different content modes of a given `LayoutBox`.
///
/// Currently, a `LayoutBox` can contain `Text`, `Bullet`s, or a `Drawing`.

View File

@ -18,6 +18,7 @@ mod avm1;
mod avm2;
mod bounding_box;
mod character;
mod collect;
pub mod color_transform;
mod context;
mod drawing;

View File

@ -259,6 +259,7 @@ impl Player {
storage,
};
player.mutate_with_update_context(|context| Avm2::load_player_globals(context))?;
player.build_matrices();
player.audio.set_frame_rate(frame_rate);