[vm] Extract reading of parameter covariance attributes

This change extracts code which reads kernel in order to get
covariance attributes of parameters into a separate function.
This removes code duplication and makes it easier to hook up
reading of covariance attributes from bytecode in future.

Change-Id: Ia19caf3edaf45df9e0bb1bdc715fac21d26788dc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96560
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 2776042..5d67407 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -4,6 +4,7 @@
 
 #include "vm/kernel.h"
 
+#include "vm/bit_vector.h"
 #include "vm/compiler/frontend/constant_evaluator.h"
 #include "vm/compiler/frontend/kernel_translation_helper.h"
 #include "vm/longjump.h"
@@ -585,89 +586,100 @@
   }
 }
 
-bool NeedsDynamicInvocationForwarder(const Function& function) {
+void ReadParameterCovariance(const Function& function,
+                             BitVector* is_covariant,
+                             BitVector* is_generic_covariant_impl) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
 
-  TranslationHelper helper(thread);
-  Script& script = Script::Handle(zone, function.script());
-  helper.InitFromScript(script);
+  const intptr_t num_params = function.NumParameters();
+  ASSERT(is_covariant->length() == num_params);
+  ASSERT(is_generic_covariant_impl->length() == num_params);
 
-  // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
-  // e.g. for type translation.
-
-  const Class& owner_class = Class::Handle(zone, function.Owner());
-  Function& outermost_function =
-      Function::Handle(zone, function.GetOutermostFunction());
-
-  ActiveClass active_class;
-  ActiveClassScope active_class_scope(&active_class, &owner_class);
-  ActiveMemberScope active_member(&active_class, &outermost_function);
-  ActiveTypeParametersScope active_type_params(&active_class, function, zone);
+  const auto& script = Script::Handle(zone, function.script());
+  TranslationHelper translation_helper(thread);
+  translation_helper.InitFromScript(script);
 
   KernelReaderHelper reader_helper(
-      zone, &helper, script,
+      zone, &translation_helper, script,
       ExternalTypedData::Handle(zone, function.KernelData()),
       function.KernelDataProgramOffset());
 
-  TypeTranslator type_translator(&reader_helper, &active_class,
-                                 /* finalize= */ true);
-
   reader_helper.SetOffset(function.kernel_offset());
-
-  // Handle setters.
-  if (reader_helper.PeekTag() == kField) {
-    ASSERT(function.IsImplicitSetterFunction());
-    const auto& field = Field::Handle(zone, function.accessor_field());
-    return !(field.is_covariant() || field.is_generic_covariant_impl());
-  }
-
   reader_helper.ReadUntilFunctionNode();
 
   FunctionNodeHelper function_node_helper(&reader_helper);
-  function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
-  intptr_t num_type_params = reader_helper.ReadListLength();
-
-  for (intptr_t i = 0; i < num_type_params; ++i) {
-    TypeParameterHelper helper(&reader_helper);
-    helper.ReadUntilExcludingAndSetJustRead(TypeParameterHelper::kBound);
-    AbstractType& bound = type_translator.BuildType();  // read bound
-    helper.Finish();
-
-    if (!bound.IsTopType() && !helper.IsGenericCovariantImpl()) {
-      return true;
-    }
-  }
-  function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
   function_node_helper.ReadUntilExcluding(
       FunctionNodeHelper::kPositionalParameters);
 
   // Positional.
   const intptr_t num_positional_params = reader_helper.ReadListLength();
-  for (intptr_t i = 0; i < num_positional_params; ++i) {
+  intptr_t param_index = function.NumImplicitParameters();
+  for (intptr_t i = 0; i < num_positional_params; ++i, ++param_index) {
     VariableDeclarationHelper helper(&reader_helper);
-    helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
-    AbstractType& type = type_translator.BuildType();  // read type.
-    helper.SetJustRead(VariableDeclarationHelper::kType);
     helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
 
-    if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
-        !helper.IsCovariant()) {
-      return true;
+    if (helper.IsCovariant()) {
+      is_covariant->Add(param_index);
+    }
+    if (helper.IsGenericCovariantImpl()) {
+      is_generic_covariant_impl->Add(param_index);
     }
   }
 
   // Named.
   const intptr_t num_named_params = reader_helper.ReadListLength();
-  for (intptr_t i = 0; i < num_named_params; ++i) {
+  for (intptr_t i = 0; i < num_named_params; ++i, ++param_index) {
     VariableDeclarationHelper helper(&reader_helper);
-    helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
-    AbstractType& type = type_translator.BuildType();  // read type.
-    helper.SetJustRead(VariableDeclarationHelper::kType);
     helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
 
-    if (!type.IsTopType() && !helper.IsGenericCovariantImpl() &&
-        !helper.IsCovariant()) {
+    if (helper.IsCovariant()) {
+      is_covariant->Add(param_index);
+    }
+    if (helper.IsGenericCovariantImpl()) {
+      is_generic_covariant_impl->Add(param_index);
+    }
+  }
+}
+
+bool NeedsDynamicInvocationForwarder(const Function& function) {
+  Zone* zone = Thread::Current()->zone();
+
+  // Covariant parameters (both explicitly covariant and generic-covariant-impl)
+  // are checked in the body of a function and therefore don't need checks in a
+  // dynamic invocation forwarder. So dynamic invocation forwarder is only
+  // needed if there are non-covariant parameters of non-top type.
+
+  ASSERT(!function.IsImplicitGetterFunction());
+  if (function.IsImplicitSetterFunction()) {
+    const auto& field = Field::Handle(zone, function.accessor_field());
+    return !(field.is_covariant() || field.is_generic_covariant_impl());
+  }
+
+  const auto& type_params =
+      TypeArguments::Handle(zone, function.type_parameters());
+  if (!type_params.IsNull()) {
+    auto& type_param = TypeParameter::Handle(zone);
+    auto& bound = AbstractType::Handle(zone);
+    for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
+      type_param ^= type_params.TypeAt(i);
+      bound = type_param.bound();
+      if (!bound.IsTopType() && !type_param.IsGenericCovariantImpl()) {
+        return true;
+      }
+    }
+  }
+
+  const intptr_t num_params = function.NumParameters();
+  BitVector is_covariant(zone, num_params);
+  BitVector is_generic_covariant_impl(zone, num_params);
+  ReadParameterCovariance(function, &is_covariant, &is_generic_covariant_impl);
+
+  auto& type = AbstractType::Handle(zone);
+  for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
+    type = function.ParameterTypeAt(i);
+    if (!type.IsTopType() && !is_generic_covariant_impl.Contains(i) &&
+        !is_covariant.Contains(i)) {
       return true;
     }
   }
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 34609b8..db52690 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -32,6 +32,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
 namespace dart {
 
+class BitVector;
 class Field;
 class ParsedFunction;
 class Zone;
@@ -194,6 +195,15 @@
                             bool is_annotations_offset);
 RawObject* BuildParameterDescriptor(const Function& function);
 
+// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
+// according to covariance attributes of [function] parameters.
+//
+// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
+// of function.NumParameters() length.
+void ReadParameterCovariance(const Function& function,
+                             BitVector* is_covariant,
+                             BitVector* is_generic_covariant_impl);
+
 // Returns true if the given function needs dynamic invocation forwarder:
 // that is if any of the arguments require checking on the dynamic
 // call-site: if function has no parameters or has only covariant parameters
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 9c9e092..1920a34 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7335,46 +7335,17 @@
 
   // Change covariant parameter types to Object in the implicit closure.
   if (!is_static()) {
-    const Script& function_script = Script::Handle(zone, script());
-    kernel::TranslationHelper translation_helper(thread);
-    translation_helper.InitFromScript(function_script);
+    BitVector is_covariant(zone, NumParameters());
+    BitVector is_generic_covariant_impl(zone, NumParameters());
+    kernel::ReadParameterCovariance(*this, &is_covariant,
+                                    &is_generic_covariant_impl);
 
-    kernel::KernelReaderHelper kernel_reader_helper(
-        zone, &translation_helper, function_script,
-        ExternalTypedData::Handle(zone, KernelData()),
-        KernelDataProgramOffset());
-
-    kernel_reader_helper.SetOffset(kernel_offset());
-    kernel_reader_helper.ReadUntilFunctionNode();
-
-    kernel::FunctionNodeHelper fn_helper(&kernel_reader_helper);
-
-    // Check the positional parameters, including the optional positional ones.
-    fn_helper.ReadUntilExcluding(
-        kernel::FunctionNodeHelper::kPositionalParameters);
-    intptr_t num_pos_params = kernel_reader_helper.ReadListLength();
-    ASSERT(num_pos_params ==
-           num_fixed_params - 1 + (has_opt_pos_params ? num_opt_params : 0));
     const Type& object_type = Type::Handle(zone, Type::ObjectType());
-    for (intptr_t i = 0; i < num_pos_params; ++i) {
-      kernel::VariableDeclarationHelper var_helper(&kernel_reader_helper);
-      var_helper.ReadUntilExcluding(kernel::VariableDeclarationHelper::kEnd);
-      if (var_helper.IsCovariant() || var_helper.IsGenericCovariantImpl()) {
-        closure_function.SetParameterTypeAt(i + 1, object_type);
-      }
-    }
-    fn_helper.SetJustRead(kernel::FunctionNodeHelper::kPositionalParameters);
-
-    // Check the optional named parameters.
-    fn_helper.ReadUntilExcluding(kernel::FunctionNodeHelper::kNamedParameters);
-    intptr_t num_named_params = kernel_reader_helper.ReadListLength();
-    ASSERT(num_named_params == (has_opt_pos_params ? 0 : num_opt_params));
-    for (intptr_t i = 0; i < num_named_params; ++i) {
-      kernel::VariableDeclarationHelper var_helper(&kernel_reader_helper);
-      var_helper.ReadUntilExcluding(kernel::VariableDeclarationHelper::kEnd);
-      if (var_helper.IsCovariant() || var_helper.IsGenericCovariantImpl()) {
-        closure_function.SetParameterTypeAt(num_pos_params + 1 + i,
-                                            object_type);
+    for (intptr_t i = kClosure; i < num_params; ++i) {
+      const intptr_t original_param_index = has_receiver - kClosure + i;
+      if (is_covariant.Contains(original_param_index) ||
+          is_generic_covariant_impl.Contains(original_param_index)) {
+        closure_function.SetParameterTypeAt(i, object_type);
       }
     }
   }