Add stub builtins for Object and Function. These are more-or-less identical to the way we did it in AVM1 (e.g. no fancy player globals file)
This commit is contained in:
parent
1945f36dc0
commit
88957b2b3d
|
@ -1,6 +1,7 @@
|
||||||
//! ActionScript Virtual Machine 2 (AS3) support
|
//! ActionScript Virtual Machine 2 (AS3) support
|
||||||
|
|
||||||
use crate::avm2::activation::{Activation, Avm2ScriptEntry};
|
use crate::avm2::activation::{Activation, Avm2ScriptEntry};
|
||||||
|
use crate::avm2::globals::SystemPrototypes;
|
||||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||||
use crate::avm2::object::{Object, TObject};
|
use crate::avm2::object::{Object, TObject};
|
||||||
use crate::avm2::return_value::ReturnValue;
|
use crate::avm2::return_value::ReturnValue;
|
||||||
|
@ -54,15 +55,21 @@ pub struct Avm2<'gc> {
|
||||||
|
|
||||||
/// Global scope object.
|
/// Global scope object.
|
||||||
globals: Object<'gc>,
|
globals: Object<'gc>,
|
||||||
|
|
||||||
|
/// System prototypes.
|
||||||
|
system_prototypes: SystemPrototypes<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> Avm2<'gc> {
|
impl<'gc> Avm2<'gc> {
|
||||||
/// Construct a new AVM interpreter.
|
/// Construct a new AVM interpreter.
|
||||||
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
|
pub fn new(mc: MutationContext<'gc, '_>) -> Self {
|
||||||
|
let (globals, system_prototypes) = globals::construct_global_scope(mc);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
stack_frames: Vec::new(),
|
stack_frames: Vec::new(),
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
globals: globals::construct_global_scope(mc),
|
globals,
|
||||||
|
system_prototypes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,13 +92,12 @@ impl<'gc> Avm2<'gc> {
|
||||||
let scope = Scope::push_scope(None, self.globals(), context.gc_context);
|
let scope = Scope::push_scope(None, self.globals(), context.gc_context);
|
||||||
|
|
||||||
for trait_entry in entrypoint.script().traits.iter() {
|
for trait_entry in entrypoint.script().traits.iter() {
|
||||||
//TODO: Actually stick the Function proto here
|
|
||||||
self.globals.install_trait(
|
self.globals.install_trait(
|
||||||
context.gc_context,
|
context.gc_context,
|
||||||
entrypoint.abc(),
|
entrypoint.abc(),
|
||||||
trait_entry,
|
trait_entry,
|
||||||
Some(scope),
|
Some(scope),
|
||||||
self.globals,
|
self.system_prototypes.function,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! AVM2 executables.
|
//! AVM2 executables.
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::names::QName;
|
use crate::avm2::names::{Namespace, QName};
|
||||||
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
||||||
use crate::avm2::return_value::ReturnValue;
|
use crate::avm2::return_value::ReturnValue;
|
||||||
use crate::avm2::scope::Scope;
|
use crate::avm2::scope::Scope;
|
||||||
|
@ -269,6 +269,48 @@ impl<'gc> FunctionObject<'gc> {
|
||||||
))
|
))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a builtin function object from a Rust function.
|
||||||
|
pub fn from_builtin(
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
nf: NativeFunction<'gc>,
|
||||||
|
fn_proto: Object<'gc>,
|
||||||
|
) -> Object<'gc> {
|
||||||
|
FunctionObject(GcCell::allocate(
|
||||||
|
mc,
|
||||||
|
FunctionObjectData {
|
||||||
|
base: ScriptObjectData::base_new(Some(fn_proto)),
|
||||||
|
exec: nf.into(),
|
||||||
|
class: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a builtin type from a Rust constructor and prototype.
|
||||||
|
pub fn from_builtin_constr(
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
constr: NativeFunction<'gc>,
|
||||||
|
prototype: Object<'gc>,
|
||||||
|
fn_proto: Object<'gc>,
|
||||||
|
) -> Result<Object<'gc>, Error> {
|
||||||
|
let mut base = ScriptObjectData::base_new(Some(fn_proto));
|
||||||
|
|
||||||
|
base.install_dynamic_property(
|
||||||
|
QName::new(Namespace::public_namespace(), "prototype"),
|
||||||
|
prototype.into(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(FunctionObject(GcCell::allocate(
|
||||||
|
mc,
|
||||||
|
FunctionObjectData {
|
||||||
|
base,
|
||||||
|
exec: constr.into(),
|
||||||
|
class: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
|
@ -332,4 +374,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
.base
|
.base
|
||||||
.install_trait(mc, abc, trait_entry, scope, fn_proto)
|
.install_trait(mc, abc, trait_entry, scope, fn_proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
||||||
|
self.0.write(mc).base.install_method(name, function)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_dynamic_property(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
name: QName,
|
||||||
|
value: Value<'gc>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.0.write(mc).base.install_dynamic_property(name, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,69 @@
|
||||||
//! Global scope built-ins
|
//! Global scope built-ins
|
||||||
|
|
||||||
|
use crate::avm2::function::FunctionObject;
|
||||||
|
use crate::avm2::names::{Namespace, QName};
|
||||||
use crate::avm2::object::{Object, TObject};
|
use crate::avm2::object::{Object, TObject};
|
||||||
use crate::avm2::script_object::ScriptObject;
|
use crate::avm2::script_object::ScriptObject;
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::{Collect, MutationContext};
|
||||||
|
|
||||||
pub fn construct_global_scope<'gc>(mc: MutationContext<'gc, '_>) -> Object<'gc> {
|
mod function;
|
||||||
let global_scope = ScriptObject::bare_object(mc);
|
mod object;
|
||||||
|
|
||||||
|
/// 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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub fn construct_global_scope<'gc>(
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
) -> (Object<'gc>, SystemPrototypes<'gc>) {
|
||||||
|
let mut global_scope = ScriptObject::bare_object(mc);
|
||||||
|
|
||||||
|
let object_proto = ScriptObject::bare_object(mc);
|
||||||
|
let function_proto = function::create_proto(mc, object_proto);
|
||||||
|
|
||||||
|
object::fill_proto(mc, object_proto, function_proto);
|
||||||
|
|
||||||
|
let system_prototypes = SystemPrototypes {
|
||||||
|
object: object_proto,
|
||||||
|
function: function_proto,
|
||||||
|
};
|
||||||
|
|
||||||
global_scope
|
global_scope
|
||||||
|
.install_dynamic_property(
|
||||||
|
mc,
|
||||||
|
QName::new(Namespace::public_namespace(), "Object"),
|
||||||
|
FunctionObject::from_builtin_constr(
|
||||||
|
mc,
|
||||||
|
object::constructor,
|
||||||
|
object_proto,
|
||||||
|
function_proto,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
global_scope
|
||||||
|
.install_dynamic_property(
|
||||||
|
mc,
|
||||||
|
QName::new(Namespace::public_namespace(), "Function"),
|
||||||
|
FunctionObject::from_builtin_constr(
|
||||||
|
mc,
|
||||||
|
function::constructor,
|
||||||
|
function_proto,
|
||||||
|
function_proto,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(global_scope, system_prototypes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
//! Function builtin and prototype
|
||||||
|
|
||||||
|
use crate::avm2::function::FunctionObject;
|
||||||
|
use crate::avm2::names::{Namespace, QName};
|
||||||
|
use crate::avm2::object::{Object, TObject};
|
||||||
|
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 `Function`
|
||||||
|
pub fn constructor<'gc>(
|
||||||
|
_avm: &mut Avm2<'gc>,
|
||||||
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
_this: Object<'gc>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
Ok(Value::Undefined.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements `Function.prototype.toString`
|
||||||
|
fn to_string<'gc>(
|
||||||
|
_: &mut Avm2<'gc>,
|
||||||
|
_: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
_: Object<'gc>,
|
||||||
|
_: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
Ok(ReturnValue::Immediate("[type Function]".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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::public_namespace(), "toString"),
|
||||||
|
FunctionObject::from_builtin(gc_context, to_string, function_proto),
|
||||||
|
);
|
||||||
|
|
||||||
|
function_proto
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
//! Object builtin and prototype
|
||||||
|
|
||||||
|
use crate::avm2::object::Object;
|
||||||
|
use crate::avm2::return_value::ReturnValue;
|
||||||
|
use crate::avm2::value::Value;
|
||||||
|
use crate::avm2::{Avm2, Error};
|
||||||
|
use crate::context::UpdateContext;
|
||||||
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
|
/// Implements `Object`
|
||||||
|
pub fn constructor<'gc>(
|
||||||
|
_avm: &mut Avm2<'gc>,
|
||||||
|
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
_this: Object<'gc>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
|
Ok(Value::Undefined.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Partially construct `Object.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 `Object` prototype.
|
||||||
|
///
|
||||||
|
/// Since Object and Function are so heavily intertwined, this function does
|
||||||
|
/// not allocate an object to store either proto. Instead, you must allocate
|
||||||
|
/// bare objects for both and let this function fill Object for you.
|
||||||
|
pub fn fill_proto<'gc>(
|
||||||
|
_gc_context: MutationContext<'gc, '_>,
|
||||||
|
_object_proto: Object<'gc>,
|
||||||
|
_fn_proto: Object<'gc>,
|
||||||
|
) {
|
||||||
|
}
|
|
@ -71,6 +71,13 @@ pub struct QName {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QName {
|
impl QName {
|
||||||
|
pub fn new(ns: Namespace, name: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
ns,
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn qualified(ns: &Namespace, name: &str) -> Self {
|
pub fn qualified(ns: &Namespace, name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ns: ns.clone(),
|
ns: ns.clone(),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::avm2::script_object::ScriptObject;
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::{Avm2, Error};
|
use crate::avm2::{Avm2, Error};
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset::EnumSet;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use ruffle_macros::enum_trait_object;
|
use ruffle_macros::enum_trait_object;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -116,6 +116,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
fn_proto: Object<'gc>,
|
fn_proto: Object<'gc>,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Install a method (not necessarily from an ABC file) on an object.
|
||||||
|
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>);
|
||||||
|
|
||||||
|
/// Install a dynamic or built-in value property on an object.
|
||||||
|
fn install_dynamic_property(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
name: QName,
|
||||||
|
value: Value<'gc>,
|
||||||
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
/// Call the object.
|
/// Call the object.
|
||||||
fn call(
|
fn call(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -78,6 +78,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
.write(mc)
|
.write(mc)
|
||||||
.install_trait(mc, abc, trait_entry, scope, fn_proto)
|
.install_trait(mc, abc, trait_entry, scope, fn_proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn install_method(&mut self, mc: MutationContext<'gc, '_>, name: QName, function: Object<'gc>) {
|
||||||
|
self.0.write(mc).install_method(name, function)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_dynamic_property(
|
||||||
|
&mut self,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
name: QName,
|
||||||
|
value: Value<'gc>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.0.write(mc).install_dynamic_property(name, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> ScriptObject<'gc> {
|
impl<'gc> ScriptObject<'gc> {
|
||||||
|
@ -89,6 +102,15 @@ impl<'gc> ScriptObject<'gc> {
|
||||||
ScriptObject(GcCell::allocate(mc, ScriptObjectData::base_new(None))).into()
|
ScriptObject(GcCell::allocate(mc, ScriptObjectData::base_new(None))).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct an object with a base class.
|
||||||
|
pub fn object(mc: MutationContext<'gc, '_>, proto: Object<'gc>) -> Object<'gc> {
|
||||||
|
ScriptObject(GcCell::allocate(
|
||||||
|
mc,
|
||||||
|
ScriptObjectData::base_new(Some(proto)),
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct the instance prototype half of a class.
|
/// Construct the instance prototype half of a class.
|
||||||
pub fn instance_prototype(
|
pub fn instance_prototype(
|
||||||
mc: MutationContext<'gc, '_>,
|
mc: MutationContext<'gc, '_>,
|
||||||
|
@ -190,7 +212,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Install a method into the object.
|
/// Install a method into the object.
|
||||||
fn install_method(&mut self, name: QName, function: Object<'gc>) {
|
pub fn install_method(&mut self, name: QName, function: Object<'gc>) {
|
||||||
self.values.insert(name, Property::new_method(function));
|
self.values.insert(name, Property::new_method(function));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,4 +247,15 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.install_virtual_setter(function)
|
.install_virtual_setter(function)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn install_dynamic_property(
|
||||||
|
&mut self,
|
||||||
|
name: QName,
|
||||||
|
value: Value<'gc>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.values
|
||||||
|
.insert(name, Property::new_dynamic_property(value));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue