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};
|
|
|
|
use crate::avm2::r#trait::Trait;
|
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-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-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-07-07 04:24:16 +00:00
|
|
|
/// Add an ES3-style builtin to the global scope.
|
|
|
|
fn oldstyle_class<'gc>(
|
2020-02-23 00:39:12 +00:00
|
|
|
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
|
|
|
constr: NativeMethod<'gc>,
|
2020-02-23 00:39:12 +00:00
|
|
|
proto: Object<'gc>,
|
|
|
|
fn_proto: 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();
|
|
|
|
}
|
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
/// Add a class builtin to the global scope.
|
|
|
|
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>>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
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-07-07 04:24:16 +00:00
|
|
|
|
2020-08-05 02:50:46 +00:00
|
|
|
global.install_foreign_trait(activation, class_trait, Some(global_scope), global)?;
|
|
|
|
|
|
|
|
Ok(())
|
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-02-19 03:26:08 +00:00
|
|
|
/// 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.
|
2020-07-07 04:24:16 +00:00
|
|
|
///
|
|
|
|
/// 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.
|
2020-02-19 03:26:08 +00:00
|
|
|
pub fn construct_global_scope<'gc>(
|
|
|
|
mc: MutationContext<'gc, '_>,
|
|
|
|
) -> (Object<'gc>, SystemPrototypes<'gc>) {
|
2020-02-23 00:39:12 +00:00
|
|
|
let gs = ScriptObject::bare_object(mc);
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-02-23 00:39:12 +00:00
|
|
|
// public / root package
|
2020-02-19 03:26:08 +00:00
|
|
|
let object_proto = ScriptObject::bare_object(mc);
|
2020-02-23 00:39:12 +00:00
|
|
|
let fn_proto = function::create_proto(mc, object_proto);
|
2020-03-09 01:53:33 +00:00
|
|
|
let class_proto = class::create_proto(mc, object_proto, fn_proto);
|
2020-07-17 02:33:26 +00:00
|
|
|
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);
|
2020-02-23 00:39:12 +00:00
|
|
|
|
|
|
|
object::fill_proto(mc, object_proto, fn_proto);
|
2020-02-19 03:26:08 +00:00
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-02-23 00:39:12 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Object",
|
|
|
|
object::constructor,
|
|
|
|
object_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-02-23 00:39:12 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Function",
|
|
|
|
function::constructor,
|
|
|
|
fn_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-03-09 01:53:33 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Class",
|
|
|
|
class::constructor,
|
|
|
|
class_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-07-17 02:33:26 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"String",
|
|
|
|
string::constructor,
|
|
|
|
string_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-07-17 02:33:26 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Boolean",
|
|
|
|
boolean::constructor,
|
|
|
|
boolean_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(
|
2020-07-17 02:33:26 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Number",
|
|
|
|
number::constructor,
|
|
|
|
number_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-07-07 04:24:16 +00:00
|
|
|
oldstyle_class(mc, gs, "", "int", int::constructor, int_proto, fn_proto);
|
|
|
|
oldstyle_class(mc, gs, "", "uint", uint::constructor, uint_proto, fn_proto);
|
|
|
|
oldstyle_class(
|
2020-07-17 02:33:26 +00:00
|
|
|
mc,
|
|
|
|
gs,
|
|
|
|
"",
|
|
|
|
"Namespace",
|
|
|
|
namespace::constructor,
|
|
|
|
namespace_proto,
|
|
|
|
fn_proto,
|
|
|
|
);
|
2020-02-23 02:27:03 +00:00
|
|
|
function(mc, gs, "", "trace", trace, fn_proto);
|
2020-03-09 03:09:30 +00:00
|
|
|
constant(mc, gs, "", "undefined", Value::Undefined);
|
|
|
|
constant(mc, gs, "", "null", Value::Null);
|
|
|
|
constant(mc, gs, "", "NaN", NAN.into());
|
2020-06-23 23:27:02 +00:00
|
|
|
constant(mc, gs, "", "Infinity", f64::INFINITY.into());
|
2020-02-23 00:39:12 +00:00
|
|
|
|
2020-07-07 04:24:16 +00:00
|
|
|
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.
|
|
|
|
pub fn load_player_globals<'gc>(activation: &mut Activation<'_, 'gc, '_>) -> Result<(), Error> {
|
|
|
|
let gs = activation.avm2().globals();
|
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
|
|
|
}
|