[kernel] Add an option to the cloner for cloning annotations

Fixes #33099

Bug: http://dartbug.com/33099
Change-Id: I291e75fa49fb6bf62557cfeab0c874c5de9b618d
Reviewed-on: https://dart-review.googlesource.com/57264
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index d331200..efd77e8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -427,8 +427,9 @@
       KernelTarget target, Procedure procedure, ClassHierarchy hierarchy) {
     CloneWithoutBody cloner = new CloneWithoutBody(
         typeSubstitution: getSubstitutionMap(
-            hierarchy.getClassAsInstanceOf(cls, procedure.enclosingClass)));
-    Procedure cloned = cloner.clone(procedure);
+            hierarchy.getClassAsInstanceOf(cls, procedure.enclosingClass)),
+        cloneAnnotations: false);
+    Procedure cloned = cloner.clone(procedure)..isExternal = false;
     transformProcedureToNoSuchMethodForwarder(noSuchMethod, target, cloned);
     cls.procedures.add(cloned);
     cloned.parent = cls;
diff --git a/pkg/front_end/testcases/bug33099.dart b/pkg/front_end/testcases/bug33099.dart
new file mode 100644
index 0000000..25fd24e
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart
@@ -0,0 +1,41 @@
+// 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:mirrors';
+
+const _FailingTest failingTest = const _FailingTest();
+
+class _FailingTest {
+  const _FailingTest();
+}
+
+class MyTest {
+  @failingTest
+  void foo() {}
+}
+
+class MyTest2 extends Object with MyTest {}
+
+main() {
+  ClassMirror classMirror = reflectClass(MyTest2);
+  classMirror.instanceMembers
+      .forEach((Symbol symbol, MethodMirror memberMirror) {
+    if (memberMirror.simpleName == #foo) {
+      print(memberMirror);
+      print(_hasFailingTestAnnotation(memberMirror));
+    }
+  });
+}
+
+bool _hasFailingTestAnnotation(MethodMirror method) {
+  var r = _hasAnnotationInstance(method, failingTest);
+  print('[_hasFailingTestAnnotation] $method $r');
+  return r;
+}
+
+bool _hasAnnotationInstance(DeclarationMirror declaration, instance) =>
+    declaration.metadata.any((InstanceMirror annotation) {
+      print('annotation: ${annotation.reflectee}');
+      return identical(annotation.reflectee, instance);
+    });
diff --git a/pkg/front_end/testcases/bug33099.dart.direct.expect b/pkg/front_end/testcases/bug33099.dart.direct.expect
new file mode 100644
index 0000000..ac82f35
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart.direct.expect
@@ -0,0 +1,44 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:mirrors" as mir;
+
+class _FailingTest extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class MyTest extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+abstract class _MyTest2&Object&MyTest = core::Object with self::MyTest {
+}
+class MyTest2 extends self::_MyTest2&Object&MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+static const field self::_FailingTest failingTest = const self::_FailingTest::•();
+static method main() → dynamic {
+  mir::ClassMirror classMirror = mir::reflectClass(self::MyTest2);
+  classMirror.instanceMembers.forEach((core::Symbol symbol, mir::MethodMirror memberMirror) → dynamic {
+    if(memberMirror.simpleName.==(#foo)) {
+      core::print(memberMirror);
+      core::print(self::_hasFailingTestAnnotation(memberMirror));
+    }
+  });
+}
+static method _hasFailingTestAnnotation(mir::MethodMirror method) → core::bool {
+  dynamic r = self::_hasAnnotationInstance(method, self::failingTest);
+  core::print("[_hasFailingTestAnnotation] ${method} ${r}");
+  return r;
+}
+static method _hasAnnotationInstance(mir::DeclarationMirror declaration, dynamic instance) → core::bool
+  return declaration.metadata.any((mir::InstanceMirror annotation) → dynamic {
+    core::print("annotation: ${annotation.reflectee}");
+    return core::identical(annotation.reflectee, instance);
+  });
diff --git a/pkg/front_end/testcases/bug33099.dart.direct.transformed.expect b/pkg/front_end/testcases/bug33099.dart.direct.transformed.expect
new file mode 100644
index 0000000..d26b033
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart.direct.transformed.expect
@@ -0,0 +1,49 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:mirrors" as mir;
+
+class _FailingTest extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class MyTest extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+abstract class _MyTest2&Object&MyTest extends core::Object implements self::MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+class MyTest2 extends self::_MyTest2&Object&MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+static const field self::_FailingTest failingTest = const self::_FailingTest::•();
+static method main() → dynamic {
+  mir::ClassMirror classMirror = mir::reflectClass(self::MyTest2);
+  classMirror.instanceMembers.forEach((core::Symbol symbol, mir::MethodMirror memberMirror) → dynamic {
+    if(memberMirror.simpleName.==(#foo)) {
+      core::print(memberMirror);
+      core::print(self::_hasFailingTestAnnotation(memberMirror));
+    }
+  });
+}
+static method _hasFailingTestAnnotation(mir::MethodMirror method) → core::bool {
+  dynamic r = self::_hasAnnotationInstance(method, self::failingTest);
+  core::print("[_hasFailingTestAnnotation] ${method} ${r}");
+  return r;
+}
+static method _hasAnnotationInstance(mir::DeclarationMirror declaration, dynamic instance) → core::bool
+  return declaration.metadata.any((mir::InstanceMirror annotation) → dynamic {
+    core::print("annotation: ${annotation.reflectee}");
+    return core::identical(annotation.reflectee, instance);
+  });
diff --git a/pkg/front_end/testcases/bug33099.dart.outline.expect b/pkg/front_end/testcases/bug33099.dart.outline.expect
new file mode 100644
index 0000000..9d360a0
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart.outline.expect
@@ -0,0 +1,28 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:mirrors" as mir;
+
+class _FailingTest extends core::Object {
+  const constructor •() → void
+    ;
+}
+class MyTest extends core::Object {
+  synthetic constructor •() → void
+    ;
+  method foo() → void
+    ;
+}
+abstract class _MyTest2&Object&MyTest = core::Object with self::MyTest {
+}
+class MyTest2 extends self::_MyTest2&Object&MyTest {
+  synthetic constructor •() → void
+    ;
+}
+static const field self::_FailingTest failingTest;
+static method main() → dynamic
+  ;
+static method _hasFailingTestAnnotation(mir::MethodMirror method) → core::bool
+  ;
+static method _hasAnnotationInstance(mir::DeclarationMirror declaration, dynamic instance) → core::bool
+  ;
diff --git a/pkg/front_end/testcases/bug33099.dart.strong.expect b/pkg/front_end/testcases/bug33099.dart.strong.expect
new file mode 100644
index 0000000..4119f1c
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart.strong.expect
@@ -0,0 +1,44 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:mirrors" as mir;
+
+class _FailingTest extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class MyTest extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+abstract class _MyTest2&Object&MyTest = core::Object with self::MyTest {
+}
+class MyTest2 extends self::_MyTest2&Object&MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+static const field self::_FailingTest failingTest = const self::_FailingTest::•();
+static method main() → dynamic {
+  mir::ClassMirror classMirror = mir::reflectClass(self::MyTest2);
+  classMirror.{mir::ClassMirror::instanceMembers}.{core::Map::forEach}((core::Symbol symbol, mir::MethodMirror memberMirror) → core::Null {
+    if(memberMirror.{mir::DeclarationMirror::simpleName}.{core::Symbol::==}(#foo)) {
+      core::print(memberMirror);
+      core::print(self::_hasFailingTestAnnotation(memberMirror));
+    }
+  });
+}
+static method _hasFailingTestAnnotation(mir::MethodMirror method) → core::bool {
+  core::bool r = self::_hasAnnotationInstance(method, self::failingTest);
+  core::print("[_hasFailingTestAnnotation] ${method} ${r}");
+  return r;
+}
+static method _hasAnnotationInstance(mir::DeclarationMirror declaration, dynamic instance) → core::bool
+  return declaration.{mir::DeclarationMirror::metadata}.{core::Iterable::any}((mir::InstanceMirror annotation) → core::bool {
+    core::print("annotation: ${annotation.{mir::InstanceMirror::reflectee}}");
+    return core::identical(annotation.{mir::InstanceMirror::reflectee}, instance);
+  });
diff --git a/pkg/front_end/testcases/bug33099.dart.strong.transformed.expect b/pkg/front_end/testcases/bug33099.dart.strong.transformed.expect
new file mode 100644
index 0000000..072ae37
--- /dev/null
+++ b/pkg/front_end/testcases/bug33099.dart.strong.transformed.expect
@@ -0,0 +1,49 @@
+library;
+import self as self;
+import "dart:core" as core;
+import "dart:mirrors" as mir;
+
+class _FailingTest extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class MyTest extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+abstract class _MyTest2&Object&MyTest extends core::Object implements self::MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  @self::failingTest
+  method foo() → void {}
+}
+class MyTest2 extends self::_MyTest2&Object&MyTest {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+static const field self::_FailingTest failingTest = const self::_FailingTest::•();
+static method main() → dynamic {
+  mir::ClassMirror classMirror = mir::reflectClass(self::MyTest2);
+  classMirror.{mir::ClassMirror::instanceMembers}.{core::Map::forEach}((core::Symbol symbol, mir::MethodMirror memberMirror) → core::Null {
+    if(memberMirror.{mir::DeclarationMirror::simpleName}.{core::Symbol::==}(#foo)) {
+      core::print(memberMirror);
+      core::print(self::_hasFailingTestAnnotation(memberMirror));
+    }
+  });
+}
+static method _hasFailingTestAnnotation(mir::MethodMirror method) → core::bool {
+  core::bool r = self::_hasAnnotationInstance(method, self::failingTest);
+  core::print("[_hasFailingTestAnnotation] ${method} ${r}");
+  return r;
+}
+static method _hasAnnotationInstance(mir::DeclarationMirror declaration, dynamic instance) → core::bool
+  return declaration.{mir::DeclarationMirror::metadata}.{core::Iterable::any}((mir::InstanceMirror annotation) → core::bool {
+    core::print("annotation: ${annotation.{mir::InstanceMirror::reflectee}}");
+    return core::identical(annotation.{mir::InstanceMirror::reflectee}, instance);
+  });
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 3a461aa1..91f5fd6 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -20,8 +20,16 @@
       <LabeledStatement, LabeledStatement>{};
   final Map<SwitchCase, SwitchCase> switchCases = <SwitchCase, SwitchCase>{};
   final Map<TypeParameter, DartType> typeSubstitution;
+  bool cloneAnnotations;
 
-  CloneVisitor({Map<TypeParameter, DartType> typeSubstitution})
+  /// Creates an instance of the cloning visitor for Kernel ASTs.
+  ///
+  /// The boolean value of [cloneAnnotations] tells if the annotations on the
+  /// outline elements in the source AST should be cloned to the target AST. The
+  /// annotations in procedure bodies are cloned unconditionally.
+  CloneVisitor(
+      {Map<TypeParameter, DartType> typeSubstitution,
+      this.cloneAnnotations = true})
       : this.typeSubstitution = ensureMutable(typeSubstitution);
 
   static Map<TypeParameter, DartType> ensureMutable(
@@ -395,6 +403,9 @@
     return variables[node] = new VariableDeclaration(node.name,
         initializer: cloneOptional(node.initializer),
         type: visitType(node.type))
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[]
       ..flags = node.flags;
   }
 
@@ -413,6 +424,9 @@
         initializers: node.initializers.map(clone).toList(),
         transformerFlags: node.transformerFlags,
         fileUri: _activeFileUri)
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[]
       ..fileOffset = _cloneFileOffset(node.fileOffset)
       ..fileEndOffset = _cloneFileOffset(node.fileEndOffset);
   }
@@ -423,6 +437,9 @@
         fileUri: _activeFileUri,
         forwardingStubSuperTarget: node.forwardingStubSuperTarget,
         forwardingStubInterfaceTarget: node.forwardingStubInterfaceTarget)
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[]
       ..fileOffset = _cloneFileOffset(node.fileOffset)
       ..fileEndOffset = _cloneFileOffset(node.fileEndOffset)
       ..flags = node.flags;
@@ -440,6 +457,9 @@
         hasImplicitSetter: node.hasImplicitSetter,
         transformerFlags: node.transformerFlags,
         fileUri: _activeFileUri)
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[]
       ..fileOffset = _cloneFileOffset(node.fileOffset)
       ..fileEndOffset = _cloneFileOffset(node.fileEndOffset)
       ..flags = node.flags;
