61f0f5bc43
This change replaces kernel AST declarations of fields and functions with bytecode declarations. Size of dilp files is reduced by 11-12%. Startup latency: Time to the first full frame: 1.945s -> 1.687s FinalizeClass: 554ms -> 277ms FinishClassLoading: 296ms -> 156ms There are following regressions in bytecode mode, which will be fixed in future: * dart:mirrors are not supported yet (implementation of mirrors relies on reading kernel AST in certain cases). As the result, lib_2/mirrors/* tests fail. * native extensions are not supported yet (annotations on libraries and classes in AST are cleaned up as they could reference members which are now removed from AST). As the result, standalone_2/entrypoints_verification_test test fails. * language_2/spread_collections/const_error_test/* tests fail due to https://github.com/dart-lang/sdk/issues/36286. Change-Id: I5130f401fd7b84038b136136e7ccc1a6e51b6cea Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97561 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
469 lines
15 KiB
C++
469 lines
15 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_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.
|
|
explicit LibraryIndex(const ExternalTypedData& kernel_data);
|
|
|
|
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;
|
|
}
|
|
|
|
private:
|
|
Reader reader_;
|
|
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);
|
|
|
|
// 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);
|
|
|
|
RawLibrary* LoadLibrary(intptr_t index);
|
|
|
|
void FinishTopLevelClassLoading(const Class& toplevel_class,
|
|
const Library& library,
|
|
const LibraryIndex& library_index);
|
|
|
|
static void FinishLoading(const Class& klass);
|
|
|
|
void ReadObfuscationProhibitions();
|
|
|
|
const Array& ReadConstantTable();
|
|
|
|
// 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(const Array& constant_table);
|
|
void LoadNativeExtensionLibraries(const Array& constant_table);
|
|
void EvaluateDelayedPragmas();
|
|
|
|
void ReadVMAnnotations(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);
|
|
}
|
|
|
|
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);
|
|
|
|
private:
|
|
friend class BuildingTranslationHelper;
|
|
|
|
KernelLoader(const Script& script,
|
|
const ExternalTypedData& kernel_data,
|
|
intptr_t data_program_offset);
|
|
|
|
void InitializeFields(
|
|
DirectChainedHashMap<UriToSourceTableTrait>* uri_to_source_table);
|
|
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 FixCoreLibraryScriptUri(const Library& library, const Script& script);
|
|
|
|
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);
|
|
}
|
|
RawScript* ScriptAt(intptr_t source_uri_index,
|
|
const Library& lib,
|
|
StringIndex import_uri);
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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& 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_;
|
|
|
|
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_
|