Add implicit coercions to remove most instances of manually constructing a `ReturnValue`.

This commit is contained in:
David Wendt 2019-10-30 20:25:52 -04:00
parent 4b824370f4
commit 5bf90653c4
8 changed files with 79 additions and 72 deletions

View File

@ -1,7 +1,6 @@
use crate::avm1::function::Avm1Function; use crate::avm1::function::Avm1Function;
use crate::avm1::globals::create_globals; use crate::avm1::globals::create_globals;
use crate::avm1::object::Object; use crate::avm1::object::Object;
use crate::avm1::return_value::ReturnValue;
use crate::backend::navigator::NavigationMethod; use crate::backend::navigator::NavigationMethod;
use crate::context::UpdateContext; use crate::context::UpdateContext;
use crate::prelude::*; use crate::prelude::*;
@ -110,7 +109,7 @@ impl<'gc> Avm1<'gc> {
form_values.insert( form_values.insert(
k, k,
v.ok() v.ok()
.unwrap_or(ReturnValue::Immediate(Value::Undefined)) .unwrap_or_else(|| Value::Undefined.into())
.resolve(self, context) .resolve(self, context)
.ok() .ok()
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)

View File

@ -268,13 +268,11 @@ impl<'gc> Activation<'gc> {
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if name == "this" { if name == "this" {
return Ok(ReturnValue::Immediate(Value::Object(self.this))); return Ok(Value::Object(self.this).into());
} }
if name == "arguments" && self.arguments.is_some() { if name == "arguments" && self.arguments.is_some() {
return Ok(ReturnValue::Immediate(Value::Object( return Ok(Value::Object(self.arguments.unwrap()).into());
self.arguments.unwrap(),
)));
} }
self.scope().resolve(name, avm, context, self.this) self.scope().resolve(name, avm, context, self.this)

View File

@ -296,7 +296,7 @@ impl<'gc> Executable<'gc> {
} }
avm.insert_stack_frame(frame_cell); avm.insert_stack_frame(frame_cell);
Ok(ReturnValue::ResultOf(frame_cell)) Ok(frame_cell.into())
} }
} }
} }

View File

@ -20,7 +20,7 @@ pub fn getURL<'a, 'gc>(
let url = url_val.clone().into_string(); let url = url_val.clone().into_string();
if let Some(fscommand) = fscommand::parse(&url) { if let Some(fscommand) = fscommand::parse(&url) {
fscommand::handle(fscommand, avm, context); fscommand::handle(fscommand, avm, context);
return Ok(ReturnValue::Immediate(Value::Undefined)); return Ok(Value::Undefined.into());
} }
let window = args.get(1).map(|v| v.clone().into_string()); let window = args.get(1).map(|v| v.clone().into_string());
@ -34,7 +34,7 @@ pub fn getURL<'a, 'gc>(
context.navigator.navigate_to_url(url, window, vars_method); context.navigator.navigate_to_url(url, window, vars_method);
} }
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} }
pub fn random<'gc>( pub fn random<'gc>(
@ -44,10 +44,10 @@ pub fn random<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
match args.get(0) { match args.get(0) {
Some(Value::Number(max)) => Ok(ReturnValue::Immediate(Value::Number( Some(Value::Number(max)) => {
action_context.rng.gen_range(0.0f64, max).floor(), Ok(Value::Number(action_context.rng.gen_range(0.0f64, max).floor()).into())
))), }
_ => Ok(ReturnValue::Immediate(Value::Undefined)), //TODO: Shouldn't this be an error condition? _ => Ok(Value::Undefined.into()), //TODO: Shouldn't this be an error condition?
} }
} }
@ -58,11 +58,9 @@ pub fn boolean<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
Ok(ReturnValue::Immediate(Value::Bool( Ok(Value::Bool(val.as_bool(avm.current_swf_version())).into())
val.as_bool(avm.current_swf_version()),
)))
} else { } else {
Ok(ReturnValue::Immediate(Value::Bool(false))) Ok(Value::Bool(false).into())
} }
} }
@ -73,9 +71,9 @@ pub fn number<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
Ok(ReturnValue::Immediate(Value::Number(val.as_number()))) Ok(Value::Number(val.as_number()).into())
} else { } else {
Ok(ReturnValue::Immediate(Value::Number(0.0))) Ok(Value::Number(0.0).into())
} }
} }
@ -86,11 +84,9 @@ pub fn is_nan<'gc>(
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
Ok(ReturnValue::Immediate(Value::Bool( Ok(Value::Bool(val.as_number().is_nan()).into())
val.as_number().is_nan(),
)))
} else { } else {
Ok(ReturnValue::Immediate(Value::Bool(true))) Ok(Value::Bool(true).into())
} }
} }

