Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index c7ca37337d6584a8869dd008768a7466a80ef938..ac631140c515473a3c9693feefe7dd5fbf492e4e 100644 |
--- a/runtime/vm/object.cc |
+++ b/runtime/vm/object.cc |
@@ -57,6 +57,7 @@ DEFINE_FLAG(bool, show_internal_names, false, |
"Show names of internal classes (e.g. \"OneByteString\") in error messages " |
"instead of showing the corresponding interface names (e.g. \"String\")"); |
DEFINE_FLAG(bool, use_lib_cache, true, "Use library name cache"); |
+DEFINE_FLAG(bool, use_exp_cache, true, "Use library exported name cache"); |
srdjan
2016/04/18 22:40:49
use_exported_name_cache sounds more readable than
Ivan Posva
2016/04/18 23:07:21
In the future please add new flags to the flag_lis
hausner
2016/04/18 23:51:58
Yes, I consider the flag temporary.
hausner
2016/04/18 23:51:58
More readable maybe, but I'm not sure it is more u
srdjan
2016/04/26 21:09:42
The flag is temporary, your choice.
|
DEFINE_FLAG(bool, ignore_patch_signature_mismatch, false, |
"Ignore patch file member signature mismatch."); |
@@ -9623,8 +9624,7 @@ bool Library::LookupResolvedNamesCache(const String& name, |
// the name does not resolve to anything in this library scope. |
void Library::AddToResolvedNamesCache(const String& name, |
const Object& obj) const { |
- ASSERT(!Compiler::IsBackgroundCompilation()); |
- if (!FLAG_use_lib_cache) { |
+ if (!FLAG_use_lib_cache || Compiler::IsBackgroundCompilation()) { |
srdjan
2016/04/18 22:40:48
Why are we starting to call AddToResolvedNamesCach
hausner
2016/04/18 23:51:58
No change here, but I think it is possible that th
|
return; |
} |
ResolvedNamesMap cache(resolved_names()); |
@@ -9633,12 +9633,53 @@ void Library::AddToResolvedNamesCache(const String& name, |
} |
+bool Library::LookupExportedNamesCache(const String& name, |
+ Object* obj) const { |
+ ASSERT(FLAG_use_exp_cache); |
+ if (exported_names() == Array::null()) { |
+ return false; |
+ } |
+ ResolvedNamesMap cache(exported_names()); |
+ bool present = false; |
+ *obj = cache.GetOrNull(name, &present); |
+ // Mutator compiler thread may add entries and therefore |
+ // change 'resolved_names()' while running a background compilation; |
srdjan
2016/04/18 22:40:49
s/resolved_names/exported_names/
hausner
2016/04/18 23:51:58
Done.
|
+ // do not ASSERT that 'exported_names()' has not changed. |
+ cache.Release(); |
srdjan
2016/04/18 22:40:49
You may want to the same as in LookupResolvedNames
hausner
2016/04/18 23:51:58
Done.
|
+ return present; |
+} |
+ |
+void Library::AddToExportedNamesCache(const String& name, |
+ const Object& obj) const { |
+ ASSERT(!Compiler::IsBackgroundCompilation()); |
+ if (!FLAG_use_exp_cache || Compiler::IsBackgroundCompilation()) { |
srdjan
2016/04/18 22:40:48
Why tests for background compilation when assertin
hausner
2016/04/18 23:51:58
Ok, assertion removed.
|
+ return; |
+ } |
+ if (exported_names() == Array::null()) { |
+ AllocateExportedNamesCache(); |
+ } |
+ ResolvedNamesMap cache(exported_names()); |
+ cache.UpdateOrInsert(name, obj); |
+ StorePointer(&raw_ptr()->exported_names_, cache.Release().raw()); |
+} |
+ |
+ |
void Library::InvalidateResolvedName(const String& name) const { |
srdjan
2016/04/18 22:40:49
You may want to:
Thread* thread = Thread::Current
srdjan
2016/04/18 22:40:49
ASSERT(FLAG_use_exp_cache)
hausner
2016/04/18 23:51:58
the flags for resolved name cache and exported nam
hausner
2016/04/18 23:51:58
Done.
|
Object& entry = Object::Handle(); |
if (LookupResolvedNamesCache(name, &entry)) { |
// TODO(koda): Support deleted sentinel in snapshots and remove only 'name'. |
InvalidateResolvedNamesCache(); |
} |
+ GrowableObjectArray& libs = GrowableObjectArray::Handle( |
Ivan Posva
2016/04/18 23:07:21
Please add a comment how we can get into a state w
hausner
2016/04/18 23:51:58
Done.
|
+ Isolate::Current()->object_store()->libraries()); |
+ Library& lib = Library::Handle(); |
+ intptr_t num_libs = libs.Length(); |
+ for (intptr_t i = 0; i < num_libs; i++) { |
+ lib ^= libs.At(i); |
+ if (LookupExportedNamesCache(name, &entry)) { |
+ InitExportedNamesCache(); |
+ } |
+ } |
} |
@@ -9648,6 +9689,19 @@ void Library::InvalidateResolvedNamesCache() const { |
} |
+// Invalidate all exported names caches in the isolate. |
+void Library::InvalidateExportedNamesCaches() { |
+ GrowableObjectArray& libs = GrowableObjectArray::Handle( |
+ Isolate::Current()->object_store()->libraries()); |
+ Library& lib = Library::Handle(); |
+ intptr_t num_libs = libs.Length(); |
+ for (intptr_t i = 0; i < num_libs; i++) { |
+ lib ^= libs.At(i); |
+ lib.InitExportedNamesCache(); |
+ } |
+} |
+ |
+ |
void Library::GrowDictionary(const Array& dict, intptr_t dict_size) const { |
// TODO(iposva): Avoid exponential growth. |
intptr_t new_dict_size = dict_size * 2; |
@@ -9723,42 +9777,45 @@ void Library::AddObject(const Object& obj, const String& name) const { |
// Lookup a name in the library's re-export namespace. |
// This lookup can occur from two different threads: background compiler and |
// mutator thread. |
-RawObject* Library::LookupReExport(const String& name) const { |
- if (HasExports()) { |
- const bool is_background_compiler = Compiler::IsBackgroundCompilation(); |
- Array& exports = Array::Handle(); |
- if (is_background_compiler) { |
- exports = this->exports2(); |
- // Break potential export cycle while looking up name. |
- StorePointer(&raw_ptr()->exports2_, Object::empty_array().raw()); |
- } else { |
- exports = this->exports(); |
- // Break potential export cycle while looking up name. |
- StorePointer(&raw_ptr()->exports_, Object::empty_array().raw()); |
- } |
- Namespace& ns = Namespace::Handle(); |
- Object& obj = Object::Handle(); |
- for (int i = 0; i < exports.Length(); i++) { |
- ns ^= exports.At(i); |
- obj = ns.Lookup(name); |
- if (!obj.IsNull()) { |
- // The Lookup call above may return a setter x= when we are looking |
- // for the name x. Make sure we only return when a matching name |
- // is found. |
- String& obj_name = String::Handle(obj.DictionaryName()); |
- if (Field::IsSetterName(obj_name) == Field::IsSetterName(name)) { |
- break; |
- } |
+RawObject* Library::LookupReExport(const String& name, |
+ ZoneGrowableArray<intptr_t>* trail) const { |
+ if (!HasExports()) { |
+ return Object::null(); |
+ } |
+ |
+ if (trail == NULL) { |
+ trail = new ZoneGrowableArray<intptr_t>(); |
+ } |
+ Object& obj = Object::Handle(); |
+ if (FLAG_use_exp_cache && LookupExportedNamesCache(name, &obj)) { |
+ return obj.raw(); |
+ } |
+ |
+ const intptr_t lib_id = this->index(); |
+ ASSERT(lib_id >= 0); // We use -1 to indicate that a cycle was found. |
+ trail->Add(lib_id); |
+ const Array& exports = Array::Handle(this->exports()); |
+ Namespace& ns = Namespace::Handle(); |
+ for (int i = 0; i < exports.Length(); i++) { |
+ ns ^= exports.At(i); |
+ obj = ns.Lookup(name, trail); |
+ if (!obj.IsNull()) { |
+ // The Lookup call above may return a setter x= when we are looking |
+ // for the name x. Make sure we only return when a matching name |
+ // is found. |
+ String& obj_name = String::Handle(obj.DictionaryName()); |
+ if (Field::IsSetterName(obj_name) == Field::IsSetterName(name)) { |
+ break; |
} |
} |
- if (is_background_compiler) { |
- StorePointer(&raw_ptr()->exports2_, exports.raw()); |
- } else { |
- StorePointer(&raw_ptr()->exports_, exports.raw()); |
- } |
- return obj.raw(); |
} |
- return Object::null(); |
+ bool in_cycle = (trail->RemoveLast() < 0); |
+ if (FLAG_use_exp_cache && |
+ !in_cycle && |
+ !Compiler::IsBackgroundCompilation()) { |
+ AddToExportedNamesCache(name, obj); |
+ } |
+ return obj.raw(); |
} |
@@ -9844,6 +9901,7 @@ bool Library::RemoveObject(const Object& obj, const String& name) const { |
dict.SetAt(dict_size, Smi::Handle(zone, Smi::New(used_elements))); |
InvalidateResolvedNamesCache(); |
+ InvalidateExportedNamesCaches(); |
return true; |
} |
@@ -10210,7 +10268,6 @@ bool Library::ImportsCorelib() const { |
void Library::DropDependencies() const { |
StorePointer(&raw_ptr()->imports_, Array::null()); |
StorePointer(&raw_ptr()->exports_, Array::null()); |
- StorePointer(&raw_ptr()->exports2_, Array::null()); |
} |
@@ -10243,7 +10300,6 @@ void Library::AddExport(const Namespace& ns) const { |
intptr_t num_exports = exports.Length(); |
exports = Array::Grow(exports, num_exports + 1); |
StorePointer(&raw_ptr()->exports_, exports.raw()); |
- StorePointer(&raw_ptr()->exports2_, exports.raw()); |
exports.SetAt(num_exports, ns); |
} |
@@ -10271,6 +10327,20 @@ void Library::InitResolvedNamesCache(intptr_t size, |
} |
+void Library::AllocateExportedNamesCache() const { |
+ StorePointer(&raw_ptr()->exported_names_, |
+ HashTables::New<ResolvedNamesMap>(16)); |
+} |
+ |
+ |
+void Library::InitExportedNamesCache() const { |
+ if (exported_names() != Array::null()) { |
+ StorePointer(&raw_ptr()->exported_names_, |
+ HashTables::New<ResolvedNamesMap>(16)); |
+ } |
+} |
+ |
+ |
void Library::InitClassDictionary() const { |
// TODO(iposva): Find reasonable initial size. |
const int kInitialElementCount = 16; |
@@ -10305,6 +10375,8 @@ RawLibrary* Library::NewLibraryHelper(const String& url, |
result.StorePointer(&result.raw_ptr()->url_, url.raw()); |
result.StorePointer(&result.raw_ptr()->resolved_names_, |
Object::empty_array().raw()); |
+ result.StorePointer(&result.raw_ptr()->exported_names_, |
+ Array::null()); |
result.StorePointer(&result.raw_ptr()->dictionary_, |
Object::empty_array().raw()); |
result.StorePointer(&result.raw_ptr()->metadata_, |
@@ -10315,8 +10387,6 @@ RawLibrary* Library::NewLibraryHelper(const String& url, |
Heap::kOld)); |
result.StorePointer(&result.raw_ptr()->imports_, Object::empty_array().raw()); |
result.StorePointer(&result.raw_ptr()->exports_, Object::empty_array().raw()); |
- result.StorePointer(&result.raw_ptr()->exports2_, |
- Object::empty_array().raw()); |
result.StorePointer(&result.raw_ptr()->loaded_scripts_, Array::null()); |
result.StorePointer(&result.raw_ptr()->load_error_, Instance::null()); |
result.set_native_entry_resolver(NULL); |
@@ -11038,11 +11108,24 @@ bool Namespace::HidesName(const String& name) const { |
// Look up object with given name in library and filter out hidden |
// names. Also look up getters and setters. |
-RawObject* Namespace::Lookup(const String& name) const { |
+RawObject* Namespace::Lookup(const String& name, |
+ ZoneGrowableArray<intptr_t>* trail) const { |
Zone* zone = Thread::Current()->zone(); |
const Library& lib = Library::Handle(zone, library()); |
- intptr_t ignore = 0; |
+ if (trail != NULL) { |
+ // Look for cycle in reexport graph. |
+ for (int i = 0; i < trail->length(); i++) { |
+ if (trail->At(i) == lib.index()) { |
+ for (int j = i+1; j < trail->length(); j++) { |
+ (*trail)[j] = -1; |
+ } |
+ return Object::null(); |
+ } |
+ } |
+ } |
+ |
+ intptr_t ignore = 0; |
// Lookup the name in the library's symbols. |
Object& obj = Object::Handle(zone, lib.LookupEntry(name, &ignore)); |
if (!Field::IsGetterName(name) && |
@@ -11064,14 +11147,14 @@ RawObject* Namespace::Lookup(const String& name) const { |
// Library prefixes are not exported. |
if (obj.IsNull() || obj.IsLibraryPrefix()) { |
// Lookup in the re-exported symbols. |
- obj = lib.LookupReExport(name); |
+ obj = lib.LookupReExport(name, trail); |
if (obj.IsNull() && !Field::IsSetterName(name)) { |
// LookupReExport() only returns objects that match the given name. |
// If there is no field/func/getter, try finding a setter. |
const String& setter_name = |
String::Handle(zone, Field::LookupSetterSymbol(name)); |
if (!setter_name.IsNull()) { |
- obj = lib.LookupReExport(setter_name); |
+ obj = lib.LookupReExport(setter_name, trail); |
} |
} |
} |