fix #33872, support typeArguments passed to reflectType

Change-Id: I84cfbeec89fa9bf3fa50b7df0ccb6861d364b802
Reviewed-on: https://dart-review.googlesource.com/65201
Commit-Queue: Jenny Messerly <jmesserly@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index c39397d..1140233 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -6381,8 +6381,8 @@
         .replaceAll(path.separator, encodedSeparator);
   } else {
     // We don't have a unique name.
-    throw 'Invalid library root. $libraryRoot does not contain ${uri
-        .toFilePath()}';
+    throw 'Invalid library root. $libraryRoot does not contain '
+        '${uri.toFilePath()}';
   }
   return pathToJSIdentifier(qualifiedPath);
 }
@@ -6395,8 +6395,8 @@
 
   var filePath = uri.toFilePath();
   if (!path.isWithin(libraryRoot, filePath)) {
-    throw 'Invalid library root. $libraryRoot does not contain ${uri
-        .toFilePath()}';
+    throw 'Invalid library root. $libraryRoot does not contain '
+        '${uri.toFilePath()}';
   }
   // Relative path to the library.
   return path.relative(filePath, from: libraryRoot);
diff --git a/pkg/dev_compiler/tool/input_sdk/patch/mirrors_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/mirrors_patch.dart
index dec3d20..e3b015b 100644
--- a/pkg/dev_compiler/tool/input_sdk/patch/mirrors_patch.dart
+++ b/pkg/dev_compiler/tool/input_sdk/patch/mirrors_patch.dart
@@ -6,6 +6,7 @@
 
 import 'dart:_js_helper' show patch;
 import 'dart:_js_mirrors' as js;
+import 'dart:_runtime' as dart;
 
 @patch
 class MirrorSystem {
@@ -43,9 +44,39 @@
 }
 
 @patch
-TypeMirror reflectType(Type key) {
-  if (key == dynamic) {
-    return currentMirrorSystem().dynamicType;
+TypeMirror reflectType(Type type, [List<Type> typeArguments]) {
+  if (typeArguments != null) {
+    type = _instantiateClass(type, typeArguments);
   }
-  return js.reflectType(key);
+  return js.reflectType(type);
+}
+
+/// Instantiates the generic class [type] with [typeArguments] and returns the
+/// result.
+///
+/// [type] may be instantiated with type arguments already. In that case, they
+/// are ignored. For example calling this function with `(List<int>, [String])`
+/// and `(List<dynamic>, [String])` will produce `List<String>` in both cases.
+Type _instantiateClass(Type type, List<Type> typeArguments) {
+  var unwrapped = dart.unwrapType(type);
+  var genericClass = dart.getGenericClass(unwrapped);
+  if (genericClass == null) {
+    throw ArgumentError('Type `$type` must be generic to apply '
+        'type arguments: `$typeArguments`.');
+  }
+
+  var typeArgsLenth = typeArguments.length;
+  var unwrappedArgs = List(typeArgsLenth);
+  for (int i = 0; i < typeArgsLenth; i++) {
+    unwrappedArgs[i] = dart.unwrapType(typeArguments[i]);
+  }
+  var typeFormals = dart.getGenericTypeFormals(genericClass);
+  if (typeFormals.length != typeArgsLenth) {
+    throw ArgumentError('Type `$type` has ${typeFormals.length} type '
+        'parameters, but $typeArgsLenth type arguments were '
+        'passed: `$typeArguments`.');
+  }
+  // TODO(jmesserly): this does not validate bounds, as we don't have them
+  // available at runtime. Consider storing them when dart:mirrors is enabled.
+  return dart.wrapType(dart.instantiateClass(genericClass, unwrappedArgs));
 }