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
9namespace v8 {
10namespace internal {
11
12SourceRangeAstVisitor::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
18void 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
28void 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
36void SourceRangeAstVisitor::VisitFunctionLiteral(FunctionLiteral* expr) {
37  AstTraversalVisitor::VisitFunctionLiteral(expr);
38  ZonePtrList<Statement>* stmts = expr->body();
39  MaybeRemoveLastContinuationRange(stmts);
40}
41
42void SourceRangeAstVisitor::VisitTryCatchStatement(TryCatchStatement* stmt) {
43  AstTraversalVisitor::VisitTryCatchStatement(stmt);
44  MaybeRemoveContinuationRange(stmt->try_block());
45  MaybeRemoveContinuationRangeOfAsyncReturn(stmt);
46}
47
48void SourceRangeAstVisitor::VisitTryFinallyStatement(
49    TryFinallyStatement* stmt) {
50  AstTraversalVisitor::VisitTryFinallyStatement(stmt);
51  MaybeRemoveContinuationRange(stmt->try_block());
52}
53
54bool 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
74void 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
95void SourceRangeAstVisitor::MaybeRemoveLastContinuationRange(
96    ZonePtrList<Statement>* statements) {
97  if (statements->is_empty()) return;
98  MaybeRemoveContinuationRange(statements->last());
99}
100
101namespace {
102Statement* 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
115void 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