| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/ast/source-range-ast-visitor.h" |
| |
| #include "src/ast/ast-source-ranges.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| SourceRangeAstVisitor::SourceRangeAstVisitor(uintptr_t stack_limit, |
| Expression* root, |
| SourceRangeMap* source_range_map) |
| : AstTraversalVisitor(stack_limit, root), |
| source_range_map_(source_range_map) {} |
| |
| void SourceRangeAstVisitor::VisitBlock(Block* stmt) { |
| AstTraversalVisitor::VisitBlock(stmt); |
| ZonePtrList<Statement>* stmts = stmt->statements(); |
| AstNodeSourceRanges* enclosingSourceRanges = source_range_map_->Find(stmt); |
| if (enclosingSourceRanges != nullptr) { |
| CHECK(enclosingSourceRanges->HasRange(SourceRangeKind::kContinuation)); |
| MaybeRemoveLastContinuationRange(stmts); |
| } |
| } |
| |
| void SourceRangeAstVisitor::VisitSwitchStatement(SwitchStatement* stmt) { |
| AstTraversalVisitor::VisitSwitchStatement(stmt); |
| ZonePtrList<CaseClause>* clauses = stmt->cases(); |
| for (CaseClause* clause : *clauses) { |
| MaybeRemoveLastContinuationRange(clause->statements()); |
| } |
| } |
| |
| void SourceRangeAstVisitor::VisitFunctionLiteral(FunctionLiteral* expr) { |
| AstTraversalVisitor::VisitFunctionLiteral(expr); |
| ZonePtrList<Statement>* stmts = expr->body(); |
| MaybeRemoveLastContinuationRange(stmts); |
| } |
| |
| void SourceRangeAstVisitor::VisitTryCatchStatement(TryCatchStatement* stmt) { |
| AstTraversalVisitor::VisitTryCatchStatement(stmt); |
| MaybeRemoveContinuationRange(stmt->try_block()); |
| MaybeRemoveContinuationRangeOfAsyncReturn(stmt); |
| } |
| |
| void SourceRangeAstVisitor::VisitTryFinallyStatement( |
| TryFinallyStatement* stmt) { |
| AstTraversalVisitor::VisitTryFinallyStatement(stmt); |
| MaybeRemoveContinuationRange(stmt->try_block()); |
| } |
| |
| bool SourceRangeAstVisitor::VisitNode(AstNode* node) { |
| AstNodeSourceRanges* range = source_range_map_->Find(node); |
| |
| if (range == nullptr) return true; |
| if (!range->HasRange(SourceRangeKind::kContinuation)) return true; |
| |
| // Called in pre-order. In case of conflicting continuation ranges, only the |
| // outermost range may survive. |
| |
| SourceRange continuation = range->GetRange(SourceRangeKind::kContinuation); |
| if (continuation_positions_.find(continuation.start) != |
| continuation_positions_.end()) { |
| range->RemoveContinuationRange(); |
| } else { |
| continuation_positions_.emplace(continuation.start); |
| } |
| |
| return true; |
| } |
| |
| void SourceRangeAstVisitor::MaybeRemoveContinuationRange( |
| Statement* last_statement) { |
| AstNodeSourceRanges* last_range = nullptr; |
| |
| if (last_statement->IsExpressionStatement() && |
| last_statement->AsExpressionStatement()->expression()->IsThrow()) { |
| // For ThrowStatement, source range is tied to Throw expression not |
| // ExpressionStatement. |
| last_range = source_range_map_->Find( |
| last_statement->AsExpressionStatement()->expression()); |
| } else { |
| last_range = source_range_map_->Find(last_statement); |
| } |
| |
| if (last_range == nullptr) return; |
| |
| if (last_range->HasRange(SourceRangeKind::kContinuation)) { |
| last_range->RemoveContinuationRange(); |
| } |
| } |
| |
| void SourceRangeAstVisitor::MaybeRemoveLastContinuationRange( |
| ZonePtrList<Statement>* statements) { |
| if (statements->is_empty()) return; |
| MaybeRemoveContinuationRange(statements->last()); |
| } |
| |
| namespace { |
| Statement* FindLastNonSyntheticStatement(ZonePtrList<Statement>* statements) { |
| for (int i = statements->length() - 1; i >= 0; --i) { |
| Statement* stmt = statements->at(i); |
| if (stmt->IsReturnStatement() && |
| stmt->AsReturnStatement()->is_synthetic_async_return()) { |
| continue; |
| } |
| return stmt; |
| } |
| return nullptr; |
| } |
| } // namespace |
| |
| void SourceRangeAstVisitor::MaybeRemoveContinuationRangeOfAsyncReturn( |
| TryCatchStatement* try_catch_stmt) { |
| // Detect try-catch inserted by NewTryCatchStatementForAsyncAwait in the |
| // parser (issued for async functions, including async generators), and |
| // remove the continuation range of the last statement, such that the |
| // range of the enclosing function body is used. |
| if (try_catch_stmt->is_try_catch_for_async()) { |
| Statement* last_non_synthetic = |
| FindLastNonSyntheticStatement(try_catch_stmt->try_block()->statements()); |
| if (last_non_synthetic) { |
| MaybeRemoveContinuationRange(last_non_synthetic); |
| } |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |