Bläddra i källkod

LibJS: Capture source text of FunctionNode and ClassExpression

Linus Groh 3 år sedan
förälder
incheckning
531d1ac734
3 ändrade filer med 46 tillägg och 20 borttagningar
  1. 12 6
      Userland/Libraries/LibJS/AST.h
  2. 33 13
      Userland/Libraries/LibJS/Parser.cpp
  3. 1 1
      Userland/Libraries/LibJS/Parser.h

+ 12 - 6
Userland/Libraries/LibJS/AST.h

@@ -484,6 +484,7 @@ public:
     };
 
     FlyString const& name() const { return m_name; }
+    String const& source_text() const { return m_source_text; }
     Statement const& body() const { return *m_body; }
     Vector<Parameter> const& parameters() const { return m_parameters; };
     i32 function_length() const { return m_function_length; }
@@ -494,8 +495,9 @@ public:
     FunctionKind kind() const { return m_kind; }
 
 protected:
-    FunctionNode(FlyString name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function)
+    FunctionNode(FlyString name, String source_text, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function)
         : m_name(move(name))
+        , m_source_text(move(source_text))
         , m_body(move(body))
         , m_parameters(move(parameters))
         , m_function_length(function_length)
@@ -520,6 +522,7 @@ protected:
 
 private:
     FlyString m_name;
+    String m_source_text;
     NonnullRefPtr<Statement> m_body;
     Vector<Parameter> const m_parameters;
     const i32 m_function_length;
@@ -536,9 +539,9 @@ class FunctionDeclaration final
 public:
     static bool must_have_name() { return true; }
 
-    FunctionDeclaration(SourceRange source_range, FlyString const& name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval)
+    FunctionDeclaration(SourceRange source_range, FlyString const& name, String source_text, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval)
         : Declaration(source_range)
-        , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false)
+        , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false)
     {
     }
 
@@ -562,9 +565,9 @@ class FunctionExpression final
 public:
     static bool must_have_name() { return false; }
 
-    FunctionExpression(SourceRange source_range, FlyString const& name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function = false)
+    FunctionExpression(SourceRange source_range, FlyString const& name, String source_text, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function = false)
         : Expression(source_range)
-        , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function)
+        , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function)
     {
     }
 
@@ -1232,9 +1235,10 @@ public:
 
 class ClassExpression final : public Expression {
 public:
-    ClassExpression(SourceRange source_range, String name, RefPtr<FunctionExpression> constructor, RefPtr<Expression> super_class, NonnullRefPtrVector<ClassElement> elements)
+    ClassExpression(SourceRange source_range, String name, String source_text, RefPtr<FunctionExpression> constructor, RefPtr<Expression> super_class, NonnullRefPtrVector<ClassElement> elements)
         : Expression(source_range)
         , m_name(move(name))
+        , m_source_text(move(source_text))
         , m_constructor(move(constructor))
         , m_super_class(move(super_class))
         , m_elements(move(elements))
@@ -1242,6 +1246,7 @@ public:
     }
 
     StringView name() const { return m_name; }
+    String const& source_text() const { return m_source_text; }
     RefPtr<FunctionExpression> constructor() const { return m_constructor; }
 
     virtual Completion execute(Interpreter&, GlobalObject&) const override;
@@ -1255,6 +1260,7 @@ private:
     virtual bool is_class_expression() const override { return true; }
 
     String m_name;
+    String m_source_text;
     RefPtr<FunctionExpression> m_constructor;
     RefPtr<Expression> m_super_class;
     NonnullRefPtrVector<ClassElement> m_elements;

+ 33 - 13
Userland/Libraries/LibJS/Parser.cpp

@@ -702,7 +702,11 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
     }
 
     save_state();
-    auto rule_start = push_start();
+    auto rule_start = (expect_parens && !is_async)
+        // Someone has consumed the opening parenthesis for us! Start there.
+        ? RulePosition { *this, m_rule_starts.last() }
+        // We've not encountered one yet, so the rule start is actually here.
+        : push_start();
 
     ArmedScopeGuard state_rollback_guard = [&] {
         load_state();
@@ -819,9 +823,12 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
         }
     }
 