View File

@ -12,9 +12,9 @@ macro_rules! wrap_std {
$name, $name,
|_avm, _context, _this, args| -> Result<ReturnValue<'gc>, Error> { |_avm, _context, _this, args| -> Result<ReturnValue<'gc>, Error> {
if let Some(input) = args.get(0) { if let Some(input) = args.get(0) {
Ok(ReturnValue::Immediate(Value::Number($std(input.as_number())))) Ok(Value::Number($std(input.as_number())).into())
} else { } else {
Ok(ReturnValue::Immediate(Value::Number(NAN))) Ok(Value::Number(NAN).into())
} }
}, },
$gc_context, $gc_context,
@ -32,16 +32,12 @@ fn atan2<'gc>(
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
if let Some(y) = args.get(0) { if let Some(y) = args.get(0) {
if let Some(x) = args.get(1) { if let Some(x) = args.get(1) {
return Ok(ReturnValue::Immediate(Value::Number( return Ok(Value::Number(y.as_number().atan2(x.as_number())).into());
y.as_number().atan2(x.as_number()),
)));
} else { } else {
return Ok(ReturnValue::Immediate(Value::Number( return Ok(Value::Number(y.as_number().atan2(0.0)).into());
y.as_number().atan2(0.0),
)));
} }
} }
Ok(ReturnValue::Immediate(Value::Number(NAN))) Ok(Value::Number(NAN).into())
} }
pub fn random<'gc>( pub fn random<'gc>(
@ -50,9 +46,7 @@ pub fn random<'gc>(
_this: GcCell<'gc, Object<'gc>>, _this: GcCell<'gc, Object<'gc>>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
Ok(ReturnValue::Immediate(Value::Number( Ok(Value::Number(action_context.rng.gen_range(0.0f64, 1.0f64)).into())
action_context.rng.gen_range(0.0f64, 1.0f64),
)))
} }
pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'gc>> { pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'gc>> {
@ -245,7 +239,7 @@ mod tests {
let math = GcCell::allocate(context.gc_context, create(context.gc_context)); let math = GcCell::allocate(context.gc_context, create(context.gc_context));
assert_eq!( assert_eq!(
atan2(avm, context, *math.read(), &[]).unwrap(), atan2(avm, context, *math.read(), &[]).unwrap(),
ReturnValue::Immediate(Value::Number(NAN)) Value::Number(NAN).into()
); );
assert_eq!( assert_eq!(
atan2( atan2(
@ -255,7 +249,7 @@ mod tests {
&[Value::Number(1.0), Value::Null] &[Value::Number(1.0), Value::Null]
) )
.unwrap(), .unwrap(),
ReturnValue::Immediate(Value::Number(NAN)) Value::Number(NAN).into()
); );
assert_eq!( assert_eq!(
atan2( atan2(
@ -265,7 +259,7 @@ mod tests {
&[Value::Number(1.0), Value::Undefined] &[Value::Number(1.0), Value::Undefined]
) )
.unwrap(), .unwrap(),
ReturnValue::Immediate(Value::Number(NAN)) Value::Number(NAN).into()
); );
assert_eq!( assert_eq!(
atan2( atan2(
@ -275,7 +269,7 @@ mod tests {
&[Value::Undefined, Value::Number(1.0)] &[Value::Undefined, Value::Number(1.0)]
) )
.unwrap(), .unwrap(),
ReturnValue::Immediate(Value::Number(NAN)) Value::Number(NAN).into()
); );
}); });
} }
@ -286,7 +280,7 @@ mod tests {
let math = GcCell::allocate(context.gc_context, create(context.gc_context)); let math = GcCell::allocate(context.gc_context, create(context.gc_context));
assert_eq!( assert_eq!(
atan2(avm, context, *math.read(), &[Value::Number(10.0)]).unwrap(), atan2(avm, context, *math.read(), &[Value::Number(10.0)]).unwrap(),
ReturnValue::Immediate(Value::Number(std::f64::consts::FRAC_PI_2)) Value::Number(std::f64::consts::FRAC_PI_2).into()
); );
assert_eq!( assert_eq!(
atan2( atan2(
@ -296,7 +290,7 @@ mod tests {
&[Value::Number(1.0), Value::Number(2.0)] &[Value::Number(1.0), Value::Number(2.0)]
) )
.unwrap(), .unwrap(),
ReturnValue::Immediate(Value::Number(f64::atan2(1.0, 2.0))) Value::Number(f64::atan2(1.0, 2.0)).into()
); );
}); });
} }

View File

@ -14,10 +14,10 @@ macro_rules! with_movie_clip {
|_avm, _context, this, args| -> Result<ReturnValue<'gc>, Error> { |_avm, _context, this, args| -> Result<ReturnValue<'gc>, Error> {
if let Some(display_object) = this.read().display_node() { if let Some(display_object) = this.read().display_node() {
if let Some(movie_clip) = display_object.read().as_movie_clip() { if let Some(movie_clip) = display_object.read().as_movie_clip() {
return Ok(ReturnValue::Immediate($fn(movie_clip, args))); return Ok($fn(movie_clip, args).into());
} }
} }
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
}, },
$gc_context, $gc_context,
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
@ -34,10 +34,10 @@ macro_rules! with_movie_clip_mut {
|_avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error> { |_avm, context: &mut UpdateContext<'_, 'gc, '_>, this, args| -> Result<ReturnValue<'gc>, Error> {
if let Some(display_object) = this.read().display_node() { if let Some(display_object) = this.read().display_node() {
if let Some(movie_clip) = display_object.write(context.gc_context).as_movie_clip_mut() { if let Some(movie_clip) = display_object.write(context.gc_context).as_movie_clip_mut() {
return Ok(ReturnValue::Immediate($fn(movie_clip, context, display_object, args))); return Ok($fn(movie_clip, context, display_object, args).into());
} }
} }
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} as crate::avm1::function::NativeFunction<'gc>, } as crate::avm1::function::NativeFunction<'gc>,
$gc_context, $gc_context,
DontDelete | ReadOnly | DontEnum, DontDelete | ReadOnly | DontEnum,
@ -59,7 +59,7 @@ pub fn overwrite_root<'gc>(
this.write(ac.gc_context) this.write(ac.gc_context)
.force_set("_root", new_val, EnumSet::new()); .force_set("_root", new_val, EnumSet::new());
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} }
pub fn overwrite_global<'gc>( pub fn overwrite_global<'gc>(
@ -75,7 +75,7 @@ pub fn overwrite_global<'gc>(
this.write(ac.gc_context) this.write(ac.gc_context)
.force_set("_global", new_val, EnumSet::new()); .force_set("_global", new_val, EnumSet::new());
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} }
pub fn create_movie_object<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<'gc> { pub fn create_movie_object<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<'gc> {
@ -120,18 +120,14 @@ pub fn create_movie_object<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<
object.force_set_virtual( object.force_set_virtual(
"_global", "_global",
Executable::Native(|avm, context, _this, _args| { Executable::Native(|avm, context, _this, _args| Ok(avm.global_object(context).into())),
Ok(ReturnValue::Immediate(avm.global_object(context)))
}),
Some(Executable::Native(overwrite_global)), Some(Executable::Native(overwrite_global)),
EnumSet::new(), EnumSet::new(),
); );
object.force_set_virtual( object.force_set_virtual(
"_root", "_root",
Executable::Native(|avm, context, _this, _args| { Executable::Native(|avm, context, _this, _args| Ok(avm.root_object(context).into())),
Ok(ReturnValue::Immediate(avm.root_object(context)))
}),
Some(Executable::Native(overwrite_root)), Some(Executable::Native(overwrite_root)),
EnumSet::new(), EnumSet::new(),
); );
@ -139,14 +135,14 @@ pub fn create_movie_object<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<
object.force_set_virtual( object.force_set_virtual(
"_parent", "_parent",
Executable::Native(|_avm, _context, this, _args| { Executable::Native(|_avm, _context, this, _args| {
Ok(ReturnValue::Immediate( Ok(this
this.read() .read()
.display_node() .display_node()
.and_then(|mc| mc.read().parent()) .and_then(|mc| mc.read().parent())
.and_then(|dn| dn.read().object().as_object().ok()) .and_then(|dn| dn.read().object().as_object().ok())
.map(|o| Value::Object(o.to_owned())) .map(|o| Value::Object(o.to_owned()))
.unwrap_or(Value::Undefined), .unwrap_or(Value::Undefined)
)) .into())
}), }),
None, None,
EnumSet::new(), EnumSet::new(),

View File

@ -57,7 +57,7 @@ impl<'gc> Property<'gc> {
) -> Result<ReturnValue<'gc>, Error> { ) -> Result<ReturnValue<'gc>, Error> {
match self { match self {
Property::Virtual { get, .. } => get.exec(avm, context, this, &[]), Property::Virtual { get, .. } => get.exec(avm, context, this, &[]),
Property::Stored { value, .. } => Ok(ReturnValue::Immediate(value.to_owned())), Property::Stored { value, .. } => Ok(value.to_owned().into()),
} }
} }
@ -318,7 +318,7 @@ impl<'gc> Object<'gc> {
return value.get(avm, context, this); return value.get(avm, context, this);
} }
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} }
/// Delete a given value off the object. /// Delete a given value off the object.
@ -364,7 +364,7 @@ impl<'gc> Object<'gc> {
if let Some(function) = &self.function { if let Some(function) = &self.function {
function.exec(avm, context, this, args) function.exec(avm, context, this, args)
} else { } else {
Ok(ReturnValue::Immediate(Value::Undefined)) Ok(Value::Undefined.into())
} }
} }
@ -458,7 +458,7 @@ mod tests {
.read() .read()
.get("not_defined", avm, context, object) .get("not_defined", avm, context, object)
.unwrap(), .unwrap(),
ReturnValue::Immediate(Value::Undefined) Value::Undefined.into()
); );
}) })
} }
@ -609,7 +609,7 @@ mod tests {
assert_eq!( assert_eq!(
object.read().get("virtual", avm, context, object).unwrap(), object.read().get("virtual", avm, context, object).unwrap(),
ReturnValue::Immediate(Value::Undefined) Value::Undefined.into()
); );
assert_eq!( assert_eq!(
object object
@ -620,7 +620,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
object.read().get("stored", avm, context, object).unwrap(), object.read().get("stored", avm, context, object).unwrap(),
ReturnValue::Immediate(Value::Undefined) Value::Undefined.into()
); );
assert_eq!( assert_eq!(
object object
@ -635,9 +635,7 @@ mod tests {
#[test] #[test]
fn test_iter_values() { fn test_iter_values() {
with_object(0, |_avm, context, object| { with_object(0, |_avm, context, object| {
let getter = Executable::Native(|_avm, _context, _this, _args| { let getter = Executable::Native(|_avm, _context, _this, _args| Ok(Value::Null.into()));
Ok(ReturnValue::Immediate(Value::Null))
});
object object
.write(context.gc_context) .write(context.gc_context)

View File

@ -6,14 +6,28 @@ use crate::context::UpdateContext;
use gc_arena::{Collect, GcCell}; use gc_arena::{Collect, GcCell};
use std::fmt; use std::fmt;
/// Represents a value which can be returned immediately or at a later time. /// Represents the return value of a function call.
///
/// 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.
///
/// 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 `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.
#[must_use = "Return values must be used"] #[must_use = "Return values must be used"]
#[derive(Clone)] #[derive(Clone)]
pub enum ReturnValue<'gc> { pub enum ReturnValue<'gc> {
/// Indicates that the return value is available immediately. /// Indicates that the return value is available immediately.
Immediate(Value<'gc>), Immediate(Value<'gc>),
/// Indicates that the return value will be calculated on the stack. /// 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>>), ResultOf(GcCell<'gc, Activation<'gc>>),
/// Indicates that there is no value to return. /// Indicates that there is no value to return.
@ -125,3 +139,15 @@ impl<'gc> ReturnValue<'gc> {
} }
} }
} }
impl<'gc> From<Value<'gc>> for ReturnValue<'gc> {
fn from(val: Value<'gc>) -> Self {
ReturnValue::Immediate(val)
}
}
impl<'gc> From<GcCell<'gc, Activation<'gc>>> for ReturnValue<'gc> {
fn from(frame: GcCell<'gc, Activation<'gc>>) -> Self {
ReturnValue::ResultOf(frame)
}
}