Invert the role of `Avm2` and it's `Activation`, similar to what was done with `Avm1` and it's `Activation`.

This also results in a far reduced role for `ReturnValue`, since I also took the liberty of removing most of it's use. Furthermore, I also made it apply equally to native and AVM2 code, which ensures all native implementations of methods don't double-borrow.

In AVM1, `ReturnValue` was actually removed entirely, because it's not needed. I attempted to do the same, but the fact that we're currently embedding `ScriptObjectData` in native objects means that we need it for virtual properties. Otherwise, virtual property implementations will see locked objects, which is bad.
This commit is contained in:
David Wendt 2020-07-04 17:18:41 -04:00
parent 5b5bf0719e
commit 97e005622b
20 changed files with 1658 additions and 1829 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,10 @@ use crate::avm2::method::{BytecodeMethod, Method, NativeMethod};
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, ObjectPtr, TObject}; use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::r#trait::Trait; use crate::avm2::r#trait::Trait;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
use crate::avm2::script_object::{ScriptObjectClass, ScriptObjectData}; use crate::avm2::script_object::{ScriptObjectClass, ScriptObjectData};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, CollectionContext, GcCell, MutationContext}; use gc_arena::{Collect, CollectionContext, GcCell, MutationContext};
use std::fmt; use std::fmt;
@ -76,42 +75,43 @@ impl<'gc> Executable<'gc> {
/// Execute a method. /// Execute a method.
/// ///
/// The function will either be called directly if it is a Rust builtin, or /// The function will either be called directly if it is a Rust builtin, or
/// placed on the stack of the passed-in AVM2 otherwise. As a result, we /// executed on the same AVM2 instance as the activation passed in here.
/// return a `ReturnValue` which can be used to force execution of the /// The value returned in either case will be provided here.
/// given stack frame and obtain it's return value or to push said value ///
/// onto the AVM operand stack. /// It is a panicing logic error to attempt to execute user code while any
/// reachable object is currently under a GcCell write lock.
pub fn exec( pub fn exec(
&self, &self,
unbound_reciever: Option<Object<'gc>>, unbound_reciever: Option<Object<'gc>>,
arguments: &[Value<'gc>], arguments: &[Value<'gc>],
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
match self { match self {
Executable::Native(nf, reciever) => { Executable::Native(nf, reciever) => nf(
nf(avm, context, reciever.or(unbound_reciever), arguments) activation,
} context,
reciever.or(unbound_reciever),
arguments,
),
Executable::Action { Executable::Action {
method, method,
scope, scope,
reciever, reciever,
} => { } => {
let reciever = reciever.or(unbound_reciever); let reciever = reciever.or(unbound_reciever);
let activation = GcCell::allocate( let mut activation = Activation::from_method(
context.gc_context, activation.avm2(),
Activation::from_action(
context, context,
method.clone(), method.clone(),
*scope, *scope,
reciever, reciever,
arguments, arguments,
base_proto, base_proto,
)?,
); );
avm.insert_stack_frame(activation); activation.run_actions(method.clone(), context)
Ok(activation.into())
} }
} }
} }
@ -161,7 +161,7 @@ impl<'gc> FunctionObject<'gc> {
/// initializer method that you should call before interacting with the /// initializer method that you should call before interacting with the
/// class. The latter should be called using the former as a reciever. /// class. The latter should be called using the former as a reciever.
pub fn from_class( pub fn from_class(
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
mut base_class: Object<'gc>, mut base_class: Object<'gc>,
@ -172,7 +172,7 @@ impl<'gc> FunctionObject<'gc> {
.get_property( .get_property(
base_class, base_class,
&QName::new(Namespace::public_namespace(), "prototype"), &QName::new(Namespace::public_namespace(), "prototype"),
avm, activation,
context, context,
)? )?
.as_object() .as_object()
@ -187,9 +187,9 @@ impl<'gc> FunctionObject<'gc> {
) )
.into() .into()
}); });
let mut class_proto = super_proto?.derive(avm, context, class, scope)?; let mut class_proto = super_proto?.derive(activation, context, class, scope)?;
let fn_proto = avm.prototypes().function; let fn_proto = activation.avm2().prototypes().function;
let class_constr_proto = avm.prototypes().class; let class_constr_proto = activation.avm2().prototypes().class;
let initializer = class_read.instance_init(); let initializer = class_read.instance_init();
@ -303,13 +303,15 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
self, self,
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
self.0 let read = self.0.read();
.read() let rv = read.base.get_property_local(reciever, name, activation)?;
.base
.get_property_local(reciever, name, avm, context) drop(read);
rv.resolve(activation, context)
} }
fn set_property_local( fn set_property_local(
@ -317,16 +319,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let rv = self let mut write = self.0.write(context.gc_context);
.0 let rv = write
.write(context.gc_context)
.base .base
.set_property_local(reciever, name, value, avm, context)?; .set_property_local(reciever, name, value, activation, context)?;
rv.resolve(avm, context)?; drop(write);
rv.resolve(activation, context)?;
Ok(()) Ok(())
} }
@ -336,16 +339,17 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let rv = self let mut write = self.0.write(context.gc_context);
.0 let rv = write
.write(context.gc_context)
.base .base
.init_property_local(reciever, name, value, avm, context)?; .init_property_local(reciever, name, value, activation, context)?;
rv.resolve(avm, context)?; drop(write);
rv.resolve(activation, context)?;
Ok(()) Ok(())
} }
@ -468,13 +472,12 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
self, self,
reciever: Option<Object<'gc>>, reciever: Option<Object<'gc>>,
arguments: &[Value<'gc>], arguments: &[Value<'gc>],
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(exec) = &self.0.read().exec { if let Some(exec) = &self.0.read().exec {
exec.exec(reciever, arguments, avm, context, base_proto)? exec.exec(reciever, arguments, activation, context, base_proto)
.resolve(avm, context)
} else { } else {
Err("Not a callable function!".into()) Err("Not a callable function!".into())
} }
@ -482,7 +485,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn construct( fn construct(
&self, &self,
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
@ -498,7 +501,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
fn derive( fn derive(
&self, &self,
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
scope: Option<GcCell<'gc, Scope<'gc>>>, scope: Option<GcCell<'gc, Scope<'gc>>>,

View File

@ -1,13 +1,13 @@
//! Global scope built-ins //! Global scope built-ins
use crate::avm2::activation::Activation;
use crate::avm2::function::FunctionObject; use crate::avm2::function::FunctionObject;
use crate::avm2::method::NativeMethod; use crate::avm2::method::NativeMethod;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, MutationContext}; use gc_arena::{Collect, MutationContext};
use std::f64::NAN; use std::f64::NAN;
@ -18,16 +18,16 @@ mod function;
mod object; mod object;
fn trace<'gc>( fn trace<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if let Some(s) = args.get(0) { if let Some(s) = args.get(0) {
log::info!(target: "avm_trace", "{}", s.clone().coerce_string()); log::info!(target: "avm_trace", "{}", s.clone().coerce_string());
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// This structure represents all system builtins' prototypes. /// This structure represents all system builtins' prototypes.

View File

@ -1,10 +1,10 @@
//! `Class` builtin/prototype //! `Class` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
@ -13,11 +13,11 @@ use gc_arena::MutationContext;
/// Notably, you cannot construct new classes this way, so this returns an /// Notably, you cannot construct new classes this way, so this returns an
/// error. /// error.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Err("Classes cannot be constructed.".into()) Err("Classes cannot be constructed.".into())
} }

View File

@ -1,21 +1,21 @@
//! `flash.display.DisplayObject` builtin/prototype //! `flash.display.DisplayObject` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.display.DisplayObject`'s constructor. /// Implements `flash.display.DisplayObject`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `DisplayObject.prototype`. /// Construct `DisplayObject.prototype`.

View File

@ -1,21 +1,21 @@
//! `flash.display.DisplayObjectContainer` builtin/prototype //! `flash.display.DisplayObjectContainer` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.display.DisplayObjectContainer`'s constructor. /// Implements `flash.display.DisplayObjectContainer`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `DisplayObjectContainer.prototype`. /// Construct `DisplayObjectContainer.prototype`.

View File

@ -1,21 +1,21 @@
//! `flash.display.InteractiveObject` builtin/prototype //! `flash.display.InteractiveObject` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.display.InteractiveObject`'s constructor. /// Implements `flash.display.InteractiveObject`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `InteractiveObject.prototype`. /// Construct `InteractiveObject.prototype`.

View File

@ -1,21 +1,21 @@
//! `flash.display.MovieClip` builtin/prototype //! `flash.display.MovieClip` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.display.MovieClip`'s constructor. /// Implements `flash.display.MovieClip`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `MovieClip.prototype`. /// Construct `MovieClip.prototype`.

View File

@ -1,21 +1,21 @@
//! `flash.display.Sprite` builtin/prototype //! `flash.display.Sprite` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.display.Sprite`'s constructor. /// Implements `flash.display.Sprite`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `Sprite.prototype`. /// Construct `Sprite.prototype`.

View File

@ -1,21 +1,21 @@
//! `flash.events.EventDispatcher` builtin/prototype //! `flash.events.EventDispatcher` builtin/prototype
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `flash.events.EventDispatcher`'s constructor. /// Implements `flash.events.EventDispatcher`'s constructor.
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Construct `EventDispatcher.prototype`. /// Construct `EventDispatcher.prototype`.

View File

@ -1,42 +1,40 @@
//! Function builtin and prototype //! Function builtin and prototype
use crate::avm2::activation::Activation;
use crate::avm2::function::FunctionObject; use crate::avm2::function::FunctionObject;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Function` /// Implements `Function`
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_action_context: &mut UpdateContext<'_, 'gc, '_>, _action_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Implements `Function.prototype.call` /// Implements `Function.prototype.call`
fn call<'gc>( fn call<'gc>(
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
func: Option<Object<'gc>>, func: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let this = args.get(0).and_then(|v| v.as_object().ok()); let this = args.get(0).and_then(|v| v.as_object().ok());
let base_proto = this.and_then(|that| that.proto()); let base_proto = this.and_then(|that| that.proto());
if let Some(func) = func { if let Some(func) = func {
if args.len() > 1 { if args.len() > 1 {
Ok(func Ok(func.call(this, &args[1..], activation, context, base_proto)?)
.call(this, &args[1..], avm, context, base_proto)?
.into())
} else { } else {
Ok(func.call(this, &[], avm, context, base_proto)?.into()) Ok(func.call(this, &[], activation, context, base_proto)?)
} }
} else { } else {
Err("Not a callable function".into()) Err("Not a callable function".into())

View File

@ -1,70 +1,65 @@
//! Object builtin and prototype //! Object builtin and prototype
use crate::avm2::activation::Activation;
use crate::avm2::function::FunctionObject; use crate::avm2::function::FunctionObject;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::MutationContext; use gc_arena::MutationContext;
/// Implements `Object` /// Implements `Object`
pub fn constructor<'gc>( pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>, _this: Option<Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Implements `Object.prototype.toString` /// Implements `Object.prototype.toString`
fn to_string<'gc>( fn to_string<'gc>(
_: &mut Avm2<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(this Ok(this
.map(|t| t.to_string()) .map(|t| t.to_string())
.unwrap_or(Ok(Value::Undefined))? .unwrap_or(Ok(Value::Undefined))?)
.into())
} }
/// Implements `Object.prototype.toLocaleString` /// Implements `Object.prototype.toLocaleString`
fn to_locale_string<'gc>( fn to_locale_string<'gc>(
_: &mut Avm2<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(this Ok(this
.map(|t| t.to_string()) .map(|t| t.to_string())
.unwrap_or(Ok(Value::Undefined))? .unwrap_or(Ok(Value::Undefined))?)
.into())
} }
/// Implements `Object.prototype.valueOf` /// Implements `Object.prototype.valueOf`
fn value_of<'gc>( fn value_of<'gc>(
_: &mut Avm2<'gc>, _: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>, _: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
_: &[Value<'gc>], _: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
Ok(this Ok(this.map(|t| t.value_of()).unwrap_or(Ok(Value::Undefined))?)
.map(|t| t.value_of())
.unwrap_or(Ok(Value::Undefined))?
.into())
} }
/// `Object.prototype.hasOwnProperty` /// `Object.prototype.hasOwnProperty`
pub fn has_own_property<'gc>( pub fn has_own_property<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into());
let this = this?; let this = this?;
let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into());
@ -82,11 +77,11 @@ pub fn has_own_property<'gc>(
/// `Object.prototype.isPrototypeOf` /// `Object.prototype.isPrototypeOf`
pub fn is_prototype_of<'gc>( pub fn is_prototype_of<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let search_proto: Result<Object<'gc>, Error> = let search_proto: Result<Object<'gc>, Error> =
this.ok_or_else(|| "No valid this parameter".into()); this.ok_or_else(|| "No valid this parameter".into());
let search_proto = search_proto?; let search_proto = search_proto?;
@ -105,11 +100,11 @@ pub fn is_prototype_of<'gc>(
/// `Object.prototype.propertyIsEnumerable` /// `Object.prototype.propertyIsEnumerable`
pub fn property_is_enumerable<'gc>( pub fn property_is_enumerable<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into());
let this = this?; let this = this?;
let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into());
@ -127,11 +122,11 @@ pub fn property_is_enumerable<'gc>(
/// `Object.prototype.setPropertyIsEnumerable` /// `Object.prototype.setPropertyIsEnumerable`
pub fn set_property_is_enumerable<'gc>( pub fn set_property_is_enumerable<'gc>(
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>, this: Option<Object<'gc>>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into()); let this: Result<Object<'gc>, Error> = this.ok_or_else(|| "No valid this parameter".into());
let this = this?; let this = this?;
let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into()); let name: Result<&Value<'gc>, Error> = args.get(0).ok_or_else(|| "No name specified".into());
@ -149,7 +144,7 @@ pub fn set_property_is_enumerable<'gc>(
} }
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined)
} }
/// Partially construct `Object.prototype`. /// Partially construct `Object.prototype`.

View File

@ -1,10 +1,10 @@
//! AVM2 methods //! AVM2 methods
use crate::avm2::activation::Activation;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::return_value::ReturnValue;
use crate::avm2::script::TranslationUnit; use crate::avm2::script::TranslationUnit;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, CollectionContext}; use gc_arena::{Collect, CollectionContext};
use std::fmt; use std::fmt;
@ -30,11 +30,11 @@ pub struct CollectWrapper<T>(T);
/// your function yields `None`, you must ensure that the top-most activation /// your function yields `None`, you must ensure that the top-most activation
/// in the AVM1 runtime will return with the value of this function. /// in the AVM1 runtime will return with the value of this function.
pub type NativeMethod<'gc> = fn( pub type NativeMethod<'gc> = fn(
&mut Avm2<'gc>, &mut Activation<'_, 'gc>,
&mut UpdateContext<'_, 'gc, '_>, &mut UpdateContext<'_, 'gc, '_>,
Option<Object<'gc>>, Option<Object<'gc>>,
&[Value<'gc>], &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error>; ) -> Result<Value<'gc>, Error>;
/// Represents a reference to an AVM2 method and body. /// Represents a reference to an AVM2 method and body.
#[derive(Collect, Clone, Debug)] #[derive(Collect, Clone, Debug)]

View File

@ -1,5 +1,6 @@
//! AVM2 objects. //! AVM2 objects.
use crate::avm2::activation::Activation;
use crate::avm2::class::Class; use crate::avm2::class::Class;
use crate::avm2::function::{Executable, FunctionObject}; use crate::avm2::function::{Executable, FunctionObject};
use crate::avm2::names::{Multiname, Namespace, QName}; use crate::avm2::names::{Multiname, Namespace, QName};
@ -7,7 +8,7 @@ use crate::avm2::r#trait::{Trait, TraitKind};
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
use crate::avm2::script_object::ScriptObject; use crate::avm2::script_object::ScriptObject;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use ruffle_macros::enum_trait_object; use ruffle_macros::enum_trait_object;
@ -30,7 +31,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
self, self,
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error>; ) -> Result<Value<'gc>, Error>;
@ -39,23 +40,23 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&mut self, &mut self,
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if !self.has_instantiated_property(name) { if !self.has_instantiated_property(name) {
for abc_trait in self.get_trait(name)? { for abc_trait in self.get_trait(name)? {
self.install_trait(avm, context, abc_trait, reciever)?; self.install_trait(activation, context, abc_trait, reciever)?;
} }
} }
let has_no_getter = self.has_own_virtual_setter(name) && !self.has_own_virtual_getter(name); let has_no_getter = self.has_own_virtual_setter(name) && !self.has_own_virtual_getter(name);
if self.has_own_property(name)? && !has_no_getter { if self.has_own_property(name)? && !has_no_getter {
return self.get_property_local(reciever, name, avm, context); return self.get_property_local(reciever, name, activation, context);
} }
if let Some(mut proto) = self.proto() { if let Some(mut proto) = self.proto() {
return proto.get_property(reciever, name, avm, context); return proto.get_property(reciever, name, activation, context);
} }
Ok(Value::Undefined) Ok(Value::Undefined)
@ -83,7 +84,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error>; ) -> Result<(), Error>;
@ -93,17 +94,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if !self.has_instantiated_property(name) { if !self.has_instantiated_property(name) {
for abc_trait in self.get_trait(name)? { for abc_trait in self.get_trait(name)? {
self.install_trait(avm, context, abc_trait, reciever)?; self.install_trait(activation, context, abc_trait, reciever)?;
} }
} }
if self.has_own_virtual_setter(name) { if self.has_own_virtual_setter(name) {
return self.set_property_local(reciever, name, value, avm, context); return self.set_property_local(reciever, name, value, activation, context);
} }
let mut proto = self.proto(); let mut proto = self.proto();
@ -112,13 +113,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
//we're calling a virtual setter. If you call `set_property` on //we're calling a virtual setter. If you call `set_property` on
//a non-virtual you will actually alter the prototype. //a non-virtual you will actually alter the prototype.
if my_proto.has_own_virtual_setter(name) { if my_proto.has_own_virtual_setter(name) {
return my_proto.set_property(reciever, name, value, avm, context); return my_proto.set_property(reciever, name, value, activation, context);
} }
proto = my_proto.proto(); proto = my_proto.proto();
} }
reciever.set_property_local(reciever, name, value, avm, context) reciever.set_property_local(reciever, name, value, activation, context)
} }
/// Init a property on this specific object. /// Init a property on this specific object.
@ -127,7 +128,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error>; ) -> Result<(), Error>;
@ -137,17 +138,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if !self.has_instantiated_property(name) { if !self.has_instantiated_property(name) {
for abc_trait in self.get_trait(name)? { for abc_trait in self.get_trait(name)? {
self.install_trait(avm, context, abc_trait, reciever)?; self.install_trait(activation, context, abc_trait, reciever)?;
} }
} }
if self.has_own_virtual_setter(name) { if self.has_own_virtual_setter(name) {
return self.init_property_local(reciever, name, value, avm, context); return self.init_property_local(reciever, name, value, activation, context);
} }
let mut proto = self.proto(); let mut proto = self.proto();
@ -156,13 +157,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
//we're calling a virtual setter. If you call `set_property` on //we're calling a virtual setter. If you call `set_property` on
//a non-virtual you will actually alter the prototype. //a non-virtual you will actually alter the prototype.
if my_proto.has_own_virtual_setter(name) { if my_proto.has_own_virtual_setter(name) {
return my_proto.init_property(reciever, name, value, avm, context); return my_proto.init_property(reciever, name, value, activation, context);
} }
proto = my_proto.proto(); proto = my_proto.proto();
} }
reciever.init_property_local(reciever, name, value, avm, context) reciever.init_property_local(reciever, name, value, activation, context)
} }
/// Retrieve a slot by it's index. /// Retrieve a slot by it's index.
@ -404,24 +405,24 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// object. /// object.
fn install_trait( fn install_trait(
&mut self, &mut self,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
trait_entry: Trait<'gc>, trait_entry: Trait<'gc>,
reciever: Object<'gc>, reciever: Object<'gc>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.install_foreign_trait(avm, context, trait_entry, self.get_scope(), reciever) self.install_foreign_trait(activation, context, trait_entry, self.get_scope(), reciever)
} }
/// Install a trait from anywyere. /// Install a trait from anywyere.
fn install_foreign_trait( fn install_foreign_trait(
&mut self, &mut self,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
trait_entry: Trait<'gc>, trait_entry: Trait<'gc>,
scope: Option<GcCell<'gc, Scope<'gc>>>, scope: Option<GcCell<'gc, Scope<'gc>>>,
reciever: Object<'gc>, reciever: Object<'gc>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let fn_proto = avm.prototypes().function; let fn_proto = activation.avm2().prototypes().function;
let trait_name = trait_entry.name().clone(); let trait_name = trait_entry.name().clone();
avm_debug!( avm_debug!(
"Installing trait {:?} of kind {:?}", "Installing trait {:?} of kind {:?}",
@ -491,14 +492,14 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
}; };
let super_class: Result<Object<'gc>, Error> = self let super_class: Result<Object<'gc>, Error> = self
.get_property(reciever, &super_name, avm, context)? .get_property(reciever, &super_name, activation, context)?
.as_object() .as_object()
.map_err(|_e| { .map_err(|_e| {
format!("Could not resolve superclass {:?}", super_name.local_name()).into() format!("Could not resolve superclass {:?}", super_name.local_name()).into()
}); });
let (class_object, _cinit) = let (class_object, _cinit) =
FunctionObject::from_class(avm, context, *class, super_class?, scope)?; FunctionObject::from_class(activation, context, *class, super_class?, scope)?;
self.install_const( self.install_const(
context.gc_context, context.gc_context,
class_read.name().clone(), class_read.name().clone(),
@ -516,7 +517,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn_proto, fn_proto,
None, None,
); );
let es3_proto = ScriptObject::object(context.gc_context, avm.prototypes().object); let es3_proto =
ScriptObject::object(context.gc_context, activation.avm2().prototypes().object);
fobject.install_slot( fobject.install_slot(
context.gc_context, context.gc_context,
@ -548,7 +550,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
self, self,
_reciever: Option<Object<'gc>>, _reciever: Option<Object<'gc>>,
_arguments: &[Value<'gc>], _arguments: &[Value<'gc>],
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_base_proto: Option<Object<'gc>>, _base_proto: Option<Object<'gc>>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
@ -574,7 +576,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// purely so that host objects can be constructed by the VM. /// purely so that host objects can be constructed by the VM.
fn construct( fn construct(
&self, &self,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Object<'gc>, Error>; ) -> Result<Object<'gc>, Error>;
@ -590,7 +592,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// traits. /// traits.
fn derive( fn derive(
&self, &self,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
scope: Option<GcCell<'gc, Scope<'gc>>>, scope: Option<GcCell<'gc, Scope<'gc>>>,

View File

@ -5,8 +5,7 @@ use crate::avm2::function::Executable;
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue; use crate::avm2::return_value::ReturnValue;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext;
use enumset::{EnumSet, EnumSetType}; use enumset::{EnumSet, EnumSetType};
use gc_arena::{Collect, CollectionContext}; use gc_arena::{Collect, CollectionContext};
@ -136,15 +135,16 @@ impl<'gc> Property<'gc> {
/// user-defined. /// user-defined.
pub fn get( pub fn get(
&self, &self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
match self { match self {
Property::Virtual { get: Some(get), .. } => { Property::Virtual { get: Some(get), .. } => Ok(ReturnValue::defer_execution(
get.exec(Some(this), &[], avm, context, base_proto) get.clone(),
} Some(this),
vec![],
base_proto,
)),
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()), Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
Property::Stored { value, .. } => Ok(value.to_owned().into()), Property::Stored { value, .. } => Ok(value.to_owned().into()),
Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()), Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()),
@ -160,8 +160,6 @@ impl<'gc> Property<'gc> {
/// is encountered. /// is encountered.
pub fn set( pub fn set(
&mut self, &mut self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
new_value: impl Into<Value<'gc>>, new_value: impl Into<Value<'gc>>,
@ -169,13 +167,12 @@ impl<'gc> Property<'gc> {
match self { match self {
Property::Virtual { set, .. } => { Property::Virtual { set, .. } => {
if let Some(function) = set { if let Some(function) = set {
return function.exec( return Ok(ReturnValue::defer_execution(
function.clone(),
Some(this), Some(this),
&[new_value.into()], vec![new_value.into()],
avm,
context,
base_proto, base_proto,
); ));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined.into())
@ -207,8 +204,6 @@ impl<'gc> Property<'gc> {
/// is encountered. /// is encountered.
pub fn init( pub fn init(
&mut self, &mut self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
base_proto: Option<Object<'gc>>, base_proto: Option<Object<'gc>>,
new_value: impl Into<Value<'gc>>, new_value: impl Into<Value<'gc>>,
@ -216,13 +211,12 @@ impl<'gc> Property<'gc> {
match self { match self {
Property::Virtual { set, .. } => { Property::Virtual { set, .. } => {
if let Some(function) = set { if let Some(function) = set {
return function.exec( return Ok(ReturnValue::defer_execution(
function.clone(),
Some(this), Some(this),
&[new_value.into()], vec![new_value.into()],
avm,
context,
base_proto, base_proto,
); ));
} }
Ok(Value::Undefined.into()) Ok(Value::Undefined.into())

View File

@ -1,190 +1,113 @@
//! Return value enum //! Return value enum
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::names::Namespace; use crate::avm2::function::Executable;
use crate::avm2::object::Object; use crate::avm2::object::Object;
use crate::avm2::{Avm2, Error, Value}; use crate::avm2::{Error, Value};
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell};
use std::fmt; use std::fmt;
/// Represents the return value of a function call. /// Represents the return value of a function call that has not yet executed.
/// ///
/// Since function calls can result in invoking native code or adding a new /// It is a panicking logic error to attempt to run AVM2 code while any
/// activation onto the AVM stack, you need another type to represent how the /// reachable object is in a locked state. Ergo, it is sometimes necessary to
/// return value will be delivered to you. /// be able to return what *should* be called rather than actually running the
/// code on the current Rust stack. This type exists to force deferred
/// execution of some child AVM2 frame.
/// ///
/// This function contains a handful of utility methods for deciding what to do /// It is also possible to stuff a regular `Value` in here - this is provided
/// with a given value regardless of how it is delivered to the calling /// for the convenience of functions that may be able to resolve a value
/// function. /// without needing a free stack. `ReturnValue` should not be used as a generic
/// wrapper for `Value`, as it can also defer actual execution, and it should
/// be resolved at the earliest safe opportunity.
/// ///
/// It is `must_use` - failing to use a return value is a compiler warning. We /// It is `must_use` - failing to resolve a return value is a compiler warning.
/// provide a helper function specifically to indicate that you aren't
/// interested in the result of a call.
#[must_use = "Return values must be used"] #[must_use = "Return values must be used"]
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub enum ReturnValue<'gc> { pub enum ReturnValue<'gc> {
/// Indicates that the return value is available immediately. /// ReturnValue has already been computed.
///
/// This exists primarily for functions that don't necessarily need to
/// always defer code execution - say, if they already have the result and
/// do not need a free stack frame to run an activation on.
Immediate(Value<'gc>), Immediate(Value<'gc>),
/// Indicates that the return value is the result of a given user-defined /// ReturnValue has not yet been computed.
/// function call. The activation record returned is the frame that needs ///
/// to return to get your value. /// This exists for functions that do need to reference the result of user
ResultOf(GcCell<'gc, Activation<'gc>>), /// code in order to produce their result.
} ResultOf {
executable: Executable<'gc>,
impl PartialEq for ReturnValue<'_> { unbound_reciever: Option<Object<'gc>>,
fn eq(&self, other: &Self) -> bool { arguments: Vec<Value<'gc>>,
use ReturnValue::*; base_proto: Option<Object<'gc>>,
},
match (self, other) {
(Immediate(val1), Immediate(val2)) => val1 == val2,
(ResultOf(frame1), ResultOf(frame2)) => GcCell::ptr_eq(*frame1, *frame2),
_ => false,
}
}
} }
impl fmt::Debug for ReturnValue<'_> { impl fmt::Debug for ReturnValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ReturnValue::*;
match self { match self {
Immediate(val) => write!(f, "Immediate({:?})", val), Self::Immediate(v) => f.debug_tuple("ReturnValue::Immediate").field(v).finish(),
ResultOf(_frame) => write!(f, "ResultOf(<activation frame>)"), Self::ResultOf {
executable: _executable,
unbound_reciever,
arguments,
base_proto,
} => f
.debug_struct("ReturnValue")
.field("executable", &"<Executable code>")
.field("unbound_reciever", unbound_reciever)
.field("arguments", arguments)
.field("base_proto", base_proto)
.finish(),
} }
} }
} }
impl<'gc> ReturnValue<'gc> { impl<'gc> ReturnValue<'gc> {
/// Mark a given return value as intended to be pushed onto the stack. /// Construct a new return value.
/// pub fn defer_execution(
/// The natural result of a stack frame retiring is to be pushed, so this executable: Executable<'gc>,
/// only ensures that Immediate values are pushed. unbound_reciever: Option<Object<'gc>>,
pub fn push(self, avm: &mut Avm2<'gc>) { arguments: Vec<Value<'gc>>,
use ReturnValue::*; base_proto: Option<Object<'gc>>,
) -> Self {
match self { Self::ResultOf {
Immediate(val) => avm.push(val), executable,
ResultOf(_frame) => {} unbound_reciever,
}; arguments,
base_proto,
}
} }
/// Force a return value to resolve on the Rust stack by recursing back /// Resolve the underlying deferred execution.
/// into the AVM. ///
/// All return values must eventually resolved - it is a compile error to
/// fail to do so.
pub fn resolve( pub fn resolve(
self, self,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
use ReturnValue::*;
match self { match self {
Immediate(val) => Ok(val), Self::Immediate(v) => Ok(v),
ResultOf(frame) => { Self::ResultOf {
avm.run_current_frame(context, frame)?; executable,
unbound_reciever,
Ok(avm.pop()) arguments,
} base_proto,
} } => executable.exec(
} unbound_reciever,
&arguments,
pub fn is_immediate(&self) -> bool { activation,
use ReturnValue::*; context,
base_proto,
if let Immediate(_v) = self { ),
true
} else {
false
}
}
/// Panic if a value is not immediate.
///
/// This should only be used in test assertions.
#[cfg(test)]
pub fn unwrap_immediate(self) -> Value<'gc> {
use ReturnValue::*;
match self {
Immediate(val) => val,
_ => panic!("Unwrapped a non-immediate return value"),
} }
} }
} }
impl<'gc> From<Value<'gc>> for ReturnValue<'gc> { impl<'gc> From<Value<'gc>> for ReturnValue<'gc> {
fn from(val: Value<'gc>) -> Self { fn from(v: Value<'gc>) -> Self {
ReturnValue::Immediate(val) Self::Immediate(v)
}
}
impl<'gc> From<String> for ReturnValue<'gc> {
fn from(string: String) -> Self {
ReturnValue::Immediate(Value::String(string))
}
}
impl<'gc> From<&str> for ReturnValue<'gc> {
fn from(string: &str) -> Self {
ReturnValue::Immediate(Value::String(string.to_owned()))
}
}
impl<'gc> From<bool> for ReturnValue<'gc> {
fn from(value: bool) -> Self {
ReturnValue::Immediate(Value::Bool(value))
}
}
impl<'gc, T> From<T> for ReturnValue<'gc>
where
Object<'gc>: From<T>,
{
fn from(value: T) -> Self {
ReturnValue::Immediate(Value::Object(Object::from(value)))
}
}
impl<'gc> From<f64> for ReturnValue<'gc> {
fn from(value: f64) -> Self {
ReturnValue::Immediate(Value::Number(value))
}
}
impl<'gc> From<f32> for ReturnValue<'gc> {
fn from(value: f32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<u8> for ReturnValue<'gc> {
fn from(value: u8) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<i32> for ReturnValue<'gc> {
fn from(value: i32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<u32> for ReturnValue<'gc> {
fn from(value: u32) -> Self {
ReturnValue::Immediate(Value::Number(f64::from(value)))
}
}
impl<'gc> From<Namespace> for ReturnValue<'gc> {
fn from(value: Namespace) -> Self {
ReturnValue::Immediate(Value::Namespace(value))
}
}
impl<'gc> From<GcCell<'gc, Activation<'gc>>> for ReturnValue<'gc> {
fn from(frame: GcCell<'gc, Activation<'gc>>) -> Self {
ReturnValue::ResultOf(frame)
} }
} }

View File

@ -1,9 +1,10 @@
//! Represents AVM2 scope chain resolution. //! Represents AVM2 scope chain resolution.
use crate::avm2::activation::Activation;
use crate::avm2::names::Multiname; use crate::avm2::names::Multiname;
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::return_value::ReturnValue; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::cell::Ref; use std::cell::Ref;
@ -99,7 +100,7 @@ impl<'gc> Scope<'gc> {
pub fn find( pub fn find(
&self, &self,
name: &Multiname, name: &Multiname,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<Object<'gc>>, Error> { ) -> Result<Option<Object<'gc>>, Error> {
if let Some(qname) = self.locals().resolve_multiname(name)? { if let Some(qname) = self.locals().resolve_multiname(name)? {
@ -109,7 +110,7 @@ impl<'gc> Scope<'gc> {
} }
if let Some(scope) = self.parent() { if let Some(scope) = self.parent() {
return scope.find(name, avm, context); return scope.find(name, activation, context);
} }
Ok(None) Ok(None)
@ -122,21 +123,24 @@ impl<'gc> Scope<'gc> {
pub fn resolve( pub fn resolve(
&mut self, &mut self,
name: &Multiname, name: &Multiname,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Option<ReturnValue<'gc>>, Error> { ) -> Result<Option<Value<'gc>>, Error> {
if let Some(qname) = self.locals().resolve_multiname(name)? { if let Some(qname) = self.locals().resolve_multiname(name)? {
if self.locals().has_property(&qname)? { if self.locals().has_property(&qname)? {
return Ok(Some( return Ok(Some(self.values.get_property(
self.values self.values,
.get_property(self.values, &qname, avm, context)? &qname,
.into(), activation,
)); context,
)?));
} }
} }
if let Some(parent) = self.parent { if let Some(parent) = self.parent {
return parent.write(context.gc_context).resolve(name, avm, context); return parent
.write(context.gc_context)
.resolve(name, activation, context);
} }
//TODO: Should undefined variables halt execution? //TODO: Should undefined variables halt execution?

View File

@ -1,5 +1,6 @@
//! Default AVM2 object impl //! Default AVM2 object impl
use crate::avm2::activation::Activation;
use crate::avm2::class::Class; use crate::avm2::class::Class;
use crate::avm2::function::Executable; use crate::avm2::function::Executable;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
@ -10,7 +11,7 @@ use crate::avm2::return_value::ReturnValue;
use crate::avm2::scope::Scope; use crate::avm2::scope::Scope;
use crate::avm2::slot::Slot; use crate::avm2::slot::Slot;
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error}; use crate::avm2::Error;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use std::collections::HashMap; use std::collections::HashMap;
@ -74,12 +75,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
self, self,
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
self.0 let rv = self
.0
.read() .read()
.get_property_local(reciever, name, avm, context) .get_property_local(reciever, name, activation)?;
rv.resolve(activation, context)
} }
fn set_property_local( fn set_property_local(
@ -87,15 +91,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let rv = self let rv = self
.0 .0
.write(context.gc_context) .write(context.gc_context)
.set_property_local(reciever, name, value, avm, context)?; .set_property_local(reciever, name, value, activation, context)?;
rv.resolve(avm, context)?; rv.resolve(activation, context)?;
Ok(()) Ok(())
} }
@ -105,15 +109,15 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let rv = self let rv = self
.0 .0
.write(context.gc_context) .write(context.gc_context)
.init_property_local(reciever, name, value, avm, context)?; .init_property_local(reciever, name, value, activation, context)?;
rv.resolve(avm, context)?; rv.resolve(activation, context)?;
Ok(()) Ok(())
} }
@ -229,7 +233,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn construct( fn construct(
&self, &self,
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
@ -239,7 +243,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
fn derive( fn derive(
&self, &self,
_avm: &mut Avm2<'gc>, _activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
class: GcCell<'gc, Class<'gc>>, class: GcCell<'gc, Class<'gc>>,
scope: Option<GcCell<'gc, Scope<'gc>>>, scope: Option<GcCell<'gc, Scope<'gc>>>,
@ -376,23 +380,14 @@ impl<'gc> ScriptObjectData<'gc> {
&self, &self,
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result<ReturnValue<'gc>, Error> {
) -> Result<Value<'gc>, Error> {
let prop = self.values.get(name); let prop = self.values.get(name);
if let Some(prop) = prop { if let Some(prop) = prop {
prop.get( prop.get(reciever, activation.base_proto().or(self.proto))
avm,
context,
reciever,
avm.current_stack_frame()
.and_then(|sf| sf.read().base_proto())
.or(self.proto),
)?
.resolve(avm, context)
} else { } else {
Ok(Value::Undefined) Ok(Value::Undefined.into())
} }
} }
@ -401,25 +396,26 @@ impl<'gc> ScriptObjectData<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(prop) = self.values.get_mut(name) { let slot_id = if let Some(prop) = self.values.get(name) {
if let Some(slot_id) = prop.slot_id() { if let Some(slot_id) = prop.slot_id() {
Some(slot_id)
} else {
None
}
} else {
None
};
if let Some(slot_id) = slot_id {
self.set_slot(slot_id, value, context.gc_context)?; self.set_slot(slot_id, value, context.gc_context)?;
Ok(Value::Undefined.into()) Ok(Value::Undefined.into())
} else { } else if self.values.contains_key(name) {
let prop = self.values.get_mut(name).unwrap();
let proto = self.proto; let proto = self.proto;
prop.set( prop.set(reciever, activation.base_proto().or(proto), value)
avm,
context,
reciever,
avm.current_stack_frame()
.and_then(|sf| sf.read().base_proto())
.or(proto),
value,
)
}
} else { } else {
//TODO: Not all classes are dynamic like this //TODO: Not all classes are dynamic like this
self.enumerants.push(name.clone()); self.enumerants.push(name.clone());
@ -435,7 +431,7 @@ impl<'gc> ScriptObjectData<'gc> {
reciever: Object<'gc>, reciever: Object<'gc>,
name: &QName, name: &QName,
value: Value<'gc>, value: Value<'gc>,
avm: &mut Avm2<'gc>, activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(prop) = self.values.get_mut(name) { if let Some(prop) = self.values.get_mut(name) {
@ -444,15 +440,7 @@ impl<'gc> ScriptObjectData<'gc> {
Ok(Value::Undefined.into()) Ok(Value::Undefined.into())
} else { } else {
let proto = self.proto; let proto = self.proto;
prop.init( prop.init(reciever, activation.base_proto().or(proto), value)
avm,
context,
reciever,
avm.current_stack_frame()
.and_then(|sf| sf.read().base_proto())
.or(proto),
value,
)
} }
} else { } else {
//TODO: Not all classes are dynamic like this //TODO: Not all classes are dynamic like this

View File

@ -878,8 +878,6 @@ impl Player {
} }
} }
} }
// Execute the stack frame (if any).
let _ = avm2.run_stack_till_empty(context);
} }
} }