summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibJS/AST.cpp9
-rw-r--r--Libraries/LibJS/AST.h1
-rw-r--r--Libraries/LibJS/Parser.cpp8
-rw-r--r--Libraries/LibJS/Runtime/Value.cpp8
-rw-r--r--Libraries/LibJS/Runtime/Value.h7
-rw-r--r--Libraries/LibJS/Tests/binary-bitwise-left-shift.js63
-rw-r--r--Libraries/LibJS/Tests/binary-bitwise-or.js (renamed from Libraries/LibJS/Tests/binary-bitwise-operators-basic.js)0
7 files changed, 95 insertions, 1 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index ed8e790df2..2540bdd2fd 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -749,6 +749,12 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const
return {};
rhs_result = div(interpreter, lhs_result, rhs_result);
break;
+ case AssignmentOp::LeftShiftAssignment:
+ lhs_result = m_lhs->execute(interpreter);
+ if (interpreter.exception())
+ return {};
+ rhs_result = left_shift(interpreter, lhs_result, rhs_result);
+ break;
}
if (interpreter.exception())
return {};
@@ -816,6 +822,9 @@ void AssignmentExpression::dump(int indent) const
case AssignmentOp::DivisionAssignment:
op_string = "/=";
break;
+ case AssignmentOp::LeftShiftAssignment:
+ op_string = "<<=";
+ break;
}
ASTNode::dump(indent);
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index 58c1bbe8ca..156d69395b 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -567,6 +567,7 @@ enum class AssignmentOp {
SubtractionAssignment,
MultiplicationAssignment,
DivisionAssignment,
+ LeftShiftAssignment,
};
class AssignmentExpression : public Expression {
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp
index d40b2fc1e2..548d916ad3 100644
--- a/Libraries/LibJS/Parser.cpp
+++ b/Libraries/LibJS/Parser.cpp
@@ -577,6 +577,12 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
case TokenType::Caret:
consume();
return create_ast_node<BinaryExpression>(BinaryOp::BitwiseXor, move(lhs), parse_expression(min_precedence, associativity));
+ case TokenType::ShiftLeft:
+ consume();
+ return create_ast_node<BinaryExpression>(BinaryOp::LeftShift, move(lhs), parse_expression(min_precedence, associativity));
+ case TokenType::ShiftLeftEquals:
+ consume();
+ return create_ast_node<AssignmentExpression>(AssignmentOp::LeftShiftAssignment, move(lhs), parse_expression(min_precedence, associativity));
case TokenType::ParenOpen:
return parse_call_expression(move(lhs));
case TokenType::Equals:
@@ -1062,6 +1068,8 @@ bool Parser::match_secondary_expression() const
|| type == TokenType::Ampersand
|| type == TokenType::Pipe
|| type == TokenType::Caret
+ || type == TokenType::ShiftLeft
+ || type == TokenType::ShiftLeftEquals
|| type == TokenType::DoubleAmpersand
|| type == TokenType::DoublePipe
|| type == TokenType::DoubleQuestionMark;
diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp
index 08fe1d67b0..e3729317c6 100644
--- a/Libraries/LibJS/Runtime/Value.cpp
+++ b/Libraries/LibJS/Runtime/Value.cpp
@@ -253,7 +253,13 @@ Value unary_minus(Interpreter&, Value lhs)
Value left_shift(Interpreter&, Value lhs, Value rhs)
{
- return Value((i32)lhs.to_number().as_double() << (i32)rhs.to_number().as_double());
+ 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((i32)lhs_number.as_double() << (i32)lhs_number.as_double());
}
Value right_shift(Interpreter&, Value lhs, Value rhs)
diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h
index 691e15f487..3eccd5cfbe 100644
--- a/Libraries/LibJS/Runtime/Value.h
+++ b/Libraries/LibJS/Runtime/Value.h
@@ -57,6 +57,13 @@ public:
bool is_nan() const { return is_number() && __builtin_isnan(as_double()); }
bool is_infinity() const { return is_number() && __builtin_isinf(as_double()); }
+ bool is_finite_number() const
+ {
+ if (!is_number())
+ return false;
+ auto number = as_double();
+ return !__builtin_isnan(number) && !__builtin_isinf(number);
+ }
Value()
: m_type(Type::Empty)
diff --git a/Libraries/LibJS/Tests/binary-bitwise-left-shift.js b/Libraries/LibJS/Tests/binary-bitwise-left-shift.js
new file mode 100644
index 0000000000..7eca53f10f
--- /dev/null
+++ b/Libraries/LibJS/Tests/binary-bitwise-left-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) === 2);
+ assert((1 << 2) === 4);
+ assert((1 << 3) === 8);
+ assert((1 << 4) === 16);
+ assert((1 << 5) === 32);
+
+ assert((2 << 0) === 2);
+ assert((2 << 1) === 4);
+ assert((2 << 2) === 8);
+ assert((2 << 3) === 16);
+ assert((2 << 4) === 32);
+ assert((2 << 5) === 64);
+
+ assert((3 << 0) === 3);
+ assert((3 << 1) === 6);
+ assert((3 << 2) === 12);
+ assert((3 << 3) === 24);
+ assert((3 << 4) === 48);
+ assert((3 << 5) === 96);
+
+ assert((4 << 0) === 4);
+ assert((4 << 1) === 8);
+ assert((4 << 2) === 16);
+ assert((4 << 3) === 32);
+ assert((4 << 4) === 64);
+ assert((4 << 5) === 128);
+
+ assert((5 << 0) === 5);
+ assert((5 << 1) === 10);
+ assert((5 << 2) === 20);
+ assert((5 << 3) === 40);
+ assert((5 << 4) === 80);
+ assert((5 << 5) === 160);
+
+ var x = 3;
+ var y = 7;
+ assert(("42" << 6) === 2688);
+ assert((x << y) === 384);
+ assert((x << [[[[12]]]]) === 12288);
+ assert((undefined << y) === 0);
+ assert(("a" << "b") === 0);
+ assert((null << null) === 0);
+ assert((undefined << undefined) === 0);
+ assert((NaN << NaN) === 0);
+ assert((NaN << 6) === 0);
+ assert((Infinity << Infinity) === 0);
+ assert((-Infinity << Infinity) === 0);
+
+ console.log("PASS");
+} catch (e) {
+ console.log("FAIL: " + e);
+}
diff --git a/Libraries/LibJS/Tests/binary-bitwise-operators-basic.js b/Libraries/LibJS/Tests/binary-bitwise-or.js
index 09eb1ae036..09eb1ae036 100644
--- a/Libraries/LibJS/Tests/binary-bitwise-operators-basic.js
+++ b/Libraries/LibJS/Tests/binary-bitwise-or.js