blob: cf9b60e203cff1b677f125b08fa8ffe5ea9e8fe3 [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_FLOWGRAPH_H_
#define RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_
#if !defined(DART_PRECOMPILED_RUNTIME)
#include <map>
#include "vm/kernel.h"
#include "vm/kernel_binary.h"
#include "vm/kernel_to_il.h"
#include "vm/object.h"
namespace dart {
namespace kernel {
class StreamingDartTypeTranslator {
public:
StreamingDartTypeTranslator(StreamingFlowGraphBuilder* builder,
bool finalize = false);
// Can return a malformed type.
AbstractType& BuildType();
// Can return a malformed type.
AbstractType& BuildTypeWithoutFinalization();
// Is guaranteed to be not malformed.
AbstractType& BuildVariableType();
// Will return `TypeArguments::null()` in case any of the arguments are
// malformed.
const TypeArguments& BuildTypeArguments(intptr_t length);
// Will return `TypeArguments::null()` in case any of the arguments are
// malformed.
const TypeArguments& BuildInstantiatedTypeArguments(
const dart::Class& receiver_class,
intptr_t length);
const Type& ReceiverType(const dart::Class& klass);
private:
// Can build a malformed type.
void BuildTypeInternal();
void BuildInterfaceType(bool simple);
void BuildFunctionType(bool simple);
void BuildTypeParameterType();
class TypeParameterScope {
public:
TypeParameterScope(StreamingDartTypeTranslator* translator,
intptr_t parameters_offset,
intptr_t parameters_count)
: parameters_offset_(parameters_offset),
parameters_count_(parameters_count),
outer_(translator->type_parameter_scope_),
translator_(translator) {
translator_->type_parameter_scope_ = this;
}
~TypeParameterScope() { translator_->type_parameter_scope_ = outer_; }
TypeParameterScope* outer() const { return outer_; }
intptr_t parameters_offset() const { return parameters_offset_; }
intptr_t parameters_count() const { return parameters_count_; }
private:
intptr_t parameters_offset_;
intptr_t parameters_count_;
TypeParameterScope* outer_;
StreamingDartTypeTranslator* translator_;
};
intptr_t FindTypeParameterIndex(intptr_t parameters_offset,
intptr_t parameters_count,
intptr_t look_for);
StreamingFlowGraphBuilder* builder_;
TranslationHelper& translation_helper_;
ActiveClass* active_class_;
TypeParameterScope* type_parameter_scope_;
Zone* zone_;
AbstractType& result_;
bool finalize_;
friend class StreamingScopeBuilder;
friend class KernelReader;
};
class StreamingScopeBuilder {
public:
StreamingScopeBuilder(ParsedFunction* parsed_function,
intptr_t kernel_offset,
const uint8_t* buffer,
intptr_t buffer_length);
virtual ~StreamingScopeBuilder();
ScopeBuildingResult* BuildScopes();
private:
void VisitField();
void VisitProcedure();
void VisitConstructor();
void VisitFunctionNode();
void VisitNode();
void VisitInitializer();
void VisitExpression();
void VisitStatement();
void VisitArguments();
void VisitVariableDeclaration();
void VisitDartType();
void VisitInterfaceType(bool simple);
void VisitFunctionType(bool simple);
void VisitTypeParameterType();
void VisitVectorType();
void HandleLocalFunction(intptr_t parent_kernel_offset);
void EnterScope(intptr_t kernel_offset);
void ExitScope(TokenPosition start_position, TokenPosition end_position);
/**
* This assumes that the reader is at a FunctionNode,
* about to read the positional parameters.
*/
void AddPositionalAndNamedParameters(intptr_t pos = 0);
/**
* This assumes that the reader is at a FunctionNode,
* about to read a parameter (i.e. VariableDeclaration).
*/
void AddVariableDeclarationParameter(intptr_t pos);
LocalVariable* MakeVariable(TokenPosition declaration_pos,
TokenPosition token_pos,
const dart::String& name,
const AbstractType& type);
void AddExceptionVariable(GrowableArray<LocalVariable*>* variables,
const char* prefix,
intptr_t nesting_depth);
void AddTryVariables();
void AddCatchVariables();
void AddIteratorVariable();
void AddSwitchVariable();
// Record an assignment or reference to a variable. If the occurrence is
// in a nested function, ensure that the variable is handled properly as a
// captured variable.
void LookupVariable(intptr_t declaration_binary_offest);
const dart::String& GenerateName(const char* prefix, intptr_t suffix);
void HandleSpecialLoad(LocalVariable** variable, const dart::String& symbol);
void LookupCapturedVariableByName(LocalVariable** variable,
const dart::String& name);
struct DepthState {
explicit DepthState(intptr_t function)
: loop_(0),
function_(function),
try_(0),
catch_(0),
finally_(0),
for_in_(0) {}
intptr_t loop_;
intptr_t function_;
intptr_t try_;
intptr_t catch_;
intptr_t finally_;
intptr_t for_in_;
};
ScopeBuildingResult* result_;
ParsedFunction* parsed_function_;
intptr_t kernel_offset_;
ActiveClass active_class_;
TranslationHelper translation_helper_;
Zone* zone_;
FunctionNode::AsyncMarker current_function_async_marker_;
LocalScope* current_function_scope_;
LocalScope* scope_;
DepthState depth_;
intptr_t name_index_;
bool needs_expr_temp_;
TokenPosition first_body_token_position_;
StreamingFlowGraphBuilder* builder_;
StreamingDartTypeTranslator type_translator_;
};
// There are several cases when we are compiling constant expressions:
//
// * constant field initializers:
// const FieldName = <expr>;
//
// * constant expressions:
// const [<expr>, ...]
// const {<expr> : <expr>, ...}
// const Constructor(<expr>, ...)
//
// * constant default parameters:
// f(a, [b = <expr>])
// f(a, {b: <expr>})
//
// * constant values to compare in a [SwitchCase]
// case <expr>:
//
// In all cases `<expr>` must be recursively evaluated and canonicalized at
// compile-time.
class StreamingConstantEvaluator {
public:
explicit StreamingConstantEvaluator(StreamingFlowGraphBuilder* builder);
virtual ~StreamingConstantEvaluator() {}
Instance& EvaluateExpression(intptr_t offset, bool reset_position = true);
Instance& EvaluateListLiteral(intptr_t offset, bool reset_position = true);
Instance& EvaluateMapLiteral(intptr_t offset, bool reset_position = true);
Instance& EvaluateConstructorInvocation(intptr_t offset,
bool reset_position = true);
Object& EvaluateExpressionSafe(intptr_t offset);
private:
void EvaluateVariableGet();
void EvaluateVariableGet(uint8_t payload);
void EvaluatePropertyGet();
void EvaluateStaticGet();
void EvaluateMethodInvocation();
void EvaluateStaticInvocation();
void EvaluateConstructorInvocationInternal();
void EvaluateNot();
void EvaluateLogicalExpression();
void EvaluateConditionalExpression();
void EvaluateStringConcatenation();
void EvaluateSymbolLiteral();
void EvaluateTypeLiteral();
void EvaluateListLiteralInternal();
void EvaluateMapLiteralInternal();
void EvaluateLet();
void EvaluateBigIntLiteral();
void EvaluateStringLiteral();
void EvaluateIntLiteral(uint8_t payload);
void EvaluateIntLiteral(bool is_negative);
void EvaluateDoubleLiteral();
void EvaluateBoolLiteral(bool value);
void EvaluateNullLiteral();
const Object& RunFunction(const Function& function,
intptr_t argument_count,
const Instance* receiver,
const TypeArguments* type_args);
const Object& RunFunction(const Function& function,
const Array& arguments,
const Array& names);
RawObject* EvaluateConstConstructorCall(const dart::Class& type_class,
const TypeArguments& type_arguments,
const Function& constructor,
const Object& argument);
const TypeArguments* TranslateTypeArguments(const Function& target,
dart::Class* target_klass);
void AssertBool() {
if (!result_.IsBool()) {
translation_helper_.ReportError("Expected boolean expression.");
}
}
bool EvaluateBooleanExpressionHere();
bool GetCachedConstant(intptr_t kernel_offset, Instance* value);
void CacheConstantValue(intptr_t kernel_offset, const Instance& value);
StreamingFlowGraphBuilder* builder_;
Isolate* isolate_;
Zone* zone_;
TranslationHelper& translation_helper_;
StreamingDartTypeTranslator& type_translator_;
Script& script_;
Instance& result_;
};
class FunctionNodeHelper;
class StreamingFlowGraphBuilder {
public:
StreamingFlowGraphBuilder(FlowGraphBuilder* flow_graph_builder,
const uint8_t* buffer,
intptr_t buffer_length)
: flow_graph_builder_(flow_graph_builder),
translation_helper_(flow_graph_builder->translation_helper_),
zone_(flow_graph_builder->zone_),
reader_(new Reader(buffer, buffer_length)),
constant_evaluator_(this),
type_translator_(this, /* finalize= */ true),
current_script_id_(-1),
record_for_script_id_(-1),
record_token_positions_into_(NULL),
record_yield_positions_into_(NULL) {}
StreamingFlowGraphBuilder(TranslationHelper* translation_helper,
Zone* zone,
const uint8_t* buffer,
intptr_t buffer_length)
: flow_graph_builder_(NULL),
translation_helper_(*translation_helper),
zone_(zone),
reader_(new Reader(buffer, buffer_length)),
constant_evaluator_(this),
type_translator_(this, /* finalize= */ true),
current_script_id_(-1),
record_for_script_id_(-1),
record_token_positions_into_(NULL),
record_yield_positions_into_(NULL) {}
~StreamingFlowGraphBuilder() { delete reader_; }
FlowGraph* BuildGraph(intptr_t kernel_offset);
Fragment BuildStatementAt(intptr_t kernel_offset);
RawObject* BuildParameterDescriptor(intptr_t kernel_offset);
RawObject* EvaluateMetadata(intptr_t kernel_offset);
void CollectTokenPositionsFor(
intptr_t script_index,
GrowableArray<intptr_t>* record_token_positions_in,
GrowableArray<intptr_t>* record_yield_positions_in);
intptr_t SourceTableSize();
String& SourceTableUriFor(intptr_t index);
String& GetSourceFor(intptr_t index);
Array& GetLineStartsFor(intptr_t index);
private:
void DiscoverEnclosingElements(Zone* zone,
const Function& function,
Function* outermost_function,
intptr_t* outermost_kernel_offset,
intptr_t* parent_class_offset);
intptr_t GetParentOffset(intptr_t offset);
void GetTypeParameterInfoForClass(intptr_t class_offset,
intptr_t* type_paremeter_counts,
intptr_t* type_paremeter_offset);
void GetTypeParameterInfoForPossibleProcedure(
intptr_t outermost_kernel_offset,
bool* member_is_procedure,
bool* is_factory_procedure,
intptr_t* member_type_parameters,
intptr_t* member_type_parameters_offset_start);
/**
* Will return kernel offset for parent class if reading a constructor.
* Will otherwise return -1.
*/
intptr_t ReadUntilFunctionNode();
StringIndex GetNameFromVariableDeclaration(intptr_t kernel_offset);
FlowGraph* BuildGraphOfStaticFieldInitializer();
FlowGraph* BuildGraphOfFieldAccessor(LocalVariable* setter_value);
void SetupDefaultParameterValues();
Fragment BuildFieldInitializer(NameIndex canonical_name);
Fragment BuildInitializers(intptr_t constructor_class_parent_offset);
FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
FlowGraph* BuildGraphOfConvertedClosureFunction(const Function& function);
FlowGraph* BuildGraphOfFunction(
intptr_t constructor_class_parent_offset = -1);
Fragment BuildExpression(TokenPosition* position = NULL);
Fragment BuildStatement();
intptr_t ReaderOffset();
void SetOffset(intptr_t offset);
void SkipBytes(intptr_t skip);
bool ReadBool();
uint8_t ReadByte();
uint32_t ReadUInt();
uint32_t PeekUInt();
intptr_t ReadListLength();
StringIndex ReadStringReference();
NameIndex ReadCanonicalNameReference();
StringIndex ReadNameAsStringIndex();
const dart::String& ReadNameAsMethodName();
const dart::String& ReadNameAsGetterName();
const dart::String& ReadNameAsSetterName();
const dart::String& ReadNameAsFieldName();
void SkipStringReference();
void SkipCanonicalNameReference();
void SkipDartType();
void SkipOptionalDartType();
void SkipInterfaceType(bool simple);
void SkipFunctionType(bool simple);
void SkipListOfExpressions();
void SkipListOfDartTypes();
void SkipListOfVariableDeclarations();
void SkipTypeParametersList();
void SkipExpression();
void SkipStatement();
void SkipFunctionNode();
void SkipName();
void SkipArguments();
void SkipVariableDeclaration();
void SkipLibraryCombinator();
void SkipLibraryDependency();
void SkipLibraryTypedef();
TokenPosition ReadPosition(bool record = true);
void record_token_position(TokenPosition position);
void record_yield_position(TokenPosition position);
Tag ReadTag(uint8_t* payload = NULL);
Tag PeekTag(uint8_t* payload = NULL);
word ReadFlags();
void loop_depth_inc();
void loop_depth_dec();
intptr_t for_in_depth();
void for_in_depth_inc();
void for_in_depth_dec();
void catch_depth_inc();
void catch_depth_dec();
void try_depth_inc();
void try_depth_dec();
intptr_t CurrentTryIndex();
intptr_t AllocateTryIndex();
LocalVariable* CurrentException();
LocalVariable* CurrentStackTrace();
CatchBlock* catch_block();
ActiveClass* active_class();
ScopeBuildingResult* scopes();
void set_scopes(ScopeBuildingResult* scope);
ParsedFunction* parsed_function();
TryFinallyBlock* try_finally_block();
SwitchBlock* switch_block();
BreakableBlock* breakable_block();
GrowableArray<YieldContinuation>& yield_continuations();
Value* stack();
void Push(Definition* definition);
Value* Pop();
Tag PeekArgumentsFirstPositionalTag();
const TypeArguments& PeekArgumentsInstantiatedType(const dart::Class& klass);
intptr_t PeekArgumentsCount();
intptr_t PeekArgumentsTypeCount();
void SkipArgumentsBeforeActualArguments();
LocalVariable* LookupVariable(intptr_t kernel_offset);
LocalVariable* MakeTemporary();
Token::Kind MethodKind(const dart::String& name);
dart::RawFunction* LookupMethodByMember(NameIndex target,
const dart::String& method_name);
bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
bool NeedsDebugStepCheck(Value* value, TokenPosition position);
void InlineBailout(const char* reason);
Fragment DebugStepCheck(TokenPosition position);
Fragment LoadLocal(LocalVariable* variable);
Fragment Return(TokenPosition position);
Fragment PushArgument();
Fragment EvaluateAssertion();
Fragment RethrowException(TokenPosition position, int catch_try_index);
Fragment ThrowNoSuchMethodError();
Fragment Constant(const Object& value);
Fragment IntConstant(int64_t value);
Fragment LoadStaticField();
Fragment StaticCall(TokenPosition position,
const Function& target,
intptr_t argument_count);
Fragment StaticCall(TokenPosition position,
const Function& target,
intptr_t argument_count,
const Array& argument_names);
Fragment InstanceCall(TokenPosition position,
const dart::String& name,
Token::Kind kind,
intptr_t argument_count,
intptr_t checked_argument_count = 1);
Fragment InstanceCall(TokenPosition position,
const dart::String& name,
Token::Kind kind,
intptr_t type_args_len,
intptr_t argument_count,
const Array& argument_names,
intptr_t checked_argument_count);
Fragment ThrowException(TokenPosition position);
Fragment BooleanNegate();
Fragment TranslateInstantiatedTypeArguments(
const TypeArguments& type_arguments);
Fragment StrictCompare(Token::Kind kind, bool number_check = false);
Fragment AllocateObject(TokenPosition position,
const dart::Class& klass,
intptr_t argument_count);
Fragment AllocateObject(const dart::Class& klass,
const Function& closure_function);
Fragment AllocateContext(intptr_t size);
Fragment LoadField(intptr_t offset);
Fragment StoreLocal(TokenPosition position, LocalVariable* variable);
Fragment StoreStaticField(TokenPosition position, const dart::Field& field);
Fragment StoreInstanceField(TokenPosition position, intptr_t offset);
Fragment StringInterpolate(TokenPosition position);
Fragment StringInterpolateSingle(TokenPosition position);
Fragment ThrowTypeError();
Fragment LoadInstantiatorTypeArguments();
Fragment LoadFunctionTypeArguments();
Fragment InstantiateType(const AbstractType& type);
Fragment CreateArray();
Fragment StoreIndexed(intptr_t class_id);
Fragment CheckStackOverflow();
Fragment CloneContext();
Fragment TranslateFinallyFinalizers(TryFinallyBlock* outer_finally,
intptr_t target_context_depth);
Fragment BranchIfTrue(TargetEntryInstr** then_entry,
TargetEntryInstr** otherwise_entry,
bool negate);
Fragment BranchIfEqual(TargetEntryInstr** then_entry,
TargetEntryInstr** otherwise_entry,
bool negate);
Fragment BranchIfNull(TargetEntryInstr** then_entry,
TargetEntryInstr** otherwise_entry,
bool negate = false);
Fragment CatchBlockEntry(const Array& handler_types,
intptr_t handler_index,
bool needs_stacktrace);
Fragment TryCatch(int try_handler_index);
Fragment Drop();
Fragment NullConstant();
JoinEntryInstr* BuildJoinEntry();
JoinEntryInstr* BuildJoinEntry(intptr_t try_index);
Fragment Goto(JoinEntryInstr* destination);
Fragment BuildImplicitClosureCreation(const Function& target);
Fragment CheckBooleanInCheckedMode();
Fragment CheckAssignableInCheckedMode(const dart::AbstractType& dst_type,
const dart::String& dst_name);
Fragment CheckVariableTypeInCheckedMode(intptr_t variable_kernel_position);
Fragment CheckVariableTypeInCheckedMode(const AbstractType& dst_type,
const dart::String& name_symbol);
Fragment EnterScope(intptr_t kernel_offset, bool* new_context = NULL);
Fragment ExitScope(intptr_t kernel_offset);
Fragment TranslateCondition(bool* negate);
const TypeArguments& BuildTypeArguments();
Fragment BuildArguments(Array* argument_names,
intptr_t* argument_count,
bool skip_push_arguments = false,
bool do_drop = false);
Fragment BuildArgumentsFromActualArguments(Array* argument_names,
bool skip_push_arguments = false,
bool do_drop = false);
Fragment BuildInvalidExpression(TokenPosition* position);
Fragment BuildVariableGet(TokenPosition* position);
Fragment BuildVariableGet(uint8_t payload, TokenPosition* position);
Fragment BuildVariableSet(TokenPosition* position);
Fragment BuildVariableSet(uint8_t payload, TokenPosition* position);
Fragment BuildPropertyGet(TokenPosition* position);
Fragment BuildPropertySet(TokenPosition* position);
Fragment BuildDirectPropertyGet(TokenPosition* position);
Fragment BuildDirectPropertySet(TokenPosition* position);
Fragment BuildStaticGet(TokenPosition* position);
Fragment BuildStaticSet(TokenPosition* position);
Fragment BuildMethodInvocation(TokenPosition* position);
Fragment BuildDirectMethodInvocation(TokenPosition* position);
Fragment BuildStaticInvocation(bool is_const, TokenPosition* position);
Fragment BuildConstructorInvocation(bool is_const, TokenPosition* position);
Fragment BuildNot(TokenPosition* position);
Fragment BuildLogicalExpression(TokenPosition* position);
Fragment BuildConditionalExpression(TokenPosition* position);
Fragment BuildStringConcatenation(TokenPosition* position);
Fragment BuildIsExpression(TokenPosition* position);
Fragment BuildAsExpression(TokenPosition* position);
Fragment BuildSymbolLiteral(TokenPosition* position);
Fragment BuildTypeLiteral(TokenPosition* position);
Fragment BuildThisExpression(TokenPosition* position);
Fragment BuildRethrow(TokenPosition* position);
Fragment BuildThrow(TokenPosition* position);
Fragment BuildListLiteral(bool is_const, TokenPosition* position);
Fragment BuildMapLiteral(bool is_const, TokenPosition* position);
Fragment BuildFunctionExpression();
Fragment BuildLet(TokenPosition* position);
Fragment BuildBigIntLiteral(TokenPosition* position);
Fragment BuildStringLiteral(TokenPosition* position);
Fragment BuildIntLiteral(uint8_t payload, TokenPosition* position);
Fragment BuildIntLiteral(bool is_negative, TokenPosition* position);
Fragment BuildDoubleLiteral(TokenPosition* position);
Fragment BuildBoolLiteral(bool value, TokenPosition* position);
Fragment BuildNullLiteral(TokenPosition* position);
Fragment BuildVectorCreation(TokenPosition* position);
Fragment BuildVectorGet(TokenPosition* position);
Fragment BuildVectorSet(TokenPosition* position);
Fragment BuildVectorCopy(TokenPosition* position);
Fragment BuildClosureCreation(TokenPosition* position);
Fragment BuildInvalidStatement();
Fragment BuildExpressionStatement();
Fragment BuildBlock();
Fragment BuildEmptyStatement();
Fragment BuildAssertStatement();
Fragment BuildLabeledStatement();
Fragment BuildBreakStatement();
Fragment BuildWhileStatement();
Fragment BuildDoStatement();
Fragment BuildForStatement();
Fragment BuildForInStatement(bool async);
Fragment BuildSwitchStatement();
Fragment BuildContinueSwitchStatement();
Fragment BuildIfStatement();
Fragment BuildReturnStatement();
Fragment BuildTryCatch();
Fragment BuildTryFinally();
Fragment BuildYieldStatement();
Fragment BuildVariableDeclaration();
Fragment BuildFunctionDeclaration();
Fragment BuildFunctionNode(intptr_t parent_kernel_offset,
TokenPosition parent_position,
bool declaration,
intptr_t variable_offeset);
void SetupFunctionParameters(const dart::Class& klass,
const dart::Function& function,
bool is_method,
bool is_closure,
FunctionNodeHelper* function_node_helper);
FlowGraphBuilder* flow_graph_builder_;
TranslationHelper& translation_helper_;
Zone* zone_;
Reader* reader_;
StreamingConstantEvaluator constant_evaluator_;
StreamingDartTypeTranslator type_translator_;
intptr_t current_script_id_;
intptr_t record_for_script_id_;
GrowableArray<intptr_t>* record_token_positions_into_;
GrowableArray<intptr_t>* record_yield_positions_into_;
friend class StreamingConstantEvaluator;
friend class StreamingDartTypeTranslator;
friend class StreamingScopeBuilder;
friend class FunctionNodeHelper;
friend class VariableDeclarationHelper;
friend class FieldHelper;
friend class ProcedureHelper;
friend class ClassHelper;
friend class LibraryHelper;
friend class ConstructorHelper;
friend class SimpleExpressionConverter;
friend class KernelReader;
};
// Helper class that reads a kernel FunctionNode from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class FunctionNodeHelper {
public:
enum Fields {
kStart, // tag.
kPosition,
kEndPosition,
kAsyncMarker,
kDartAsyncMarker,
kTypeParameters,
kTotalParameterCount,
kRequiredParameterCount,
kPositionalParameters,
kNamedParameters,
kReturnType,
kBody,
kEnd
};
explicit FunctionNodeHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kStart;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = builder_->ReadTag(); // read tag.
ASSERT(tag == kFunctionNode);
if (++next_read_ == field) return;
}
case kPosition:
position_ = builder_->ReadPosition(); // read position.
if (++next_read_ == field) return;
case kEndPosition:
end_position_ = builder_->ReadPosition(); // read end position.
if (++next_read_ == field) return;
case kAsyncMarker:
async_marker_ = static_cast<FunctionNode::AsyncMarker>(
builder_->ReadByte()); // read async marker.
if (++next_read_ == field) return;
case kDartAsyncMarker:
dart_async_marker_ = static_cast<FunctionNode::AsyncMarker>(
builder_->ReadByte()); // read dart async marker.
if (++next_read_ == field) return;
case kTypeParameters:
builder_->SkipTypeParametersList(); // read type parameters.
if (++next_read_ == field) return;
case kTotalParameterCount:
total_parameter_count_ =
builder_->ReadUInt(); // read total parameter count.
if (++next_read_ == field) return;
case kRequiredParameterCount:
required_parameter_count_ =
builder_->ReadUInt(); // read required parameter count.
if (++next_read_ == field) return;
case kPositionalParameters:
builder_->SkipListOfVariableDeclarations(); // read positionals.
if (++next_read_ == field) return;
case kNamedParameters:
builder_->SkipListOfVariableDeclarations(); // read named.
if (++next_read_ == field) return;
case kReturnType:
builder_->SkipDartType(); // read return type.
if (++next_read_ == field) return;
case kBody:
if (builder_->ReadTag() == kSomething)
builder_->SkipStatement(); // read body.
if (++next_read_ == field) return;
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
TokenPosition position_;
TokenPosition end_position_;
FunctionNode::AsyncMarker async_marker_;
FunctionNode::AsyncMarker dart_async_marker_;
intptr_t total_parameter_count_;
intptr_t required_parameter_count_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// Helper class that reads a kernel VariableDeclaration from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class VariableDeclarationHelper {
public:
enum Fields {
kPosition,
kEqualPosition,
kFlags,
kNameIndex,
kType,
kInitializer,
kEnd
};
explicit VariableDeclarationHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kPosition;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kPosition:
position_ = builder_->ReadPosition(); // read position.
if (++next_read_ == field) return;
case kEqualPosition:
equals_position_ = builder_->ReadPosition(); // read equals position.
if (++next_read_ == field) return;
case kFlags:
flags_ = builder_->ReadFlags(); // read flags.
if (++next_read_ == field) return;
case kNameIndex:
name_index_ = builder_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
case kType:
builder_->SkipDartType(); // read type.
if (++next_read_ == field) return;
case kInitializer:
if (builder_->ReadTag() == kSomething)
builder_->SkipExpression(); // read initializer.
if (++next_read_ == field) return;
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
bool IsConst() {
return (flags_ & VariableDeclaration::kFlagConst) ==
VariableDeclaration::kFlagConst;
}
bool IsFinal() {
return (flags_ & VariableDeclaration::kFlagFinal) ==
VariableDeclaration::kFlagFinal;
}
TokenPosition position_;
TokenPosition equals_position_;
word flags_;
StringIndex name_index_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// Helper class that reads a kernel Field from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class FieldHelper {
public:
enum Fields {
kStart, // tag.
kCanonicalName,
kPosition,
kEndPosition,
kFlags,
kParentClassBinaryOffset,
kName,
kSourceUriIndex,
kAnnotations,
kType,
kInitializer,
kEnd
};
explicit FieldHelper(StreamingFlowGraphBuilder* builder)
: builder_(builder),
next_read_(kStart),
has_function_literal_initializer_(false) {}
FieldHelper(StreamingFlowGraphBuilder* builder, intptr_t offset)
: builder_(builder),
next_read_(kStart),
has_function_literal_initializer_(false) {
builder_->SetOffset(offset);
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field,
bool detect_function_literal_initializer = false) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = builder_->ReadTag(); // read tag.
ASSERT(tag == kField);
if (++next_read_ == field) return;
}
case kCanonicalName:
canonical_name_ =
builder_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
case kPosition:
position_ = builder_->ReadPosition(false); // read position.
if (++next_read_ == field) return;
case kEndPosition:
end_position_ = builder_->ReadPosition(false); // read end position.
if (++next_read_ == field) return;
case kFlags:
flags_ = builder_->ReadFlags(); // read flags.
if (++next_read_ == field) return;
case kParentClassBinaryOffset:
parent_class_binary_offset_ =
builder_->ReadUInt(); // read parent class binary offset.
if (++next_read_ == field) return;
case kName:
builder_->SkipName(); // read name.
if (++next_read_ == field) return;
case kSourceUriIndex:
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
builder_->current_script_id_ = source_uri_index_;
builder_->record_token_position(position_);
builder_->record_token_position(end_position_);
if (++next_read_ == field) return;
case kAnnotations: {
annotation_count_ = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
builder_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
case kType:
builder_->SkipDartType(); // read type.
if (++next_read_ == field) return;
case kInitializer:
if (builder_->ReadTag() == kSomething) {
if (detect_function_literal_initializer &&
builder_->PeekTag() == kFunctionExpression) {
has_function_literal_initializer_ = true;
intptr_t expr_offset = builder_->ReaderOffset();
Tag tag = builder_->ReadTag();
ASSERT(tag == kFunctionExpression);
tag = builder_->ReadTag();
ASSERT(tag == kFunctionNode);
function_literal_start_ = builder_->ReadPosition();
function_literal_end_ = builder_->ReadPosition();
builder_->SetOffset(expr_offset);
}
builder_->SkipExpression(); // read initializer.
}
if (++next_read_ == field) return;
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
bool IsConst() { return (flags_ & Field::kFlagConst) == Field::kFlagConst; }
bool IsFinal() { return (flags_ & Field::kFlagFinal) == Field::kFlagFinal; }
bool IsStatic() {
return (flags_ & Field::kFlagStatic) == Field::kFlagStatic;
}
bool FieldHasFunctionLiteralInitializer(TokenPosition* start,
TokenPosition* end) {
if (has_function_literal_initializer_) {
*start = function_literal_start_;
*end = function_literal_end_;
}
return has_function_literal_initializer_;
}
NameIndex canonical_name_;
TokenPosition position_;
TokenPosition end_position_;
word flags_;
intptr_t parent_class_binary_offset_;
intptr_t source_uri_index_;
intptr_t annotation_count_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
bool has_function_literal_initializer_;
TokenPosition function_literal_start_;
TokenPosition function_literal_end_;
};
// Helper class that reads a kernel Procedure from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class ProcedureHelper {
public:
enum Fields {
kStart, // tag.
kCanonicalName,
kPosition,
kEndPosition,
kKind,
kFlags,
kParentClassBinaryOffset,
kName,
kSourceUriIndex,
kAnnotations,
kFunction,
kEnd
};
explicit ProcedureHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kStart;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = builder_->ReadTag(); // read tag.
ASSERT(tag == kProcedure);
if (++next_read_ == field) return;
}
case kCanonicalName:
canonical_name_ =
builder_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
case kPosition:
position_ = builder_->ReadPosition(false); // read position.
if (++next_read_ == field) return;
case kEndPosition:
end_position_ = builder_->ReadPosition(false); // read end position.
if (++next_read_ == field) return;
case kKind:
kind_ = static_cast<Procedure::ProcedureKind>(
builder_->ReadByte()); // read kind.
if (++next_read_ == field) return;
case kFlags:
flags_ = builder_->ReadFlags(); // read flags.
if (++next_read_ == field) return;
case kParentClassBinaryOffset:
parent_class_binary_offset_ =
builder_->ReadUInt(); // read parent class binary offset.
if (++next_read_ == field) return;
case kName:
builder_->SkipName(); // read name.
if (++next_read_ == field) return;
case kSourceUriIndex:
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
builder_->current_script_id_ = source_uri_index_;
builder_->record_token_position(position_);
builder_->record_token_position(end_position_);
if (++next_read_ == field) return;
case kAnnotations: {
annotation_count_ = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
builder_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
case kFunction:
if (builder_->ReadTag() == kSomething)
builder_->SkipFunctionNode(); // read function node.
if (++next_read_ == field) return;
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
bool IsStatic() {
return (flags_ & Procedure::kFlagStatic) == Procedure::kFlagStatic;
}
bool IsAbstract() {
return (flags_ & Procedure::kFlagAbstract) == Procedure::kFlagAbstract;
}
bool IsExternal() {
return (flags_ & Procedure::kFlagExternal) == Procedure::kFlagExternal;
}
bool IsConst() {
return (flags_ & Procedure::kFlagConst) == Procedure::kFlagConst;
}
NameIndex canonical_name_;
TokenPosition position_;
TokenPosition end_position_;
Procedure::ProcedureKind kind_;
word flags_;
intptr_t parent_class_binary_offset_;
intptr_t source_uri_index_;
intptr_t annotation_count_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// Helper class that reads a kernel Constructor from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class ConstructorHelper {
public:
enum Fields {
kStart, // tag.
kCanonicalName,
kPosition,
kEndPosition,
kFlags,
kParentClassBinaryOffset,
kName,
kAnnotations,
kFunction,
kInitializers,
kEnd
};
explicit ConstructorHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kStart;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = builder_->ReadTag(); // read tag.
ASSERT(tag == kConstructor);
if (++next_read_ == field) return;
}
case kCanonicalName:
canonical_name_ =
builder_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
case kPosition:
position_ = builder_->ReadPosition(); // read position.
if (++next_read_ == field) return;
case kEndPosition:
end_position_ = builder_->ReadPosition(); // read end position.
if (++next_read_ == field) return;
case kFlags:
flags_ = builder_->ReadFlags(); // read flags.
if (++next_read_ == field) return;
case kParentClassBinaryOffset:
parent_class_binary_offset_ =
builder_->ReadUInt(); // read parent class binary offset.
if (++next_read_ == field) return;
case kName:
builder_->SkipName(); // read name.
if (++next_read_ == field) return;
case kAnnotations: {
annotation_count_ = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
builder_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
case kFunction:
builder_->SkipFunctionNode(); // read function.
if (++next_read_ == field) return;
case kInitializers: {
intptr_t list_length =
builder_->ReadListLength(); // read initializers list length.
for (intptr_t i = 0; i < list_length; i++) {
Tag tag = builder_->ReadTag();
switch (tag) {
case kInvalidInitializer:
continue;
case kFieldInitializer:
builder_->SkipCanonicalNameReference(); // read field_reference.
builder_->SkipExpression(); // read value.
continue;
case kSuperInitializer:
builder_->SkipCanonicalNameReference(); // read target_reference.
builder_->SkipArguments(); // read arguments.
continue;
case kRedirectingInitializer:
builder_->SkipCanonicalNameReference(); // read target_reference.
builder_->SkipArguments(); // read arguments.
continue;
case kLocalInitializer:
builder_->SkipVariableDeclaration(); // read variable.
continue;
default:
UNREACHABLE();
}
}
if (++next_read_ == field) return;
}
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
bool IsExternal() {
return (flags_ & Constructor::kFlagExternal) == Constructor::kFlagExternal;
}
bool IsConst() {
return (flags_ & Constructor::kFlagConst) == Constructor::kFlagConst;
}
NameIndex canonical_name_;
TokenPosition position_;
TokenPosition end_position_;
word flags_;
intptr_t parent_class_binary_offset_;
intptr_t annotation_count_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// Helper class that reads a kernel Class from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class ClassHelper {
public:
enum Fields {
kStart, // tag.
kCanonicalName,
kPosition,
kEndPosition,
kIsAbstract,
kNameIndex,
kSourceUriIndex,
kDocumentationCommentIndex,
kAnnotations,
kTypeParameters,
kSuperClass,
kMixinType,
kImplementedClasses,
kFields,
kConstructors,
kProcedures,
kEnd
};
explicit ClassHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kStart;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kStart: {
Tag tag = builder_->ReadTag(); // read tag.
ASSERT(tag == kClass);
if (++next_read_ == field) return;
}
case kCanonicalName:
canonical_name_ =
builder_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
case kPosition:
position_ = builder_->ReadPosition(false); // read position.
if (++next_read_ == field) return;
case kEndPosition:
end_position_ = builder_->ReadPosition(); // read end position.
if (++next_read_ == field) return;
case kIsAbstract:
is_abstract_ = builder_->ReadBool(); // read is_abstract.
if (++next_read_ == field) return;
case kNameIndex:
name_index_ = builder_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
case kSourceUriIndex:
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
builder_->current_script_id_ = source_uri_index_;
builder_->record_token_position(position_);
if (++next_read_ == field) return;
case kDocumentationCommentIndex:
builder_->ReadStringReference();
if (++next_read_ == field) return;
case kAnnotations: {
annotation_count_ = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < annotation_count_; ++i) {
builder_->SkipExpression(); // read ith expression.
}
if (++next_read_ == field) return;
}
case kTypeParameters:
builder_->SkipTypeParametersList(); // read type parameters.
if (++next_read_ == field) return;
case kSuperClass: {
Tag type_tag = builder_->ReadTag(); // read super class type (part 1).
if (type_tag == kSomething) {
builder_->SkipDartType(); // read super class type (part 2).
}
if (++next_read_ == field) return;
}
case kMixinType: {
Tag type_tag = builder_->ReadTag(); // read mixin type (part 1).
if (type_tag == kSomething) {
builder_->SkipDartType(); // read mixin type (part 2).
}
if (++next_read_ == field) return;
}
case kImplementedClasses:
builder_->SkipListOfDartTypes(); // read implemented_classes.
if (++next_read_ == field) return;
case kFields: {
intptr_t list_length =
builder_->ReadListLength(); // read fields list length.
for (intptr_t i = 0; i < list_length; i++) {
FieldHelper field_helper(builder_);
field_helper.ReadUntilExcluding(FieldHelper::kEnd); // read field.
}
if (++next_read_ == field) return;
}
case kConstructors: {
intptr_t list_length =
builder_->ReadListLength(); // read constructors list length.
for (intptr_t i = 0; i < list_length; i++) {
ConstructorHelper constructor_helper(builder_);
constructor_helper.ReadUntilExcluding(
ConstructorHelper::kEnd); // read constructor.
}
if (++next_read_ == field) return;
}
case kProcedures: {
intptr_t list_length =
builder_->ReadListLength(); // read procedures list length.
for (intptr_t i = 0; i < list_length; i++) {
ProcedureHelper procedure_helper(builder_);
procedure_helper.ReadUntilExcluding(
ProcedureHelper::kEnd); // read procedure.
}
if (++next_read_ == field) return;
}
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
NameIndex canonical_name_;
TokenPosition position_;
TokenPosition end_position_;
bool is_abstract_;
StringIndex name_index_;
intptr_t source_uri_index_;
intptr_t annotation_count_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// Helper class that reads a kernel Library from binary.
//
// Use ReadUntilExcluding to read up to but not including a field.
// One can then for instance read the field from the call-site (and remember to
// call SetAt to inform this helper class), and then use this to read more.
// "Dumb" fields are stored (e.g. integers) and can be fetched from this class.
// If asked to read a "non-dumb" field (e.g. an expression) it will be skipped.
class LibraryHelper {
public:
enum Fields {
kFlags,
kCanonicalName,
kName,
kSourceUriIndex,
kAnnotations,
kDependencies,
kTypedefs,
kClasses,
kToplevelField,
kToplevelProcedures,
kEnd
};
explicit LibraryHelper(StreamingFlowGraphBuilder* builder) {
builder_ = builder;
next_read_ = kFlags;
}
void ReadUntilIncluding(Fields field) {
ReadUntilExcluding(static_cast<Fields>(static_cast<int>(field) + 1));
}
void ReadUntilExcluding(Fields field) {
if (field <= next_read_) return;
// Ordered with fall-through.
switch (next_read_) {
case kFlags: {
word flags = builder_->ReadFlags(); // read flags.
ASSERT(flags == 0); // external libraries not supported
if (++next_read_ == field) return;
}
case kCanonicalName:
canonical_name_ =
builder_->ReadCanonicalNameReference(); // read canonical_name.
if (++next_read_ == field) return;
case kName:
name_index_ = builder_->ReadStringReference(); // read name index.
if (++next_read_ == field) return;
case kSourceUriIndex:
source_uri_index_ = builder_->ReadUInt(); // read source_uri_index.
builder_->current_script_id_ = source_uri_index_;
if (++next_read_ == field) return;
case kAnnotations:
builder_->SkipListOfExpressions(); // read annotations.
if (++next_read_ == field) return;
case kDependencies: {
intptr_t dependency_count = builder_->ReadUInt(); // read list length.
for (intptr_t i = 0; i < dependency_count; ++i) {
builder_->SkipLibraryDependency();
}
if (++next_read_ == field) return;
}
case kTypedefs: {
intptr_t typedef_count =
builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < typedef_count; i++) {
builder_->SkipLibraryTypedef();
}
if (++next_read_ == field) return;
}
case kClasses: {
int class_count = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < class_count; ++i) {
ClassHelper class_helper(builder_);
class_helper.ReadUntilExcluding(ClassHelper::kEnd);
}
if (++next_read_ == field) return;
}
case kToplevelField: {
intptr_t field_count = builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < field_count; ++i) {
FieldHelper field_helper(builder_);
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
}
if (++next_read_ == field) return;
}
case kToplevelProcedures: {
intptr_t procedure_count =
builder_->ReadListLength(); // read list length.
for (intptr_t i = 0; i < procedure_count; ++i) {
ProcedureHelper procedure_helper(builder_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
}
if (++next_read_ == field) return;
}
case kEnd:
return;
}
}
void SetNext(Fields field) { next_read_ = field; }
void SetJustRead(Fields field) {
next_read_ = field;
++next_read_;
}
NameIndex canonical_name_;
StringIndex name_index_;
intptr_t source_uri_index_;
private:
StreamingFlowGraphBuilder* builder_;
intptr_t next_read_;
};
// 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_->set_offset(new_position);
}
explicit AlternativeReadingScope(Reader* reader)
: reader_(reader), saved_offset_(reader_->offset()) {}
~AlternativeReadingScope() { reader_->set_offset(saved_offset_); }
intptr_t saved_offset() { return saved_offset_; }
private:
Reader* reader_;
intptr_t saved_offset_;
};
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_