blob: 5eb7442b3110f3e0a31b3d479b794e6bd63810cf [file] [log] [blame]
// 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_LOADER_H_
#define RUNTIME_VM_KERNEL_LOADER_H_
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/bit_vector.h"
#include "vm/compiler/frontend/bytecode_reader.h"
#include "vm/compiler/frontend/kernel_translation_helper.h"
#include "vm/hash_map.h"
#include "vm/kernel.h"
#include "vm/object.h"
#include "vm/symbols.h"
namespace dart {
namespace kernel {
class KernelLoader;
class BuildingTranslationHelper : public TranslationHelper {
public:
BuildingTranslationHelper(KernelLoader* loader,
Thread* thread,
Heap::Space space)
: TranslationHelper(thread, space),
loader_(loader),
library_lookup_handle_(Library::Handle(thread->zone())) {}
virtual ~BuildingTranslationHelper() {}
virtual RawLibrary* LookupLibraryByKernelLibrary(NameIndex library);
virtual RawClass* LookupClassByKernelClass(NameIndex klass);
private:
KernelLoader* loader_;
#if defined(DEBUG)
class LibraryLookupHandleScope {
public:
explicit LibraryLookupHandleScope(Library& lib) : lib_(lib) {
ASSERT(lib_.IsNull());
}
~LibraryLookupHandleScope() { lib_ = Library::null(); }
private:
Library& lib_;
DISALLOW_COPY_AND_ASSIGN(LibraryLookupHandleScope);
};
#endif // defined(DEBUG)
// Preallocated handle for use in LookupClassByKernelClass().
Library& library_lookup_handle_;
DISALLOW_COPY_AND_ASSIGN(BuildingTranslationHelper);
};
template <typename VmType>
class Mapping {
public:
bool Lookup(intptr_t canonical_name, VmType** handle) {
typename MapType::Pair* pair = map_.LookupPair(canonical_name);
if (pair != NULL) {
*handle = pair->value;
return true;
}
return false;
}
void Insert(intptr_t canonical_name, VmType* object) {
map_.Insert(canonical_name, object);
}
private:
typedef IntMap<VmType*> MapType;
MapType map_;
};
class LibraryIndex {
public:
// |kernel_data| is the kernel data for one library alone.
// binary_version can be -1 in which case some parts of the index might not
// be read.
explicit LibraryIndex(const ExternalTypedData& kernel_data,
int32_t binary_version);
intptr_t class_count() const { return class_count_; }
intptr_t procedure_count() const { return procedure_count_; }
intptr_t ClassOffset(intptr_t index) const {
return reader_.ReadUInt32At(class_index_offset_ + index * 4);
}
intptr_t ProcedureOffset(intptr_t index) const {
return reader_.ReadUInt32At(procedure_index_offset_ + index * 4);
}
intptr_t SizeOfClassAtOffset(intptr_t class_offset) const {
for (intptr_t i = 0, offset = class_index_offset_; i < class_count_;
++i, offset += 4) {
if (static_cast<intptr_t>(reader_.ReadUInt32At(offset)) == class_offset) {
return reader_.ReadUInt32At(offset + 4) - class_offset;
}
}
UNREACHABLE();
return -1;
}
bool HasSourceReferences() {
if (binary_version_ < 25) return false;
return true;
}
intptr_t SourceReferencesOffset() { return source_references_offset_; }
private:
Reader reader_;
int32_t binary_version_;
intptr_t source_references_offset_;
intptr_t class_index_offset_;
intptr_t class_count_;
intptr_t procedure_index_offset_;
intptr_t procedure_count_;
DISALLOW_COPY_AND_ASSIGN(LibraryIndex);
};
class ClassIndex {
public:
// |class_offset| is the offset of class' kernel data in |buffer| of
// size |size|. The size of the class' kernel data is |class_size|.
ClassIndex(const uint8_t* buffer,
intptr_t buffer_size,
intptr_t class_offset,
intptr_t class_size);
// |class_offset| is the offset of class' kernel data in |kernel_data|.
// The size of the class' kernel data is |class_size|.
ClassIndex(const ExternalTypedData& kernel_data,
intptr_t class_offset,
intptr_t class_size);
intptr_t procedure_count() const { return procedure_count_; }
intptr_t ProcedureOffset(intptr_t index) const {
return reader_.ReadUInt32At(procedure_index_offset_ + index * 4);
}
private:
void Init(intptr_t class_offset, intptr_t class_size);
Reader reader_;
intptr_t procedure_count_;
intptr_t procedure_index_offset_;
DISALLOW_COPY_AND_ASSIGN(ClassIndex);
};
struct UriToSourceTableEntry : public ZoneAllocated {
UriToSourceTableEntry() {}
const String* uri = nullptr;
const String* sources = nullptr;
const TypedData* line_starts = nullptr;
};
struct UriToSourceTableTrait {
typedef UriToSourceTableEntry* Value;
typedef const UriToSourceTableEntry* Key;
typedef UriToSourceTableEntry* Pair;
static Key KeyOf(Pair kv) { return kv; }
static Value ValueOf(Pair kv) { return kv; }
static inline intptr_t Hashcode(Key key) { return key->uri->Hash(); }
static inline bool IsKeyEqual(Pair kv, Key key) {
// Only compare uri.
return kv->uri->CompareTo(*key->uri) == 0;
}
};
class KernelLoader : public ValueObject {
public:
explicit KernelLoader(
Program* program,
DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
static Object& LoadEntireProgram(Program* program,
bool process_pending_classes = true);
// Returns the library containing the main procedure, null if there
// was no main procedure, or a failure object if there was an error.
RawObject* LoadProgram(bool process_pending_classes = true);
// Load given library.
void LoadLibrary(const Library& library);
// Returns the function which will evaluate the expression, or a failure
// object if there was an error.
RawObject* LoadExpressionEvaluationFunction(const String& library_url,
const String& klass);
// Finds all libraries that have been modified in this incremental
// version of the kernel program file.
//
// When [force_reload] is false and if [p_num_classes], [p_num_procedures] are
// not nullptr, then they are populated with number of classes and top-level
// procedures in [program].
static void FindModifiedLibraries(Program* program,
Isolate* isolate,
BitVector* modified_libs,
bool force_reload,
bool* is_empty_program,
intptr_t* p_num_classes,
intptr_t* p_num_procedures);
static RawString* FindSourceForScript(const uint8_t* kernel_buffer,
intptr_t kernel_buffer_length,
const String& url);
void FinishTopLevelClassLoading(const Class& toplevel_class,
const Library& library,
const LibraryIndex& library_index);
static void FinishLoading(const Class& klass);
void ReadObfuscationProhibitions();
// Check for the presence of a (possibly const) constructor for the
// 'ExternalName' class. If found, returns the name parameter to the
// constructor.
RawString* DetectExternalNameCtor();
// Check for the presence of a (possibly const) constructor for the 'pragma'
// class. Returns whether it was found (no details about the type of pragma).
bool DetectPragmaCtor();
bool IsClassName(NameIndex name, const String& library, const String& klass);
void AnnotateNativeProcedures();
void LoadNativeExtensionLibraries();
void EvaluateDelayedPragmas();
void ReadVMAnnotations(const Library& library,
intptr_t annotation_count,
String* native_name,
bool* is_potential_native,
bool* has_pragma_annotation);
const String& DartSymbolPlain(StringIndex index) {
return translation_helper_.DartSymbolPlain(index);
}
const String& DartSymbolObfuscate(StringIndex index) {
return translation_helper_.DartSymbolObfuscate(index);
}
private:
KernelLoader(const Script& script,
const ExternalTypedData& kernel_data,
intptr_t data_program_offset);
void InitializeFields(
DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
RawLibrary* LoadLibrary(intptr_t index);
const String& LibraryUri(intptr_t library_index) {
return translation_helper_.DartSymbolPlain(
translation_helper_.CanonicalNameString(
library_canonical_name(library_index)));
}
intptr_t library_offset(intptr_t index) {
kernel::Reader reader(program_->kernel_data(),
program_->kernel_data_size());
return reader.ReadFromIndexNoReset(reader.size(),
LibraryCountFieldCountFromEnd + 1,
program_->library_count() + 1, index);
}
NameIndex library_canonical_name(intptr_t index) {
kernel::Reader reader(program_->kernel_data(),
program_->kernel_data_size());
reader.set_offset(library_offset(index));
// Start reading library.
reader.ReadFlags();
return reader.ReadCanonicalNameReference();
}
uint8_t CharacterAt(StringIndex string_index, intptr_t index);
static void index_programs(kernel::Reader* reader,
GrowableArray<intptr_t>* subprogram_file_starts);
void walk_incremental_kernel(BitVector* modified_libs,
bool* is_empty_program,
intptr_t* p_num_classes,
intptr_t* p_num_procedures);
void LoadPreliminaryClass(ClassHelper* class_helper,
intptr_t type_parameter_count);
void ReadInferredType(const Field& field, intptr_t kernel_offset);
void CheckForInitializer(const Field& field);
void LoadClass(const Library& library,
const Class& toplevel_class,
intptr_t class_end,
Class* out_class);
void FinishClassLoading(const Class& klass,
const Library& library,
const Class& toplevel_class,
intptr_t class_offset,
const ClassIndex& class_index,
ClassHelper* class_helper);
void LoadProcedure(const Library& library,
const Class& owner,
bool in_class,
intptr_t procedure_end);
RawArray* MakeFieldsArray();
RawArray* MakeFunctionsArray();
RawScript* LoadScriptAt(
intptr_t index,
DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
// If klass's script is not the script at the uri index, return a PatchClass
// for klass whose script corresponds to the uri index.
// Otherwise return klass.
const Object& ClassForScriptAt(const Class& klass, intptr_t source_uri_index);
RawScript* ScriptAt(intptr_t source_uri_index) {
return kernel_program_info_.ScriptAt(source_uri_index);
}
void GenerateFieldAccessors(const Class& klass,
const Field& field,
FieldHelper* field_helper);
void LoadLibraryImportsAndExports(Library* library,
const Class& toplevel_class);
RawLibrary* LookupLibraryOrNull(NameIndex library);
RawLibrary* LookupLibrary(NameIndex library);
RawLibrary* LookupLibraryFromClass(NameIndex klass);
RawClass* LookupClass(const Library& library, NameIndex klass);
RawFunction::Kind GetFunctionType(ProcedureHelper::Kind procedure_kind);
void EnsureExternalClassIsLookedUp() {
if (external_name_class_.IsNull()) {
ASSERT(external_name_field_.IsNull());
const Library& internal_lib =
Library::Handle(zone_, dart::Library::InternalLibrary());
external_name_class_ = internal_lib.LookupClass(Symbols::ExternalName());
external_name_field_ = external_name_class_.LookupField(Symbols::name());
} else {
ASSERT(!external_name_field_.IsNull());
}
}
void EnsurePragmaClassIsLookedUp() {
if (pragma_class_.IsNull()) {
const Library& core_lib =
Library::Handle(zone_, dart::Library::CoreLibrary());
pragma_class_ = core_lib.LookupLocalClass(Symbols::Pragma());
ASSERT(!pragma_class_.IsNull());
}
}
void EnsurePotentialNatives() {
potential_natives_ = kernel_program_info_.potential_natives();
if (potential_natives_.IsNull()) {
// To avoid too many grows in this array, we'll set it's initial size to
// something close to the actual number of potential native functions.
potential_natives_ = GrowableObjectArray::New(100, Heap::kNew);
kernel_program_info_.set_potential_natives(potential_natives_);
}
}
void EnsurePotentialPragmaFunctions() {
potential_pragma_functions_ =
translation_helper_.EnsurePotentialPragmaFunctions();
}
void EnsurePotentialExtensionLibraries() {
if (potential_extension_libraries_.IsNull()) {
potential_extension_libraries_ = GrowableObjectArray::New();
}
}
// Returns `true` if the [library] was newly enqueued or `false`
// if it was already enqueued. Allocates storage on first enqueue.
bool EnqueueLibraryForEvaluation(const Library& library) {
evaluating_ = kernel_program_info_.evaluating();
if (evaluating_.IsNull()) {
evaluating_ = GrowableObjectArray::New();
kernel_program_info_.set_evaluating(evaluating_);
ASSERT(!evaluating_.IsNull());
} else {
for (intptr_t i = 0, n = evaluating_.Length(); i < n; i++) {
if (library.raw() == evaluating_.At(i)) {
return false;
}
}
}
evaluating_.Add(library);
return true;
}
// Dequeues most recent libary. Releases storage when empty.
void DequeueLibraryForEvaluation(const Library& library) {
ASSERT(!evaluating_.IsNull());
RawObject* object = evaluating_.RemoveLast();
ASSERT(library.raw() == object);
if (evaluating_.Length() == 0) {
evaluating_ = GrowableObjectArray::null();
kernel_program_info_.set_evaluating(evaluating_);
ASSERT(evaluating_.IsNull());
}
}
Program* program_;
Thread* thread_;
Zone* zone_;
Isolate* isolate_;
Array& patch_classes_;
ActiveClass active_class_;
// This is the offset of the current library within
// the whole kernel program.
intptr_t library_kernel_offset_;
// This is the offset by which offsets, which are set relative
// to their library's kernel data, have to be corrected.
intptr_t correction_offset_;
bool loading_native_wrappers_library_;
NameIndex skip_vmservice_library_;
ExternalTypedData& library_kernel_data_;
KernelProgramInfo& kernel_program_info_;
BuildingTranslationHelper translation_helper_;
KernelReaderHelper helper_;
TypeTranslator type_translator_;
InferredTypeMetadataHelper inferred_type_metadata_helper_;
BytecodeMetadataHelper bytecode_metadata_helper_;
Class& external_name_class_;
Field& external_name_field_;
GrowableObjectArray& evaluating_;
GrowableObjectArray& potential_natives_;
GrowableObjectArray& potential_pragma_functions_;
GrowableObjectArray& potential_extension_libraries_;
Class& pragma_class_;
Smi& name_index_handle_;
// We "re-use" the normal .dill file format for encoding compiled evaluation
// expressions from the debugger. This allows us to also reuse the normal
// a) kernel loader b) flow graph building code. The encoding is either one
// of the following two options:
//
// * Option a) The expression is evaluated inside an instance method call
// context:
//
// Program:
// |> library "evaluate:source"
// |> class "#DebugClass"
// |> procedure ":Eval"
//
// * Option b) The expression is evaluated outside an instance method call
// context:
//
// Program:
// |> library "evaluate:source"
// |> procedure ":Eval"
//
// See
// * pkg/front_end/lib/src/fasta/incremental_compiler.dart:compileExpression
// * pkg/front_end/lib/src/fasta/kernel/utils.dart:serializeProcedure
//
Library& expression_evaluation_library_;
Function& expression_evaluation_function_;
GrowableArray<const Function*> functions_;
GrowableArray<const Field*> fields_;
friend class BuildingTranslationHelper;
DISALLOW_COPY_AND_ASSIGN(KernelLoader);
};
RawFunction* CreateFieldInitializerFunction(Thread* thread,
Zone* zone,
const Field& field);
ParsedFunction* ParseStaticFieldInitializer(Zone* zone, const Field& field);
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // RUNTIME_VM_KERNEL_LOADER_H_