Add & test ECMA-262 ToInt32 and `convert_i` opcode.

The ECMA-262 documentation is awfully overwrought for something that boils down to "chop off the non-whole part, wrap to 32 bits, then reinterpret as signed". Bitwise operations are *hell* to describe mathematically, and such descriptions are even harder to understand.
This commit is contained in:
David Wendt 2020-06-26 00:09:42 -04:00 committed by Mike Welsh
parent 345a244ed4
commit 6eb41035cf
7 changed files with 268 additions and 0 deletions

View File

@ -475,6 +475,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Op::NewClass { index } => self.op_new_class(method, index),
Op::CoerceA => self.op_coerce_a(),
Op::ConvertB => self.op_convert_b(),
Op::ConvertI => self.op_convert_i(),
Op::ConvertD => self.op_convert_d(),
Op::Jump { offset } => self.op_jump(offset, reader),
Op::IfTrue { offset } => self.op_if_true(offset, reader),
@ -1327,6 +1328,14 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
Ok(FrameControl::Continue)
}
fn op_convert_i(&mut self) -> Result<FrameControl<'gc>, Error> {
let value = self.context.avm2.pop().coerce_to_i32(self)?;
self.context.avm2.push(Value::Number(value.into()));
Ok(FrameControl::Continue)
}
fn op_jump(
&mut self,
offset: i32,

View File

@ -425,4 +425,27 @@ impl<'gc> Value<'gc> {
.coerce_to_number(activation)?,
})
}
/// Coerce the value to a 32-bit signed integer.
///
/// This function returns the resulting i32 directly; or a TypeError if the
/// value is an `Object` that cannot be converted to a primitive value.
///
/// Numerical conversions occur according to ECMA-262 3rd Edition's
/// ToInt32 algorithm which appears to match AVM2.
pub fn coerce_to_i32(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<i32, Error> {
let number = self.coerce_to_number(activation)?;
Ok(
if number == f64::INFINITY
|| number == f64::NEG_INFINITY
|| number.is_nan()
|| number.abs() == 0.0
{
0
} else {
(number.abs().floor() * number.signum()) as u32 as i32
},
)
}
}

View File

@ -278,6 +278,7 @@ swf_tests! {
(as3_boolean_negation, "avm2/boolean_negation", 1),
(as3_convert_boolean, "avm2/convert_boolean", 1),
(as3_convert_number, "avm2/convert_number", 1),
(as3_convert_integer, "avm2/convert_integer", 1),
}
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -0,0 +1,145 @@
package {
public class Test {
}
}
function assert_integer(val) {
var num_val : int = val;
trace(num_val);
}
trace("//true");
assert_integer(true);
trace("//false");
assert_integer(false);
trace("//null");
assert_integer(null);
trace("//undefined");
assert_integer(undefined);
trace("//\"\"");
assert_integer("");
trace("//\"str\"");
assert_integer("str");
trace("//\"true\"");
assert_integer("true");
trace("//\"false\"");
assert_integer("false");
trace("//0.0");
assert_integer(0.0);
trace("//NaN");
assert_integer(NaN);
trace("//-0.0");
assert_integer(-0.0);
trace("//Infinity");
assert_integer(Infinity);
trace("//1.0");
assert_integer(1.0);
trace("//-1.0");
assert_integer(-1.0);
trace("//0xFF1306");
assert_integer(0xFF1306);
trace("//1.2315e2");
assert_integer(1.2315e2);
trace("//0x7FFFFFFF");
assert_integer(0x7FFFFFFF);
trace("//0x80000000");
assert_integer(0x80000000);
trace("//0x80000001");
assert_integer(0x80000001);
trace("//0x180000001");
assert_integer(0x180000001);
trace("//0x100000001");
assert_integer(0x100000001);
trace("//-0x7FFFFFFF");
assert_integer(-0x7FFFFFFF);
trace("//-0x80000000");
assert_integer(-0x80000000);
trace("//-0x80000001");
assert_integer(-0x80000001);
trace("//-0x180000001");
assert_integer(-0x180000001);
trace("//-0x100000001");
assert_integer(-0x100000001);
trace("//new Object()");
assert_integer({});
trace("//\"0.0\"");
assert_integer("0.0");
trace("//\"NaN\"");
assert_integer("NaN");
trace("//\"-0.0\"");
assert_integer("-0.0");
trace("//\"Infinity\"");
assert_integer("Infinity");
trace("//\"1.0\"");
assert_integer("1.0");
trace("//\"-1.0\"");
assert_integer("-1.0");
trace("//\"0xFF1306\"");
assert_integer("0xFF1306");
trace("//\"1.2315e2\"");
assert_integer("1.2315e2");
trace("//\"0x7FFFFFFF\"");
assert_integer(0x7FFFFFFF);
trace("//\"0x80000000\"");
assert_integer(0x80000000);
trace("//\"0x80000001\"");
assert_integer(0x80000001);
trace("//\"0x180000001\"");
assert_integer(0x180000001);
trace("//\"0x100000001\"");
assert_integer(0x100000001);
trace("//\"-0x7FFFFFFF\"");
assert_integer(-0x7FFFFFFF);
trace("//\"-0x80000000\"");
assert_integer(-0x80000000);
trace("//\"-0x80000001\"");
assert_integer(-0x80000001);
trace("//\"-0x180000001\"");
assert_integer(-0x180000001);
trace("//\"-0x100000001\"");
assert_integer(-0x100000001);

View File

@ -0,0 +1,90 @@
//true
1
//false
0
//null
0
//undefined
0
//""
0
//"str"
0
//"true"
0
//"false"
0
//0.0
0
//NaN
0
//-0.0
0
//Infinity
0
//1.0
1
//-1.0
-1
//0xFF1306
16716550
//1.2315e2
123
//0x7FFFFFFF
2147483647
//0x80000000
-2147483648
//0x80000001
-2147483647
//0x180000001
-2147483647
//0x100000001
1
//-0x7FFFFFFF
-2147483647
//-0x80000000
-2147483648
//-0x80000001
2147483647
//-0x180000001
2147483647
//-0x100000001
-1
//new Object()
0
//"0.0"
0
//"NaN"
0
//"-0.0"
0
//"Infinity"
0
//"1.0"
1
//"-1.0"
-1
//"0xFF1306"
16716550
//"1.2315e2"
123
//"0x7FFFFFFF"
2147483647
//"0x80000000"
-2147483648
//"0x80000001"
-2147483647
//"0x180000001"
-2147483647
//"0x100000001"
1
//"-0x7FFFFFFF"
-2147483647
//"-0x80000000"
-2147483648
//"-0x80000001"
2147483647
//"-0x180000001"
2147483647
//"-0x100000001"
-1

Binary file not shown.

Binary file not shown.