LibJS: Implement 'in' operator

This commit is contained in:
Linus Groh 2020-04-23 16:06:01 +01:00 committed by Andreas Kling
parent 396ecfa2d7
commit 11728b7db5
Notes: sideshowbarker 2024-07-19 07:21:32 +09:00
6 changed files with 54 additions and 0 deletions

View file

@ -331,6 +331,8 @@ Value BinaryExpression::execute(Interpreter& interpreter) const
return right_shift(interpreter, lhs_result, rhs_result);
case BinaryOp::UnsignedRightShift:
return unsigned_right_shift(interpreter, lhs_result, rhs_result);
case BinaryOp::In:
return in(interpreter, lhs_result, rhs_result);
case BinaryOp::InstanceOf:
return instance_of(interpreter, lhs_result, rhs_result);
}
@ -512,6 +514,9 @@ void BinaryExpression::dump(int indent) const
case BinaryOp::UnsignedRightShift:
op_string = ">>>";
break;
case BinaryOp::In:
op_string = "in";
break;
case BinaryOp::InstanceOf:
op_string = "instanceof";
break;

View file

@ -343,6 +343,7 @@ enum class BinaryOp {
LeftShift,
RightShift,
UnsignedRightShift,
In,
InstanceOf,
};

View file

@ -566,6 +566,9 @@ NonnullRefPtr<Expression> Parser::parse_secondary_expression(NonnullRefPtr<Expre
case TokenType::ExclamationMarkEquals:
consume();
return create_ast_node<BinaryExpression>(BinaryOp::AbstractInequals, move(lhs), parse_expression(min_precedence, associativity));
case TokenType::In:
consume();
return create_ast_node<BinaryExpression>(BinaryOp::In, move(lhs), parse_expression(min_precedence, associativity));
case TokenType::Instanceof:
consume();
return create_ast_node<BinaryExpression>(BinaryOp::InstanceOf, move(lhs), parse_expression(min_precedence, associativity));
@ -1076,6 +1079,7 @@ bool Parser::match_secondary_expression() const
|| type == TokenType::BracketOpen
|| type == TokenType::PlusPlus
|| type == TokenType::MinusMinus
|| type == TokenType::In
|| type == TokenType::Instanceof
|| type == TokenType::QuestionMark
|| type == TokenType::Ampersand

View file

@ -382,6 +382,14 @@ Value eq(Interpreter& interpreter, Value lhs, Value rhs)
return Value(false);
}
Value in(Interpreter& interpreter, Value lhs, Value rhs)
{
if (!rhs.is_object())
return interpreter.throw_exception<TypeError>("'in' operator must be used on object");
return Value(rhs.as_object().get(lhs.to_string()).has_value());
}
Value instance_of(Interpreter&, Value lhs, Value rhs)
{
if (!lhs.is_object() || !rhs.is_object())

View file

@ -222,6 +222,7 @@ Value mod(Interpreter&, Value lhs, Value rhs);
Value exp(Interpreter&, Value lhs, Value rhs);
Value eq(Interpreter&, Value lhs, Value rhs);
Value typed_eq(Interpreter&, Value lhs, Value rhs);
Value in(Interpreter&, Value lhs, Value rhs);
Value instance_of(Interpreter&, Value lhs, Value rhs);
const LogStream& operator<<(const LogStream&, const Value&);

View file

@ -0,0 +1,35 @@
load("test-common.js");
try {
["foo", 123, null, undefined].forEach(value => {
assertThrowsError(() => {
"prop" in value;
}, {
error: TypeError,
message: "'in' operator must be used on object"
});
});
var o = {foo: "bar", bar: undefined};
assert("" in o === false);
assert("foo" in o === true);
assert("bar" in o === true);
assert("baz" in o === false);
assert("toString" in o === true);
var a = ["hello", "friends"];
assert(0 in a === true);
assert(1 in a === true);
assert(2 in a === false);
assert("0" in a === true);
assert("hello" in a === false);
assert("friends" in a === false);
assert("length" in a === true);
var s = new String("foo");
assert("length" in s);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);
}