// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/closure_functions_cache.h" #include "vm/compiler/jit/compiler.h" #include "vm/hash_table.h" #include "vm/object.h" #include "vm/object_store.h" namespace dart { class FunctionHashMapTraits { public: static const char* Name() { return "FunctionMapTraits"; } static bool ReportStats() { return false; } static bool IsMatch(const Object& a, const Object& b) { return a.ptr() == b.ptr(); } static uword Hash(const Object& key) { return Function::Cast(key).Hash(); } }; using FunctionHashMap = UnorderedHashMap; FunctionPtr ClosureFunctionsCache::LookupClosureFunction( const Function& member_function, intptr_t local_function_id) { auto thread = Thread::Current(); SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); return LookupClosureFunctionLocked(member_function, local_function_id); } FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked( const Function& member_function, intptr_t local_function_id) { auto thread = Thread::Current(); auto zone = thread->zone(); auto object_store = thread->isolate_group()->object_store(); DEBUG_ASSERT( thread->isolate_group()->program_lock()->IsCurrentThreadReader()); auto& map_array = Array::Handle(zone, object_store->closure_functions_table()); if (map_array.IsNull()) { return Function::null(); } FunctionHashMap map(zone, map_array.ptr()); map_array ^= map.GetOrNull(member_function); map.Release(); if (map_array.IsNull()) { return Function::null(); } auto& result = Function::Handle(zone); IntHashMap map2(zone, map_array.ptr()); result ^= map2.GetOrNull(Smi::Handle(zone, Smi::New(local_function_id))); map2.Release(); return result.ptr(); } void ClosureFunctionsCache::AddClosureFunctionLocked( const Function& function, intptr_t local_function_id, bool allow_implicit_closure_functions /* = false */) { ASSERT(!Compiler::IsBackgroundCompilation()); auto thread = Thread::Current(); auto zone = thread->zone(); auto object_store = thread->isolate_group()->object_store(); DEBUG_ASSERT( thread->isolate_group()->program_lock()->IsCurrentThreadWriter()); auto& closures = GrowableObjectArray::Handle(zone, object_store->closure_functions()); if (closures.IsNull()) { closures = GrowableObjectArray::New(); object_store->set_closure_functions(closures); } ASSERT(allow_implicit_closure_functions || function.IsNonImplicitClosureFunction()); closures.Add(function, Heap::kOld); if (local_function_id < 0) { return; } const Function& member_function = Function::Handle(zone, function.GetOutermostFunction()); auto& map_array = Array::Handle(zone, object_store->closure_functions_table()); if (map_array.IsNull()) { map_array = HashTables::New(16, Heap::kOld); } FunctionHashMap map(zone, map_array.ptr()); map_array ^= map.GetOrNull(member_function); if (map_array.IsNull()) { map_array = HashTables::New(4, Heap::kOld); } IntHashMap map2(zone, map_array.ptr()); map2.UpdateOrInsert(Smi::Handle(zone, Smi::New(local_function_id)), function); map.UpdateOrInsert(member_function, map2.Release()); object_store->set_closure_functions_table(map.Release()); } intptr_t ClosureFunctionsCache::FindClosureIndex(const Function& needle) { auto thread = Thread::Current(); auto zone = thread->zone(); auto object_store = thread->isolate_group()->object_store(); SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); const auto& closures_array = GrowableObjectArray::Handle(zone, object_store->closure_functions()); if (!closures_array.IsNull()) { intptr_t num_closures = closures_array.Length(); for (intptr_t i = 0; i < num_closures; i++) { if (closures_array.At(i) == needle.ptr()) { return i; } } } return -1; } FunctionPtr ClosureFunctionsCache::ClosureFunctionFromIndex(intptr_t idx) { auto thread = Thread::Current(); auto zone = thread->zone(); auto object_store = thread->isolate_group()->object_store(); SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); const auto& closures_array = GrowableObjectArray::Handle(zone, object_store->closure_functions()); if (idx < 0 || closures_array.IsNull() || idx >= closures_array.Length()) { return Function::null(); } return Function::RawCast(closures_array.At(idx)); } void ClosureFunctionsCache::ForAllClosureFunctions( std::function callback) { auto thread = Thread::Current(); auto zone = thread->zone(); auto object_store = thread->isolate_group()->object_store(); auto& current_data = Array::Handle(zone); auto& entry = Function::Handle(zone); // NOTE: Inner functions may get added to the closures array while iterating - // we guarantee that any closure functions added on this thread by a // [callback] call will be visited as well. // // We avoid holding a lock while accessing the closures array, since often // times [callback] will do very heavy things (e.g. compiling the function). // // This means we can possibly miss a concurrently added closure function - // which the caller should be ok with (or it guarantees that this cannot // happen). const auto& closures = GrowableObjectArray::Handle(zone, object_store->closure_functions()); if (closures.IsNull()) { return; } if (!thread->IsInStoppedMutatorsScope()) { // The empty read locker scope will implicitly issue an acquire memory // fence, which means any closure functions added so far will be visible and // iterated further down. SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); } // We have an outer loop to ensure any new closure functions added by // [callback] will be iterated as well. intptr_t i = 0; while (true) { intptr_t current_length = closures.Length(); if (i == current_length) break; current_data = closures.data(); if (current_data.Length() < current_length) { current_length = current_data.Length(); } for (; i < current_length; ++i) { entry ^= current_data.At(i); if (!callback(entry)) { return; // Stop iteration. } } } } } // namespace dart