blob: 093255d525d81315ddf302859d673b1e76f3e8c9 [file] [log] [blame]
// 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 <map>
#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 <typename T>
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<T>* 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<T>* decl) {
variables_.TruncateTo(variables_.length() - decl->length());
current_count_ -= decl->length();
}
private:
int current_count_;
MallocGrowableArray<T*> variables_;
MallocGrowableArray<int> variable_count_;
};
template <typename T>
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<T, int>::Pair* result = variables_.LookupPair(object);
ASSERT(result != NULL);
if (result == NULL) FATAL("lookup failure");
return RawPointerKeyValueTrait<T, int>::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<T, int>::Pair* entry = variables_.LookupPair(v);
ASSERT(entry != NULL);
entry->value = index;
}
void Push(List<T>* 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<T, int> variables_;
MallocGrowableArray<int> variable_count_;
};
template <typename T>
class VariableScope {
public:
explicit VariableScope(T* builder) : builder_(builder) {
builder_->variables().EnterScope();
}
~VariableScope() { builder_->variables().LeaveScope(); }
private:
T* builder_;
};
template <typename T>
class TypeParameterScope {
public:
explicit TypeParameterScope(T* builder) : builder_(builder) {
builder_->type_parameters().EnterScope();
}
~TypeParameterScope() { builder_->type_parameters().LeaveScope(); }
private:
T* builder_;
};
template <typename T>
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 <typename Builder, typename Block>
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<VariableDeclaration>& variables() { return scope_; }
BlockStack<TypeParameter>& type_parameters() { return type_parameters_; }
BlockStack<SwitchCase>& switch_cases() { return switch_cases_; }
BlockStack<LabeledStatement>* labels() { return labels_; }
void set_labels(BlockStack<LabeledStatement>* 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<CanonicalName*> canonical_names_;
BlockStack<VariableDeclaration> scope_;
BlockStack<TypeParameter> type_parameters_;
BlockStack<SwitchCase> switch_cases_;
BlockStack<LabeledStatement>* 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<MallocGrowableArray<intptr_t>*>* list,
TokenPosition position) {
intptr_t size = list->length();
while (size <= current_script_id_) {
MallocGrowableArray<intptr_t>* tmp = new MallocGrowableArray<intptr_t>();
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<Tag>(byte & kSpecializedTagMask);
} else {
return static_cast<Tag>(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 <typename T, typename RT>
T* ReadOptional() {
Tag tag = ReadTag();
if (tag == kNothing) {
return NULL;
}
ASSERT(tag == kSomething);
return RT::ReadFrom(this);
}
template <typename T>
T* ReadOptional() {
return ReadOptional<T, T>();
}
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_