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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,70 +1,65 @@
//! Object builtin and prototype
use crate::avm2::activation::Activation;
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::value::Value;
use crate::avm2::{Avm2, Error};
use crate::avm2::Error;
use crate::context::UpdateContext;
use gc_arena::MutationContext;
/// Implements `Object`
pub fn constructor<'gc>(
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
Ok(Value::Undefined.into())
) -> Result<Value<'gc>, Error> {
Ok(Value::Undefined)
}
/// Implements `Object.prototype.toString`
fn to_string<'gc>(
_: &mut Avm2<'gc>,
_: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
) -> Result<Value<'gc>, Error> {
Ok(this
.map(|t| t.to_string())
.unwrap_or(Ok(Value::Undefined))?
.into())
.unwrap_or(Ok(Value::Undefined))?)
}
/// Implements `Object.prototype.toLocaleString`
fn to_locale_string<'gc>(
_: &mut Avm2<'gc>,
_: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
) -> Result<Value<'gc>, Error> {
Ok(this
.map(|t| t.to_string())
.unwrap_or(Ok(Value::Undefined))?
.into())
.unwrap_or(Ok(Value::Undefined))?)
}
/// Implements `Object.prototype.valueOf`
fn value_of<'gc>(
_: &mut Avm2<'gc>,
_: &mut Activation<'_, 'gc>,
_: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
Ok(this
.map(|t| t.value_of())
.unwrap_or(Ok(Value::Undefined))?
.into())
) -> Result<Value<'gc>, Error> {
Ok(this.map(|t| t.value_of()).unwrap_or(Ok(Value::Undefined))?)
}
/// `Object.prototype.hasOwnProperty`
pub fn has_own_property<'gc>(
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'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 = this?;
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`
pub fn is_prototype_of<'gc>(
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> {
) -> Result<Value<'gc>, Error> {
let search_proto: Result<Object<'gc>, Error> =
this.ok_or_else(|| "No valid this parameter".into());
let search_proto = search_proto?;
@ -105,11 +100,11 @@ pub fn is_prototype_of<'gc>(
/// `Object.prototype.propertyIsEnumerable`
pub fn property_is_enumerable<'gc>(
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'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 = this?;
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`
pub fn set_property_is_enumerable<'gc>(
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Option<Object<'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 = this?;
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`.

View File

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

View File

@ -1,5 +1,6 @@
//! AVM2 objects.
use crate::avm2::activation::Activation;
use crate::avm2::class::Class;
use crate::avm2::function::{Executable, FunctionObject};
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::script_object::ScriptObject;
use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error};
use crate::avm2::Error;
use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell, MutationContext};
use ruffle_macros::enum_trait_object;
@ -30,7 +31,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
self,
reciever: Object<'gc>,
name: &QName,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error>;
@ -39,23 +40,23 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
&mut self,
reciever: Object<'gc>,
name: &QName,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
if !self.has_instantiated_property(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);
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() {
return proto.get_property(reciever, name, avm, context);
return proto.get_property(reciever, name, activation, context);
}
Ok(Value::Undefined)
@ -83,7 +84,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>,
name: &QName,
value: Value<'gc>,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error>;
@ -93,17 +94,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>,
name: &QName,
value: Value<'gc>,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
if !self.has_instantiated_property(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) {
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();
@ -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
//a non-virtual you will actually alter the prototype.
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();
}
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.
@ -127,7 +128,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>,
name: &QName,
value: Value<'gc>,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error>;
@ -137,17 +138,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
reciever: Object<'gc>,
name: &QName,
value: Value<'gc>,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<(), Error> {
if !self.has_instantiated_property(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) {
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();
@ -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
//a non-virtual you will actually alter the prototype.
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();
}
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.
@ -404,24 +405,24 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// object.
fn install_trait(
&mut self,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
trait_entry: Trait<'gc>,
reciever: Object<'gc>,
) -> 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.
fn install_foreign_trait(
&mut self,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
trait_entry: Trait<'gc>,
scope: Option<GcCell<'gc, Scope<'gc>>>,
reciever: Object<'gc>,
) -> Result<(), Error> {
let fn_proto = avm.prototypes().function;
let fn_proto = activation.avm2().prototypes().function;
let trait_name = trait_entry.name().clone();
avm_debug!(
"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
.get_property(reciever, &super_name, avm, context)?
.get_property(reciever, &super_name, activation, context)?
.as_object()
.map_err(|_e| {
format!("Could not resolve superclass {:?}", super_name.local_name()).into()
});
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(
context.gc_context,
class_read.name().clone(),
@ -516,7 +517,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
fn_proto,
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(
context.gc_context,
@ -548,7 +550,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
self,
_reciever: Option<Object<'gc>>,
_arguments: &[Value<'gc>],
_avm: &mut Avm2<'gc>,
_activation: &mut Activation<'_, 'gc>,
_context: &mut UpdateContext<'_, 'gc, '_>,
_base_proto: Option<Object<'gc>>,
) -> 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.
fn construct(
&self,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<Object<'gc>, Error>;
@ -590,7 +592,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// traits.
fn derive(
&self,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
class: GcCell<'gc, Class<'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::return_value::ReturnValue;
use crate::avm2::value::Value;
use crate::avm2::{Avm2, Error};
use crate::context::UpdateContext;
use crate::avm2::Error;
use enumset::{EnumSet, EnumSetType};
use gc_arena::{Collect, CollectionContext};
@ -136,15 +135,16 @@ impl<'gc> Property<'gc> {
/// user-defined.
pub fn get(
&self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
) -> Result<ReturnValue<'gc>, Error> {
match self {
Property::Virtual { get: Some(get), .. } => {
get.exec(Some(this), &[], avm, context, base_proto)
}
Property::Virtual { get: Some(get), .. } => Ok(ReturnValue::defer_execution(
get.clone(),
Some(this),
vec![],
base_proto,
)),
Property::Virtual { get: None, .. } => Ok(Value::Undefined.into()),
Property::Stored { value, .. } => Ok(value.to_owned().into()),
Property::Slot { slot_id, .. } => this.get_slot(*slot_id).map(|v| v.into()),
@ -160,8 +160,6 @@ impl<'gc> Property<'gc> {
/// is encountered.
pub fn set(
&mut self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
new_value: impl Into<Value<'gc>>,
@ -169,13 +167,12 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
return function.exec(
return Ok(ReturnValue::defer_execution(
function.clone(),
Some(this),
&[new_value.into()],
avm,
context,
vec![new_value.into()],
base_proto,
);
));
}
Ok(Value::Undefined.into())
@ -207,8 +204,6 @@ impl<'gc> Property<'gc> {
/// is encountered.
pub fn init(
&mut self,
avm: &mut Avm2<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
base_proto: Option<Object<'gc>>,
new_value: impl Into<Value<'gc>>,
@ -216,13 +211,12 @@ impl<'gc> Property<'gc> {
match self {
Property::Virtual { set, .. } => {
if let Some(function) = set {
return function.exec(
return Ok(ReturnValue::defer_execution(
function.clone(),
Some(this),
&[new_value.into()],
avm,
context,
vec![new_value.into()],
base_proto,
);
));
}
Ok(Value::Undefined.into())

View File

@ -1,190 +1,113 @@
//! Return value enum
use crate::avm2::activation::Activation;
use crate::avm2::names::Namespace;
use crate::avm2::function::Executable;
use crate::avm2::object::Object;
use crate::avm2::{Avm2, Error, Value};
use crate::avm2::{Error, Value};
use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell};
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
/// activation onto the AVM stack, you need another type to represent how the
/// return value will be delivered to you.
/// It is a panicking logic error to attempt to run AVM2 code while any
/// reachable object is in a locked state. Ergo, it is sometimes necessary to
/// 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
/// with a given value regardless of how it is delivered to the calling
/// function.
/// It is also possible to stuff a regular `Value` in here - this is provided
/// for the convenience of functions that may be able to resolve a value
/// 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
/// provide a helper function specifically to indicate that you aren't
/// interested in the result of a call.
/// It is `must_use` - failing to resolve a return value is a compiler warning.
#[must_use = "Return values must be used"]
#[derive(Clone, Collect)]
#[collect(no_drop)]
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>),
/// Indicates that the return value is the result of a given user-defined
/// function call. The activation record returned is the frame that needs
/// to return to get your value.
ResultOf(GcCell<'gc, Activation<'gc>>),
}
impl PartialEq for ReturnValue<'_> {
fn eq(&self, other: &Self) -> bool {
use ReturnValue::*;
match (self, other) {
(Immediate(val1), Immediate(val2)) => val1 == val2,
(ResultOf(frame1), ResultOf(frame2)) => GcCell::ptr_eq(*frame1, *frame2),
_ => false,
}
}
/// ReturnValue has not yet been computed.
///
/// This exists for functions that do need to reference the result of user
/// code in order to produce their result.
ResultOf {
executable: Executable<'gc>,
unbound_reciever: Option<Object<'gc>>,
arguments: Vec<Value<'gc>>,
base_proto: Option<Object<'gc>>,
},
}
impl fmt::Debug for ReturnValue<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ReturnValue::*;
match self {
Immediate(val) => write!(f, "Immediate({:?})", val),
ResultOf(_frame) => write!(f, "ResultOf(<activation frame>)"),
Self::Immediate(v) => f.debug_tuple("ReturnValue::Immediate").field(v).finish(),
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> {
/// Mark a given return value as intended to be pushed onto the stack.
///
/// The natural result of a stack frame retiring is to be pushed, so this
/// only ensures that Immediate values are pushed.
pub fn push(self, avm: &mut Avm2<'gc>) {
use ReturnValue::*;
match self {
Immediate(val) => avm.push(val),
ResultOf(_frame) => {}
};
/// Construct a new return value.
pub fn defer_execution(
executable: Executable<'gc>,
unbound_reciever: Option<Object<'gc>>,
arguments: Vec<Value<'gc>>,
base_proto: Option<Object<'gc>>,
) -> Self {
Self::ResultOf {
executable,
unbound_reciever,
arguments,
base_proto,
}
}
/// Force a return value to resolve on the Rust stack by recursing back
/// into the AVM.
/// Resolve the underlying deferred execution.
///
/// All return values must eventually resolved - it is a compile error to
/// fail to do so.
pub fn resolve(
self,
avm: &mut Avm2<'gc>,
activation: &mut Activation<'_, 'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> {
use ReturnValue::*;
match self {
Immediate(val) => Ok(val),
ResultOf(frame) => {
avm.run_current_frame(context, frame)?;
Ok(avm.pop())
}
}
}
pub fn is_immediate(&self) -> bool {
use ReturnValue::*;
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"),
Self::Immediate(v) => Ok(v),
Self::ResultOf {
executable,
unbound_reciever,
arguments,
base_proto,
} => executable.exec(
unbound_reciever,
&arguments,
activation,
context,
base_proto,
),
}
}
}
impl<'gc> From<Value<'gc>> for ReturnValue<'gc> {
fn from(val: Value<'gc>) -> Self {
ReturnValue::Immediate(val)
}
}
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)
fn from(v: Value<'gc>) -> Self {
Self::Immediate(v)
}
}

View File

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

View File

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