core: implement isNaN and Number functions. Involves updating to_number function in Value. Note: this varies a little from the ECMA spec such as not allowing spaces in numbers (i.e. ' 5' => NaN). No definitive reference for this but was found experimentally. Same with not supporting 'Infinity'

This commit is contained in:
Will Brindle 2019-10-11 15:11:02 +01:00
parent 38c66b5b8d
commit 463d0fc352
2 changed files with 85 additions and 1 deletions

View File

@ -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)
);
}

View File

@ -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