From 46cc1f718e18339ddbff7c609a3de0464777acec Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Mon, 19 Oct 2020 18:01:28 +0100 Subject: [PATCH] LibJS: Unprefixed octal numbers are a syntax error in strict mode --- Libraries/LibJS/Lexer.cpp | 3 +-- Libraries/LibJS/Parser.cpp | 19 +++++++++++++++++-- Libraries/LibJS/Parser.h | 1 + .../LibJS/Tests/numeric-literals-basic.js | 5 +++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Libraries/LibJS/Lexer.cpp b/Libraries/LibJS/Lexer.cpp index aa1bbbaf16..d7e6516b63 100644 --- a/Libraries/LibJS/Lexer.cpp +++ b/Libraries/LibJS/Lexer.cpp @@ -422,8 +422,7 @@ Token Lexer::next() consume(); token_type = TokenType::BigIntLiteral; } else if (isdigit(m_current_char)) { - // octal without 'O' prefix. Forbidden in 'strict mode' - // FIXME: We need to make sure this produces a syntax error when in strict mode + // octal without '0o' prefix. Forbidden in 'strict mode' do { consume(); } while (isdigit(m_current_char)); diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index b725982005..5c77edadac 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace JS { @@ -591,7 +592,7 @@ NonnullRefPtr Parser::parse_primary_expression() return create_ast_node(consume().value()); } case TokenType::NumericLiteral: - return create_ast_node(consume().double_value()); + return create_ast_node(consume_and_validate_numeric_literal().double_value()); case TokenType::BigIntLiteral: return create_ast_node(consume().value()); case TokenType::BoolLiteral: @@ -687,7 +688,8 @@ NonnullRefPtr Parser::parse_property_key() if (match(TokenType::StringLiteral)) { return parse_string_literal(consume()); } else if (match(TokenType::NumericLiteral)) { - return create_ast_node(consume(TokenType::NumericLiteral).value()); + // FIXME: "evaluate" key to double value, see https://github.com/SerenityOS/serenity/issues/3717 + return create_ast_node(consume_and_validate_numeric_literal().value()); } else if (match(TokenType::BigIntLiteral)) { auto value = consume(TokenType::BigIntLiteral).value(); return create_ast_node(value.substring_view(0, value.length() - 1)); @@ -1831,6 +1833,19 @@ Token Parser::consume(TokenType expected_type) return consume(); } +Token Parser::consume_and_validate_numeric_literal() +{ + auto is_unprefixed_octal_number = [](const StringView& value) { + return value.length() > 1 && value[0] == '0' && isdigit(value[1]); + }; + auto literal_start_line = m_parser_state.m_current_token.line_number(); + auto literal_start_column = m_parser_state.m_current_token.line_column(); + auto token = consume(TokenType::NumericLiteral); + if (m_parser_state.m_strict_mode && is_unprefixed_octal_number(token.value())) + syntax_error("Unprefixed octal number not allowed in strict mode", literal_start_line, literal_start_column); + return token; +} + void Parser::expected(const char* what) { syntax_error(String::formatted("Unexpected token {}. Expected {}", m_parser_state.m_current_token.name(), what)); diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index 6a730038aa..ff41b8e240 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -139,6 +139,7 @@ private: void syntax_error(const String& message, size_t line = 0, size_t column = 0); Token consume(); Token consume(TokenType type); + Token consume_and_validate_numeric_literal(); void consume_or_insert_semicolon(); void save_state(); void load_state(); diff --git a/Libraries/LibJS/Tests/numeric-literals-basic.js b/Libraries/LibJS/Tests/numeric-literals-basic.js index 652fa9d2c1..93a1815a49 100644 --- a/Libraries/LibJS/Tests/numeric-literals-basic.js +++ b/Libraries/LibJS/Tests/numeric-literals-basic.js @@ -1,3 +1,6 @@ +// FIXME: Some of the test cases below are duplicated, presumably to test +// uppercase as well which then got lowercased by Prettier at some point. + test("hex literals", () => { expect(0xff).toBe(255); expect(0xff).toBe(255); @@ -6,6 +9,7 @@ test("hex literals", () => { test("octal literals", () => { expect(0o10).toBe(8); expect(0o10).toBe(8); + expect(010).toBe(8); }); test("binary literals", () => { @@ -41,4 +45,5 @@ test("invalid numeric literals", () => { expect("0x").not.toEval(); expect("0b").not.toEval(); expect("0o").not.toEval(); + expect("'use strict'; 0755").not.toEval(); });