@@ -456,7 +476,10 @@
         positionalParameters: node.positionalParameters.map(clone).toList(),
         namedParameters: node.namedParameters.map(clone).toList(),
         requiredParameterCount: node.requiredParameterCount,
-        fileUri: _activeFileUri);
+        fileUri: _activeFileUri)
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[];
   }
 
   visitTypeParameter(TypeParameter node) {
@@ -466,10 +489,22 @@
     if (node.defaultType != null) {
       newNode.defaultType = visitType(node.defaultType);
     }
-    return newNode..flags = node.flags;
+    return newNode
+      ..annotations = cloneAnnotations && !node.annotations.isEmpty
+          ? node.annotations.map(clone).toList()
+          : const <Expression>[]
+      ..flags = node.flags;
   }
 
-  TreeNode cloneFunctionNodeBody(FunctionNode node) => cloneOptional(node.body);
+  TreeNode cloneFunctionNodeBody(FunctionNode node) {
+    bool savedCloneAnnotations = this.cloneAnnotations;
+    try {
+      this.cloneAnnotations = true;
+      return cloneOptional(node.body);
+    } finally {
+      this.cloneAnnotations = savedCloneAnnotations;
+    }
+  }
 
   visitFunctionNode(FunctionNode node) {
     var typeParameters = node.typeParameters.map(clone).toList();
@@ -584,8 +619,12 @@
 }
 
 class CloneWithoutBody extends CloneVisitor {
-  CloneWithoutBody({Map<TypeParameter, DartType> typeSubstitution})
-      : super(typeSubstitution: typeSubstitution);
+  CloneWithoutBody(
+      {Map<TypeParameter, DartType> typeSubstitution,
+      bool cloneAnnotations = true})
+      : super(
+            typeSubstitution: typeSubstitution,
+            cloneAnnotations: cloneAnnotations);
 
   @override
   TreeNode cloneFunctionNodeBody(FunctionNode node) => null;