diff --git a/core/src/avm2/activation.rs b/core/src/avm2/activation.rs index c91fe6196..1ed23ffa9 100644 --- a/core/src/avm2/activation.rs +++ b/core/src/avm2/activation.rs @@ -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, 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, diff --git a/core/src/avm2/value.rs b/core/src/avm2/value.rs index 96b69d897..e2edbe033 100644 --- a/core/src/avm2/value.rs +++ b/core/src/avm2/value.rs @@ -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 { + 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 + }, + ) + } } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index 471be0fcb..bdf9d40d0 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -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. diff --git a/core/tests/swfs/avm2/convert_integer/Test.as b/core/tests/swfs/avm2/convert_integer/Test.as new file mode 100644 index 000000000..df5b28ab3 --- /dev/null +++ b/core/tests/swfs/avm2/convert_integer/Test.as @@ -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); \ No newline at end of file diff --git a/core/tests/swfs/avm2/convert_integer/output.txt b/core/tests/swfs/avm2/convert_integer/output.txt new file mode 100644 index 000000000..cdbd4775c --- /dev/null +++ b/core/tests/swfs/avm2/convert_integer/output.txt @@ -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 diff --git a/core/tests/swfs/avm2/convert_integer/test.fla b/core/tests/swfs/avm2/convert_integer/test.fla new file mode 100644 index 000000000..c41c9f293 Binary files /dev/null and b/core/tests/swfs/avm2/convert_integer/test.fla differ diff --git a/core/tests/swfs/avm2/convert_integer/test.swf b/core/tests/swfs/avm2/convert_integer/test.swf new file mode 100644 index 000000000..93ed90634 Binary files /dev/null and b/core/tests/swfs/avm2/convert_integer/test.swf differ