[vm/kernel] Pass type variables through ActivationFrame for expression compilation.

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

Change-Id: I37bbda2dbc6052925aa840865c62a170604c6cdd
Reviewed-on: https://dart-review.googlesource.com/44784
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Samir Jindel <sjindel@google.com>
diff --git a/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart b/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart
new file mode 100644
index 0000000..0909420
--- /dev/null
+++ b/runtime/observatory/tests/service/evaluate_class_type_parameters_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:developer';
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class A<T> {
+  void foo() {
+    debugger();
+  }
+}
+
+class B<S> extends A<int> {
+  void bar() {
+    debugger();
+  }
+}
+
+testFunction() {
+  var v = new B<String>();
+  v.bar();
+  v.foo();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 20);
+
+    Instance result = await isolate.evalFrame(topFrame, '"\$S"');
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+    Instance result = await isolate.evalFrame(topFrame, '"\$T"');
+    print(result);
+    expect(result.valueAsString, equals("int"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart b/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart
new file mode 100644
index 0000000..1ee01db
--- /dev/null
+++ b/runtime/observatory/tests/service/evaluate_function_type_parameters_test.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:developer';
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+topLevel<S>() {
+  debugger();
+
+  void inner1<T>() {
+    debugger();
+  }
+
+  inner1<int>();
+
+  void inner2() {
+    debugger();
+  }
+
+  inner2();
+}
+
+class A {
+  foo<T>() {
+    debugger();
+  }
+}
+
+void testMain() {
+  topLevel<String>();
+  (new A()).foo<int>();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+    Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 16);
+
+    Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+    print(result);
+    expect(result.valueAsString, equals("int"));
+
+    result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 22);
+
+    Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 30);
+
+    Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+    print(result);
+    expect(result.valueAsString, equals("int"));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index 63f1252..8a40809 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -43,6 +43,7 @@
 # Kernel version of tests
 [ $compiler != dartk ]
 add_breakpoint_rpc_kernel_test: SkipByDesign # kernel specific version of add_breakpoint_rpc_test
+evaluate_function_type_parameters_test: SkipByDesign # only supported in kernel
 
 [ $compiler == precompiler ]
 *: Skip # Issue 24651
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 22ec1ae..4253ba1 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -121,6 +121,8 @@
 eval_test: RuntimeError # Issue #33087
 evaluate_activation_test/none: RuntimeError # Issue #33087
 evaluate_async_closure_test: RuntimeError # Issue #33087
+evaluate_class_type_parameters_test: RuntimeError # Issue 33087
+evaluate_function_type_parameters_test: RuntimeError # Issue 33087
 evaluate_in_async_activation_test: RuntimeError # Issue #33087
 evaluate_in_async_star_activation_test: RuntimeError # Issue #33087
 evaluate_in_frame_rpc_test: RuntimeError # Issue #33087
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 85adc30..559b954 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1266,6 +1266,8 @@
                                      const GrowableObjectArray& param_names,
                                      const GrowableObjectArray& param_values) {
   GetDescIndices();
+  bool type_arguments_available = false;
+  TypeArguments& type_arguments = TypeArguments::Handle();
   String& name = String::Handle();
   String& existing_name = String::Handle();
   Object& value = Instance::Handle();
@@ -1273,7 +1275,11 @@
   for (intptr_t i = 0; i < num_variables; i++) {
     TokenPosition ignore;
     VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
-    if (!name.Equals(Symbols::This()) && !IsSyntheticVariableName(name)) {
+    if (name.Equals(Symbols::FunctionTypeArgumentsVar())) {
+      type_arguments_available = true;
+      type_arguments = TypeArguments::RawCast(value.raw());
+    } else if (!name.Equals(Symbols::This()) &&
+               !IsSyntheticVariableName(name)) {
       if (IsPrivateVariableName(name)) {
         name = String::ScrubName(name);
       }
@@ -1295,11 +1301,44 @@
     }
   }
 
+  Array& type_param_names = Array::Handle();
+  if ((function().IsGeneric() || function().HasGenericParent()) &&
+      type_arguments_available) {
+    intptr_t num_vars =
+        function().NumTypeParameters() + function().NumParentTypeParameters();
+    type_param_names = Array::New(num_vars);
+    TypeArguments& type_params = TypeArguments::Handle();
+    TypeParameter& type_param = TypeParameter::Handle();
+    String& name = String::Handle();
+    Function& current = Function::Handle(function().raw());
+    for (intptr_t i = 0; !current.IsNull(); i += current.NumTypeParameters(),
+                  current = current.parent_function()) {
+      type_params = current.type_parameters();
+      for (intptr_t j = 0; j < current.NumTypeParameters(); ++j) {
+        type_param = TypeParameter::RawCast(type_params.TypeAt(j));
+        name = type_param.Name();
+        // Write the names in backwards so they match up with the order of the
+        // types in 'type_arguments'.
+        type_param_names.SetAt(num_vars - (i + j) - 1, name);
+      }
+    }
+    if (type_arguments.IsNull()) {
+      type_arguments = TypeArguments::New(num_vars);
+      for (intptr_t i = 0; i < num_vars; ++i) {
+        type_arguments.SetTypeAt(i, Object::dynamic_type());
+      }
+    }
+    ASSERT(type_arguments.Length() == num_vars);
+  } else {
+    type_param_names = Object::empty_array().raw();
+  }
+
   if (function().is_static()) {
     const Class& cls = Class::Handle(function().Owner());
     return cls.Evaluate(expr,
                         Array::Handle(Array::MakeFixedLength(param_names)),
-                        Array::Handle(Array::MakeFixedLength(param_values)));
+                        Array::Handle(Array::MakeFixedLength(param_values)),
+                        type_param_names, type_arguments);
   } else {
     const Object& receiver = Object::Handle(GetReceiver());
     const Class& method_cls = Class::Handle(function().origin());
@@ -1310,7 +1349,8 @@
     const Instance& inst = Instance::Cast(receiver);
     return inst.Evaluate(method_cls, expr,
                          Array::Handle(Array::MakeFixedLength(param_names)),
-                         Array::Handle(Array::MakeFixedLength(param_values)));
+                         Array::Handle(Array::MakeFixedLength(param_values)),
+                         type_param_names, type_arguments);
   }
   UNREACHABLE();
   return Object::null();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a52b4f6..c50f3ee 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3205,7 +3205,8 @@
                                         const String& library_url,
                                         const String& klass,
                                         bool is_static,
-                                        const Array& arguments);
+                                        const Array& arguments,
+                                        const TypeArguments& type_arguments);
 
 RawFunction* Function::EvaluateHelper(const Class& cls,
                                       const String& expr,
@@ -3242,6 +3243,15 @@
 RawObject* Class::Evaluate(const String& expr,
                            const Array& param_names,
                            const Array& param_values) const {
+  return Evaluate(expr, param_names, param_values, Object::empty_array(),
+                  Object::null_type_arguments());
+}
+
+RawObject* Class::Evaluate(const String& expr,
+                           const Array& param_names,
+                           const Array& param_values,
+                           const Array& type_param_names,
+                           const TypeArguments& type_param_values) const {
   ASSERT(Thread::Current()->IsMutatorThread());
   if (id() < kInstanceCid) {
     const Instance& exception = Instance::Handle(
@@ -3258,10 +3268,10 @@
   }
 
   return EvaluateWithDFEHelper(
-      expr, param_names, Array::Handle(Array::New(0)),
+      expr, param_names, type_param_names,
       String::Handle(Library::Handle(library()).url()),
       IsTopLevel() ? String::Handle() : String::Handle(UserVisibleName()),
-      !IsTopLevel(), param_values);
+      !IsTopLevel(), param_values, type_param_values);
 }
 
 // Ensure that top level parsing of the class has been done.
@@ -11404,6 +11414,15 @@
 RawObject* Library::Evaluate(const String& expr,
                              const Array& param_names,
                              const Array& param_values) const {
+  return Evaluate(expr, param_names, param_values, Array::empty_array(),
+                  TypeArguments::null_type_arguments());
+}
+
+RawObject* Library::Evaluate(const String& expr,
+                             const Array& param_names,
+                             const Array& param_values,
+                             const Array& type_param_names,
+                             const TypeArguments& type_param_values) const {
   if (kernel_data() == TypedData::null() ||
       !FLAG_enable_kernel_expression_compilation) {
     // Evaluate the expression as a static function of the toplevel class.
@@ -11411,9 +11430,9 @@
     ASSERT(top_level_class.is_finalized());
     return top_level_class.Evaluate(expr, param_names, param_values);
   }
-  return EvaluateWithDFEHelper(expr, param_names, Array::Handle(Array::New(0)),
+  return EvaluateWithDFEHelper(expr, param_names, type_param_names,
                                String::Handle(url()), String::Handle(), false,
-                               param_values);
+                               param_values, type_param_values);
 }
 
 void Library::InitNativeWrappersLibrary(Isolate* isolate, bool is_kernel) {
@@ -11476,7 +11495,8 @@
                                         const String& library_url,
                                         const String& klass,
                                         bool is_static,
-                                        const Array& arguments) {
+                                        const Array& arguments,
+                                        const TypeArguments& type_arguments) {
 #if defined(DART_PRECOMPILED_RUNTIME)
   const String& error_str = String::Handle(
       String::New("Kernel service isolate not available in precompiled mode."));
@@ -11581,7 +11601,21 @@
   ASSERT(removed);
   I->object_store()->set_libraries_map(libraries_map.Release());
 
-  return DartEntry::InvokeFunction(callee, arguments);
+  if (type_definitions.Length() == 0) {
+    return DartEntry::InvokeFunction(callee, arguments);
+  }
+
+  intptr_t num_type_args = type_arguments.Length();
+  Array& real_arguments = Array::Handle(Array::New(arguments.Length() + 1));
+  real_arguments.SetAt(0, type_arguments);
+  Object& arg = Object::Handle();
+  for (intptr_t i = 0; i < arguments.Length(); ++i) {
+    arg = arguments.At(i);
+    real_arguments.SetAt(i + 1, arg);
+  }
+  const Array& args_desc = Array::Handle(
+      ArgumentsDescriptor::New(num_type_args, real_arguments.Length()));
+  return DartEntry::InvokeFunction(callee, real_arguments, args_desc);
 #endif
 }
 
@@ -15801,6 +15835,16 @@
                               const String& expr,
                               const Array& param_names,
                               const Array& param_values) const {
+  return Evaluate(method_cls, expr, param_names, param_values,
+                  Object::empty_array(), TypeArguments::null_type_arguments());
+}
+
+RawObject* Instance::Evaluate(const Class& method_cls,
+                              const String& expr,
+                              const Array& param_names,
+                              const Array& param_values,
+                              const Array& type_param_names,
+                              const TypeArguments& type_param_values) const {
   const Array& args = Array::Handle(Array::New(1 + param_values.Length()));
   PassiveObject& param = PassiveObject::Handle();
   args.SetAt(0, *this);
@@ -15817,9 +15861,10 @@
     return DartEntry::InvokeFunction(eval_func, args);
   }
   return EvaluateWithDFEHelper(
-      expr, param_names, Array::Handle(Array::New(0)),
+      expr, param_names, type_param_names,
       String::Handle(Library::Handle(method_cls.library()).url()),
-      String::Handle(method_cls.UserVisibleName()), false, args);
+      String::Handle(method_cls.UserVisibleName()), false, args,
+      type_param_values);
 }
 
 RawObject* Instance::HashCode() const {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 49cbc89..c7749c5 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1365,14 +1365,19 @@
   // Return true on success, or false and error otherwise.
   bool ApplyPatch(const Class& patch, Error* error) const;
 
-  // Evaluate the given expression as if it appeared in a static
-  // method of this class and return the resulting value, or an
-  // error object if evaluating the expression fails. The method has
-  // the formal parameters given in param_names, and is invoked with
-  // the argument values given in param_values.
+  // Evaluate the given expression as if it appeared in a static method of this
+  // class and return the resulting value, or an error object if evaluating the
+  // expression fails. The method has the formal (type) parameters given in
+  // (type_)param_names, and is invoked with the (type)argument values given in
+  // (type_)param_values.
   RawObject* Evaluate(const String& expr,
                       const Array& param_names,
                       const Array& param_values) const;
+  RawObject* Evaluate(const String& expr,
+                      const Array& param_names,
+                      const Array& param_values,
+                      const Array& type_param_names,
+                      const TypeArguments& type_param_values) const;
 
   RawError* EnsureIsFinalized(Thread* thread) const;
 
@@ -3713,15 +3718,21 @@
 
   static RawLibrary* New(const String& url);
 
-  // Evaluate the given expression as if it appeared in an top-level
-  // method of this library and return the resulting value, or an
-  // error object if evaluating the expression fails. The method has
-  // the formal parameters given in param_names, and is invoked with
-  // the argument values given in param_values.
+  // Evaluate the given expression as if it appeared in an top-level method of
+  // this library and return the resulting value, or an error object if
+  // evaluating the expression fails. The method has the formal (type)
+  // parameters given in (type_)param_names, and is invoked with the (type)
+  // argument values given in (type_)param_values.
   RawObject* Evaluate(const String& expr,
                       const Array& param_names,
                       const Array& param_values) const;
 
+  RawObject* Evaluate(const String& expr,
+                      const Array& param_names,
+                      const Array& param_values,
+                      const Array& type_param_names,
+                      const TypeArguments& type_arguments) const;
+
   // Library scope name dictionary.
   //
   // TODO(turnidge): The Lookup functions are not consistent in how
@@ -5579,16 +5590,23 @@
   // (if not NULL) to call.
   bool IsCallable(Function* function) const;
 
-  // Evaluate the given expression as if it appeared in an instance
-  // method of this instance and return the resulting value, or an
-  // error object if evaluating the expression fails. The method has
-  // the formal parameters given in param_names, and is invoked with
-  // the argument values given in param_values.
+  // Evaluate the given expression as if it appeared in an instance method of
+  // this instance and return the resulting value, or an error object if
+  // evaluating the expression fails. The method has the formal (type)
+  // parameters given in (type_)param_names, and is invoked with the (type)
+  // argument values given in (type_)param_values.
   RawObject* Evaluate(const Class& method_cls,
                       const String& expr,
                       const Array& param_names,
                       const Array& param_values) const;
 
+  RawObject* Evaluate(const Class& method_cls,
+                      const String& expr,
+                      const Array& param_names,
+                      const Array& param_values,
+                      const Array& type_param_names,
+                      const TypeArguments& type_param_values) const;
+
   // Equivalent to invoking hashCode on this instance.
   virtual RawObject* HashCode() const;
 
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index e07e393..e38d191 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -268,6 +268,10 @@
     // Keep :async_stack_trace for asynchronous debugging.
     return false;
   }
+  if (str.raw() == Symbols::FunctionTypeArgumentsVar().raw()) {
+    // Keep :function_type_arguments for accessing type variables in debugging.
+    return false;
+  }
   return str.CharAt(0) == ':';
 }
 
@@ -644,7 +648,8 @@
           (variable->name().raw() == Symbols::ExceptionVar().raw()) ||
           (variable->name().raw() == Symbols::SavedTryContextVar().raw()) ||
           (variable->name().raw() == Symbols::ArgDescVar().raw()) ||
-          (variable->name().raw() == Symbols::FunctionTypeArgumentsVar().raw())) {
+          (variable->name().raw() ==
+           Symbols::FunctionTypeArgumentsVar().raw())) {
         // Don't capture those variables because the VM expects them to be on
         // the stack.
         continue;