feat: 支持位运算字节码与 ToInt32/ToUint32
This commit is contained in:
@@ -669,6 +669,12 @@ impl Compiler {
|
|||||||
BinaryOperator::Multiplication => OpCode::MUL,
|
BinaryOperator::Multiplication => OpCode::MUL,
|
||||||
BinaryOperator::Division => OpCode::DIV,
|
BinaryOperator::Division => OpCode::DIV,
|
||||||
BinaryOperator::Remainder => OpCode::MOD,
|
BinaryOperator::Remainder => OpCode::MOD,
|
||||||
|
BinaryOperator::BitwiseAnd => OpCode::BIT_AND,
|
||||||
|
BinaryOperator::BitwiseOR => OpCode::BIT_OR,
|
||||||
|
BinaryOperator::BitwiseXOR => OpCode::BIT_XOR,
|
||||||
|
BinaryOperator::ShiftLeft => OpCode::SHL,
|
||||||
|
BinaryOperator::ShiftRight => OpCode::SHR,
|
||||||
|
BinaryOperator::ShiftRightZeroFill => OpCode::USHR,
|
||||||
BinaryOperator::Equality => OpCode::EQ,
|
BinaryOperator::Equality => OpCode::EQ,
|
||||||
BinaryOperator::Inequality => OpCode::NEQ,
|
BinaryOperator::Inequality => OpCode::NEQ,
|
||||||
BinaryOperator::LessThan => OpCode::LT,
|
BinaryOperator::LessThan => OpCode::LT,
|
||||||
@@ -708,6 +714,11 @@ impl Compiler {
|
|||||||
ctx.emit(opcode::encode(OpCode::NOT, r, arg, 0));
|
ctx.emit(opcode::encode(OpCode::NOT, r, arg, 0));
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
UnaryOperator::BitwiseNot => {
|
||||||
|
let r = ctx.alloc_reg();
|
||||||
|
ctx.emit(opcode::encode(OpCode::BIT_NOT, r, arg, 0));
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
UnaryOperator::UnaryPlus => {
|
UnaryOperator::UnaryPlus => {
|
||||||
let r = ctx.alloc_reg();
|
let r = ctx.alloc_reg();
|
||||||
ctx.emit(opcode::encode(OpCode::UNARY_PLUS, r, arg, 0));
|
ctx.emit(opcode::encode(OpCode::UNARY_PLUS, r, arg, 0));
|
||||||
@@ -735,7 +746,6 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
_ => Err("invalid delete target".into()),
|
_ => Err("invalid delete target".into()),
|
||||||
},
|
},
|
||||||
_ => Err(format!("unsupported unary operator: {:?}", un.operator)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::ConditionalExpression(cond) => {
|
Expression::ConditionalExpression(cond) => {
|
||||||
@@ -938,6 +948,12 @@ impl Compiler {
|
|||||||
|| assign.operator == AssignmentOperator::Division
|
|| assign.operator == AssignmentOperator::Division
|
||||||
|| assign.operator == AssignmentOperator::Remainder
|
|| assign.operator == AssignmentOperator::Remainder
|
||||||
|| assign.operator == AssignmentOperator::Exponential
|
|| assign.operator == AssignmentOperator::Exponential
|
||||||
|
|| assign.operator == AssignmentOperator::BitwiseAnd
|
||||||
|
|| assign.operator == AssignmentOperator::BitwiseOR
|
||||||
|
|| assign.operator == AssignmentOperator::BitwiseXOR
|
||||||
|
|| assign.operator == AssignmentOperator::ShiftLeft
|
||||||
|
|| assign.operator == AssignmentOperator::ShiftRight
|
||||||
|
|| assign.operator == AssignmentOperator::ShiftRightZeroFill
|
||||||
{
|
{
|
||||||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||||||
let name = id_ref.name.as_str();
|
let name = id_ref.name.as_str();
|
||||||
@@ -949,6 +965,12 @@ impl Compiler {
|
|||||||
AssignmentOperator::Division => OpCode::COMPOUND_DIV,
|
AssignmentOperator::Division => OpCode::COMPOUND_DIV,
|
||||||
AssignmentOperator::Remainder => OpCode::COMPOUND_MOD,
|
AssignmentOperator::Remainder => OpCode::COMPOUND_MOD,
|
||||||
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
|
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
|
||||||
|
AssignmentOperator::BitwiseAnd => OpCode::COMPOUND_AND,
|
||||||
|
AssignmentOperator::BitwiseOR => OpCode::COMPOUND_OR,
|
||||||
|
AssignmentOperator::BitwiseXOR => OpCode::COMPOUND_XOR,
|
||||||
|
AssignmentOperator::ShiftLeft => OpCode::COMPOUND_SHL,
|
||||||
|
AssignmentOperator::ShiftRight => OpCode::COMPOUND_SHR,
|
||||||
|
AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"compound assignment operator {:?} not supported",
|
"compound assignment operator {:?} not supported",
|
||||||
|
|||||||
@@ -122,6 +122,21 @@ pub enum OpCode {
|
|||||||
FORK = 0x70,
|
FORK = 0x70,
|
||||||
JOIN = 0x71,
|
JOIN = 0x71,
|
||||||
|
|
||||||
|
// -- Bitwise (0x80-0x8F) --
|
||||||
|
BIT_AND = 0x80,
|
||||||
|
BIT_OR = 0x81,
|
||||||
|
BIT_XOR = 0x82,
|
||||||
|
SHL = 0x83,
|
||||||
|
SHR = 0x84,
|
||||||
|
USHR = 0x85,
|
||||||
|
BIT_NOT = 0x86,
|
||||||
|
COMPOUND_AND = 0x88,
|
||||||
|
COMPOUND_OR = 0x89,
|
||||||
|
COMPOUND_XOR = 0x8A,
|
||||||
|
COMPOUND_SHL = 0x8B,
|
||||||
|
COMPOUND_SHR = 0x8C,
|
||||||
|
COMPOUND_USHR = 0x8D,
|
||||||
|
|
||||||
// ── Misc (0xF0-0xFF) ──
|
// ── Misc (0xF0-0xFF) ──
|
||||||
NOP = 0xF0,
|
NOP = 0xF0,
|
||||||
HALT = 0xF1,
|
HALT = 0xF1,
|
||||||
@@ -250,6 +265,19 @@ impl TryFrom<u8> for OpCode {
|
|||||||
0x66 => Ok(OpCode::PROFILE_CALL),
|
0x66 => Ok(OpCode::PROFILE_CALL),
|
||||||
0x70 => Ok(OpCode::FORK),
|
0x70 => Ok(OpCode::FORK),
|
||||||
0x71 => Ok(OpCode::JOIN),
|
0x71 => Ok(OpCode::JOIN),
|
||||||
|
0x80 => Ok(OpCode::BIT_AND),
|
||||||
|
0x81 => Ok(OpCode::BIT_OR),
|
||||||
|
0x82 => Ok(OpCode::BIT_XOR),
|
||||||
|
0x83 => Ok(OpCode::SHL),
|
||||||
|
0x84 => Ok(OpCode::SHR),
|
||||||
|
0x85 => Ok(OpCode::USHR),
|
||||||
|
0x86 => Ok(OpCode::BIT_NOT),
|
||||||
|
0x88 => Ok(OpCode::COMPOUND_AND),
|
||||||
|
0x89 => Ok(OpCode::COMPOUND_OR),
|
||||||
|
0x8A => Ok(OpCode::COMPOUND_XOR),
|
||||||
|
0x8B => Ok(OpCode::COMPOUND_SHL),
|
||||||
|
0x8C => Ok(OpCode::COMPOUND_SHR),
|
||||||
|
0x8D => Ok(OpCode::COMPOUND_USHR),
|
||||||
0xF0 => Ok(OpCode::NOP),
|
0xF0 => Ok(OpCode::NOP),
|
||||||
0xF1 => Ok(OpCode::HALT),
|
0xF1 => Ok(OpCode::HALT),
|
||||||
0xF2 => Ok(OpCode::TYPEOF),
|
0xF2 => Ok(OpCode::TYPEOF),
|
||||||
@@ -348,6 +376,19 @@ impl fmt::Display for OpCode {
|
|||||||
OpCode::PROFILE_CALL => "PROFILE_CALL",
|
OpCode::PROFILE_CALL => "PROFILE_CALL",
|
||||||
OpCode::FORK => "FORK",
|
OpCode::FORK => "FORK",
|
||||||
OpCode::JOIN => "JOIN",
|
OpCode::JOIN => "JOIN",
|
||||||
|
OpCode::BIT_AND => "BIT_AND",
|
||||||
|
OpCode::BIT_OR => "BIT_OR",
|
||||||
|
OpCode::BIT_XOR => "BIT_XOR",
|
||||||
|
OpCode::SHL => "SHL",
|
||||||
|
OpCode::SHR => "SHR",
|
||||||
|
OpCode::USHR => "USHR",
|
||||||
|
OpCode::BIT_NOT => "BIT_NOT",
|
||||||
|
OpCode::COMPOUND_AND => "COMPOUND_AND",
|
||||||
|
OpCode::COMPOUND_OR => "COMPOUND_OR",
|
||||||
|
OpCode::COMPOUND_XOR => "COMPOUND_XOR",
|
||||||
|
OpCode::COMPOUND_SHL => "COMPOUND_SHL",
|
||||||
|
OpCode::COMPOUND_SHR => "COMPOUND_SHR",
|
||||||
|
OpCode::COMPOUND_USHR => "COMPOUND_USHR",
|
||||||
OpCode::NOP => "NOP",
|
OpCode::NOP => "NOP",
|
||||||
OpCode::HALT => "HALT",
|
OpCode::HALT => "HALT",
|
||||||
OpCode::TYPEOF => "TYPEOF",
|
OpCode::TYPEOF => "TYPEOF",
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ fn compile_binary_ops() {
|
|||||||
("10 / 2", OpCode::DIV),
|
("10 / 2", OpCode::DIV),
|
||||||
("7 % 3", OpCode::MOD),
|
("7 % 3", OpCode::MOD),
|
||||||
("5 - 2", OpCode::SUB),
|
("5 - 2", OpCode::SUB),
|
||||||
|
("5 & 3", OpCode::BIT_AND),
|
||||||
|
("5 | 2", OpCode::BIT_OR),
|
||||||
|
("5 ^ 1", OpCode::BIT_XOR),
|
||||||
|
("1 << 3", OpCode::SHL),
|
||||||
|
("-8 >> 1", OpCode::SHR),
|
||||||
|
("-1 >>> 0", OpCode::USHR),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (src, expected_op) in tests {
|
for (src, expected_op) in tests {
|
||||||
@@ -88,6 +94,15 @@ fn compile_binary_ops() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_bitwise_not() {
|
||||||
|
let module = compile_source("~0");
|
||||||
|
assert!(
|
||||||
|
module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::BIT_NOT),
|
||||||
|
"~0 should emit BIT_NOT opcode"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_ternary_emits_jumps() {
|
fn compile_ternary_emits_jumps() {
|
||||||
let module = compile_source("true ? 1 : 2");
|
let module = compile_source("true ? 1 : 2");
|
||||||
@@ -206,6 +221,27 @@ fn compile_compound_exp() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_compound_bitwise_ops() {
|
||||||
|
let tests = [
|
||||||
|
("var x=5; x&=3", OpCode::COMPOUND_AND),
|
||||||
|
("var x=5; x|=2", OpCode::COMPOUND_OR),
|
||||||
|
("var x=5; x^=1", OpCode::COMPOUND_XOR),
|
||||||
|
("var x=1; x<<=5", OpCode::COMPOUND_SHL),
|
||||||
|
("var x=-8; x>>=1", OpCode::COMPOUND_SHR),
|
||||||
|
("var x=-1; x>>>=0", OpCode::COMPOUND_USHR),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (src, expected_op) in tests {
|
||||||
|
let module = compile_source(src);
|
||||||
|
assert!(
|
||||||
|
module.bytecode.iter().any(|&i| opcode::opcode(i) == expected_op),
|
||||||
|
"expected {:?} in '{src}'",
|
||||||
|
expected_op
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_compound_assign_no_error() {
|
fn compile_compound_assign_no_error() {
|
||||||
let result = std::panic::catch_unwind(|| {
|
let result = std::panic::catch_unwind(|| {
|
||||||
|
|||||||
@@ -34,6 +34,23 @@ pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
|
|||||||
f64::NAN
|
f64::NAN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_uint32(val: JsValue, string_forge: &StringForge) -> u32 {
|
||||||
|
let n = to_number(val, string_forge);
|
||||||
|
if n == 0.0 || !n.is_finite() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
n.trunc().rem_euclid(4_294_967_296.0) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int32(val: JsValue, string_forge: &StringForge) -> i32 {
|
||||||
|
let int = to_uint32(val, string_forge);
|
||||||
|
if int > i32::MAX as u32 {
|
||||||
|
(int as i64 - 4_294_967_296i64) as i32
|
||||||
|
} else {
|
||||||
|
int as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
|
pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
|
||||||
if val.is_int() {
|
if val.is_int() {
|
||||||
return val.as_int().to_string();
|
return val.as_int().to_string();
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
use crate::coercion;
|
||||||
|
use crate::vm::Vm;
|
||||||
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
|
impl Vm {
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_ushr_result(&mut self, rd: usize, result: u32) {
|
||||||
|
if result <= i32::MAX as u32 {
|
||||||
|
self.regs[rd] = JsValue::int(result as i32);
|
||||||
|
} else {
|
||||||
|
self.regs[rd] = JsValue::float(result as f64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_bit_and(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[a], sf) & coercion::to_int32(self.regs[b], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_bit_or(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[a], sf) | coercion::to_int32(self.regs[b], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_bit_xor(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[a], sf) ^ coercion::to_int32(self.regs[b], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_shl(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_int32(self.regs[a], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[b], sf) & 0x1F;
|
||||||
|
self.regs[rd] = JsValue::int(lhs.wrapping_shl(shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_shr(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_int32(self.regs[a], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[b], sf) & 0x1F;
|
||||||
|
self.regs[rd] = JsValue::int(lhs >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_ushr(&mut self, rd: usize, a: usize, b: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_uint32(self.regs[a], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[b], sf) & 0x1F;
|
||||||
|
self.write_ushr_result(rd, lhs >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_bit_not(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(!coercion::to_int32(self.regs[a], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_bit_and(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[rd], sf) & coercion::to_int32(self.regs[a], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_bit_or(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[rd], sf) | coercion::to_int32(self.regs[a], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_bit_xor(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
self.regs[rd] = JsValue::int(coercion::to_int32(self.regs[rd], sf) ^ coercion::to_int32(self.regs[a], sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_shl(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_int32(self.regs[rd], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[a], sf) & 0x1F;
|
||||||
|
self.regs[rd] = JsValue::int(lhs.wrapping_shl(shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_shr(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_int32(self.regs[rd], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[a], sf) & 0x1F;
|
||||||
|
self.regs[rd] = JsValue::int(lhs >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn dispatch_compound_ushr(&mut self, rd: usize, a: usize) {
|
||||||
|
let sf = self.kernel.string_forge().as_ref();
|
||||||
|
let lhs = coercion::to_uint32(self.regs[rd], sf);
|
||||||
|
let shift = coercion::to_uint32(self.regs[a], sf) & 0x1F;
|
||||||
|
self.write_ushr_result(rd, lhs >> shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
|
pub(crate) mod bitwise;
|
||||||
pub(crate) mod call;
|
pub(crate) mod call;
|
||||||
pub(crate) mod comparison;
|
pub(crate) mod comparison;
|
||||||
pub(crate) mod control_flow;
|
pub(crate) mod control_flow;
|
||||||
|
|||||||
@@ -657,6 +657,34 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(-v);
|
self.regs[rd] = JsValue::float(-v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpCode::BIT_AND => {
|
||||||
|
self.dispatch_bit_and(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::BIT_OR => {
|
||||||
|
self.dispatch_bit_or(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::BIT_XOR => {
|
||||||
|
self.dispatch_bit_xor(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::SHL => {
|
||||||
|
self.dispatch_shl(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::SHR => {
|
||||||
|
self.dispatch_shr(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::USHR => {
|
||||||
|
self.dispatch_ushr(rd, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::BIT_NOT => {
|
||||||
|
self.dispatch_bit_not(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
OpCode::EQ => {
|
OpCode::EQ => {
|
||||||
self.dispatch_eq(rd, a, b);
|
self.dispatch_eq(rd, a, b);
|
||||||
}
|
}
|
||||||
@@ -1277,6 +1305,30 @@ impl Vm {
|
|||||||
self.regs[rd] = JsValue::float(l.powf(r));
|
self.regs[rd] = JsValue::float(l.powf(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_AND => {
|
||||||
|
self.dispatch_compound_bit_and(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_OR => {
|
||||||
|
self.dispatch_compound_bit_or(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_XOR => {
|
||||||
|
self.dispatch_compound_bit_xor(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_SHL => {
|
||||||
|
self.dispatch_compound_shl(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_SHR => {
|
||||||
|
self.dispatch_compound_shr(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCode::COMPOUND_USHR => {
|
||||||
|
self.dispatch_compound_ushr(rd, a);
|
||||||
|
}
|
||||||
|
|
||||||
OpCode::TYPEOF => {
|
OpCode::TYPEOF => {
|
||||||
self.dispatch_typeof(rd, a);
|
self.dispatch_typeof(rd, a);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,3 +103,53 @@ fn eval_compound_add_expr_value() {
|
|||||||
fn eval_compound_undefined() {
|
fn eval_compound_undefined() {
|
||||||
assert_eq!(eval("var x; x+=1; x"), "NaN");
|
assert_eq!(eval("var x; x+=1; x"), "NaN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_bitwise_direct_ops() {
|
||||||
|
assert_eq!(eval("5 & 3"), "1");
|
||||||
|
assert_eq!(eval("5 | 2"), "7");
|
||||||
|
assert_eq!(eval("5 ^ 1"), "4");
|
||||||
|
assert_eq!(eval("1 << 3"), "8");
|
||||||
|
assert_eq!(eval("-8 >> 1"), "-4");
|
||||||
|
assert_eq!(eval("-1 >>> 0"), "4294967295");
|
||||||
|
assert_eq!(eval("~0"), "-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_bitwise_coercion() {
|
||||||
|
assert_eq!(eval("'3' | 0"), "3");
|
||||||
|
assert_eq!(eval("'x' | 0"), "0");
|
||||||
|
assert_eq!(eval("undefined | 0"), "0");
|
||||||
|
assert_eq!(eval("NaN | 0"), "0");
|
||||||
|
assert_eq!(eval("1.9 | 0"), "1");
|
||||||
|
assert_eq!(eval("-1.9 | 0"), "-1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_shift_masking() {
|
||||||
|
assert_eq!(eval("1 << 32"), "1");
|
||||||
|
assert_eq!(eval("1 << 33"), "2");
|
||||||
|
assert_eq!(eval("8 >> 33"), "4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_ushr_boundaries() {
|
||||||
|
assert_eq!(eval("2147483647 >>> 0"), "2147483647");
|
||||||
|
assert_eq!(eval("2147483648 >>> 0"), "2147483648");
|
||||||
|
assert_eq!(eval("-1 >>> 1"), "2147483647");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_compound_bitwise_ops() {
|
||||||
|
assert_eq!(eval("var x=5; x &= 3; x"), "1");
|
||||||
|
assert_eq!(eval("var x=5; x |= 2; x"), "7");
|
||||||
|
assert_eq!(eval("var x=5; x ^= 1; x"), "4");
|
||||||
|
assert_eq!(eval("var x=1; x <<= 5; x"), "32");
|
||||||
|
assert_eq!(eval("var x=-8; x >>= 1; x"), "-4");
|
||||||
|
assert_eq!(eval("var x=-1; x >>>= 0; x"), "4294967295");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_bitwise_in_control_flow() {
|
||||||
|
assert_eq!(eval("var x=3; if ((x & 1) == 1) { 7 } else { 9 }"), "7");
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,3 +82,26 @@ fn test_strict_equality_int_float_equal() {
|
|||||||
fn test_strict_equality_null_undefined() {
|
fn test_strict_equality_null_undefined() {
|
||||||
assert!(!coercion::strict_equality(JsValue::null(), JsValue::undefined()));
|
assert!(!coercion::strict_equality(JsValue::null(), JsValue::undefined()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_int32_and_to_uint32() {
|
||||||
|
let vm = Vm::new();
|
||||||
|
let sf = vm.kernel().string_forge().as_ref();
|
||||||
|
let string_three = {
|
||||||
|
let (idx, hash) = vm.kernel().string_forge().intern("3");
|
||||||
|
JsValue::string(idx, hash)
|
||||||
|
};
|
||||||
|
let string_bad = {
|
||||||
|
let (idx, hash) = vm.kernel().string_forge().intern("x");
|
||||||
|
JsValue::string(idx, hash)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(coercion::to_int32(string_three, sf), 3);
|
||||||
|
assert_eq!(coercion::to_int32(string_bad, sf), 0);
|
||||||
|
assert_eq!(coercion::to_int32(JsValue::undefined(), sf), 0);
|
||||||
|
assert_eq!(coercion::to_int32(JsValue::float(1.9), sf), 1);
|
||||||
|
assert_eq!(coercion::to_int32(JsValue::float(-1.9), sf), -1);
|
||||||
|
assert_eq!(coercion::to_int32(JsValue::float(f64::NAN), sf), 0);
|
||||||
|
assert_eq!(coercion::to_int32(JsValue::float(f64::INFINITY), sf), 0);
|
||||||
|
assert_eq!(coercion::to_uint32(JsValue::int(-1), sf), 4_294_967_295);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user