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
|
||||
|
||||
use crate::avm2::activation::{Activation, Avm2ScriptEntry};
|
||||
use crate::avm2::globals::SystemPrototypes;
|
||||
use crate::avm2::names::{Multiname, Namespace, QName};
|
||||
use crate::avm2::object::{Object, TObject};
|
||||
use crate::avm2::return_value::ReturnValue;
|
||||
|
@ -54,15 +55,21 @@ pub struct Avm2<'gc> {
|
|||
|
||||
/// Global scope object.
|
||||
globals: Object<'gc>,
|
||||
|
||||
/// System prototypes.
|
||||
system_prototypes: SystemPrototypes<'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);
|
||||
|
||||
Self {
|
||||
stack_frames: 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);
|
||||
|
||||
for trait_entry in entrypoint.script().traits.iter() {
|
||||
//TODO: Actually stick the Function proto here
|
||||
self.globals.install_trait(
|
||||
context.gc_context,
|
||||
entrypoint.abc(),
|
||||
trait_entry,
|
||||
Some(scope),
|
||||
self.globals,
|
||||
self.system_prototypes.function,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! AVM2 executables.
|
||||
|
||||
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::return_value::ReturnValue;
|
||||
use crate::avm2::scope::Scope;
|
||||
|
@ -269,6 +269,48 @@ impl<'gc> FunctionObject<'gc> {
|
|||
))
|
||||
.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> {
|
||||
|
@ -332,4 +374,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
.base
|
||||
.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
|
||||
|
||||
use crate::avm2::function::FunctionObject;
|
||||
use crate::avm2::names::{Namespace, QName};
|
||||
use crate::avm2::object::{Object, TObject};
|
||||
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> {
|
||||
let global_scope = ScriptObject::bare_object(mc);
|
||||
mod function;
|
||||
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
|
||||
.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 {
|
||||
pub fn new(ns: Namespace, name: &str) -> Self {
|
||||
Self {
|
||||
ns,
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn qualified(ns: &Namespace, name: &str) -> Self {
|
||||
Self {
|
||||
ns: ns.clone(),
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::avm2::script_object::ScriptObject;
|
|||
use crate::avm2::value::Value;
|
||||
use crate::avm2::{Avm2, Error};
|
||||
use crate::context::UpdateContext;
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_macros::enum_trait_object;
|
||||
use std::fmt::Debug;
|
||||
|
@ -116,6 +116,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
fn_proto: Object<'gc>,
|
||||
) -> 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.
|
||||
fn call(
|
||||
self,
|
||||
|
|
|
@ -78,6 +78,19 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
.write(mc)
|
||||
.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> {
|
||||
|
@ -89,6 +102,15 @@ impl<'gc> ScriptObject<'gc> {
|
|||
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.
|
||||
pub fn instance_prototype(
|
||||
mc: MutationContext<'gc, '_>,
|
||||
|
@ -190,7 +212,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
}
|
||||
|
||||
/// 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));
|
||||
}
|
||||
|
||||
|
@ -225,4 +247,15 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
.unwrap()
|
||||
.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