d0a7bad121
With generic methods, uninstantiated types will require 2 instantiators, one reflecting the class type arguments (as of today) and one reflecting the function type arguments (new). This is work in progress and the second instantiator is always null for now. R=asiva@google.com Review-Url: https://codereview.chromium.org/2799373002 .
1146 lines
39 KiB
C++
1146 lines
39 KiB
C++
// Copyright (c) 2016, 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_TO_IL_H_
|
|
#define RUNTIME_VM_KERNEL_TO_IL_H_
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/growable_array.h"
|
|
#include "vm/hash_map.h"
|
|
|
|
#include "vm/flow_graph.h"
|
|
#include "vm/flow_graph_builder.h"
|
|
#include "vm/intermediate_language.h"
|
|
#include "vm/kernel.h"
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
class StreamingFlowGraphBuilder;
|
|
|
|
class KernelConstMapKeyEqualsTraits {
|
|
public:
|
|
static const char* Name() { return "KernelConstMapKeyEqualsTraits"; }
|
|
static bool ReportStats() { return false; }
|
|
|
|
static bool IsMatch(const Object& a, const Object& b) {
|
|
const Smi& key1 = Smi::Cast(a);
|
|
const Smi& key2 = Smi::Cast(b);
|
|
return (key1.Value() == key2.Value());
|
|
}
|
|
static bool IsMatch(const intptr_t key1, const Object& b) {
|
|
return KeyAsSmi(key1) == Smi::Cast(b).raw();
|
|
}
|
|
static uword Hash(const Object& obj) {
|
|
const Smi& key = Smi::Cast(obj);
|
|
return HashValue(key.Value());
|
|
}
|
|
static uword Hash(const intptr_t key) {
|
|
return HashValue(Smi::Value(KeyAsSmi(key)));
|
|
}
|
|
static RawObject* NewKey(const intptr_t key) { return KeyAsSmi(key); }
|
|
|
|
private:
|
|
static uword HashValue(intptr_t pos) { return pos % (Smi::kMaxValue - 13); }
|
|
|
|
static RawSmi* KeyAsSmi(const intptr_t key) {
|
|
ASSERT(key >= 0);
|
|
return Smi::New(key);
|
|
}
|
|
};
|
|
typedef UnorderedHashMap<KernelConstMapKeyEqualsTraits> KernelConstantsMap;
|
|
|
|
|
|
template <typename K, typename V>
|
|
class Map : public DirectChainedHashMap<RawPointerKeyValueTrait<K, V> > {
|
|
public:
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Key Key;
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Value Value;
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Pair Pair;
|
|
|
|
inline void Insert(const Key& key, const Value& value) {
|
|
Pair pair(key, value);
|
|
DirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Insert(pair);
|
|
}
|
|
|
|
inline V Lookup(const Key& key) {
|
|
Pair* pair =
|
|
DirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Lookup(key);
|
|
if (pair == NULL) {
|
|
return V();
|
|
} else {
|
|
return pair->value;
|
|
}
|
|
}
|
|
|
|
inline Pair* LookupPair(const Key& key) {
|
|
return DirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Lookup(key);
|
|
}
|
|
};
|
|
|
|
|
|
template <typename V>
|
|
class IntKeyRawPointerValueTrait {
|
|
public:
|
|
typedef intptr_t Key;
|
|
typedef V Value;
|
|
|
|
struct Pair {
|
|
Key key;
|
|
Value value;
|
|
Pair() : key(NULL), value() {}
|
|
Pair(const Key key, const Value& value) : key(key), value(value) {}
|
|
Pair(const Pair& other) : key(other.key), value(other.value) {}
|
|
};
|
|
|
|
static Key KeyOf(Pair kv) { return kv.key; }
|
|
static Value ValueOf(Pair kv) { return kv.value; }
|
|
static intptr_t Hashcode(Key key) { return key; }
|
|
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
|
|
};
|
|
|
|
template <typename V>
|
|
class IntMap : public DirectChainedHashMap<IntKeyRawPointerValueTrait<V> > {
|
|
public:
|
|
typedef typename IntKeyRawPointerValueTrait<V>::Key Key;
|
|
typedef typename IntKeyRawPointerValueTrait<V>::Value Value;
|
|
typedef typename IntKeyRawPointerValueTrait<V>::Pair Pair;
|
|
|
|
inline void Insert(const Key& key, const Value& value) {
|
|
Pair pair(key, value);
|
|
DirectChainedHashMap<IntKeyRawPointerValueTrait<V> >::Insert(pair);
|
|
}
|
|
|
|
inline V Lookup(const Key& key) {
|
|
Pair* pair =
|
|
DirectChainedHashMap<IntKeyRawPointerValueTrait<V> >::Lookup(key);
|
|
if (pair == NULL) {
|
|
return V();
|
|
} else {
|
|
return pair->value;
|
|
}
|
|
}
|
|
|
|
inline Pair* LookupPair(const Key& key) {
|
|
return DirectChainedHashMap<IntKeyRawPointerValueTrait<V> >::Lookup(key);
|
|
}
|
|
};
|
|
|
|
template <typename K, typename V>
|
|
class MallocMap
|
|
: public MallocDirectChainedHashMap<RawPointerKeyValueTrait<K, V> > {
|
|
public:
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Key Key;
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Value Value;
|
|
typedef typename RawPointerKeyValueTrait<K, V>::Pair Pair;
|
|
|
|
inline void Insert(const Key& key, const Value& value) {
|
|
Pair pair(key, value);
|
|
MallocDirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Insert(pair);
|
|
}
|
|
|
|
inline V Lookup(const Key& key) {
|
|
Pair* pair =
|
|
MallocDirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Lookup(key);
|
|
if (pair == NULL) {
|
|
return V();
|
|
} else {
|
|
return pair->value;
|
|
}
|
|
}
|
|
|
|
inline Pair* LookupPair(const Key& key) {
|
|
return MallocDirectChainedHashMap<RawPointerKeyValueTrait<K, V> >::Lookup(
|
|
key);
|
|
}
|
|
};
|
|
|
|
|
|
class BreakableBlock;
|
|
class CatchBlock;
|
|
class FlowGraphBuilder;
|
|
class SwitchBlock;
|
|
class TryCatchBlock;
|
|
class TryFinallyBlock;
|
|
|
|
class Fragment {
|
|
public:
|
|
Instruction* entry;
|
|
Instruction* current;
|
|
|
|
Fragment() : entry(NULL), current(NULL) {}
|
|
|
|
explicit Fragment(Instruction* instruction)
|
|
: entry(instruction), current(instruction) {}
|
|
|
|
Fragment(Instruction* entry, Instruction* current)
|
|
: entry(entry), current(current) {}
|
|
|
|
bool is_open() { return entry == NULL || current != NULL; }
|
|
bool is_closed() { return !is_open(); }
|
|
|
|
Fragment& operator+=(const Fragment& other);
|
|
Fragment& operator<<=(Instruction* next);
|
|
|
|
Fragment closed();
|
|
};
|
|
|
|
Fragment operator+(const Fragment& first, const Fragment& second);
|
|
Fragment operator<<(const Fragment& fragment, Instruction* next);
|
|
|
|
typedef ZoneGrowableArray<PushArgumentInstr*>* ArgumentArray;
|
|
|
|
|
|
class ActiveClass {
|
|
public:
|
|
ActiveClass()
|
|
: kernel_class(NULL), klass(NULL), member(NULL), kernel_function(NULL) {}
|
|
|
|
// The current enclosing kernel class (if available, otherwise NULL).
|
|
Class* kernel_class;
|
|
|
|
// The current enclosing class (or the library top-level class). When this is
|
|
// a library's top-level class, the kernel_class will be NULL.
|
|
const dart::Class* klass;
|
|
|
|
// The enclosing member (e.g., Constructor, Procedure, or Field) if there
|
|
// is one.
|
|
Member* member;
|
|
|
|
// The current function.
|
|
FunctionNode* kernel_function;
|
|
};
|
|
|
|
|
|
class ActiveClassScope {
|
|
public:
|
|
ActiveClassScope(ActiveClass* active_class,
|
|
Class* kernel_class,
|
|
const dart::Class* klass)
|
|
: active_class_(active_class), saved_(*active_class) {
|
|
active_class_->kernel_class = kernel_class;
|
|
active_class_->klass = klass;
|
|
active_class_->member = NULL;
|
|
active_class_->kernel_function = NULL;
|
|
}
|
|
|
|
~ActiveClassScope() { *active_class_ = saved_; }
|
|
|
|
private:
|
|
ActiveClass* active_class_;
|
|
ActiveClass saved_;
|
|
};
|
|
|
|
|
|
class ActiveMemberScope {
|
|
public:
|
|
ActiveMemberScope(ActiveClass* active_class, Member* member)
|
|
: active_class_(active_class), saved_(*active_class) {
|
|
// The class and kernel_class is inherited.
|
|
active_class_->member = member;
|
|
active_class_->kernel_function = NULL;
|
|
}
|
|
|
|
~ActiveMemberScope() { *active_class_ = saved_; }
|
|
|
|
private:
|
|
ActiveClass* active_class_;
|
|
ActiveClass saved_;
|
|
};
|
|
|
|
|
|
class ActiveFunctionScope {
|
|
public:
|
|
ActiveFunctionScope(ActiveClass* active_class, FunctionNode* kernel_function)
|
|
: active_class_(active_class), saved_(*active_class) {
|
|
// The class, kernel_class, and member are inherited.
|
|
active_class_->kernel_function = kernel_function;
|
|
}
|
|
|
|
~ActiveFunctionScope() { *active_class_ = saved_; }
|
|
|
|
private:
|
|
ActiveClass* active_class_;
|
|
ActiveClass saved_;
|
|
};
|
|
|
|
|
|
class TranslationHelper {
|
|
public:
|
|
explicit TranslationHelper(dart::Thread* thread)
|
|
: thread_(thread),
|
|
zone_(thread->zone()),
|
|
isolate_(thread->isolate()),
|
|
allocation_space_(thread->IsMutatorThread() ? Heap::kNew : Heap::kOld) {
|
|
}
|
|
virtual ~TranslationHelper() {}
|
|
|
|
Thread* thread() { return thread_; }
|
|
|
|
Zone* zone() { return zone_; }
|
|
|
|
Isolate* isolate() { return isolate_; }
|
|
|
|
Heap::Space allocation_space() { return allocation_space_; }
|
|
|
|
RawInstance* Canonicalize(const Instance& instance);
|
|
|
|
const dart::String& DartString(const char* content) {
|
|
return DartString(content, allocation_space_);
|
|
}
|
|
const dart::String& DartString(const char* content, Heap::Space space);
|
|
|
|
dart::String& DartString(String* content) {
|
|
return DartString(content, allocation_space_);
|
|
}
|
|
dart::String& DartString(String* content, Heap::Space space);
|
|
|
|
dart::String& DartString(const uint8_t* utf8_array, intptr_t len) {
|
|
return DartString(utf8_array, len, allocation_space_);
|
|
}
|
|
dart::String& DartString(const uint8_t* utf8_array,
|
|
intptr_t len,
|
|
Heap::Space space);
|
|
|
|
const dart::String& DartSymbol(const char* content) const;
|
|
dart::String& DartSymbol(String* content) const;
|
|
dart::String& DartSymbol(const uint8_t* utf8_array, intptr_t len) const;
|
|
|
|
const dart::String& DartClassName(CanonicalName* kernel_class);
|
|
|
|
const dart::String& DartConstructorName(CanonicalName* constructor);
|
|
|
|
const dart::String& DartProcedureName(CanonicalName* procedure);
|
|
|
|
const dart::String& DartSetterName(CanonicalName* setter);
|
|
const dart::String& DartSetterName(Name* setter_name);
|
|
|
|
const dart::String& DartGetterName(CanonicalName* getter);
|
|
const dart::String& DartGetterName(Name* getter_name);
|
|
|
|
const dart::String& DartFieldName(Name* kernel_name);
|
|
|
|
const dart::String& DartInitializerName(Name* kernel_name);
|
|
|
|
const dart::String& DartMethodName(CanonicalName* method);
|
|
const dart::String& DartMethodName(Name* method_name);
|
|
|
|
const dart::String& DartFactoryName(CanonicalName* factory);
|
|
|
|
const Array& ArgumentNames(List<NamedExpression>* named);
|
|
|
|
// A subclass overrides these when reading in the Kernel program in order to
|
|
// support recursive type expressions (e.g. for "implements X" ...
|
|
// annotations).
|
|
virtual RawLibrary* LookupLibraryByKernelLibrary(CanonicalName* library);
|
|
virtual RawClass* LookupClassByKernelClass(CanonicalName* klass);
|
|
|
|
RawField* LookupFieldByKernelField(CanonicalName* field);
|
|
RawFunction* LookupStaticMethodByKernelProcedure(CanonicalName* procedure);
|
|
RawFunction* LookupConstructorByKernelConstructor(CanonicalName* constructor);
|
|
dart::RawFunction* LookupConstructorByKernelConstructor(
|
|
const dart::Class& owner,
|
|
CanonicalName* constructor);
|
|
|
|
dart::Type& GetCanonicalType(const dart::Class& klass);
|
|
|
|
void ReportError(const char* format, ...);
|
|
void ReportError(const Error& prev_error, const char* format, ...);
|
|
|
|
private:
|
|
// This will mangle [name_to_modify] if necessary and make the result a symbol
|
|
// if asked. The result will be avilable in [name_to_modify] and it is also
|
|
// returned. If the name is private, the canonical name [parent] will be used
|
|
// to get the import URI of the library where the name is visible.
|
|
dart::String& ManglePrivateName(CanonicalName* parent,
|
|
dart::String* name_to_modify,
|
|
bool symbolize = true);
|
|
|
|
const dart::String& DartSetterName(CanonicalName* parent, String* setter);
|
|
const dart::String& DartGetterName(CanonicalName* parent, String* getter);
|
|
const dart::String& DartMethodName(CanonicalName* parent, String* method);
|
|
|
|
dart::Thread* thread_;
|
|
dart::Zone* zone_;
|
|
dart::Isolate* isolate_;
|
|
Heap::Space allocation_space_;
|
|
};
|
|
|
|
// Regarding malformed types:
|
|
// The spec says in section "19.1 Static Types" roughly:
|
|
//
|
|
// A type T is malformed iff:
|
|
// * T does not denote a type in scope
|
|
// * T refers to a type parameter in a static member
|
|
// * T is a parametrized Type G<T1, ...> and G is malformed
|
|
// * T denotes declarations from multiple imports
|
|
//
|
|
// Any use of a malformed type gives rise to a static warning. A malformed
|
|
// type is then interpreted as dynamic by the static type checker and the
|
|
// runtime unless explicitly specified otherwise.
|
|
class DartTypeTranslator : public DartTypeVisitor {
|
|
public:
|
|
DartTypeTranslator(TranslationHelper* helper,
|
|
ActiveClass* active_class,
|
|
bool finalize = false)
|
|
: translation_helper_(*helper),
|
|
active_class_(active_class),
|
|
type_parameter_scope_(NULL),
|
|
zone_(helper->zone()),
|
|
result_(AbstractType::Handle(helper->zone())),
|
|
finalize_(finalize) {}
|
|
|
|
// Can return a malformed type.
|
|
AbstractType& TranslateType(DartType* node);
|
|
|
|
// Can return a malformed type.
|
|
AbstractType& TranslateTypeWithoutFinalization(DartType* node);
|
|
|
|
// Is guaranteed to be not malformed.
|
|
const AbstractType& TranslateVariableType(VariableDeclaration* variable);
|
|
|
|
|
|
virtual void VisitDefaultDartType(DartType* node) { UNREACHABLE(); }
|
|
|
|
virtual void VisitInvalidType(InvalidType* node);
|
|
|
|
virtual void VisitFunctionType(FunctionType* node);
|
|
|
|
virtual void VisitTypeParameterType(TypeParameterType* node);
|
|
|
|
virtual void VisitInterfaceType(InterfaceType* node);
|
|
|
|
virtual void VisitDynamicType(DynamicType* node);
|
|
|
|
virtual void VisitVoidType(VoidType* node);
|
|
|
|
// Will return `TypeArguments::null()` in case any of the arguments are
|
|
// malformed.
|
|
const TypeArguments& TranslateInstantiatedTypeArguments(
|
|
const dart::Class& receiver_class,
|
|
DartType** receiver_type_arguments,
|
|
intptr_t length);
|
|
|
|
// Will return `TypeArguments::null()` in case any of the arguments are
|
|
// malformed.
|
|
const TypeArguments& TranslateTypeArguments(DartType** dart_types,
|
|
intptr_t length);
|
|
|
|
const Type& ReceiverType(const dart::Class& klass);
|
|
|
|
private:
|
|
class TypeParameterScope {
|
|
public:
|
|
TypeParameterScope(DartTypeTranslator* translator,
|
|
List<TypeParameter>* parameters)
|
|
: parameters_(parameters),
|
|
outer_(translator->type_parameter_scope_),
|
|
translator_(translator) {
|
|
translator_->type_parameter_scope_ = this;
|
|
}
|
|
~TypeParameterScope() { translator_->type_parameter_scope_ = outer_; }
|
|
|
|
TypeParameterScope* outer() const { return outer_; }
|
|
List<TypeParameter>* parameters() const { return parameters_; }
|
|
|
|
private:
|
|
List<TypeParameter>* parameters_;
|
|
TypeParameterScope* outer_;
|
|
DartTypeTranslator* translator_;
|
|
};
|
|
|
|
TranslationHelper& translation_helper_;
|
|
ActiveClass* active_class_;
|
|
TypeParameterScope* type_parameter_scope_;
|
|
Zone* zone_;
|
|
AbstractType& result_;
|
|
bool finalize_;
|
|
};
|
|
|
|
|
|
// 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 ConstantEvaluator : public ExpressionVisitor {
|
|
public:
|
|
ConstantEvaluator(FlowGraphBuilder* builder,
|
|
Zone* zone,
|
|
TranslationHelper* h,
|
|
DartTypeTranslator* type_translator);
|
|
virtual ~ConstantEvaluator() {}
|
|
|
|
Instance& EvaluateExpression(Expression* node);
|
|
Object& EvaluateExpressionSafe(Expression* node);
|
|
Instance& EvaluateConstructorInvocation(ConstructorInvocation* node);
|
|
Instance& EvaluateListLiteral(ListLiteral* node);
|
|
Instance& EvaluateMapLiteral(MapLiteral* node);
|
|
|
|
virtual void VisitDefaultExpression(Expression* node) { UNREACHABLE(); }
|
|
|
|
virtual void VisitBigintLiteral(BigintLiteral* node);
|
|
virtual void VisitBoolLiteral(BoolLiteral* node);
|
|
virtual void VisitDoubleLiteral(DoubleLiteral* node);
|
|
virtual void VisitIntLiteral(IntLiteral* node);
|
|
virtual void VisitNullLiteral(NullLiteral* node);
|
|
virtual void VisitStringLiteral(StringLiteral* node);
|
|
virtual void VisitSymbolLiteral(SymbolLiteral* node);
|
|
virtual void VisitTypeLiteral(TypeLiteral* node);
|
|
|
|
virtual void VisitListLiteral(ListLiteral* node);
|
|
virtual void VisitMapLiteral(MapLiteral* node);
|
|
|
|
virtual void VisitConstructorInvocation(ConstructorInvocation* node);
|
|
virtual void VisitMethodInvocation(MethodInvocation* node);
|
|
virtual void VisitStaticGet(StaticGet* node);
|
|
virtual void VisitVariableGet(VariableGet* node);
|
|
virtual void VisitLet(Let* node);
|
|
virtual void VisitStaticInvocation(StaticInvocation* node);
|
|
virtual void VisitStringConcatenation(StringConcatenation* node);
|
|
virtual void VisitConditionalExpression(ConditionalExpression* node);
|
|
virtual void VisitLogicalExpression(LogicalExpression* node);
|
|
virtual void VisitNot(Not* node);
|
|
virtual void VisitPropertyGet(PropertyGet* node);
|
|
|
|
private:
|
|
// This will translate type arguments form [kernel_arguments]. If no type
|
|
// arguments are passed and the [target] is a factory then the null type
|
|
// argument array will be returned.
|
|
//
|
|
// If none of these cases apply, NULL will be returned.
|
|
const TypeArguments* TranslateTypeArguments(const Function& target,
|
|
dart::Class* target_klass,
|
|
Arguments* kernel_arguments);
|
|
|
|
const Object& RunFunction(const Function& function,
|
|
Arguments* arguments,
|
|
const Instance* receiver = NULL,
|
|
const TypeArguments* type_args = NULL);
|
|
|
|
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);
|
|
|
|
void AssertBoolInCheckedMode() {
|
|
if (isolate_->type_checks() && !result_.IsBool()) {
|
|
translation_helper_.ReportError("Expected boolean expression.");
|
|
}
|
|
}
|
|
|
|
bool EvaluateBooleanExpression(Expression* expression) {
|
|
EvaluateExpression(expression);
|
|
AssertBoolInCheckedMode();
|
|
return result_.raw() == Bool::True().raw();
|
|
}
|
|
|
|
bool GetCachedConstant(TreeNode* node, Instance* value);
|
|
void CacheConstantValue(TreeNode* node, const Instance& value);
|
|
|
|
FlowGraphBuilder* builder_;
|
|
Isolate* isolate_;
|
|
Zone* zone_;
|
|
TranslationHelper& translation_helper_;
|
|
DartTypeTranslator& type_translator_;
|
|
|
|
Script& script_;
|
|
Instance& result_;
|
|
};
|
|
|
|
|
|
struct FunctionScope {
|
|
intptr_t kernel_offset;
|
|
LocalScope* scope;
|
|
};
|
|
|
|
|
|
class ScopeBuildingResult : public ZoneAllocated {
|
|
public:
|
|
ScopeBuildingResult()
|
|
: this_variable(NULL),
|
|
type_arguments_variable(NULL),
|
|
switch_variable(NULL),
|
|
finally_return_variable(NULL),
|
|
setter_value(NULL),
|
|
yield_jump_variable(NULL),
|
|
yield_context_variable(NULL) {}
|
|
|
|
IntMap<LocalVariable*> locals;
|
|
IntMap<LocalScope*> scopes;
|
|
GrowableArray<FunctionScope> function_scopes;
|
|
|
|
// Only non-NULL for instance functions.
|
|
LocalVariable* this_variable;
|
|
|
|
// Only non-NULL for factory constructor functions.
|
|
LocalVariable* type_arguments_variable;
|
|
|
|
// Non-NULL when the function contains a switch statement.
|
|
LocalVariable* switch_variable;
|
|
|
|
// Non-NULL when the function contains a return inside a finally block.
|
|
LocalVariable* finally_return_variable;
|
|
|
|
// Non-NULL when the function is a setter.
|
|
LocalVariable* setter_value;
|
|
|
|
// Non-NULL if the function contains yield statement.
|
|
// TODO(27590) actual variable is called :await_jump_var, we should rename
|
|
// it to reflect the fact that it is used for both await and yield.
|
|
LocalVariable* yield_jump_variable;
|
|
|
|
// Non-NULL if the function contains yield statement.
|
|
// TODO(27590) actual variable is called :await_ctx_var, we should rename
|
|
// it to reflect the fact that it is used for both await and yield.
|
|
LocalVariable* yield_context_variable;
|
|
|
|
// Variables used in exception handlers, one per exception handler nesting
|
|
// level.
|
|
GrowableArray<LocalVariable*> exception_variables;
|
|
GrowableArray<LocalVariable*> stack_trace_variables;
|
|
GrowableArray<LocalVariable*> catch_context_variables;
|
|
|
|
// For-in iterators, one per for-in nesting level.
|
|
GrowableArray<LocalVariable*> iterator_variables;
|
|
};
|
|
|
|
|
|
class ScopeBuilder : public RecursiveVisitor {
|
|
public:
|
|
ScopeBuilder(ParsedFunction* parsed_function, TreeNode* node)
|
|
: result_(NULL),
|
|
parsed_function_(parsed_function),
|
|
node_(node),
|
|
translation_helper_(Thread::Current()),
|
|
zone_(translation_helper_.zone()),
|
|
type_translator_(&translation_helper_,
|
|
&active_class_,
|
|
/*finalize=*/true),
|
|
current_function_scope_(NULL),
|
|
scope_(NULL),
|
|
depth_(0),
|
|
name_index_(0),
|
|
needs_expr_temp_(false) {}
|
|
|
|
virtual ~ScopeBuilder() {}
|
|
|
|
ScopeBuildingResult* BuildScopes();
|
|
|
|
virtual void VisitName(Name* node) { /* NOP */
|
|
}
|
|
|
|
virtual void VisitThisExpression(ThisExpression* node);
|
|
virtual void VisitTypeParameterType(TypeParameterType* node);
|
|
virtual void VisitVariableGet(VariableGet* node);
|
|
virtual void VisitVariableSet(VariableSet* node);
|
|
virtual void VisitConditionalExpression(ConditionalExpression* node);
|
|
virtual void VisitLogicalExpression(LogicalExpression* node);
|
|
virtual void VisitFunctionExpression(FunctionExpression* node);
|
|
virtual void VisitLet(Let* node);
|
|
virtual void VisitBlock(Block* node);
|
|
virtual void VisitVariableDeclaration(VariableDeclaration* node);
|
|
virtual void VisitFunctionDeclaration(FunctionDeclaration* node);
|
|
virtual void VisitWhileStatement(WhileStatement* node);
|
|
virtual void VisitDoStatement(DoStatement* node);
|
|
virtual void VisitForStatement(ForStatement* node);
|
|
virtual void VisitForInStatement(ForInStatement* node);
|
|
virtual void VisitSwitchStatement(SwitchStatement* node);
|
|
virtual void VisitReturnStatement(ReturnStatement* node);
|
|
virtual void VisitTryCatch(TryCatch* node);
|
|
virtual void VisitTryFinally(TryFinally* node);
|
|
virtual void VisitYieldStatement(YieldStatement* node);
|
|
virtual void VisitAssertStatement(AssertStatement* node);
|
|
|
|
virtual void VisitFunctionNode(FunctionNode* node);
|
|
|
|
virtual void VisitConstructor(Constructor* node);
|
|
|
|
private:
|
|
void EnterScope(TreeNode* node, TokenPosition start_position);
|
|
void ExitScope(TokenPosition end_position);
|
|
|
|
const Type& TranslateVariableType(VariableDeclaration* variable);
|
|
LocalVariable* MakeVariable(TokenPosition declaration_pos,
|
|
TokenPosition token_pos,
|
|
const dart::String& name,
|
|
const AbstractType& type);
|
|
|
|
void AddParameters(FunctionNode* function, intptr_t pos = 0);
|
|
void AddParameter(VariableDeclaration* declaration, intptr_t pos);
|
|
void AddVariable(VariableDeclaration* declaration);
|
|
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(VariableDeclaration* declaration);
|
|
|
|
const dart::String& GenerateName(const char* prefix, intptr_t suffix);
|
|
|
|
void HandleLocalFunction(TreeNode* parent, FunctionNode* function);
|
|
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_;
|
|
TreeNode* node_;
|
|
|
|
ActiveClass active_class_;
|
|
|
|
TranslationHelper translation_helper_;
|
|
Zone* zone_;
|
|
DartTypeTranslator type_translator_;
|
|
|
|
FunctionNode* current_function_node_;
|
|
LocalScope* current_function_scope_;
|
|
LocalScope* scope_;
|
|
DepthState depth_;
|
|
|
|
intptr_t name_index_;
|
|
|
|
bool needs_expr_temp_;
|
|
};
|
|
|
|
class FlowGraphBuilder : public ExpressionVisitor, public StatementVisitor {
|
|
public:
|
|
FlowGraphBuilder(TreeNode* node,
|
|
ParsedFunction* parsed_function,
|
|
const ZoneGrowableArray<const ICData*>& ic_data_array,
|
|
InlineExitCollector* exit_collector,
|
|
intptr_t osr_id,
|
|
intptr_t first_block_id = 1);
|
|
virtual ~FlowGraphBuilder();
|
|
|
|
FlowGraph* BuildGraph();
|
|
|
|
virtual void VisitDefaultExpression(Expression* node) { UNREACHABLE(); }
|
|
virtual void VisitDefaultStatement(Statement* node) { UNREACHABLE(); }
|
|
|
|
virtual void VisitInvalidExpression(InvalidExpression* node);
|
|
virtual void VisitNullLiteral(NullLiteral* node);
|
|
virtual void VisitBoolLiteral(BoolLiteral* node);
|
|
virtual void VisitIntLiteral(IntLiteral* node);
|
|
virtual void VisitBigintLiteral(BigintLiteral* node);
|
|
virtual void VisitDoubleLiteral(DoubleLiteral* node);
|
|
virtual void VisitStringLiteral(StringLiteral* node);
|
|
virtual void VisitSymbolLiteral(SymbolLiteral* node);
|
|
virtual void VisitTypeLiteral(TypeLiteral* node);
|
|
virtual void VisitVariableGet(VariableGet* node);
|
|
virtual void VisitVariableSet(VariableSet* node);
|
|
virtual void VisitStaticGet(StaticGet* node);
|
|
virtual void VisitStaticSet(StaticSet* node);
|
|
virtual void VisitPropertyGet(PropertyGet* node);
|
|
virtual void VisitPropertySet(PropertySet* node);
|
|
virtual void VisitDirectPropertyGet(DirectPropertyGet* node);
|
|
virtual void VisitDirectPropertySet(DirectPropertySet* node);
|
|
virtual void VisitStaticInvocation(StaticInvocation* node);
|
|
virtual void VisitMethodInvocation(MethodInvocation* node);
|
|
virtual void VisitDirectMethodInvocation(DirectMethodInvocation* node);
|
|
virtual void VisitConstructorInvocation(ConstructorInvocation* node);
|
|
virtual void VisitIsExpression(IsExpression* node);
|
|
virtual void VisitAsExpression(AsExpression* node);
|
|
virtual void VisitConditionalExpression(ConditionalExpression* node);
|
|
virtual void VisitLogicalExpression(LogicalExpression* node);
|
|
virtual void VisitNot(Not* node);
|
|
virtual void VisitThisExpression(ThisExpression* node);
|
|
virtual void VisitStringConcatenation(StringConcatenation* node);
|
|
virtual void VisitListLiteral(ListLiteral* node);
|
|
virtual void VisitMapLiteral(MapLiteral* node);
|
|
virtual void VisitFunctionExpression(FunctionExpression* node);
|
|
virtual void VisitLet(Let* node);
|
|
virtual void VisitThrow(Throw* node);
|
|
virtual void VisitRethrow(Rethrow* node);
|
|
|
|
virtual void VisitInvalidStatement(InvalidStatement* node);
|
|
virtual void VisitEmptyStatement(EmptyStatement* node);
|
|
virtual void VisitBlock(Block* node);
|
|
virtual void VisitReturnStatement(ReturnStatement* node);
|
|
virtual void VisitExpressionStatement(ExpressionStatement* node);
|
|
virtual void VisitVariableDeclaration(VariableDeclaration* node);
|
|
virtual void VisitFunctionDeclaration(FunctionDeclaration* node);
|
|
virtual void VisitIfStatement(IfStatement* node);
|
|
virtual void VisitWhileStatement(WhileStatement* node);
|
|
virtual void VisitDoStatement(DoStatement* node);
|
|
virtual void VisitForStatement(ForStatement* node);
|
|
virtual void VisitForInStatement(ForInStatement* node);
|
|
virtual void VisitLabeledStatement(LabeledStatement* node);
|
|
virtual void VisitBreakStatement(BreakStatement* node);
|
|
virtual void VisitSwitchStatement(SwitchStatement* node);
|
|
virtual void VisitContinueSwitchStatement(ContinueSwitchStatement* node);
|
|
virtual void VisitAssertStatement(AssertStatement* node);
|
|
virtual void VisitTryFinally(TryFinally* node);
|
|
virtual void VisitTryCatch(TryCatch* node);
|
|
virtual void VisitYieldStatement(YieldStatement* node);
|
|
|
|
private:
|
|
FlowGraph* BuildGraphOfFunction(FunctionNode* node,
|
|
Constructor* constructor = NULL);
|
|
FlowGraph* BuildGraphOfFieldAccessor(Field* node,
|
|
LocalVariable* setter_value);
|
|
FlowGraph* BuildGraphOfStaticFieldInitializer(Field* node);
|
|
FlowGraph* BuildGraphOfMethodExtractor(const Function& method);
|
|
FlowGraph* BuildGraphOfImplicitClosureFunction(FunctionNode* kernel_function,
|
|
const Function& function);
|
|
FlowGraph* BuildGraphOfNoSuchMethodDispatcher(const Function& function);
|
|
FlowGraph* BuildGraphOfInvokeFieldDispatcher(const Function& function);
|
|
|
|
Fragment NativeFunctionBody(FunctionNode* kernel_function,
|
|
const Function& function);
|
|
|
|
void SetupDefaultParameterValues(FunctionNode* function);
|
|
|
|
TargetEntryInstr* BuildTargetEntry();
|
|
JoinEntryInstr* BuildJoinEntry();
|
|
JoinEntryInstr* BuildJoinEntry(intptr_t try_index);
|
|
|
|
Fragment TranslateArguments(Arguments* node, Array* argument_names);
|
|
ArgumentArray GetArguments(int count);
|
|
|
|
Fragment TranslateInitializers(Class* kernel_class,
|
|
List<Initializer>* initialiers);
|
|
Fragment TranslateFieldInitializer(CanonicalName* canonical_name,
|
|
Expression* init);
|
|
|
|
Fragment TranslateStatement(Statement* statement);
|
|
Fragment TranslateCondition(Expression* expression, bool* negate);
|
|
Fragment TranslateExpression(Expression* expression);
|
|
|
|
Fragment TranslateFinallyFinalizers(TryFinallyBlock* outer_finally,
|
|
intptr_t target_context_depth);
|
|
|
|
Fragment TranslateFunctionNode(FunctionNode* node, TreeNode* parent);
|
|
|
|
Fragment EnterScope(TreeNode* node, bool* new_context = NULL);
|
|
Fragment ExitScope(TreeNode* node);
|
|
|
|
Fragment LoadContextAt(int depth);
|
|
Fragment AdjustContextTo(int depth);
|
|
|
|
Fragment PushContext(int size);
|
|
Fragment PopContext();
|
|
|
|
Fragment LoadInstantiatorTypeArguments();
|
|
Fragment LoadFunctionTypeArguments();
|
|
Fragment InstantiateType(const AbstractType& type);
|
|
Fragment InstantiateTypeArguments(const TypeArguments& type_arguments);
|
|
Fragment TranslateInstantiatedTypeArguments(
|
|
const TypeArguments& type_arguments);
|
|
|
|
Fragment AllocateContext(int size);
|
|
Fragment AllocateObject(const dart::Class& klass, intptr_t argument_count);
|
|
Fragment AllocateObject(const dart::Class& klass,
|
|
const Function& closure_function);
|
|
Fragment BooleanNegate();
|
|
Fragment StrictCompare(Token::Kind kind, bool number_check = false);
|
|
Fragment BranchIfTrue(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate = false);
|
|
Fragment BranchIfNull(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate = false);
|
|
Fragment BranchIfEqual(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry,
|
|
bool negate = false);
|
|
Fragment BranchIfStrictEqual(TargetEntryInstr** then_entry,
|
|
TargetEntryInstr** otherwise_entry);
|
|
Fragment CatchBlockEntry(const Array& handler_types,
|
|
intptr_t handler_index,
|
|
bool needs_stacktrace);
|
|
Fragment TryCatch(int try_handler_index);
|
|
Fragment CheckStackOverflowInPrologue();
|
|
Fragment CheckStackOverflow();
|
|
Fragment CloneContext();
|
|
Fragment Constant(const Object& value);
|
|
Fragment CreateArray();
|
|
Fragment Goto(JoinEntryInstr* destination);
|
|
Fragment IntConstant(int64_t value);
|
|
Fragment InstanceCall(TokenPosition position,
|
|
const dart::String& name,
|
|
Token::Kind kind,
|
|
intptr_t argument_count,
|
|
intptr_t num_args_checked = 1);
|
|
Fragment InstanceCall(TokenPosition position,
|
|
const dart::String& name,
|
|
Token::Kind kind,
|
|
intptr_t argument_count,
|
|
const Array& argument_names,
|
|
intptr_t num_args_checked = 1);
|
|
Fragment ClosureCall(int argument_count, const Array& argument_names);
|
|
Fragment ThrowException(TokenPosition position);
|
|
Fragment RethrowException(TokenPosition position, int catch_try_index);
|
|
Fragment LoadClassId();
|
|
Fragment LoadField(const dart::Field& field);
|
|
Fragment LoadField(intptr_t offset, intptr_t class_id = kDynamicCid);
|
|
Fragment LoadNativeField(MethodRecognizer::Kind kind,
|
|
intptr_t offset,
|
|
const Type& type,
|
|
intptr_t class_id,
|
|
bool is_immutable = false);
|
|
Fragment LoadLocal(LocalVariable* variable);
|
|
Fragment InitStaticField(const dart::Field& field);
|
|
Fragment LoadStaticField();
|
|
Fragment NullConstant();
|
|
Fragment NativeCall(const dart::String* name, const Function* function);
|
|
Fragment PushArgument();
|
|
Fragment Return(TokenPosition position);
|
|
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 StoreIndexed(intptr_t class_id);
|
|
Fragment StoreInstanceFieldGuarded(const dart::Field& field,
|
|
bool is_initialization_store);
|
|
Fragment StoreInstanceField(
|
|
const dart::Field& field,
|
|
bool is_initialization_store,
|
|
StoreBarrierType emit_store_barrier = kEmitStoreBarrier);
|
|
Fragment StoreInstanceField(
|
|
TokenPosition position,
|
|
intptr_t offset,
|
|
StoreBarrierType emit_store_barrier = kEmitStoreBarrier);
|
|
Fragment StoreLocal(TokenPosition position, LocalVariable* variable);
|
|
Fragment StoreStaticField(TokenPosition position, const dart::Field& field);
|
|
Fragment StringInterpolate(TokenPosition position);
|
|
Fragment StringInterpolateSingle(TokenPosition position);
|
|
Fragment ThrowTypeError();
|
|
Fragment ThrowNoSuchMethodError();
|
|
Fragment BuildImplicitClosureCreation(const Function& target);
|
|
Fragment GuardFieldLength(const dart::Field& field, intptr_t deopt_id);
|
|
Fragment GuardFieldClass(const dart::Field& field, intptr_t deopt_id);
|
|
|
|
Fragment EvaluateAssertion();
|
|
Fragment CheckReturnTypeInCheckedMode();
|
|
Fragment CheckVariableTypeInCheckedMode(VariableDeclaration* variable);
|
|
Fragment CheckBooleanInCheckedMode();
|
|
Fragment CheckAssignableInCheckedMode(const dart::AbstractType& dst_type,
|
|
const dart::String& dst_name);
|
|
|
|
Fragment AssertBool();
|
|
Fragment AssertAssignable(const dart::AbstractType& dst_type,
|
|
const dart::String& dst_name);
|
|
|
|
bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
|
|
bool NeedsDebugStepCheck(Value* value, TokenPosition position);
|
|
Fragment DebugStepCheck(TokenPosition position);
|
|
|
|
dart::RawFunction* LookupMethodByMember(CanonicalName* target,
|
|
const dart::String& method_name);
|
|
|
|
LocalVariable* MakeTemporary();
|
|
LocalVariable* MakeNonTemporary(const dart::String& symbol);
|
|
|
|
intptr_t CurrentTryIndex();
|
|
intptr_t AllocateTryIndex() { return next_used_try_index_++; }
|
|
|
|
void AddVariable(VariableDeclaration* declaration, LocalVariable* variable);
|
|
void AddParameter(VariableDeclaration* declaration,
|
|
LocalVariable* variable,
|
|
intptr_t pos);
|
|
dart::LocalVariable* LookupVariable(VariableDeclaration* var);
|
|
|
|
void SetTempIndex(Definition* definition);
|
|
|
|
void Push(Definition* definition);
|
|
Value* Pop();
|
|
Fragment Drop();
|
|
|
|
bool IsInlining() { return exit_collector_ != NULL; }
|
|
|
|
Token::Kind MethodKind(const dart::String& name);
|
|
|
|
void InlineBailout(const char* reason);
|
|
|
|
TranslationHelper translation_helper_;
|
|
Zone* zone_;
|
|
|
|
// The node we are currently compiling (e.g. FunctionNode, Constructor,
|
|
// Field)
|
|
TreeNode* node_;
|
|
|
|
ParsedFunction* parsed_function_;
|
|
intptr_t osr_id_;
|
|
const ZoneGrowableArray<const ICData*>& ic_data_array_;
|
|
InlineExitCollector* exit_collector_;
|
|
|
|
intptr_t next_block_id_;
|
|
intptr_t AllocateBlockId() { return next_block_id_++; }
|
|
|
|
intptr_t next_function_id_;
|
|
intptr_t AllocateFunctionId() { return next_function_id_++; }
|
|
|
|
intptr_t context_depth_;
|
|
intptr_t loop_depth_;
|
|
intptr_t try_depth_;
|
|
intptr_t catch_depth_;
|
|
intptr_t for_in_depth_;
|
|
Fragment fragment_;
|
|
Value* stack_;
|
|
intptr_t pending_argument_count_;
|
|
|
|
GraphEntryInstr* graph_entry_;
|
|
|
|
ScopeBuildingResult* scopes_;
|
|
|
|
struct YieldContinuation {
|
|
Instruction* entry;
|
|
intptr_t try_index;
|
|
|
|
YieldContinuation(Instruction* entry, intptr_t try_index)
|
|
: entry(entry), try_index(try_index) {}
|
|
|
|
YieldContinuation()
|
|
: entry(NULL), try_index(CatchClauseNode::kInvalidTryIndex) {}
|
|
};
|
|
|
|
GrowableArray<YieldContinuation> yield_continuations_;
|
|
|
|
LocalVariable* CurrentException() {
|
|
return scopes_->exception_variables[catch_depth_ - 1];
|
|
}
|
|
LocalVariable* CurrentStackTrace() {
|
|
return scopes_->stack_trace_variables[catch_depth_ - 1];
|
|
}
|
|
LocalVariable* CurrentCatchContext() {
|
|
return scopes_->catch_context_variables[try_depth_];
|
|
}
|
|
|
|
|
|
// A chained list of breakable blocks. Chaining and lookup is done by the
|
|
// [BreakableBlock] class.
|
|
BreakableBlock* breakable_block_;
|
|
|
|
// A chained list of switch blocks. Chaining and lookup is done by the
|
|
// [SwitchBlock] class.
|
|
SwitchBlock* switch_block_;
|
|
|
|
// A chained list of try-finally blocks. Chaining and lookup is done by the
|
|
// [TryFinallyBlock] class.
|
|
TryFinallyBlock* try_finally_block_;
|
|
|
|
// A chained list of try-catch blocks. Chaining and lookup is done by the
|
|
// [TryCatchBlock] class.
|
|
TryCatchBlock* try_catch_block_;
|
|
intptr_t next_used_try_index_;
|
|
|
|
// A chained list of catch blocks. Chaining and lookup is done by the
|
|
// [CatchBlock] class.
|
|
CatchBlock* catch_block_;
|
|
|
|
ActiveClass active_class_;
|
|
DartTypeTranslator type_translator_;
|
|
ConstantEvaluator constant_evaluator_;
|
|
|
|
StreamingFlowGraphBuilder* streaming_flow_graph_builder_;
|
|
|
|
friend class BreakableBlock;
|
|
friend class CatchBlock;
|
|
friend class ConstantEvaluator;
|
|
friend class DartTypeTranslator;
|
|
friend class StreamingFlowGraphBuilder;
|
|
friend class ScopeBuilder;
|
|
friend class SwitchBlock;
|
|
friend class TryCatchBlock;
|
|
friend class TryFinallyBlock;
|
|
};
|
|
|
|
|
|
class CatchBlock {
|
|
public:
|
|
CatchBlock(FlowGraphBuilder* builder,
|
|
LocalVariable* exception_var,
|
|
LocalVariable* stack_trace_var,
|
|
intptr_t catch_try_index)
|
|
: builder_(builder),
|
|
outer_(builder->catch_block_),
|
|
exception_var_(exception_var),
|
|
stack_trace_var_(stack_trace_var),
|
|
catch_try_index_(catch_try_index) {
|
|
builder_->catch_block_ = this;
|
|
}
|
|
~CatchBlock() { builder_->catch_block_ = outer_; }
|
|
|
|
LocalVariable* exception_var() { return exception_var_; }
|
|
LocalVariable* stack_trace_var() { return stack_trace_var_; }
|
|
intptr_t catch_try_index() { return catch_try_index_; }
|
|
|
|
private:
|
|
FlowGraphBuilder* builder_;
|
|
CatchBlock* outer_;
|
|
LocalVariable* exception_var_;
|
|
LocalVariable* stack_trace_var_;
|
|
intptr_t catch_try_index_;
|
|
};
|
|
|
|
|
|
RawObject* EvaluateMetadata(TreeNode* const kernel_node);
|
|
RawObject* BuildParameterDescriptor(TreeNode* const kernel_node);
|
|
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#else // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/object.h"
|
|
#include "vm/kernel.h"
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
RawObject* EvaluateMetadata(TreeNode* const kernel_node);
|
|
RawObject* BuildParameterDescriptor(TreeNode* const kernel_node);
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
#endif // RUNTIME_VM_KERNEL_TO_IL_H_
|