[vm/aot] Fix retaining of targets of FFI callbacks in precompiler

Account for the fact that a Function could be only reachable via
FfiTrampolineData in the FFI trampoline function.

TEST=ffi_2/function_callbacks_test
Fixes https://github.com/dart-lang/sdk/issues/45510

Change-Id: I2db81c7730ae48d1f1355aa236339e54562ee6c4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193893
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index e84949f..4eddc2e 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -144,6 +144,8 @@
   static constexpr const char* kLocalParent = "parent of a local function";
   // The object has an entry point pragma that requires it be retained.
   static constexpr const char* kEntryPointPragma = "entry point pragma";
+  // The function is a target of FFI callback.
+  static constexpr const char* kFfiCallbackTarget = "ffi callback target";
 };
 
 class RetainedReasonsWriter : public ValueObject {
@@ -935,6 +937,13 @@
     // Local closure function.
     const auto& target = Function::Cast(entry);
     AddFunction(target, RetainReasons::kLocalClosure);
+    if (target.IsFfiTrampoline()) {
+      const auto& callback_target =
+          Function::Handle(Z, target.FfiCallbackTarget());
+      if (!callback_target.IsNull()) {
+        AddFunction(callback_target, RetainReasons::kFfiCallbackTarget);
+      }
+    }
   } else if (entry.IsCode()) {
     const auto& target_code = Code::Cast(entry);
     if (target_code.IsAllocationStubCode()) {
diff --git a/tests/ffi/function_callbacks_test.dart b/tests/ffi/function_callbacks_test.dart
index 11baff6..e9a853c 100644
--- a/tests/ffi/function_callbacks_test.dart
+++ b/tests/ffi/function_callbacks_test.dart
@@ -13,6 +13,7 @@
 // VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code
 // VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code --stacktrace-every=100
 // VMOptions=--use-bare-instructions=false
+// VMOptions=--enable-testing-pragmas --dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
 // SharedObjects=ffi_test_functions
 
 import 'dart:ffi';
diff --git a/tests/ffi_2/function_callbacks_test.dart b/tests/ffi_2/function_callbacks_test.dart
index 11baff6..e9a853c 100644
--- a/tests/ffi_2/function_callbacks_test.dart
+++ b/tests/ffi_2/function_callbacks_test.dart
@@ -13,6 +13,7 @@
 // VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code
 // VMOptions=--use-slow-path --enable-testing-pragmas --write-protect-code --no-dual-map-code --stacktrace-every=100
 // VMOptions=--use-bare-instructions=false
+// VMOptions=--enable-testing-pragmas --dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
 // SharedObjects=ffi_test_functions
 
 import 'dart:ffi';