1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/ast/source-range-ast-visitor.h"
6
7 #include "src/ast/ast-source-ranges.h"
8
9 namespace v8 {
10 namespace internal {
11
SourceRangeAstVisitor(uintptr_t stack_limit,Expression * root,SourceRangeMap * source_range_map)12 SourceRangeAstVisitor::SourceRangeAstVisitor(uintptr_t stack_limit,
13 Expression* root,
14 SourceRangeMap* source_range_map)
15 : AstTraversalVisitor(stack_limit, root),
16 source_range_map_(source_range_map) {}
17
VisitBlock(Block * stmt)18 void SourceRangeAstVisitor::VisitBlock(Block* stmt) {
19 AstTraversalVisitor::VisitBlock(stmt);
20 ZonePtrList<Statement>* stmts = stmt->statements();
21 AstNodeSourceRanges* enclosingSourceRanges = source_range_map_->Find(stmt);
22 if (enclosingSourceRanges != nullptr) {
23 CHECK(enclosingSourceRanges->HasRange(SourceRangeKind::kContinuation));
24 MaybeRemoveLastContinuationRange(stmts);
25 }
26 }
27
VisitSwitchStatement(SwitchStatement * stmt)28 void SourceRangeAstVisitor::VisitSwitchStatement(SwitchStatement* stmt) {
29 AstTraversalVisitor::VisitSwitchStatement(stmt);
30 ZonePtrList<CaseClause>* clauses = stmt->cases();
31 for (CaseClause* clause : *clauses) {
32 MaybeRemoveLastContinuationRange(clause->statements());
33 }
34 }
35
VisitFunctionLiteral(FunctionLiteral * expr)36 void SourceRangeAstVisitor::VisitFunctionLiteral(FunctionLiteral* expr) {
37 AstTraversalVisitor::VisitFunctionLiteral(expr);
38 ZonePtrList<Statement>* stmts = expr->body();
39 MaybeRemoveLastContinuationRange(stmts);
40 }
41
VisitTryCatchStatement(TryCatchStatement * stmt)42 void SourceRangeAstVisitor::VisitTryCatchStatement(TryCatchStatement* stmt) {
43 AstTraversalVisitor::VisitTryCatchStatement(stmt);
44 MaybeRemoveContinuationRange(stmt->try_block());
45 MaybeRemoveContinuationRangeOfAsyncReturn(stmt);
46 }
47
VisitTryFinallyStatement(TryFinallyStatement * stmt)48 void SourceRangeAstVisitor::VisitTryFinallyStatement(
49 TryFinallyStatement* stmt) {
50 AstTraversalVisitor::VisitTryFinallyStatement(stmt);
51 MaybeRemoveContinuationRange(stmt->try_block());
52 }
53
VisitNode(AstNode * node)54 bool SourceRangeAstVisitor::VisitNode(AstNode* node) {
55 AstNodeSourceRanges* range = source_range_map_->Find(node);
56
57 if (range == nullptr) return true;
58 if (!range->HasRange(SourceRangeKind::kContinuation)) return true;
59
60 // Called in pre-order. In case of conflicting continuation ranges, only the
61 // outermost range may survive.
62
63 SourceRange continuation = range->GetRange(SourceRangeKind::kContinuation);
64 if (continuation_positions_.find(continuation.start) !=
65 continuation_positions_.end()) {
66 range->RemoveContinuationRange();
67 } else {
68 continuation_positions_.emplace(continuation.start);
69 }
70
71 return true;
72 }
73
MaybeRemoveContinuationRange(Statement * last_statement)74 void SourceRangeAstVisitor::MaybeRemoveContinuationRange(
75 Statement* last_statement) {
76 AstNodeSourceRanges* last_range = nullptr;
77
78 if (last_statement->IsExpressionStatement() &&
79 last_statement->AsExpressionStatement()->expression()->IsThrow()) {
80 // For ThrowStatement, source range is tied to Throw expression not
81 // ExpressionStatement.
82 last_range = source_range_map_->Find(
83 last_statement->AsExpressionStatement()->expression());
84 } else {
85 last_range = source_range_map_->Find(last_statement);
86 }
87
88 if (last_range == nullptr) return;
89
90 if (last_range->HasRange(SourceRangeKind::kContinuation)) {
91 last_range->RemoveContinuationRange();
92 }
93 }
94
MaybeRemoveLastContinuationRange(ZonePtrList<Statement> * statements)95 void SourceRangeAstVisitor::MaybeRemoveLastContinuationRange(
96 ZonePtrList<Statement>* statements) {
97 if (statements->is_empty()) return;
98 MaybeRemoveContinuationRange(statements->last());
99 }
100
101 namespace {
FindLastNonSyntheticStatement(ZonePtrList<Statement> * statements)102 Statement* FindLastNonSyntheticStatement(ZonePtrList<Statement>* statements) {
103 for (int i = statements->length() - 1; i >= 0; --i) {
104 Statement* stmt = statements->at(i);
105 if (stmt->IsReturnStatement() &&
106 stmt->AsReturnStatement()->is_synthetic_async_return()) {
107 continue;
108 }
109 return stmt;
110 }
111 return nullptr;
112 }
113 } // namespace
114
MaybeRemoveContinuationRangeOfAsyncReturn(TryCatchStatement * try_catch_stmt)115 void SourceRangeAstVisitor::MaybeRemoveContinuationRangeOfAsyncReturn(
116 TryCatchStatement* try_catch_stmt) {
117 // Detect try-catch inserted by NewTryCatchStatementForAsyncAwait in the
118 // parser (issued for async functions, including async generators), and
119 // remove the continuation range of the last statement, such that the
120 // range of the enclosing function body is used.
121 if (try_catch_stmt->is_try_catch_for_async()) {
122 Statement* last_non_synthetic =
123 FindLastNonSyntheticStatement(try_catch_stmt->try_block()->statements());
124 if (last_non_synthetic) {
125 MaybeRemoveContinuationRange(last_non_synthetic);
126 }
127 }
128 }
129
130 } // namespace internal
131 } // namespace v8
132