2020-02-12 19:58:33 +00:00
|
|
|
//! Global scope built-ins
|
|
|
|
|
2020-07-04 21:18:41 +00:00
|
|
|
use crate::avm2::activation::Activation;
|
2020-07-07 04:24:16 +00:00
|
|
|
use crate::avm2::class::Class;
|
2020-07-03 01:49:53 +00:00
|
|
|
use crate::avm2::method::NativeMethod;
|
2020-02-19 03:26:08 +00:00
|
|
|
use crate::avm2::names::{Namespace, QName};
|
2020-07-07 04:24:16 +00:00
|
|
|
use crate::avm2::object::{FunctionObject, Object, ScriptObject, TObject};
|
2020-07-11 21:39:45 +00:00
|
|
|
use crate::avm2::scope::Scope;
|
2020-07-18 20:20:58 +00:00
|
|
|
use crate::avm2::string::AvmString;
|
2020-08-15 01:20:41 +00:00
|
|
|
use crate::avm2::traits::Trait;
|
2020-02-23 02:27:03 +00:00
|
|
|
use crate::avm2::value::Value;
|
2020-07-04 21:18:41 +00:00
|
|
|
use crate::avm2::Error;
|
2020-07-07 04:24:16 +00:00
|
|
|
use gc_arena::{Collect, GcCell, MutationContext};
|
2020-03-09 03:09:30 +00:00
|
|
|
use std::f64::NAN;
|
2020-02-12 19:58:33 +00:00
|
|
|
|
2020-07-17 02:33:26 +00:00
|
|
|
mod boolean;
|
2020-03-09 01:53:33 +00:00
|
|
|
mod class;
|
2020-02-23 00:12:33 +00:00
|
|
|
mod flash;
|
2020-02-19 03:26:08 +00:00
|
|
|
mod function;
|
2020-07-17 02:33:26 +00:00
|
|
|
mod int;
|
|
|
|
mod namespace;
|
|
|
|
mod number;
|
2020-02-19 03:26:08 +00:00
|
|
|
mod object;
|
2020-07-17 02:33:26 +00:00
|
|
|
mod string;
|
|
|
|
mod r#uint;
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-02-23 02:27:03 +00:00
|
|
|
fn trace<'gc>(
|
2020-06-24 03:34:06 +00:00
|
|
|
activation: &mut Activation<'_, 'gc, '_>,
|
2020-02-24 03:11:02 +00:00
|
|
|
_this: Option<Object<'gc>>,
|
2020-02-23 02:27:03 +00:00
|
|
|
args: &[Value<'gc>],
|
2020-07-04 21:18:41 +00:00
|
|
|
) -> Result<Value<'gc>, Error> {
|
2020-02-23 02:27:03 +00:00
|
|
|
if let Some(s) = args.get(0) {
|
2020-06-27 20:37:02 +00:00
|
|
|
log::info!(target: "avm_trace", "{}", s.clone().coerce_to_string(activation)?);
|
2020-02-23 02:27:03 +00:00
|
|
|
}
|
|
|
|
|
2020-07-04 21:18:41 +00:00
|
|
|
Ok(Value::Undefined)
|
2020-02-23 02:27:03 +00:00
|
|
|
}
|
|
|
|
|
2020-02-19 03:26:08 +00:00
|
|
|
/// This structure represents all system builtins' prototypes.
|
|
|
|
#[derive(Clone, Collect)]
|
|
|
|
#[collect(no_drop)]
|
|
|
|
pub struct SystemPrototypes<'gc> {
|
|
|
|
pub object: Object<'gc>,
|
|
|
|
pub function: Object<'gc>,
|
2020-03-09 01:53:33 +00:00
|
|
|
pub class: Object<'gc>,
|
2020-07-17 02:33:26 +00:00
|
|
|
pub string: Object<'gc>,
|
|
|
|
pub boolean: Object<'gc>,
|
|
|
|
pub number: Object<'gc>,
|
|
|
|
pub int: Object<'gc>,
|
|
|
|
pub uint: Object<'gc>,
|
|
|
|
pub namespace: Object<'gc>,
|
2020-02-19 03:26:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-23 02:27:03 +00:00
|
|
|
/// Add a free-function builtin to the global scope.
|
|
|
|
fn function<'gc>(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
mut global_scope: Object<'gc>,
|
2020-07-14 02:21:18 +00:00
|
|
|
package: impl Into<AvmString<'gc>>,
|
|
|
|
name: impl Into<AvmString<'gc>>,
|
2020-07-03 01:49:53 +00:00
|
|
|
nf: NativeMethod<'gc>,
|
2020-02-23 02:27:03 +00:00
|
|
|
fn_proto: Object<'gc>,
|
|
|
|
) {
|
|
|
|
global_scope
|
|
|
|
.install_dynamic_property(
|
|
|
|
mc,
|
|
|
|
QName::new(Namespace::package(package), name),
|
|
|
|
FunctionObject::from_builtin(mc, nf, fn_proto).into(),
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
/// 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>(
|
2020-02-23 00:39:12 +00:00
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
mut global_scope: Object<'gc>,
|
2020-08-05 03:00:17 +00:00
|
|
|
constr: Object<'gc>,
|
2020-02-23 00:39:12 +00:00
|
|
|
) {
|
2020-08-05 03:00:17 +00:00
|
|
|
let name = constr
|
|
|
|
.as_class()
|
|
|
|
.expect("constrs have classes in them")
|
|
|
|
.read()
|
|
|
|
.name()
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
global_scope.install_const(mc, name, 0, constr.into());
|
2020-02-23 00:39:12 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
/// Add a class builtin to the global scope.
|
2020-08-05 03:00:17 +00:00
|
|
|
///
|
|
|
|
/// This function returns a prototype which may be stored in `SystemPrototypes`.
|
2020-07-07 04:24:16 +00:00
|
|
|
fn class<'gc>(
|
|
|
|
activation: &mut Activation<'_, 'gc, '_>,
|
2020-07-11 21:39:45 +00:00
|
|
|
mut global: Object<'gc>,
|
2020-07-07 04:24:16 +00:00
|
|
|
class_def: GcCell<'gc, Class<'gc>>,
|
2020-08-05 03:00:17 +00:00
|
|
|
) -> Result<Object<'gc>, Error> {
|
2020-07-07 04:24:16 +00:00
|
|
|
let class_trait = Trait::from_class(class_def);
|
2020-07-11 21:39:45 +00:00
|
|
|
let global_scope = Scope::push_scope(global.get_scope(), global, activation.context.gc_context);
|
2020-08-05 03:00:17 +00:00
|
|
|
let mut constr = global
|
|
|
|
.install_foreign_trait(activation, class_trait, Some(global_scope), global)?
|
|
|
|
.coerce_to_object(activation)?;
|
2020-07-07 04:24:16 +00:00
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
constr
|
|
|
|
.get_property(
|
|
|
|
constr,
|
|
|
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
|
|
|
activation,
|
|
|
|
)?
|
|
|
|
.coerce_to_object(activation)
|
2020-07-07 04:24:16 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 03:09:30 +00:00
|
|
|
/// Add a builtin constant to the global scope.
|
|
|
|
fn constant<'gc>(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
mut global_scope: Object<'gc>,
|
2020-07-14 02:21:18 +00:00
|
|
|
package: impl Into<AvmString<'gc>>,
|
|
|
|
name: impl Into<AvmString<'gc>>,
|
2020-03-09 03:09:30 +00:00
|
|
|
value: Value<'gc>,
|
|
|
|
) {
|
|
|
|
global_scope.install_const(mc, QName::new(Namespace::package(package), name), 0, value)
|
|
|
|
}
|
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
/// Initialize all remaining builtin classes.
|
2020-07-07 04:24:16 +00:00
|
|
|
///
|
2020-08-05 03:00:17 +00:00
|
|
|
/// 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();
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-02-23 00:39:12 +00:00
|
|
|
// public / root package
|
2020-08-05 03:00:17 +00:00
|
|
|
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);
|
2020-02-23 00:39:12 +00:00
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
let object_constr = object::fill_proto(activation.context.gc_context, object_proto, fn_proto);
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-08-05 03:00:17 +00:00
|
|
|
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(
|
2020-02-23 00:39:12 +00:00
|
|
|
object_proto,
|
|
|
|
fn_proto,
|
2020-08-05 03:00:17 +00:00
|
|
|
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,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-08-05 03:00:17 +00:00
|
|
|
string::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
|
|
|
sp.boolean = class(
|
|
|
|
activation,
|
2020-03-09 01:53:33 +00:00
|
|
|
gs,
|
2020-08-05 03:00:17 +00:00
|
|
|
boolean::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
|
|
|
sp.number = class(
|
|
|
|
activation,
|
2020-07-17 02:33:26 +00:00
|
|
|
gs,
|
2020-08-05 03:00:17 +00:00
|
|
|
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,
|
2020-07-17 02:33:26 +00:00
|
|
|
gs,
|
|
|
|
"",
|
2020-08-05 03:00:17 +00:00
|
|
|
"trace",
|
|
|
|
trace,
|
2020-07-17 02:33:26 +00:00
|
|
|
fn_proto,
|
|
|
|
);
|
2020-08-05 03:00:17 +00:00
|
|
|
constant(
|
|
|
|
activation.context.gc_context,
|
2020-07-17 02:33:26 +00:00
|
|
|
gs,
|
|
|
|
"",
|
2020-08-05 03:00:17 +00:00
|
|
|
"undefined",
|
|
|
|
Value::Undefined,
|
2020-07-17 02:33:26 +00:00
|
|
|
);
|
2020-08-05 03:00:17 +00:00
|
|
|
constant(activation.context.gc_context, gs, "", "null", Value::Null);
|
|
|
|
constant(activation.context.gc_context, gs, "", "NaN", NAN.into());
|
|
|
|
constant(
|
|
|
|
activation.context.gc_context,
|
2020-07-17 02:33:26 +00:00
|
|
|
gs,
|
|
|
|
"",
|
2020-08-05 03:00:17 +00:00
|
|
|
"Infinity",
|
|
|
|
f64::INFINITY.into(),
|
2020-07-17 02:33:26 +00:00
|
|
|
);
|
2020-02-23 00:39:12 +00:00
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
// package `flash.events`
|
2020-02-23 00:39:12 +00:00
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::events::eventdispatcher::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-23 00:39:12 +00:00
|
|
|
|
|
|
|
// package `flash.display`
|
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::display::displayobject::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-23 00:39:12 +00:00
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::display::interactiveobject::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-23 00:39:12 +00:00
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::display::displayobjectcontainer::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-23 00:39:12 +00:00
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::display::sprite::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-23 00:39:12 +00:00
|
|
|
class(
|
2020-07-07 04:24:16 +00:00
|
|
|
activation,
|
2020-02-23 00:39:12 +00:00
|
|
|
gs,
|
2020-07-07 04:24:16 +00:00
|
|
|
flash::display::movieclip::create_class(activation.context.gc_context),
|
|
|
|
)?;
|
2020-02-12 19:58:33 +00:00
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
Ok(())
|
2020-02-12 19:58:33 +00:00
|
|
|
}
|