diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index dc2ebefff..c19ec5571 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -63,9 +63,36 @@ pub fn boolean<'gc>( } } +pub fn number<'gc>( + _avm: &mut Avm1<'gc>, + _action_context: &mut ActionContext<'_, 'gc, '_>, + _this: GcCell<'gc, Object<'gc>>, + args: &[Value<'gc>], +) -> Value<'gc> { + if let Some(val) = args.get(0) { + Value::Number(val.as_number()) + } else { + Value::Number(0.0) + } +} + +pub fn is_nan<'gc>( + _avm: &mut Avm1<'gc>, + _action_context: &mut ActionContext<'_, 'gc, '_>, + _this: GcCell<'gc, Object<'gc>>, + args: &[Value<'gc>], +) -> Value<'gc> { + if let Some(val) = args.get(0) { + Value::Bool(val.as_number().is_nan()) + } else { + Value::Bool(true) + } +} + pub fn create_globals<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<'gc> { let mut globals = Object::object(gc_context); + globals.force_set_function("isNaN", is_nan, gc_context, EnumSet::empty()); globals.force_set_function("Boolean", boolean, gc_context, EnumSet::empty()); globals.force_set( "Math", @@ -73,7 +100,9 @@ pub fn create_globals<'gc>(gc_context: MutationContext<'gc, '_>) -> Object<'gc> EnumSet::empty(), ); globals.force_set_function("getURL", getURL, gc_context, EnumSet::empty()); + globals.force_set_function("Number", number, gc_context, EnumSet::empty()); globals.force_set_function("random", random, gc_context, EnumSet::empty()); + globals.force_set("NaN", Value::Number(std::f64::NAN), EnumSet::empty()); globals.force_set( "Infinity", @@ -178,4 +207,48 @@ mod tests { &[] => Value::Bool(false) ); + test_std!(is_nan_function, is_nan, 19, + &[Value::Bool(true)] => Value::Bool(false), + &[Value::Bool(false)] => Value::Bool(false), + &[Value::Number(10.0)] => Value::Bool(false), + &[Value::Number(-10.0)] => Value::Bool(false), + &[Value::Number(0.0)] => Value::Bool(false), + &[Value::Number(std::f64::INFINITY)] => Value::Bool(false), + &[Value::Number(std::f64::NAN)] => Value::Bool(true), + &[Value::String("".to_string())] => Value::Bool(false), + &[Value::String("Hello".to_string())] => Value::Bool(true), + &[Value::String(" ".to_string())] => Value::Bool(true), + &[Value::String(" 5 ".to_string())] => Value::Bool(true), + &[Value::String("0".to_string())] => Value::Bool(false), + &[Value::String("1".to_string())] => Value::Bool(false), + &[Value::String("Infinity".to_string())] => Value::Bool(true), + &[Value::String("100a".to_string())] => Value::Bool(true), + &[Value::String("0x10".to_string())] => Value::Bool(false), + &[Value::String("0xhello".to_string())] => Value::Bool(true), + &[Value::String("123e-1".to_string())] => Value::Bool(false), + &[] => Value::Bool(true) + ); + + test_std!(number_function, number, 19, + &[Value::Bool(true)] => Value::Number(1.0), + &[Value::Bool(false)] => Value::Number(0.0), + &[Value::Number(10.0)] => Value::Number(10.0), + &[Value::Number(-10.0)] => Value::Number(-10.0), + &[Value::Number(0.0)] => Value::Number(0.0), + &[Value::Number(std::f64::INFINITY)] => Value::Number(std::f64::INFINITY), + &[Value::Number(std::f64::NAN)] => Value::Number(std::f64::NAN), + &[Value::String("".to_string())] => Value::Number(0.0), + &[Value::String("Hello".to_string())] => Value::Number(std::f64::NAN), + &[Value::String(" ".to_string())] => Value::Number(std::f64::NAN), + &[Value::String(" 5 ".to_string())] => Value::Number(std::f64::NAN), + &[Value::String("0".to_string())] => Value::Number(0.0), + &[Value::String("1".to_string())] => Value::Number(1.0), + &[Value::String("Infinity".to_string())] => Value::Number(std::f64::NAN), + &[Value::String("100a".to_string())] => Value::Number(std::f64::NAN), + &[Value::String("0x10".to_string())] => Value::Number(16.0), + &[Value::String("0xhello".to_string())] => Value::Number(std::f64::NAN), + &[Value::String("123e-1".to_string())] => Value::Number(12.3), + &[] => Value::Number(0.0) + ); + } diff --git a/core/src/avm1/value.rs b/core/src/avm1/value.rs index 737887dbc..e25cad93f 100644 --- a/core/src/avm1/value.rs +++ b/core/src/avm1/value.rs @@ -73,7 +73,18 @@ impl<'gc> Value<'gc> { Value::Bool(false) => 0.0, Value::Bool(true) => 1.0, Value::Number(v) => *v, - Value::String(v) => v.parse().unwrap_or(NAN), // TODO(Herschel): Handle Infinity/etc.? + Value::String(v) => match v.as_str() { + v if v.starts_with("0x") => { + let parsed = i64::from_str_radix(&v[2..], 16); + if parsed.is_ok() { + parsed.unwrap_or_default() as f64 + } else { + std::f64::NAN + } + } + "" => 0.0, + _ => v.parse().unwrap_or(NAN), + }, Value::Object(_object) => { log::error!("Unimplemented: Object ToNumber"); 0.0