Chromium Code Reviews| 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); |
| } |
| } |
| } |