diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 198f1b297f..1fc0b29bf0 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -328,6 +329,8 @@ Value BinaryExpression::execute(Interpreter& interpreter) const return left_shift(interpreter, lhs_result, rhs_result); case BinaryOp::RightShift: return right_shift(interpreter, lhs_result, rhs_result); + case BinaryOp::UnsignedRightShift: + return unsigned_right_shift(interpreter, lhs_result, rhs_result); case BinaryOp::InstanceOf: return instance_of(interpreter, lhs_result, rhs_result); } @@ -506,6 +509,9 @@ void BinaryExpression::dump(int indent) const case BinaryOp::RightShift: op_string = ">>"; break; + case BinaryOp::UnsignedRightShift: + op_string = ">>>"; + break; case BinaryOp::InstanceOf: op_string = "instanceof"; break; @@ -761,6 +767,12 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const return {}; rhs_result = right_shift(interpreter, lhs_result, rhs_result); break; + case AssignmentOp::UnsignedRightShiftAssignment: + lhs_result = m_lhs->execute(interpreter); + if (interpreter.exception()) + return {}; + rhs_result = unsigned_right_shift(interpreter, lhs_result, rhs_result); + break; } if (interpreter.exception()) return {}; @@ -834,6 +846,9 @@ void AssignmentExpression::dump(int indent) const case AssignmentOp::RightShiftAssignment: op_string = ">>="; break; + case AssignmentOp::UnsignedRightShiftAssignment: + op_string = ">>>="; + break; } ASTNode::dump(indent); diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index acb78abe74..8005441c74 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -341,6 +342,7 @@ enum class BinaryOp { BitwiseXor, LeftShift, RightShift, + UnsignedRightShift, InstanceOf, }; @@ -569,6 +571,7 @@ enum class AssignmentOp { DivisionAssignment, LeftShiftAssignment, RightShiftAssignment, + UnsignedRightShiftAssignment, }; class AssignmentExpression : public Expression { diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 182d76653a..c58a9a3f27 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Stephan Unverwerth + * Copyright (c) 2020, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -589,6 +590,12 @@ NonnullRefPtr Parser::parse_secondary_expression(NonnullRefPtr(AssignmentOp::RightShiftAssignment, move(lhs), parse_expression(min_precedence, associativity)); + case TokenType::UnsignedShiftRight: + consume(); + return create_ast_node(BinaryOp::UnsignedRightShift, move(lhs), parse_expression(min_precedence, associativity)); + case TokenType::UnsignedShiftRightEquals: + consume(); + return create_ast_node(AssignmentOp::UnsignedRightShiftAssignment, move(lhs), parse_expression(min_precedence, associativity)); case TokenType::ParenOpen: return parse_call_expression(move(lhs)); case TokenType::Equals: @@ -1078,6 +1085,8 @@ bool Parser::match_secondary_expression() const || type == TokenType::ShiftLeftEquals || type == TokenType::ShiftRight || type == TokenType::ShiftRightEquals + || type == TokenType::UnsignedShiftRight + || type == TokenType::UnsignedShiftRightEquals || type == TokenType::DoubleAmpersand || type == TokenType::DoublePipe || type == TokenType::DoubleQuestionMark; diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index 5da5c5765f..2540621c85 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -273,6 +273,17 @@ Value right_shift(Interpreter&, Value lhs, Value rhs) return Value((i32)lhs_number.as_double() >> (i32)rhs_number.as_double()); } +Value unsigned_right_shift(Interpreter&, Value lhs, Value rhs) +{ + auto lhs_number = lhs.to_number(); + if (!lhs_number.is_finite_number()) + return Value(0); + auto rhs_number = rhs.to_number(); + if (!rhs_number.is_finite_number()) + return lhs_number; + return Value((unsigned)lhs_number.as_double() >> (i32)rhs_number.as_double()); +} + Value add(Interpreter& interpreter, Value lhs, Value rhs) { auto lhs_primitive = lhs.to_primitive(interpreter); diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index 3eccd5cfbe..c05ca22cc2 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -82,6 +82,12 @@ public: m_value.as_double = value; } + explicit Value(unsigned value) + : m_type(Type::Number) + { + m_value.as_double = static_cast(value); + } + explicit Value(i32 value) : m_type(Type::Number) { @@ -207,6 +213,7 @@ Value unary_plus(Interpreter&, Value); Value unary_minus(Interpreter&, Value); Value left_shift(Interpreter&, Value lhs, Value rhs); Value right_shift(Interpreter&, Value lhs, Value rhs); +Value unsigned_right_shift(Interpreter&, Value lhs, Value rhs); Value add(Interpreter&, Value lhs, Value rhs); Value sub(Interpreter&, Value lhs, Value rhs); Value mul(Interpreter&, Value lhs, Value rhs); diff --git a/Libraries/LibJS/Tests/binary-bitwise-unsigned-right-shift.js b/Libraries/LibJS/Tests/binary-bitwise-unsigned-right-shift.js new file mode 100644 index 0000000000..0f69b6b63a --- /dev/null +++ b/Libraries/LibJS/Tests/binary-bitwise-unsigned-right-shift.js @@ -0,0 +1,63 @@ +load("test-common.js"); + +try { + assert((0 >>> 0) === 0); + assert((0 >>> 1) === 0); + assert((0 >>> 2) === 0); + assert((0 >>> 3) === 0); + assert((0 >>> 4) === 0); + assert((0 >>> 5) === 0); + + assert((1 >>> 0) === 1); + assert((1 >>> 1) === 0); + assert((1 >>> 2) === 0); + assert((1 >>> 3) === 0); + assert((1 >>> 4) === 0); + assert((1 >>> 5) === 0); + + assert((5 >>> 0) === 5); + assert((5 >>> 1) === 2); + assert((5 >>> 2) === 1); + assert((5 >>> 3) === 0); + assert((5 >>> 4) === 0); + assert((5 >>> 5) === 0); + + assert((42 >>> 0) === 42); + assert((42 >>> 1) === 21); + assert((42 >>> 2) === 10); + assert((42 >>> 3) === 5); + assert((42 >>> 4) === 2); + assert((42 >>> 5) === 1); + + assert((-1 >>> 0) === 4294967295); + assert((-1 >>> 1) === 2147483647); + assert((-1 >>> 2) === 1073741823); + assert((-1 >>> 3) === 536870911); + assert((-1 >>> 4) === 268435455); + assert((-1 >>> 5) === 134217727); + + assert((-5 >>> 0) === 4294967291); + assert((-5 >>> 1) === 2147483645); + assert((-5 >>> 2) === 1073741822); + assert((-5 >>> 3) === 536870911); + assert((-5 >>> 4) === 268435455); + assert((-5 >>> 5) === 134217727); + + var x = -67; + var y = 4; + assert(("-42" >>> 3) === 536870906); + assert((x >>> y) === 268435451); + assert((x >>> [[[[5]]]]) === 134217725); + assert((undefined >>> y) === 0); + assert(("a" >>> "b") === 0); + assert((null >>> null) === 0); + assert((undefined >>> undefined) === 0); + assert((NaN >>> NaN) === 0); + assert((6 >>> NaN) === 6); + assert((Infinity >>> Infinity) === 0); + assert((-Infinity >>> Infinity) === 0); + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}