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:
parent
5b5bf0719e
commit
97e005622b
1416
core/src/avm2.rs
1416
core/src/avm2.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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>>>,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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>>>,
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -878,8 +878,6 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute the stack frame (if any).
|
|
||||||
let _ = avm2.run_stack_till_empty(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue