diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 2211b5455a5..208769deac2 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -1890,18 +1890,62 @@ bool Class::HasInstanceFields() const { } +class FunctionName { + public: + FunctionName(const String& name, String* tmp_string) + : name_(name), tmp_string_(tmp_string) {} + bool Matches(const Function& function) const { + if (name_.IsSymbol()) { + return name_.raw() == function.name(); + } else { + *tmp_string_ = function.name(); + return name_.Equals(*tmp_string_); + } + } + intptr_t Hash() const { return name_.Hash(); } + private: + const String& name_; + String* tmp_string_; +}; + + +// Traits for looking up Functions by name. +class ClassFunctionsTraits { + public: + // Called when growing the table. + static bool IsMatch(const Object& a, const Object& b) { + ASSERT(a.IsFunction() && b.IsFunction()); + // Function objects are always canonical. + return a.raw() == b.raw(); + } + static bool IsMatch(const FunctionName& name, const Object& obj) { + return name.Matches(Function::Cast(obj)); + } + static uword Hash(const Object& key) { + return String::HashRawSymbol(Function::Cast(key).name()); + } + static uword Hash(const FunctionName& name) { + return name.Hash(); + } +}; +typedef UnorderedHashSet ClassFunctionsSet; + + void Class::SetFunctions(const Array& value) const { ASSERT(!value.IsNull()); -#if defined(DEBUG) - // Verify that all the functions in the array have this class as owner. - Function& func = Function::Handle(); - intptr_t len = value.Length(); - for (intptr_t i = 0; i < len; i++) { - func ^= value.At(i); - ASSERT(func.Owner() == raw()); - } -#endif StorePointer(&raw_ptr()->functions_, value.raw()); + const intptr_t len = value.Length(); + ClassFunctionsSet set(HashTables::New(len)); + if (len >= kFunctionLookupHashTreshold) { + Function& func = Function::Handle(); + for (intptr_t i = 0; i < len; ++i) { + func ^= value.At(i); + // Verify that all the functions in the array have this class as owner. + ASSERT(func.Owner() == raw()); + set.Insert(func); + } + } + StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw()); } @@ -1909,7 +1953,17 @@ void Class::AddFunction(const Function& function) const { const Array& arr = Array::Handle(functions()); const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1)); new_arr.SetAt(arr.Length(), function); - SetFunctions(new_arr); + StorePointer(&raw_ptr()->functions_, new_arr.raw()); + // Add to hash table, if any. + const intptr_t new_len = new_arr.Length(); + if (new_len == kFunctionLookupHashTreshold) { + // Transition to using hash table. + SetFunctions(new_arr); + } else if (new_len > kFunctionLookupHashTreshold) { + ClassFunctionsSet set(raw_ptr()->functions_hash_table_); + set.Insert(function); + StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw()); + } } @@ -3831,6 +3885,15 @@ RawFunction* Class::LookupFunction(const String& name, MemberKind kind) const { ASSERT(!funcs.IsNull()); const intptr_t len = funcs.Length(); Function& function = isolate->FunctionHandle(); + if (len >= kFunctionLookupHashTreshold) { + ClassFunctionsSet set(raw_ptr()->functions_hash_table_); + REUSABLE_STRING_HANDLESCOPE(isolate); + function ^= set.GetOrNull(FunctionName(name, &(isolate->StringHandle()))); + // No mutations. + ASSERT(set.Release().raw() == raw_ptr()->functions_hash_table_); + return function.IsNull() ? Function::null() + : CheckFunctionType(function, kind); + } if (name.IsSymbol()) { // Quick Symbol compare. NoGCScope no_gc; diff --git a/runtime/vm/object.h b/runtime/vm/object.h index a41e01ddada..cede5f6bb91 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -949,6 +949,7 @@ class Class : public Object { // Returns true if non-static fields are defined. bool HasInstanceFields() const; + // TODO(koda): Unite w/ hash table. RawArray* functions() const { return raw_ptr()->functions_; } void SetFunctions(const Array& value) const; void AddFunction(const Function& function) const; @@ -1211,6 +1212,9 @@ class Class : public Object { void CalculateFieldOffsets() const; + // functions_hash_table is in use iff there are at least this many functions. + static const intptr_t kFunctionLookupHashTreshold = 16; + // Initial value for the cached number of type arguments. static const intptr_t kUnknownNumTypeArguments = -1; diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 8b49789be97..09f58e936bb 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -499,6 +499,7 @@ class RawClass : public RawObject { RawString* name_; RawString* user_name_; RawArray* functions_; + RawArray* functions_hash_table_; RawArray* fields_; RawArray* offset_in_words_to_field_; RawGrowableObjectArray* closure_functions_; // Local functions and literals. diff --git a/tests/standalone/issue14236_test.dart b/tests/standalone/issue14236_test.dart index f720a652860..7e248f33249 100644 Binary files a/tests/standalone/issue14236_test.dart and b/tests/standalone/issue14236_test.dart differ