浏览代码

LibJS: Unprefixed octal numbers are a syntax error in strict mode

Linus Groh 4 年之前
父节点
当前提交
46cc1f718e
共有 4 个文件被更改,包括 24 次插入4 次删除
  1. 1 2
      Libraries/LibJS/Lexer.cpp
  2. 17 2
      Libraries/LibJS/Parser.cpp
  3. 1 0
      Libraries/LibJS/Parser.h
  4. 5 0
      Libraries/LibJS/Tests/numeric-literals-basic.js

+ 1 - 2
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));

+ 17 - 2
Libraries/LibJS/Parser.cpp

@@ -29,6 +29,7 @@
 #include <AK/ScopeGuard.h>
 #include <AK/StdLibExtras.h>
 #include <AK/TemporaryChange.h>
+#include <ctype.h>
 
 namespace JS {
 
@@ -591,7 +592,7 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
         return create_ast_node<Identifier>(consume().value());
     }
     case TokenType::NumericLiteral:
-        return create_ast_node<NumericLiteral>(consume().double_value());
+        return create_ast_node<NumericLiteral>(consume_and_validate_numeric_literal().double_value());
     case TokenType::BigIntLiteral:
         return create_ast_node<BigIntLiteral>(consume().value());
     case TokenType::BoolLiteral:
@@ -687,7 +688,8 @@ NonnullRefPtr<Expression> Parser::parse_property_key()
     if (match(TokenType::StringLiteral)) {
         return parse_string_literal(consume());
     } else if (match(TokenType::NumericLiteral)) {
-        return create_ast_node<StringLiteral>(consume(TokenType::NumericLiteral).value());
+        // FIXME: "evaluate" key to double value, see https://github.com/SerenityOS/serenity/issues/3717
+        return create_ast_node<StringLiteral>(consume_and_validate_numeric_literal().value());
     } else if (match(TokenType::BigIntLiteral)) {
         auto value = consume(TokenType::BigIntLiteral).value();
         return create_ast_node<StringLiteral>(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));

+ 1 - 0
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();

+ 5 - 0
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();
 });