+    auto function_start_offset = rule_start.position().offset;
+    auto function_end_offset = position().offset - m_state.current_token.trivia().length();
+    auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) };
     return create_ast_node<FunctionExpression>(
-        { m_state.current_token.filename(), rule_start.position(), position() }, "", move(body),
-        move(parameters), function_length, function_kind, body->in_strict_mode(),
+        { m_state.current_token.filename(), rule_start.position(), position() }, "", move(source_text),
+        move(body), move(parameters), function_length, function_kind, body->in_strict_mode(),
         /* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true);
 }
 
@@ -1066,6 +1073,8 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
             continue;
         }
 
+        auto function_start = position();
+
         if (match(TokenType::Async)) {
             auto lookahead_token = next_token();
             if (lookahead_token.type() != TokenType::Semicolon && lookahead_token.type() != TokenType::CurlyClose
@@ -1086,6 +1095,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
                 if (match(TokenType::Identifier)) {
                     consume();
                     is_static = true;
+                    function_start = position();
                     if (match(TokenType::Async)) {
                         consume();
                         is_async = true;
@@ -1242,7 +1252,7 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
                 parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
             if (is_async)
                 parse_options |= FunctionNodeParseOptions::IsAsyncFunction;
-            auto function = parse_function_node<FunctionExpression>(parse_options);
+            auto function = parse_function_node<FunctionExpression>(parse_options, function_start);
             if (is_constructor) {
                 constructor = move(function);
             } else if (!property_key.is_null()) {
@@ -1295,13 +1305,13 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
             constructor_body->append(create_ast_node<ReturnStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(super_call)));
 
             constructor = create_ast_node<FunctionExpression>(
-                { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
-                Vector { FunctionNode::Parameter { FlyString { "args" }, nullptr, true } }, 0, FunctionKind::Normal,
+                { m_state.current_token.filename(), rule_start.position(), position() }, class_name, "",
+                move(constructor_body), Vector { FunctionNode::Parameter { FlyString { "args" }, nullptr, true } }, 0, FunctionKind::Normal,
                 /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
         } else {
             constructor = create_ast_node<FunctionExpression>(
-                { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
-                Vector<FunctionNode::Parameter> {}, 0, FunctionKind::Normal,
+                { m_state.current_token.filename(), rule_start.position(), position() }, class_name, "",
+                move(constructor_body), Vector<FunctionNode::Parameter> {}, 0, FunctionKind::Normal,
                 /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
         }
     }
@@ -1316,7 +1326,11 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
             syntax_error(String::formatted("Reference to undeclared private field or method '{}'", private_name));
     }
 
-    return create_ast_node<ClassExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(class_name), move(constructor), move(super_class), move(elements));
+    auto function_start_offset = rule_start.position().offset;
+    auto function_end_offset = position().offset - m_state.current_token.trivia().length();
+    auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) };
+
+    return create_ast_node<ClassExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(class_name), move(source_text), move(constructor), move(super_class), move(elements));
 }
 
 Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
@@ -1640,6 +1654,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
         }
 
         auto type = m_state.current_token.type();
+        auto function_start = position();
 
         if (match(TokenType::Async)) {
             auto lookahead_token = next_token();
@@ -1701,7 +1716,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
                 parse_options |= FunctionNodeParseOptions::IsGeneratorFunction;
             if (function_kind == FunctionKind::Async || function_kind == FunctionKind::AsyncGenerator)
                 parse_options |= FunctionNodeParseOptions::IsAsyncFunction;
-            auto function = parse_function_node<FunctionExpression>(parse_options);
+            auto function = parse_function_node<FunctionExpression>(parse_options, function_start);
             properties.append(create_ast_node<ObjectProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, *property_name, function, property_type, true));
         } else if (match(TokenType::Colon)) {
             if (!property_name) {
@@ -2451,9 +2466,11 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
 }
 
 template<typename FunctionNodeType>
-NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
+NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options, Optional<Position> const& function_start)
 {
-    auto rule_start = push_start();
+    auto rule_start = function_start.has_value()
+        ? RulePosition { *this, *function_start }
+        : push_start();
     VERIFY(!(parse_options & FunctionNodeParseOptions::IsGetterFunction && parse_options & FunctionNodeParseOptions::IsSetterFunction));
 
     TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, !!(parse_options & FunctionNodeParseOptions::AllowSuperPropertyLookup));
@@ -2529,9 +2546,12 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
     if (has_strict_directive)
         check_identifier_name_for_assignment_validity(name, true);
 
+    auto function_start_offset = rule_start.position().offset;
+    auto function_end_offset = position().offset - m_state.current_token.trivia().length();
+    auto source_text = String { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) };
     return create_ast_node<FunctionNodeType>(
         { m_state.current_token.filename(), rule_start.position(), position() },
-        name, move(body), move(parameters), function_length,
+        name, move(source_text), move(body), move(parameters), function_length,
         function_kind, has_strict_directive, m_state.function_might_need_arguments_object,
         contains_direct_call_to_eval);
 }

+ 1 - 1
Userland/Libraries/LibJS/Parser.h

@@ -45,7 +45,7 @@ public:
     NonnullRefPtr<Program> parse_program(bool starts_in_strict_mode = false);
 
     template<typename FunctionNodeType>
-    NonnullRefPtr<FunctionNodeType> parse_function_node(u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName);
+    NonnullRefPtr<FunctionNodeType> parse_function_node(u8 parse_options = FunctionNodeParseOptions::CheckForFunctionAndName, Optional<Position> const& function_start = {});
     Vector<FunctionNode::Parameter> parse_formal_parameters(int& function_length, u8 parse_options = 0);
 
     enum class AllowDuplicates {