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:
parent
345a244ed4
commit
6eb41035cf
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
|
@ -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.
Loading…
Reference in New Issue