Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index fe1b7c7f3ae64f048dd9064c3ec3ec19a8e6ed32..f8d56f6907867ae4d72382ee54ccfafa6d1f4a23 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"); |
DEFINE_FLAG(bool, ignore_patch_signature_mismatch, false, |
"Ignore patch file member signature mismatch."); |
@@ -9698,8 +9699,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()) { |
return; |
} |
ResolvedNamesMap cache(resolved_names()); |
@@ -9708,12 +9708,64 @@ 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 'exported_names()' while running a background compilation; |
+ // do not ASSERT that 'exported_names()' has not changed. |
+#if defined(DEBUG) |
+ if (Thread::Current()->IsMutatorThread()) { |
+ ASSERT(cache.Release().raw() == exported_names()); |
+ } else { |
+ // Release must be called in debug mode. |
+ cache.Release(); |
+ } |
+#endif |
+ return present; |
+} |
+ |
+void Library::AddToExportedNamesCache(const String& name, |
+ const Object& obj) const { |
+ if (!FLAG_use_exp_cache || Compiler::IsBackgroundCompilation()) { |
+ 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 { |
- Object& entry = Object::Handle(); |
+ Thread* thread = Thread::Current(); |
+ Zone* zone = thread->zone(); |
+ Object& entry = Object::Handle(zone); |
if (LookupResolvedNamesCache(name, &entry)) { |
// TODO(koda): Support deleted sentinel in snapshots and remove only 'name'. |
InvalidateResolvedNamesCache(); |
} |
+ // When a new name is added to a library, we need to invalidate all |
+ // caches that contain an entry for this name. If the name was previously |
+ // looked up but could not be resolved, the cache contains a null entry. |
+ GrowableObjectArray& libs = GrowableObjectArray::Handle( |
+ zone, thread->isolate()->object_store()->libraries()); |
+ Library& lib = Library::Handle(zone); |
+ intptr_t num_libs = libs.Length(); |
+ for (intptr_t i = 0; i < num_libs; i++) { |
+ lib ^= libs.At(i); |
+ if (LookupExportedNamesCache(name, &entry)) { |
+ InitExportedNamesCache(); |
+ } |
+ } |
} |
@@ -9723,6 +9775,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; |
@@ -9798,42 +9863,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(); |
} |
@@ -9919,6 +9987,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; |
} |
@@ -10285,7 +10354,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()); |
} |
@@ -10318,7 +10386,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); |
} |
@@ -10346,6 +10413,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; |
@@ -10380,6 +10461,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_, |
@@ -10390,8 +10473,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); |
@@ -11113,11 +11194,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) && |
@@ -11139,14 +11233,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); |
} |
} |
} |