[vm] Load top-level functions and variables lazily

LoadKernel part of startup: 409ms -> 196-210ms.
FinishTopLevelClassLoading: 15ms

Change-Id: If47f06fe235a70ed08e6a3d75e8f871edae5e45c
Reviewed-on: https://dart-review.googlesource.com/c/92155
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
This commit is contained in:
Alexander Markov 2019-02-07 18:12:34 +00:00 committed by commit-bot@chromium.org
parent 20f12ad874
commit a0516965b4
7 changed files with 120 additions and 59 deletions

View file

@ -1032,6 +1032,8 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_members, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
const Library& library = Library::Handle(ref.GetLibraryReferent());
library.EnsureTopLevelClassIsFinalized();
Instance& member_mirror = Instance::Handle();
const GrowableObjectArray& member_mirrors =
GrowableObjectArray::Handle(GrowableObjectArray::New());

View file

@ -1101,11 +1101,6 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
interface_class.DisableCHAImplementorUsers();
}
}
// A top level class is loaded eagerly so just finalize it.
if (cls.IsTopLevel()) {
FinalizeClass(cls);
}
}
void ClassFinalizer::FinalizeClass(const Class& cls) {
@ -1132,8 +1127,8 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
#if !defined(DART_PRECOMPILED_RUNTIME)
// If loading from a kernel, make sure that the class is fully loaded.
// Top level classes are always fully loaded.
if (!cls.IsTopLevel() && cls.kernel_offset() > 0) {
ASSERT(cls.IsTopLevel() || (cls.kernel_offset() > 0));
if (!cls.is_loaded()) {
kernel::KernelLoader::FinishLoading(cls);
if (cls.is_finalized()) {
return;
@ -1181,10 +1176,6 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
RawError* ClassFinalizer::LoadClassMembers(const Class& cls) {
ASSERT(Thread::Current()->IsMutatorThread());
// If class is a top level class it is already loaded.
if (cls.IsTopLevel()) {
return Error::null();
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
ClassFinalizer::FinalizeClass(cls);

View file

@ -298,6 +298,7 @@ void CollectTokenPositionsFor(const Script& interesting_script) {
auto& temp_function = Function::Handle(zone);
for (intptr_t i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
lib.EnsureTopLevelClassIsFinalized();
DictionaryIterator it(lib);
while (it.HasNext()) {
entry = it.GetNext();

View file

@ -900,7 +900,6 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
LibraryIndex library_index(library_kernel_data_);
intptr_t class_count = library_index.class_count();
intptr_t procedure_count = library_index.procedure_count();
library_helper.ReadUntilIncluding(LibraryHelper::kName);
library.SetName(H.DartSymbolObfuscate(library_helper.name_index_));
@ -969,13 +968,47 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
classes.Add(klass, Heap::kOld);
}
}
helper_.SetOffset(next_class_offset);
if (loading_native_wrappers_library_ || !register_class) {
FinishTopLevelClassLoading(toplevel_class, library, library_index);
}
if (FLAG_enable_mirrors && annotation_count > 0) {
ASSERT(annotations_kernel_offset > 0);
library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
annotations_kernel_offset);
}
if (register_class) {
classes.Add(toplevel_class, Heap::kOld);
}
if (!library.Loaded()) library.SetLoaded();
return library.raw();
}
void KernelLoader::FinishTopLevelClassLoading(
const Class& toplevel_class,
const Library& library,
const LibraryIndex& library_index) {
if (toplevel_class.is_loaded()) {
return;
}
TIMELINE_DURATION(Thread::Current(), Isolate, "FinishTopLevelClassLoading");
// Offsets within library index are whole program offsets and not
// relative to the library.
const intptr_t correction = correction_offset_ - library_kernel_offset_;
helper_.SetOffset(library_index.ClassOffset(library_index.class_count()) +
correction);
fields_.Clear();
functions_.Clear();
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
// Load toplevel fields.
intptr_t field_count = helper_.ReadListLength(); // read list length.
const intptr_t field_count = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < field_count; ++i) {
intptr_t field_offset = helper_.ReaderOffset() - correction_offset_;
ActiveMemberScope active_member_scope(&active_class_, NULL);
@ -1002,7 +1035,7 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
// In the VM all const fields are implicitly final whereas in Kernel they
// are not final because they are not explicitly declared that way.
const bool is_final = field_helper.IsConst() || field_helper.IsFinal();
Field& field = Field::Handle(
const Field& field = Field::Handle(
Z,
Field::NewTopLevel(name, is_final, field_helper.IsConst(), script_class,
field_helper.position_, field_helper.end_position_));
@ -1026,31 +1059,45 @@ RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
library.AddFieldMetadata(field, TokenPosition::kNoSource, field_offset);
}
fields_.Add(&field);
library.AddObject(field, name);
}
toplevel_class.AddFields(fields_);
ASSERT(!toplevel_class.is_loaded());
// Load toplevel procedures.
intptr_t next_procedure_offset = library_index.ProcedureOffset(0);
intptr_t next_procedure_offset =
library_index.ProcedureOffset(0) + correction;
const intptr_t procedure_count = library_index.procedure_count();
for (intptr_t i = 0; i < procedure_count; ++i) {
helper_.SetOffset(next_procedure_offset);
next_procedure_offset = library_index.ProcedureOffset(i + 1);
next_procedure_offset = library_index.ProcedureOffset(i + 1) + correction;
LoadProcedure(library, toplevel_class, false, next_procedure_offset);
// LoadProcedure calls Library::GetMetadata which invokes Dart code
// which may recursively trigger class finalization and
// FinishTopLevelClassLoading.
// In such case, return immediately and avoid overwriting already finalized
// functions with freshly loaded and not yet finalized.
if (toplevel_class.is_loaded()) {
return;
}
}
if (FLAG_enable_mirrors && annotation_count > 0) {
ASSERT(annotations_kernel_offset > 0);
library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
annotations_kernel_offset);
}
toplevel_class.SetFields(Array::Handle(MakeFieldsArray()));
toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray()));
if (register_class) {
classes.Add(toplevel_class, Heap::kOld);
}
if (!library.Loaded()) library.SetLoaded();
return library.raw();
String& name = String::Handle(Z);
for (intptr_t i = 0, n = fields_.length(); i < n; ++i) {
const Field* field = fields_.At(i);
name = field->name();
library.AddObject(*field, name);
}
for (intptr_t i = 0, n = functions_.length(); i < n; ++i) {
const Function* function = functions_.At(i);
name = function->name();
library.AddObject(*function, name);
}
ASSERT(!toplevel_class.is_loaded());
toplevel_class.set_is_loaded(true);
}
void KernelLoader::LoadLibraryImportsAndExports(Library* library,
@ -1544,7 +1591,7 @@ void KernelLoader::FinishClassLoading(const Class& klass,
}
void KernelLoader::FinishLoading(const Class& klass) {
ASSERT(klass.kernel_offset() > 0);
ASSERT(klass.IsTopLevel() || (klass.kernel_offset() > 0));
Zone* zone = Thread::Current()->zone();
const Script& script = Script::Handle(zone, klass.script());
@ -1556,10 +1603,17 @@ void KernelLoader::FinishLoading(const Class& klass) {
const intptr_t library_kernel_offset = library.kernel_offset();
ASSERT(library_kernel_offset > 0);
const intptr_t class_offset = klass.kernel_offset();
KernelLoader kernel_loader(script, library_kernel_data,
library_kernel_offset);
LibraryIndex library_index(library_kernel_data);
if (klass.IsTopLevel()) {
ASSERT(klass.raw() == toplevel_class.raw());
kernel_loader.FinishTopLevelClassLoading(klass, library, library_index);
return;
}
const intptr_t class_offset = klass.kernel_offset();
ClassIndex class_index(
library_kernel_data, class_offset,
// Class offsets in library index are whole program offsets.
@ -1790,14 +1844,6 @@ void KernelLoader::LoadProcedure(const Library& library,
// function_node_helper are no longer used.
helper_.SetOffset(procedure_end);
if (!in_class) {
library.AddObject(function, name);
ASSERT(!Object::Handle(
Z, library.LookupObjectAllowPrivate(
H.DartProcedureName(procedure_helper.canonical_name_)))
.IsNull());
}
if (annotation_count > 0) {
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
procedure_offset);

View file

@ -178,6 +178,10 @@ class KernelLoader : public ValueObject {
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();
@ -321,9 +325,9 @@ class KernelLoader : public ValueObject {
void EnsurePragmaClassIsLookedUp() {
if (pragma_class_.IsNull()) {
const Library& internal_lib =
Library::Handle(zone_, dart::Library::InternalLibrary());
pragma_class_ = internal_lib.LookupClass(Symbols::Pragma());
const Library& core_lib =
Library::Handle(zone_, dart::Library::CoreLibrary());
pragma_class_ = core_lib.LookupLocalClass(Symbols::Pragma());
ASSERT(!pragma_class_.IsNull());
}
}

View file

@ -9882,6 +9882,7 @@ RawObject* Library::ResolveName(const String& name) const {
if (FLAG_use_lib_cache && LookupResolvedNamesCache(name, &obj)) {
return obj.raw();
}
EnsureTopLevelClassIsFinalized();
obj = LookupLocalObject(name);
if (!obj.IsNull()) {
// Names that are in this library's dictionary and are unmangled
@ -10169,18 +10170,6 @@ RawObject* Library::LookupEntry(const String& name, intptr_t* index) const {
return Object::null();
}
void Library::ReplaceObject(const Object& obj, const String& name) const {
ASSERT(!Compiler::IsBackgroundCompilation());
ASSERT(obj.IsClass() || obj.IsFunction() || obj.IsField());
ASSERT(LookupLocalObject(name) != Object::null());
intptr_t index;
LookupEntry(name, &index);
// The value is guaranteed to be found.
const Array& dict = Array::Handle(dictionary());
dict.SetAt(index, obj);
}
void Library::AddClass(const Class& cls) const {
ASSERT(!Compiler::IsBackgroundCompilation());
const String& class_name = String::Handle(cls.Name());
@ -10307,6 +10296,22 @@ RawScript* Library::LookupScript(const String& url,
return Script::null();
}
void Library::EnsureTopLevelClassIsFinalized() const {
if (toplevel_class() == Object::null()) {
return;
}
Thread* thread = Thread::Current();
const Class& cls = Class::Handle(thread->zone(), toplevel_class());
if (cls.is_finalized()) {
return;
}
const Error& error =
Error::Handle(thread->zone(), cls.EnsureIsFinalized(thread));
if (!error.IsNull()) {
Exceptions::PropagateError(error);
}
}
RawObject* Library::LookupLocalObject(const String& name) const {
intptr_t index;
return LookupEntry(name, &index);
@ -10314,6 +10319,7 @@ RawObject* Library::LookupLocalObject(const String& name) const {
RawObject* Library::LookupLocalOrReExportObject(const String& name) const {
intptr_t index;
EnsureTopLevelClassIsFinalized();
const Object& result = Object::Handle(LookupEntry(name, &index));
if (!result.IsNull() && !result.IsLibraryPrefix()) {
return result.raw();
@ -10322,6 +10328,7 @@ RawObject* Library::LookupLocalOrReExportObject(const String& name) const {
}
RawField* Library::LookupFieldAllowPrivate(const String& name) const {
EnsureTopLevelClassIsFinalized();
Object& obj = Object::Handle(LookupObjectAllowPrivate(name));
if (obj.IsField()) {
return Field::Cast(obj).raw();
@ -10330,6 +10337,7 @@ RawField* Library::LookupFieldAllowPrivate(const String& name) const {
}
RawField* Library::LookupLocalField(const String& name) const {
EnsureTopLevelClassIsFinalized();
Object& obj = Object::Handle(LookupLocalObjectAllowPrivate(name));
if (obj.IsField()) {
return Field::Cast(obj).raw();
@ -10338,6 +10346,7 @@ RawField* Library::LookupLocalField(const String& name) const {
}
RawFunction* Library::LookupFunctionAllowPrivate(const String& name) const {
EnsureTopLevelClassIsFinalized();
Object& obj = Object::Handle(LookupObjectAllowPrivate(name));
if (obj.IsFunction()) {
return Function::Cast(obj).raw();
@ -10346,6 +10355,7 @@ RawFunction* Library::LookupFunctionAllowPrivate(const String& name) const {
}
RawFunction* Library::LookupLocalFunction(const String& name) const {
EnsureTopLevelClassIsFinalized();
Object& obj = Object::Handle(LookupLocalObjectAllowPrivate(name));
if (obj.IsFunction()) {
return Function::Cast(obj).raw();
@ -10443,7 +10453,10 @@ RawObject* Library::LookupImportedObject(const String& name) const {
}
RawClass* Library::LookupClass(const String& name) const {
Object& obj = Object::Handle(ResolveName(name));
Object& obj = Object::Handle(LookupLocalObject(name));
if (obj.IsNull() && !ShouldBePrivate(name)) {
obj = LookupImportedObject(name);
}
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
@ -11708,6 +11721,8 @@ RawObject* Namespace::Lookup(const String& name,
}
}
lib.EnsureTopLevelClassIsFinalized();
intptr_t ignore = 0;
// Lookup the name in the library's symbols.
Object& obj = Object::Handle(zone, lib.LookupEntry(name, &ignore));

View file

@ -3689,12 +3689,9 @@ class Library : public Object {
// more regular.
void AddClass(const Class& cls) const;
void AddObject(const Object& obj, const String& name) const;
void ReplaceObject(const Object& obj, const String& name) const;
RawObject* LookupReExport(const String& name,
ZoneGrowableArray<intptr_t>* visited = NULL) const;
RawObject* LookupObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObject(const String& name) const;
RawObject* LookupLocalOrReExportObject(const String& name) const;
RawObject* LookupImportedObject(const String& name) const;
RawClass* LookupClass(const String& name) const;
@ -3897,6 +3894,9 @@ class Library : public Object {
// for a top level getter 'name' that returns a closure.
RawObject* GetFunctionClosure(const String& name) const;
// Ensures that all top-level functions and variables (fields) are loaded.
void EnsureTopLevelClassIsFinalized() const;
private:
static const int kInitialImportsCapacity = 4;
static const int kImportsCapacityIncrement = 8;
@ -3935,6 +3935,8 @@ class Library : public Object {
void RehashDictionary(const Array& old_dict, intptr_t new_dict_size) const;
static RawLibrary* NewLibraryHelper(const String& url, bool import_core_lib);
RawObject* LookupEntry(const String& name, intptr_t* index) const;
RawObject* LookupLocalObjectAllowPrivate(const String& name) const;
RawObject* LookupLocalObject(const String& name) const;
void AllocatePrivateKey() const;