| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_VM_KERNEL_BINARY_H_ | |
| #define RUNTIME_VM_KERNEL_BINARY_H_ | |
| #if !defined(DART_PRECOMPILED_RUNTIME) | |
| #include "platform/unaligned.h" | |
| #include "vm/kernel.h" | |
| #include "vm/object.h" | |
| namespace dart { | |
| namespace kernel { | |
| // Keep in sync with package:kernel/lib/binary/tag.dart, | |
| // package:kernel/binary.md. | |
| static const uint32_t kMagicProgramFile = 0x90ABCDEFu; | |
| static const uint32_t kSupportedKernelFormatVersion = 141; | |
| // Keep in sync with package:kernel/lib/binary/tag.dart | |
| #define KERNEL_TAG_LIST(V) \ | |
| V(Nothing, 0) \ | |
| V(Something, 1) \ | |
| V(Class, 2) \ | |
| V(Extension, 115) \ | |
| V(ExtensionTypeDeclaration, 85) \ | |
| V(FunctionNode, 3) \ | |
| V(Field, 4) \ | |
| V(Constructor, 5) \ | |
| V(Procedure, 6) \ | |
| V(InvalidInitializer, 7) \ | |
| V(FieldInitializer, 8) \ | |
| V(SuperInitializer, 9) \ | |
| V(RedirectingInitializer, 10) \ | |
| V(LocalInitializer, 11) \ | |
| V(AssertInitializer, 12) \ | |
| V(CheckLibraryIsLoaded, 13) \ | |
| V(LoadLibrary, 14) \ | |
| V(EqualsNull, 15) \ | |
| V(EqualsCall, 16) \ | |
| V(StaticTearOff, 17) \ | |
| V(ConstStaticInvocation, 18) \ | |
| V(InvalidExpression, 19) \ | |
| V(VariableGet, 20) \ | |
| V(VariableSet, 21) \ | |
| V(AbstractSuperPropertyGet, 22) \ | |
| V(AbstractSuperPropertySet, 23) \ | |
| V(SuperPropertyGet, 24) \ | |
| V(SuperPropertySet, 25) \ | |
| V(StaticGet, 26) \ | |
| V(StaticSet, 27) \ | |
| V(AbstractSuperMethodInvocation, 28) \ | |
| V(SuperMethodInvocation, 29) \ | |
| V(StaticInvocation, 30) \ | |
| V(ConstructorInvocation, 31) \ | |
| V(ConstConstructorInvocation, 32) \ | |
| V(Not, 33) \ | |
| V(NullCheck, 117) \ | |
| V(LogicalExpression, 34) \ | |
| V(ConditionalExpression, 35) \ | |
| V(StringConcatenation, 36) \ | |
| V(ListConcatenation, 111) \ | |
| V(SetConcatenation, 112) \ | |
| V(MapConcatenation, 113) \ | |
| V(InstanceCreation, 114) \ | |
| V(FileUriExpression, 116) \ | |
| V(IsExpression, 37) \ | |
| V(AsExpression, 38) \ | |
| V(StringLiteral, 39) \ | |
| V(DoubleLiteral, 40) \ | |
| V(TrueLiteral, 41) \ | |
| V(FalseLiteral, 42) \ | |
| V(NullLiteral, 43) \ | |
| V(SymbolLiteral, 44) \ | |
| V(TypeLiteral, 45) \ | |
| V(ThisExpression, 46) \ | |
| V(Rethrow, 47) \ | |
| V(Throw, 48) \ | |
| V(ListLiteral, 49) \ | |
| V(SetLiteral, 109) \ | |
| V(MapLiteral, 50) \ | |
| V(AwaitExpression, 51) \ | |
| V(FunctionExpression, 52) \ | |
| V(Let, 53) \ | |
| V(BlockExpression, 82) \ | |
| V(Instantiation, 54) \ | |
| V(PositiveIntLiteral, 55) \ | |
| V(NegativeIntLiteral, 56) \ | |
| V(BigIntLiteral, 57) \ | |
| V(ConstListLiteral, 58) \ | |
| V(ConstSetLiteral, 110) \ | |
| V(ConstMapLiteral, 59) \ | |
| V(ConstructorTearOff, 60) \ | |
| V(TypedefTearOff, 83) \ | |
| V(RedirectingFactoryTearOff, 84) \ | |
| V(RecordIndexGet, 101) \ | |
| V(RecordNameGet, 102) \ | |
| V(RecordLiteral, 104) \ | |
| V(ConstRecordLiteral, 105) \ | |
| V(ExpressionStatement, 61) \ | |
| V(Block, 62) \ | |
| V(EmptyStatement, 63) \ | |
| V(AssertStatement, 64) \ | |
| V(LabeledStatement, 65) \ | |
| V(BreakStatement, 66) \ | |
| V(WhileStatement, 67) \ | |
| V(DoStatement, 68) \ | |
| V(ForStatement, 69) \ | |
| V(ForInStatement, 70) \ | |
| V(SwitchStatement, 71) \ | |
| V(ContinueSwitchStatement, 72) \ | |
| V(IfStatement, 73) \ | |
| V(ReturnStatement, 74) \ | |
| V(TryCatch, 75) \ | |
| V(TryFinally, 76) \ | |
| V(YieldStatement, 77) \ | |
| V(VariableStatement, 78) \ | |
| V(FunctionDeclaration, 79) \ | |
| V(AsyncForInStatement, 80) \ | |
| V(AssertBlock, 81) \ | |
| V(TypedefType, 87) \ | |
| V(InvalidType, 90) \ | |
| V(DynamicType, 91) \ | |
| V(VoidType, 92) \ | |
| V(InterfaceType, 93) \ | |
| V(FunctionType, 94) \ | |
| V(TypeParameterType, 95) \ | |
| V(SimpleInterfaceType, 96) \ | |
| V(SimpleFunctionType, 97) \ | |
| V(NeverType, 98) \ | |
| V(IntersectionType, 99) \ | |
| V(RecordType, 100) \ | |
| V(ExtensionType, 103) \ | |
| V(ConstantExpression, 106) \ | |
| V(FutureOrType, 107) \ | |
| V(FileUriConstantExpression, 108) \ | |
| V(InstanceGet, 118) \ | |
| V(InstanceSet, 119) \ | |
| V(InstanceInvocation, 120) \ | |
| V(InstanceGetterInvocation, 89) \ | |
| V(InstanceTearOff, 121) \ | |
| V(DynamicGet, 122) \ | |
| V(DynamicSet, 123) \ | |
| V(DynamicInvocation, 124) \ | |
| V(FunctionInvocation, 125) \ | |
| V(FunctionTearOff, 126) \ | |
| V(LocalFunctionInvocation, 127) \ | |
| V(AndPattern, 128) \ | |
| V(AssignedVariablePattern, 129) \ | |
| V(CastPattern, 130) \ | |
| V(ConstantPattern, 131) \ | |
| V(InvalidPattern, 132) \ | |
| V(ListPattern, 133) \ | |
| V(MapPattern, 134) \ | |
| V(NamedPattern, 135) \ | |
| V(NullAssertPattern, 136) \ | |
| V(NullCheckPattern, 137) \ | |
| V(ObjectPattern, 138) \ | |
| V(OrPattern, 139) \ | |
| V(RecordPattern, 140) \ | |
| V(RelationalPattern, 141) \ | |
| V(RestPattern, 142) \ | |
| V(VariablePattern, 143) \ | |
| V(WildcardPattern, 144) \ | |
| V(MapPatternEntry, 145) \ | |
| V(MapPatternRestEntry, 146) \ | |
| V(PatternSwitchStatement, 147) \ | |
| V(SwitchExpression, 148) \ | |
| V(IfCaseStatement, 149) \ | |
| V(PatternAssignment, 150) \ | |
| V(PatternVariableDeclaration, 151) \ | |
| V(NullType, 152) \ | |
| V(RedirectingFactoryInvocation, 153) \ | |
| V(VariableDeclaration, 154) \ | |
| V(LocalVariable, 155) \ | |
| V(LateVariable, 156) \ | |
| V(SyntheticVariable, 157) \ | |
| V(CatchVariable, 158) \ | |
| V(PositionalParameter, 159) \ | |
| V(NamedParameter, 160) \ | |
| V(ThisVariable, 161) \ | |
| V(LocalFunctionVariable, 162) \ | |
| V(ConstVariable, 163) \ | |
| V(SpecializedVariableGet, 224) \ | |
| V(SpecializedVariableSet, 232) \ | |
| V(SpecializedIntLiteral, 240) | |
| static constexpr intptr_t kSpecializedTagHighBits = 0xe0; | |
| static constexpr intptr_t kSpecializedTagMask = 0xf8; | |
| static constexpr intptr_t kSpecializedPayloadMask = 0x7; | |
| enum Tag { | |
| #define DECLARE(Name, value) k##Name = value, | |
| KERNEL_TAG_LIST(DECLARE) | |
| #undef DECLARE | |
| }; | |
| // Keep in sync with package:kernel/lib/binary/tag.dart | |
| enum ConstantTag { | |
| kNullConstant = 0, | |
| kBoolConstant = 1, | |
| kIntConstant = 2, | |
| kDoubleConstant = 3, | |
| kStringConstant = 4, | |
| kSymbolConstant = 5, | |
| kMapConstant = 6, | |
| kListConstant = 7, | |
| kSetConstant = 13, | |
| kInstanceConstant = 8, | |
| kInstantiationConstant = 9, | |
| kStaticTearOffConstant = 10, | |
| kTypeLiteralConstant = 11, | |
| // These constants are not expected to be seen by the VM, because all | |
| // constants are fully evaluated. | |
| kUnevaluatedConstant = 12, | |
| kTypedefTearOffConstant = 14, | |
| kConstructorTearOffConstant = 15, | |
| kRedirectingFactoryTearOffConstant = 16, | |
| kRecordConstant = 17, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum class KernelNullability : int8_t { | |
| kUndetermined = 0, | |
| kNullable = 1, | |
| kNonNullable = 2, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum Variance { | |
| kUnrelated = 0, | |
| kCovariant = 1, | |
| kContravariant = 2, | |
| kInvariant = 3, | |
| kLegacyCovariant = 4, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum AsExpressionFlags { | |
| kAsExpressionFlagTypeError = 1 << 0, | |
| kAsExpressionFlagCovarianceCheck = 1 << 1, | |
| kAsExpressionFlagForDynamic = 1 << 2, | |
| kAsExpressionFlagUnchecked = 1 << 3, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum InstanceInvocationFlags { | |
| kInstanceInvocationFlagInvariant = 1 << 0, | |
| kInstanceInvocationFlagBoundsSafe = 1 << 1, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum DynamicInvocationFlags { | |
| kDynamicInvocationFlagImplicitCall = 1 << 0, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum ThrowFlags { | |
| kThrowForErrorHandling = 1 << 0, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum YieldStatementFlags { | |
| kYieldStatementFlagYieldStar = 1 << 0, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum class NamedTypeFlags : uint8_t { | |
| kIsRequired = 1 << 0, | |
| }; | |
| // Keep in sync with package:kernel/lib/ast.dart | |
| enum class FunctionAccessKind { | |
| kFunction, | |
| kFunctionType, | |
| kInapplicable, | |
| kNullable, | |
| }; | |
| static constexpr int SpecializedIntLiteralBias = 3; | |
| static constexpr int KernelFormatVersionOffset = 4; | |
| // These should be kept in sync with the constants in kernels tag.dart. | |
| static constexpr int KernelFixedFieldsBeforeLibraries = 9; | |
| static constexpr int KernelFixedFieldsAfterLibraries = 2; | |
| static inline int KernelNumberOfFixedFields(int numberOfLibraries) { | |
| return KernelFixedFieldsBeforeLibraries + numberOfLibraries + 1 + | |
| KernelFixedFieldsAfterLibraries; | |
| } | |
| static constexpr int HeaderSize = 8; // 'magic', 'formatVersion'. | |
| class Reader : public ValueObject { | |
| public: | |
| explicit Reader(const TypedDataBase& typed_data) | |
| : thread_(Thread::Current()), typed_data_(&typed_data) { | |
| Init(); | |
| } | |
| uint32_t ReadFromIndex(intptr_t end_offset, | |
| intptr_t fields_before, | |
| intptr_t list_size, | |
| intptr_t list_index) { | |
| intptr_t org_offset = offset(); | |
| uint32_t result = | |
| ReadFromIndexNoReset(end_offset, fields_before, list_size, list_index); | |
| offset_ = org_offset; | |
| return result; | |
| } | |
| uint32_t ReadUInt32At(intptr_t offset) const { | |
| ASSERT((size_ >= 4) && (offset >= 0) && (offset <= size_ - 4)); | |
| uint32_t value = | |
| LoadUnaligned(reinterpret_cast<const uint32_t*>(raw_buffer_ + offset)); | |
| return Utils::BigEndianToHost32(value); | |
| } | |
| uint32_t ReadFromIndexNoReset(intptr_t end_offset, | |
| intptr_t fields_before, | |
| intptr_t list_size, | |
| intptr_t list_index) { | |
| offset_ = end_offset - (fields_before + list_size - list_index) * 4; | |
| return ReadUInt32(); | |
| } | |
| uint32_t ReadSingleFieldFromIndexNoReset(intptr_t end_offset, | |
| intptr_t fields_before) { | |
| offset_ = end_offset - fields_before * 4; | |
| return ReadUInt32(); | |
| } | |
| uint32_t ReadUInt32() { | |
| uint32_t value = ReadUInt32At(offset_); | |
| offset_ += 4; | |
| return value; | |
| } | |
| double ReadDouble() { | |
| ASSERT((size_ >= 8) && (offset_ >= 0) && (offset_ <= size_ - 8)); | |
| double value = | |
| LoadUnaligned(reinterpret_cast<const double*>(&raw_buffer_[offset_])); | |
| offset_ += 8; | |
| return value; | |
| } | |
| uint32_t ReadUInt() { | |
| ASSERT((size_ >= 1) && (offset_ >= 0) && (offset_ <= size_ - 1)); | |
| const uint8_t* buffer = raw_buffer_; | |
| uword byte0 = buffer[offset_]; | |
| if ((byte0 & 0x80) == 0) { | |
| // 0... | |
| offset_++; | |
| return byte0; | |
| } else if ((byte0 & 0xc0) == 0x80) { | |
| // 10... | |
| ASSERT((size_ >= 2) && (offset_ >= 0) && (offset_ <= size_ - 2)); | |
| uint32_t value = | |
| ((byte0 & ~static_cast<uword>(0x80)) << 8) | (buffer[offset_ + 1]); | |
| offset_ += 2; | |
| return value; | |
| } else { | |
| // 11... | |
| ASSERT((size_ >= 4) && (offset_ >= 0) && (offset_ <= size_ - 4)); | |
| uint32_t value = ((byte0 & ~static_cast<uword>(0xc0)) << 24) | | |
| (buffer[offset_ + 1] << 16) | | |
| (buffer[offset_ + 2] << 8) | (buffer[offset_ + 3] << 0); | |
| offset_ += 4; | |
| return value; | |
| } | |
| } | |
| intptr_t ReadSLEB128() { | |
| ReadStream stream(raw_buffer_, size_, offset_); | |
| const intptr_t result = stream.ReadSLEB128(); | |
| offset_ = stream.Position(); | |
| return result; | |
| } | |
| int64_t ReadSLEB128AsInt64() { | |
| ReadStream stream(raw_buffer_, size_, offset_); | |
| const int64_t result = stream.ReadSLEB128<int64_t>(); | |
| offset_ = stream.Position(); | |
| return result; | |
| } | |
| /** | |
| * Read and return a TokenPosition from this reader. | |
| */ | |
| TokenPosition ReadPosition() { | |
| // Position is saved as unsigned, | |
| // but actually ranges from -1 and up (thus the -1) | |
| intptr_t value = ReadUInt() - 1; | |
| TokenPosition result = TokenPosition::Deserialize(value); | |
| max_position_ = TokenPosition::Max(max_position_, result); | |
| min_position_ = TokenPosition::Min(min_position_, result); | |
| return result; | |
| } | |
| intptr_t ReadListLength() { return ReadUInt(); } | |
| uint8_t ReadByte() { return raw_buffer_[offset_++]; } | |
| uint8_t PeekByte() { return raw_buffer_[offset_]; } | |
| void ReadBytes(uint8_t* buffer, uint8_t size) { | |
| for (int i = 0; i < size; i++) { | |
| buffer[i] = ReadByte(); | |
| } | |
| } | |
| bool ReadBool() { return (ReadByte() & 1) == 1; } | |
| uint8_t ReadFlags() { return ReadByte(); } | |
| static const char* TagName(Tag tag); | |
| Tag ReadTag(uint8_t* payload = nullptr) { | |
| uint8_t byte = ReadByte(); | |
| bool has_payload = | |
| (byte & kSpecializedTagHighBits) == kSpecializedTagHighBits; | |
| if (has_payload) { | |
| if (payload != nullptr) { | |
| *payload = byte & kSpecializedPayloadMask; | |
| } | |
| return static_cast<Tag>(byte & kSpecializedTagMask); | |
| } else { | |
| return static_cast<Tag>(byte); | |
| } | |
| } | |
| Tag PeekTag(uint8_t* payload = nullptr) { | |
| uint8_t byte = PeekByte(); | |
| bool has_payload = | |
| (byte & kSpecializedTagHighBits) == kSpecializedTagHighBits; | |
| if (has_payload) { | |
| if (payload != nullptr) { | |
| *payload = byte & kSpecializedPayloadMask; | |
| } | |
| return static_cast<Tag>(byte & kSpecializedTagMask); | |
| } else { | |
| return static_cast<Tag>(byte); | |
| } | |
| } | |
| static Nullability ConvertNullability(KernelNullability kernel_nullability) { | |
| switch (kernel_nullability) { | |
| case KernelNullability::kNullable: | |
| return Nullability::kNullable; | |
| case KernelNullability::kNonNullable: | |
| case KernelNullability::kUndetermined: | |
| return Nullability::kNonNullable; | |
| } | |
| UNREACHABLE(); | |
| } | |
| Nullability ReadNullability() { | |
| const uint8_t byte = ReadByte(); | |
| return ConvertNullability(static_cast<KernelNullability>(byte)); | |
| } | |
| Variance ReadVariance() { | |
| uint8_t byte = ReadByte(); | |
| return static_cast<Variance>(byte); | |
| } | |
| void EnsureEnd() { | |
| if (offset_ != size_) { | |
| FATAL( | |
| "Reading Kernel file: Expected to be at EOF " | |
| "(offset: %" Pd ", size: %" Pd ")", | |
| offset_, size_); | |
| } | |
| } | |
| // The largest position read yet (since last reset). | |
| // This is automatically updated when calling ReadPosition, | |
| // but can be overwritten (e.g. via the PositionScope class). | |
| TokenPosition max_position() { return max_position_; } | |
| // The smallest position read yet (since last reset). | |
| // This is automatically updated when calling ReadPosition, | |
| // but can be overwritten (e.g. via the PositionScope class). | |
| TokenPosition min_position() { return min_position_; } | |
| // A canonical name reference of -1 indicates none (for optional names), not | |
| // the root name as in the canonical name table. | |
| NameIndex ReadCanonicalNameReference() { return NameIndex(ReadUInt() - 1); } | |
| const TypedDataBase* typed_data() { return typed_data_; } | |
| intptr_t offset() const { return offset_; } | |
| void set_offset(intptr_t offset) { | |
| ASSERT(offset < size_); | |
| offset_ = offset; | |
| } | |
| intptr_t size() const { return size_; } | |
| TypedDataViewPtr ViewFromTo(intptr_t start, intptr_t end) { | |
| return typed_data_->ViewFromTo(start, end, Heap::kOld); | |
| } | |
| const uint8_t* BufferAt(intptr_t offset) { | |
| ASSERT((offset >= 0) && (offset < size_)); | |
| return &raw_buffer_[offset]; | |
| } | |
| TypedDataPtr ReadLineStartsData(intptr_t line_start_count); | |
| private: | |
| friend class Program; | |
| friend class AlternativeReadingScopeWithNewData; | |
| friend class AlternativeReadingScope; | |
| Reader(const uint8_t* buffer, intptr_t size) | |
| : thread_(nullptr), raw_buffer_(buffer), size_(size) {} | |
| void Init() { | |
| if (typed_data_->IsNull()) { | |
| raw_buffer_ = nullptr; | |
| size_ = 0; | |
| } else { | |
| ASSERT(typed_data_->IsExternalOrExternalView()); | |
| raw_buffer_ = reinterpret_cast<uint8_t*>(typed_data_->DataAddr(0)); | |
| size_ = typed_data_->LengthInBytes(); | |
| } | |
| offset_ = 0; | |
| } | |
| Thread* thread_ = nullptr; | |
| // A external typed data or a view on an external typed data. | |
| const TypedDataBase* typed_data_ = nullptr; | |
| // The raw data size/length of [typed_data_]. | |
| const uint8_t* raw_buffer_ = nullptr; | |
| intptr_t size_ = 0; | |
| intptr_t offset_ = 0; | |
| TokenPosition max_position_ = TokenPosition::kNoSource; | |
| TokenPosition min_position_ = TokenPosition::kNoSource; | |
| intptr_t current_script_id_ = -1; | |
| friend class PositionScope; | |
| friend class Program; | |
| }; | |
| // A helper class that saves the current reader position, goes to another reader | |
| // position, and upon destruction, resets to the original reader position. | |
| class AlternativeReadingScope { | |
| public: | |
| AlternativeReadingScope(Reader* reader, intptr_t new_position) | |
| : reader_(reader), saved_offset_(reader_->offset_) { | |
| reader_->offset_ = new_position; | |
| } | |
| explicit AlternativeReadingScope(Reader* reader) | |
| : reader_(reader), saved_offset_(reader_->offset_) {} | |
| ~AlternativeReadingScope() { reader_->offset_ = saved_offset_; } | |
| intptr_t saved_offset() { return saved_offset_; } | |
| private: | |
| Reader* const reader_; | |
| const intptr_t saved_offset_; | |
| DISALLOW_COPY_AND_ASSIGN(AlternativeReadingScope); | |
| }; | |
| // Similar to AlternativeReadingScope, but also switches reading to another | |
| // typed data array. | |
| class AlternativeReadingScopeWithNewData { | |
| public: | |
| AlternativeReadingScopeWithNewData(Reader* reader, | |
| const TypedDataBase* new_typed_data, | |
| intptr_t new_position) | |
| : reader_(reader), | |
| saved_size_(reader_->size_), | |
| saved_raw_buffer_(reader_->raw_buffer_), | |
| saved_typed_data_(reader_->typed_data_), | |
| saved_offset_(reader_->offset_) { | |
| reader_->typed_data_ = new_typed_data; | |
| reader_->Init(); | |
| reader_->offset_ = new_position; | |
| } | |
| ~AlternativeReadingScopeWithNewData() { | |
| reader_->raw_buffer_ = saved_raw_buffer_; | |
| reader_->typed_data_ = saved_typed_data_; | |
| reader_->size_ = saved_size_; | |
| reader_->offset_ = saved_offset_; | |
| } | |
| intptr_t saved_offset() { return saved_offset_; } | |
| private: | |
| Reader* reader_; | |
| intptr_t saved_size_; | |
| const uint8_t* saved_raw_buffer_; | |
| const TypedDataBase* saved_typed_data_; | |
| intptr_t saved_offset_; | |
| DISALLOW_COPY_AND_ASSIGN(AlternativeReadingScopeWithNewData); | |
| }; | |
| // A helper class that resets the readers min and max positions both upon | |
| // initialization and upon destruction, i.e. when created the min an max | |
| // positions will be reset to "noSource", when destructing the min and max will | |
| // be reset to have they value they would have had, if they hadn't been reset in | |
| // the first place. | |
| class PositionScope { | |
| public: | |
| explicit PositionScope(Reader* reader) | |
| : reader_(reader), | |
| min_(reader->min_position_), | |
| max_(reader->max_position_) { | |
| reader->min_position_ = reader->max_position_ = TokenPosition::kNoSource; | |
| } | |
| ~PositionScope() { | |
| reader_->min_position_ = TokenPosition::Min(reader_->min_position_, min_); | |
| reader_->max_position_ = TokenPosition::Max(reader_->max_position_, max_); | |
| } | |
| private: | |
| Reader* reader_; | |
| TokenPosition min_; | |
| TokenPosition max_; | |
| DISALLOW_COPY_AND_ASSIGN(PositionScope); | |
| }; | |
| } // namespace kernel | |
| } // namespace dart | |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) | |
| #endif // RUNTIME_VM_KERNEL_BINARY_H_ |