// Copyright 2017 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. #ifndef V8_AST_AST_SOURCE_RANGES_H_ #define V8_AST_AST_SOURCE_RANGES_H_ #include "src/ast/ast.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { // Specifies a range within the source code. {start} is 0-based and inclusive, // {end} is 0-based and exclusive. struct SourceRange { SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {} SourceRange(int start, int end) : start(start), end(end) {} bool IsEmpty() const { return start == kNoSourcePosition; } static SourceRange Empty() { return SourceRange(); } static SourceRange OpenEnded(int32_t start) { return SourceRange(start, kNoSourcePosition); } static SourceRange ContinuationOf(const SourceRange& that, int end = kNoSourcePosition) { return that.IsEmpty() ? Empty() : SourceRange(that.end, end); } int32_t start, end; }; // The list of ast node kinds that have associated source ranges. Note that this // macro is not undefined at the end of this file. #define AST_SOURCE_RANGE_LIST(V) \ V(BinaryOperation) \ V(Block) \ V(CaseClause) \ V(Conditional) \ V(IfStatement) \ V(IterationStatement) \ V(JumpStatement) \ V(NaryOperation) \ V(Suspend) \ V(SwitchStatement) \ V(Throw) \ V(TryCatchStatement) \ V(TryFinallyStatement) enum class SourceRangeKind { kBody, kCatch, kContinuation, kElse, kFinally, kRight, kThen, }; class AstNodeSourceRanges : public ZoneObject { public: virtual ~AstNodeSourceRanges() = default; virtual SourceRange GetRange(SourceRangeKind kind) = 0; virtual bool HasRange(SourceRangeKind kind) = 0; virtual void RemoveContinuationRange() { UNREACHABLE(); } }; class BinaryOperationSourceRanges final : public AstNodeSourceRanges { public: explicit BinaryOperationSourceRanges(const SourceRange& right_range) : right_range_(right_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); return right_range_; } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kRight; } private: SourceRange right_range_; }; class ContinuationSourceRanges : public AstNodeSourceRanges { public: explicit ContinuationSourceRanges(int32_t continuation_position) : continuation_position_(continuation_position) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); return SourceRange::OpenEnded(continuation_position_); } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kContinuation; } void RemoveContinuationRange() override { DCHECK(HasRange(SourceRangeKind::kContinuation)); continuation_position_ = kNoSourcePosition; } private: int32_t continuation_position_; }; class BlockSourceRanges final : public ContinuationSourceRanges { public: explicit BlockSourceRanges(int32_t continuation_position) : ContinuationSourceRanges(continuation_position) {} }; class CaseClauseSourceRanges final : public AstNodeSourceRanges { public: explicit CaseClauseSourceRanges(const SourceRange& body_range) : body_range_(body_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); return body_range_; } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kBody; } private: SourceRange body_range_; }; class ConditionalSourceRanges final : public AstNodeSourceRanges { public: explicit ConditionalSourceRanges(const SourceRange& then_range, const SourceRange& else_range) : then_range_(then_range), else_range_(else_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); switch (kind) { case SourceRangeKind::kThen: return then_range_; case SourceRangeKind::kElse: return else_range_; default: UNREACHABLE(); } } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kThen || kind == SourceRangeKind::kElse; } private: SourceRange then_range_; SourceRange else_range_; }; class IfStatementSourceRanges final : public AstNodeSourceRanges { public: explicit IfStatementSourceRanges(const SourceRange& then_range, const SourceRange& else_range) : then_range_(then_range), else_range_(else_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); switch (kind) { case SourceRangeKind::kElse: return else_range_; case SourceRangeKind::kThen: return then_range_; case SourceRangeKind::kContinuation: { if (!has_continuation_) return SourceRange::Empty(); const SourceRange& trailing_range = else_range_.IsEmpty() ? then_range_ : else_range_; return SourceRange::ContinuationOf(trailing_range); } default: UNREACHABLE(); } } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kThen || kind == SourceRangeKind::kElse || kind == SourceRangeKind::kContinuation; } void RemoveContinuationRange() override { DCHECK(HasRange(SourceRangeKind::kContinuation)); has_continuation_ = false; } private: SourceRange then_range_; SourceRange else_range_; bool has_continuation_ = true; }; class IterationStatementSourceRanges final : public AstNodeSourceRanges { public: explicit IterationStatementSourceRanges(const SourceRange& body_range) : body_range_(body_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); switch (kind) { case SourceRangeKind::kBody: return body_range_; case SourceRangeKind::kContinuation: if (!has_continuation_) return SourceRange::Empty(); return SourceRange::ContinuationOf(body_range_); default: UNREACHABLE(); } } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kBody || kind == SourceRangeKind::kContinuation; } void RemoveContinuationRange() override { DCHECK(HasRange(SourceRangeKind::kContinuation)); has_continuation_ = false; } private: SourceRange body_range_; bool has_continuation_ = true; }; class JumpStatementSourceRanges final : public ContinuationSourceRanges { public: explicit JumpStatementSourceRanges(int32_t continuation_position) : ContinuationSourceRanges(continuation_position) {} }; class NaryOperationSourceRanges final : public AstNodeSourceRanges { public: NaryOperationSourceRanges(Zone* zone, const SourceRange& range) : ranges_(zone) { AddRange(range); } SourceRange GetRangeAtIndex(size_t index) { DCHECK(index < ranges_.size()); return ranges_[index]; } void AddRange(const SourceRange& range) { ranges_.push_back(range); } size_t RangeCount() const { return ranges_.size(); } SourceRange GetRange(SourceRangeKind kind) override { UNREACHABLE(); } bool HasRange(SourceRangeKind kind) override { return false; } private: ZoneVector ranges_; }; class SuspendSourceRanges final : public ContinuationSourceRanges { public: explicit SuspendSourceRanges(int32_t continuation_position) : ContinuationSourceRanges(continuation_position) {} }; class SwitchStatementSourceRanges final : public ContinuationSourceRanges { public: explicit SwitchStatementSourceRanges(int32_t continuation_position) : ContinuationSourceRanges(continuation_position) {} }; class ThrowSourceRanges final : public ContinuationSourceRanges { public: explicit ThrowSourceRanges(int32_t continuation_position) : ContinuationSourceRanges(continuation_position) {} }; class TryCatchStatementSourceRanges final : public AstNodeSourceRanges { public: explicit TryCatchStatementSourceRanges(const SourceRange& catch_range) : catch_range_(catch_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); switch (kind) { case SourceRangeKind::kCatch: return catch_range_; case SourceRangeKind::kContinuation: if (!has_continuation_) return SourceRange::Empty(); return SourceRange::ContinuationOf(catch_range_); default: UNREACHABLE(); } } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kCatch || kind == SourceRangeKind::kContinuation; } void RemoveContinuationRange() override { DCHECK(HasRange(SourceRangeKind::kContinuation)); has_continuation_ = false; } private: SourceRange catch_range_; bool has_continuation_ = true; }; class TryFinallyStatementSourceRanges final : public AstNodeSourceRanges { public: explicit TryFinallyStatementSourceRanges(const SourceRange& finally_range) : finally_range_(finally_range) {} SourceRange GetRange(SourceRangeKind kind) override { DCHECK(HasRange(kind)); switch (kind) { case SourceRangeKind::kFinally: return finally_range_; case SourceRangeKind::kContinuation: if (!has_continuation_) return SourceRange::Empty(); return SourceRange::ContinuationOf(finally_range_); default: UNREACHABLE(); } } bool HasRange(SourceRangeKind kind) override { return kind == SourceRangeKind::kFinally || kind == SourceRangeKind::kContinuation; } void RemoveContinuationRange() override { DCHECK(HasRange(SourceRangeKind::kContinuation)); has_continuation_ = false; } private: SourceRange finally_range_; bool has_continuation_ = true; }; // Maps ast node pointers to associated source ranges. The parser creates these // mappings and the bytecode generator consumes them. class SourceRangeMap final : public ZoneObject { public: explicit SourceRangeMap(Zone* zone) : map_(zone) {} AstNodeSourceRanges* Find(ZoneObject* node) { auto it = map_.find(node); if (it == map_.end()) return nullptr; return it->second; } // Type-checked insertion. #define DEFINE_MAP_INSERT(type) \ void Insert(type* node, type##SourceRanges* ranges) { \ DCHECK_NOT_NULL(node); \ map_.emplace(node, ranges); \ } AST_SOURCE_RANGE_LIST(DEFINE_MAP_INSERT) #undef DEFINE_MAP_INSERT private: ZoneMap map_; }; } // namespace internal } // namespace v8 #endif // V8_AST_AST_SOURCE_RANGES_H_