diff --git a/core/src/avm1/globals/math.rs b/core/src/avm1/globals/math.rs index a8b3940f7..11f1a5bd0 100644 --- a/core/src/avm1/globals/math.rs +++ b/core/src/avm1/globals/math.rs @@ -4,7 +4,7 @@ use crate::avm1::return_value::ReturnValue; use crate::avm1::{Avm1, Error, ScriptObject, TObject, UpdateContext, Value}; use gc_arena::MutationContext; use rand::Rng; -use std::f64::NAN; +use std::f64::{INFINITY, NAN, NEG_INFINITY}; macro_rules! wrap_std { ( $object: ident, $gc_context: ident, $proto: ident, $($name:expr => $std:path),* ) => {{ @@ -45,6 +45,74 @@ fn atan2<'gc>( Ok(NAN.into()) } +fn pow<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + _this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(y) = args.get(0) { + if let Some(x) = args.get(1) { + let x = x.as_number(avm, context)?; + if x.is_nan() { + return Ok(NAN.into()); + } + return Ok(y.as_number(avm, context)?.powf(x).into()); + } + } + Ok(NAN.into()) +} + +fn max<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + _this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(a) = args.get(0) { + return if let Some(b) = args.get(1) { + match a.abstract_lt(b.to_owned(), avm, context)? { + Value::Bool(value) => { + if value { + Ok(b.as_number(avm, context)?.into()) + } else { + Ok(a.as_number(avm, context)?.into()) + } + } + _ => Ok(NAN.into()), + } + } else { + Ok(NAN.into()) + }; + } + Ok(NEG_INFINITY.into()) +} + +fn min<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + _this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(a) = args.get(0) { + return if let Some(b) = args.get(1) { + match a.abstract_lt(b.to_owned(), avm, context)? { + Value::Bool(value) => { + if value { + Ok(a.as_number(avm, context)?.into()) + } else { + Ok(b.as_number(avm, context)?.into()) + } + } + _ => Ok(NAN.into()), + } + } else { + Ok(NAN.into()) + }; + } + Ok(INFINITY.into()) +} + pub fn random<'gc>( _avm: &mut Avm1<'gc>, action_context: &mut UpdateContext<'_, 'gc, '_>, @@ -122,7 +190,8 @@ pub fn create<'gc>( "round" => f64::round, "sin" => f64::sin, "sqrt" => f64::sqrt, - "tan" => f64::tan + "tan" => f64::tan, + "log" => f64::ln ); math.force_set_function( @@ -132,6 +201,27 @@ pub fn create<'gc>( DontDelete | ReadOnly | DontEnum, fn_proto, ); + math.force_set_function( + "pow", + pow, + gc_context, + DontDelete | ReadOnly | DontEnum, + fn_proto, + ); + math.force_set_function( + "max", + max, + gc_context, + DontDelete | ReadOnly | DontEnum, + fn_proto, + ); + math.force_set_function( + "min", + min, + gc_context, + DontDelete | ReadOnly | DontEnum, + fn_proto, + ); math.force_set_function( "random", random, @@ -276,6 +366,79 @@ mod tests { } ); + test_method!(test_pow, "pow", setup, + [5, 6, 7, 8] => { + [] => NAN, + [1.0] => NAN, + [NAN] => NAN, + [Value::Null] => NAN, + [Value::Undefined] => NAN, + ["5"] => NAN, + [1.0, 2.0] => 1.0, + [3.0, 2.0, 1.0] => 9.0 + }, + [5, 6] => { + [1.0, Value::Null] => 1.0, + [Value::Undefined, 3.0] => 0.0 + }, + [7, 8] => { + [1.0, Value::Null] => NAN, + [Value::Undefined, 3.0] => NAN + } + ); + + test_method!(test_log, "log", setup, + [19] => { + [] => NAN, + [Value::Null] => NAN, + [2.0] => f64::ln(2.0), + [0.0] => f64::ln(0.0), + [1.0] => f64::ln(1.0) + } + ); + + test_method!(test_max, "max", setup, + [5, 6, 7, 8] => { + [] => NEG_INFINITY, + [1.0] => NAN, + [NAN] => NAN, + [Value::Null] => NAN, + [Value::Undefined] => NAN, + ["5"] => NAN, + [1.0, 2.0] => 2.0, + [3.0, 2.0, 1.0] => 3.0 + }, + [5, 6] => { + [1.0, Value::Null] => 1.0, + [Value::Undefined, 3.0] => 3.0 + }, + [7, 8] => { + [1.0, Value::Null] => NAN, + [Value::Undefined, 3.0] => NAN + } + ); + + test_method!(test_min, "min", setup, + [5, 6, 7, 8] => { + [] => INFINITY, + [1.0] => NAN, + [NAN] => NAN, + [Value::Null] => NAN, + [Value::Undefined] => NAN, + ["5"] => NAN, + [1.0, 2.0] => 1.0, + [3.0, 2.0, 1.0] => 2.0 + }, + [5, 6] => { + [1.0, Value::Null] => 0.0, + [Value::Undefined, 3.0] => 0.0 + }, + [7, 8] => { + [1.0, Value::Null] => NAN, + [Value::Undefined, 3.0] => NAN + } + ); + #[test] fn test_atan2_nan() { with_avm(19, |avm, context, _root| {