// 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 #include "vm/kernel.h" #include "vm/kernel_to_il.h" #include "vm/object.h" namespace dart { namespace kernel { static const uint32_t kMagicProgramFile = 0x90ABCDEFu; // Keep in sync with package:dynamo/lib/binary/tag.dart enum Tag { kNothing = 0, kSomething = 1, kClass = 2, kField = 4, kConstructor = 5, kProcedure = 6, kInvalidInitializer = 7, kFieldInitializer = 8, kSuperInitializer = 9, kRedirectingInitializer = 10, kLocalInitializer = 11, kDirectPropertyGet = 15, kDirectPropertySet = 16, kDirectMethodInvocation = 17, kConstStaticInvocation = 18, kInvalidExpression = 19, kVariableGet = 20, kVariableSet = 21, kPropertyGet = 22, kPropertySet = 23, kSuperPropertyGet = 24, kSuperPropertySet = 25, kStaticGet = 26, kStaticSet = 27, kMethodInvocation = 28, kSuperMethodInvocation = 29, kStaticInvocation = 30, kConstructorInvocation = 31, kConstConstructorInvocation = 32, kNot = 33, kLogicalExpression = 34, kConditionalExpression = 35, kStringConcatenation = 36, kIsExpression = 37, kAsExpression = 38, kStringLiteral = 39, kDoubleLiteral = 40, kTrueLiteral = 41, kFalseLiteral = 42, kNullLiteral = 43, kSymbolLiteral = 44, kTypeLiteral = 45, kThisExpression = 46, kRethrow = 47, kThrow = 48, kListLiteral = 49, kMapLiteral = 50, kAwaitExpression = 51, kFunctionExpression = 52, kLet = 53, kPositiveIntLiteral = 55, kNegativeIntLiteral = 56, kBigIntLiteral = 57, kConstListLiteral = 58, kConstMapLiteral = 59, kInvalidStatement = 60, kExpressionStatement = 61, kBlock = 62, kEmptyStatement = 63, kAssertStatement = 64, kLabeledStatement = 65, kBreakStatement = 66, kWhileStatement = 67, kDoStatement = 68, kForStatement = 69, kForInStatement = 70, kSwitchStatement = 71, kContinueSwitchStatement = 72, kIfStatement = 73, kReturnStatement = 74, kTryCatch = 75, kTryFinally = 76, kYieldStatement = 77, kVariableDeclaration = 78, kFunctionDeclaration = 79, kAsyncForInStatement = 80, kVectorType = 88, kInvalidType = 90, kDynamicType = 91, kVoidType = 92, kInterfaceType = 93, kFunctionType = 94, kTypeParameterType = 95, kSimpleInterfaceType = 96, kSimpleFunctionType = 97, kVectorCreation = 102, kVectorGet = 103, kVectorSet = 104, kVectorCopy = 105, kClosureCreation = 106, kSpecializedTagHighBit = 0x80, // 10000000 kSpecializedTagMask = 0xF8, // 11111000 kSpecializedPayloadMask = 0x7, // 00000111 kSpecializedVariableGet = 128, kSpecializedVariableSet = 136, kSpecialIntLiteral = 144, }; static const int SpecializedIntLiteralBias = 3; template class BlockStack { public: BlockStack() : current_count_(0) {} void EnterScope() { variable_count_.Add(current_count_); current_count_ = 0; } void LeaveScope() { variables_.TruncateTo(variables_.length() - current_count_); current_count_ = variable_count_[variable_count_.length() - 1]; variable_count_.RemoveLast(); } T* Lookup(int index) { ASSERT(index < variables_.length()); return variables_[index]; } void Push(T* v) { variables_.Add(v); current_count_++; } void Push(List* decl) { for (intptr_t i = 0; i < decl->length(); i++) { variables_.Add(decl[i]); current_count_++; } } void Pop(T* decl) { variables_.RemoveLast(); current_count_--; } void Pop(List* decl) { variables_.TruncateTo(variables_.length() - decl->length()); current_count_ -= decl->length(); } private: int current_count_; MallocGrowableArray variables_; MallocGrowableArray variable_count_; }; template class BlockMap { public: BlockMap() : current_count_(0), stack_height_(0) {} void EnterScope() { variable_count_.Add(current_count_); current_count_ = 0; } void LeaveScope() { stack_height_ -= current_count_; current_count_ = variable_count_[variable_count_.length() - 1]; variable_count_.RemoveLast(); } int Lookup(T* object) { typename MallocMap::Pair* result = variables_.LookupPair(object); ASSERT(result != NULL); if (result == NULL) FATAL("lookup failure"); return RawPointerKeyValueTrait::ValueOf(*result); } void Push(T* v) { ASSERT(variables_.LookupPair(v) == NULL); int index = stack_height_++; variables_.Insert(v, index); current_count_++; } void Set(T* v, int index) { typename MallocMap::Pair* entry = variables_.LookupPair(v); ASSERT(entry != NULL); entry->value = index; } void Push(List* decl) { for (intptr_t i = 0; i < decl->length(); i++) { Push(decl[i]); } } void Pop(T* v) { current_count_--; stack_height_--; } private: int current_count_; int stack_height_; MallocMap variables_; MallocGrowableArray variable_count_; }; template class VariableScope { public: explicit VariableScope(T* builder) : builder_(builder) { builder_->variables().EnterScope(); } ~VariableScope() { builder_->variables().LeaveScope(); } private: T* builder_; }; template class TypeParameterScope { public: explicit TypeParameterScope(T* builder) : builder_(builder) { builder_->type_parameters().EnterScope(); } ~TypeParameterScope() { builder_->type_parameters().LeaveScope(); } private: T* builder_; }; template class SwitchCaseScope { public: explicit SwitchCaseScope(T* builder) : builder_(builder) { builder_->switch_cases().EnterScope(); } ~SwitchCaseScope() { builder_->switch_cases().LeaveScope(); } private: T* builder_; }; // Unlike other scopes, labels from enclosing functions are not visible in // nested functions. The LabelScope class is used to hide outer labels. template class LabelScope { public: explicit LabelScope(Builder* builder) : builder_(builder) { outer_block_ = builder_->labels(); builder_->set_labels(&block_); } ~LabelScope() { builder_->set_labels(outer_block_); } private: Builder* builder_; Block block_; Block* outer_block_; }; class ReaderHelper { public: ReaderHelper() : program_(NULL), labels_(NULL) {} Program* program() { return program_; } void set_program(Program* program) { program_ = program; } BlockStack& variables() { return scope_; } BlockStack& type_parameters() { return type_parameters_; } BlockStack& switch_cases() { return switch_cases_; } BlockStack* labels() { return labels_; } void set_labels(BlockStack* labels) { labels_ = labels; } CanonicalName* GetCanonicalName(int index) { return canonical_names_[index]; } void SetCanonicalName(int index, CanonicalName* name) { canonical_names_[index] = name; } void SetCanonicalNameCount(int count) { canonical_names_.SetLength(count); } private: Program* program_; MallocGrowableArray canonical_names_; BlockStack scope_; BlockStack type_parameters_; BlockStack switch_cases_; BlockStack* labels_; }; class Reader { public: Reader(const uint8_t* buffer, intptr_t size) : buffer_(buffer), size_(size), offset_(0), string_data_offset_(-1) {} uint32_t ReadUInt32() { ASSERT(offset_ + 4 <= size_); uint32_t value = (buffer_[offset_ + 0] << 24) | (buffer_[offset_ + 1] << 16) | (buffer_[offset_ + 2] << 8) | (buffer_[offset_ + 3] << 0); offset_ += 4; return value; } uint32_t ReadUInt() { ASSERT(offset_ + 1 <= size_); uint8_t byte0 = buffer_[offset_]; if ((byte0 & 0x80) == 0) { // 0... offset_++; return byte0; } else if ((byte0 & 0xc0) == 0x80) { // 10... ASSERT(offset_ + 2 <= size_); uint32_t value = ((byte0 & ~0x80) << 8) | (buffer_[offset_ + 1]); offset_ += 2; return value; } else { // 11... ASSERT(offset_ + 4 <= size_); uint32_t value = ((byte0 & ~0xc0) << 24) | (buffer_[offset_ + 1] << 16) | (buffer_[offset_ + 2] << 8) | (buffer_[offset_ + 3] << 0); offset_ += 4; return value; } } void add_token_position( MallocGrowableArray*>* list, TokenPosition position) { intptr_t size = list->length(); while (size <= current_script_id_) { MallocGrowableArray* tmp = new MallocGrowableArray(); list->Add(tmp); size = list->length(); } list->At(current_script_id_)->Add(position.value()); } void record_token_position(TokenPosition position) { if (position.IsReal() && helper()->program() != NULL) { add_token_position(&helper()->program()->valid_token_positions, position); } } void record_yield_token_position(TokenPosition position) { if (helper()->program() != NULL) { add_token_position(&helper()->program()->yield_token_positions, position); } } /** * Read and return a TokenPosition from this reader. * @param record specifies whether or not the read position is saved as a * valid token position in the current script. * If not be sure to record it later by calling record_token_position (after * setting the correct current_script_id). */ TokenPosition ReadPosition(bool record = true) { // Position is saved as unsigned, // but actually ranges from -1 and up (thus the -1) intptr_t value = ReadUInt() - 1; TokenPosition result = TokenPosition(value); max_position_ = Utils::Maximum(max_position_, result); if (min_position_.IsNoSource()) { min_position_ = result; } else if (result.IsReal()) { min_position_ = Utils::Minimum(min_position_, result); } if (record) { record_token_position(result); } return result; } intptr_t ReadListLength() { return ReadUInt(); } uint8_t ReadByte() { return buffer_[offset_++]; } bool ReadBool() { return (ReadByte() & 1) == 1; } word ReadFlags() { return ReadByte(); } Tag ReadTag(uint8_t* payload = NULL) { uint8_t byte = ReadByte(); bool has_payload = (byte & kSpecializedTagHighBit) != 0; if (has_payload) { if (payload != NULL) { *payload = byte & kSpecializedPayloadMask; } return static_cast(byte & kSpecializedTagMask); } else { return static_cast(byte); } } const uint8_t* Consume(int count) { ASSERT(offset_ + count <= size_); const uint8_t* old = buffer_ + offset_; offset_ += count; return old; } void EnsureEnd() { if (offset_ != size_) { FATAL2( "Reading Kernel file: Expected to be at EOF " "(offset: %" Pd ", size: %" Pd ")", offset_, size_); } } void DumpOffset(const char* str) { OS::PrintErr("@%" Pd " %s\n", offset_, str); } // 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_; } // The current script id for what we are currently processing. // Note though that this is only a convenience helper and has to be set // manually. intptr_t current_script_id() { return current_script_id_; } void set_current_script_id(intptr_t script_id) { current_script_id_ = script_id; } template T* ReadOptional() { Tag tag = ReadTag(); if (tag == kNothing) { return NULL; } ASSERT(tag == kSomething); return RT::ReadFrom(this); } template T* ReadOptional() { return ReadOptional(); } ReaderHelper* helper() { return &builder_; } CanonicalName* ReadCanonicalNameReference() { int index = ReadUInt(); if (index == 0) return NULL; CanonicalName* name = builder_.GetCanonicalName(index - 1); ASSERT(name != NULL); return name; } intptr_t offset() { return offset_; } void set_offset(intptr_t offset) { offset_ = offset; } intptr_t size() { return size_; } const uint8_t* buffer() { return buffer_; } intptr_t string_data_offset() { return string_data_offset_; } void MarkStringDataOffset() { ASSERT(string_data_offset_ == -1); string_data_offset_ = offset_; } uint8_t CharacterAt(String* str, intptr_t index) { ASSERT(index < str->size()); return buffer_[string_data_offset_ + str->offset() + index]; } private: const uint8_t* buffer_; intptr_t size_; intptr_t offset_; ReaderHelper builder_; TokenPosition max_position_; TokenPosition min_position_; intptr_t current_script_id_; intptr_t string_data_offset_; friend class PositionScope; }; // 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() { if (reader_->min_position_.IsNoSource()) { reader_->min_position_ = min_; } else if (min_.IsReal()) { reader_->min_position_ = Utils::Minimum(reader_->min_position_, min_); } reader_->max_position_ = Utils::Maximum(reader_->max_position_, max_); } private: Reader* reader_; TokenPosition min_; TokenPosition max_; }; } // namespace kernel } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME) #endif // RUNTIME_VM_KERNEL_BINARY_H_