d26558b7be
This CL allows for streaming big parts of the binary,
i.e. without using the AST nodes.
It is thus a stepping-stone in getting rid of the AST nodes in the VM.
Generally, all Expressions except "FunctionExpression",
and all Statements except "FunctionDeclaration" can be streamed.
There are currently not streamed because they create new functions,
which has a pointer to an AstNode (which we don't have when streaming).
Once we no longer need AstNodes at all these can be streamed as well.
This is, I think, mostly a matter of streaming the ScopeBuilder as well,
something that is not currently done.
The way the streaming is build, one has to stream an entire subtree.
That means, that if an expression (or statement), A, that is generally
streamable contains an expression or a statement, B, that is not streamable,
A cannot be streamed.
The way this is build is by marking AstNodes as streamable or not
("cannot_stream_" field). That way we know up front whether we can stream
a subtree or not.
The streaming is done via "kernel_binary_flowgraph".
In this file there are many obvious comments, e.g.
```
TokenPosition position = ReadPosition(); // read position.
```
This has been done in an attempt to add a comment to everything that
reads from the binary to make it stand out more.
All changes from kernel_to_il up to and including May 2nd 2017
should be included.
R=kmillikin@google.com
Review-Url: https://codereview.chromium.org/2854393002 .
591 lines
15 KiB
C++
591 lines
15 KiB
C++
// 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,
|
|
|
|
kTypedefType = 87,
|
|
kVectorType = 88,
|
|
kBottomType = 89,
|
|
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_;
|
|
};
|
|
|
|
|
|
// 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<LabeledStatement>* labels() { return labels_; }
|
|
void set_labels(BlockStack<LabeledStatement>* labels) { labels_ = labels; }
|
|
|
|
private:
|
|
Program* program_;
|
|
BlockStack<VariableDeclaration> scope_;
|
|
BlockStack<TypeParameter> type_parameters_;
|
|
BlockStack<LabeledStatement>* labels_;
|
|
};
|
|
|
|
|
|
class Reader {
|
|
public:
|
|
Reader(const uint8_t* buffer, intptr_t size)
|
|
: buffer_(buffer),
|
|
size_(size),
|
|
offset_(0),
|
|
string_data_offset_(-1),
|
|
string_offsets_(NULL),
|
|
canonical_name_parents_(NULL),
|
|
canonical_name_strings_(NULL) {}
|
|
|
|
~Reader();
|
|
|
|
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_++]; }
|
|
|
|
uint8_t PeekByte() { 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);
|
|
}
|
|
}
|
|
|
|
Tag PeekTag(uint8_t* payload = NULL) {
|
|
uint8_t byte = PeekByte();
|
|
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_; }
|
|
|
|
// 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); }
|
|
|
|
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_;
|
|
}
|
|
|
|
intptr_t StringLength(StringIndex index) {
|
|
return string_offsets_[index + 1] - string_offsets_[index];
|
|
}
|
|
|
|
uint8_t CharacterAt(StringIndex string_index, intptr_t index) {
|
|
ASSERT(index < StringLength(string_index));
|
|
return buffer_[string_data_offset_ + string_offsets_[string_index] + index];
|
|
}
|
|
|
|
// The canonical name index of a canonical name's parent (-1 indicates that
|
|
// the parent is the root name).
|
|
NameIndex CanonicalNameParent(NameIndex index) {
|
|
return canonical_name_parents_[index];
|
|
}
|
|
|
|
// The string index of a canonical name's name string.
|
|
StringIndex CanonicalNameString(NameIndex index) {
|
|
return canonical_name_strings_[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_;
|
|
|
|
// The offset of the start of the string data is recorded to allow access to
|
|
// the strings during deserialization.
|
|
intptr_t string_data_offset_;
|
|
|
|
// The string offsets are decoded to support efficient access to string UTF-8
|
|
// encodings.
|
|
intptr_t* string_offsets_;
|
|
|
|
// The canonical names are decoded.
|
|
NameIndex* canonical_name_parents_;
|
|
StringIndex* canonical_name_strings_;
|
|
|
|
friend class PositionScope;
|
|
friend class Program;
|
|
};
|
|
|
|
|
|
// 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_
|