|  | // 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/object.h" | 
|  | #include "vm/object_store.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | FunctionPtr ClosureFunctionsCache::LookupClosureFunction( | 
|  | const Function& parent, | 
|  | TokenPosition token_pos) { | 
|  | auto thread = Thread::Current(); | 
|  | SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); | 
|  | return LookupClosureFunctionLocked(parent, token_pos); | 
|  | } | 
|  |  | 
|  | FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked( | 
|  | const Function& parent, | 
|  | TokenPosition token_pos) { | 
|  | auto thread = Thread::Current(); | 
|  | auto zone = thread->zone(); | 
|  | auto object_store = thread->isolate_group()->object_store(); | 
|  |  | 
|  | DEBUG_ASSERT( | 
|  | thread->isolate_group()->program_lock()->IsCurrentThreadReader()); | 
|  |  | 
|  | const auto& closures = | 
|  | GrowableObjectArray::Handle(zone, object_store->closure_functions()); | 
|  | auto& closure = Function::Handle(zone); | 
|  | intptr_t num_closures = closures.Length(); | 
|  | for (intptr_t i = 0; i < num_closures; i++) { | 
|  | closure ^= closures.At(i); | 
|  | if (closure.token_pos() == token_pos && | 
|  | closure.parent_function() == parent.ptr()) { | 
|  | return closure.ptr(); | 
|  | } | 
|  | } | 
|  | return Function::null(); | 
|  | } | 
|  |  | 
|  | void ClosureFunctionsCache::AddClosureFunctionLocked( | 
|  | const Function& function, | 
|  | 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()); | 
|  |  | 
|  | const auto& closures = | 
|  | GrowableObjectArray::Handle(zone, object_store->closure_functions()); | 
|  | ASSERT(!closures.IsNull()); | 
|  | ASSERT(allow_implicit_closure_functions || | 
|  | function.IsNonImplicitClosureFunction()); | 
|  | closures.Add(function, Heap::kOld); | 
|  | } | 
|  |  | 
|  | 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()); | 
|  | 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 || idx >= closures_array.Length()) { | 
|  | return Function::null(); | 
|  | } | 
|  | return Function::RawCast(closures_array.At(idx)); | 
|  | } | 
|  |  | 
|  | void ClosureFunctionsCache::ForAllClosureFunctions( | 
|  | std::function<bool(const 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 (!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 |