[vm] Avoid duplicate entries in invocation dispatcher cache.

We would add function itself to the invocation dispatcher cache
if the function itself can handle dynamic invocation.

However GetInvocationDispatcher will never be able to find this
entry because it looks specifically for functions with
kDynamicInvocationForwarder kind.

This lead us to add many duplicated entries to the cache.

The logic in the resolver does not actually need the cache to contain
the function because it falls through to lookup using unmangled name.

For a large Flutter application: before this change invocation dispatcher caches were contributing 129244 bytes to the snapshot, after this change they contribute 7764 bytes.

Fixes https://github.com/dart-lang/sdk/issues/48914

TEST=ci

Change-Id: I639d268e75fd43d2f4f0b1ea5a7873ba169f9d66
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242862
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 16d0178..cb9df67 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3592,10 +3592,21 @@
   auto& cache = Array::Handle(zone, invocation_dispatcher_cache());
   InvocationDispatcherTable dispatchers(cache);
   intptr_t i = 0;
-  for (auto dispatcher : dispatchers) {
-    if (dispatcher.Get<kInvocationDispatcherName>() == String::null()) {
+#if defined(DEBUG)
+  auto& function = Function::Handle();
+#endif
+  for (auto entry : dispatchers) {
+    if (entry.Get<kInvocationDispatcherName>() == String::null()) {
       break;
     }
+
+#if defined(DEBUG)
+    // Check for duplicate entries in the cache.
+    function = entry.Get<kInvocationDispatcherFunction>();
+    ASSERT(entry.Get<kInvocationDispatcherName>() != target_name.ptr() ||
+           function.kind() != dispatcher.kind() ||
+           entry.Get<kInvocationDispatcherArgsDesc>() != args_desc.ptr());
+#endif  // defined(DEBUG)
     i++;
   }
   if (i == dispatchers.Length()) {
@@ -3973,8 +3984,12 @@
 
   const bool needs_dyn_forwarder =
       kernel::NeedsDynamicInvocationForwarder(*this);
+  if (!needs_dyn_forwarder) {
+    return ptr();
+  }
+
   if (!allow_add) {
-    return needs_dyn_forwarder ? Function::null() : ptr();
+    return Function::null();
   }
 
   // If we failed to find it and possibly need to create it, use a write lock.
@@ -3988,10 +4003,8 @@
   if (!result.IsNull()) return result.ptr();
 
   // Otherwise create it & add it.
-  result = needs_dyn_forwarder ? CreateDynamicInvocationForwarder(mangled_name)
-                               : ptr();
+  result = CreateDynamicInvocationForwarder(mangled_name);
   owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
-
   return result.ptr();
 }