[kernel] Support metadata annotations on classes and members.

BUG=
R=jensj@google.com, kustermann@google.com, vegorov@google.com

Review URL: https://chromereviews.googleplex.com/481447014 .
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 5d84cfd..776829d 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -173,6 +173,7 @@
   Byte tag = 2;
   Byte flags (isAbstract);
   StringReference name;
+  List<Expression> annotations;
   List<TypeParameter> typeParameters;
   Option<InterfaceType> superClass;
   List<InterfaceType> implementedClasses;
@@ -185,6 +186,7 @@
   Byte tag = 3;
   Byte flags (isAbstract);
   StringReference name;
+  List<Expression> annotations;
   List<TypeParameter> typeParameters;
   InterfaceType firstSuperClass;
   InterfaceType secondSuperClass;
@@ -198,6 +200,7 @@
   Byte tag = 4;
   Byte flags (isFinal, isConst, isStatic);
   Name name;
+  List<Expression> annotations;
   DartType type;
   Option<Expression> initializer;
 }
@@ -206,6 +209,7 @@
   Byte tag = 5;
   Byte flags (isConst, isExternal);
   Name name;
+  List<Expression> annotations;
   FunctionNode function;
   List<Initializer> initializers;
 }
@@ -225,6 +229,7 @@
   Byte kind; // Index into the ProcedureKind enum above.
   Byte flags (isStatic, isAbstract, isExternal, isConst);
   Name name;
+  List<Expression> annotations;
   // Can only be absent if abstract, but tag is there anyway.
   Option<FunctionNode> function;
 }
diff --git a/pkg/kernel/lib/analyzer/ast_from_analyzer.dart b/pkg/kernel/lib/analyzer/ast_from_analyzer.dart
index 175793b..bec4e8d 100644
--- a/pkg/kernel/lib/analyzer/ast_from_analyzer.dart
+++ b/pkg/kernel/lib/analyzer/ast_from_analyzer.dart
@@ -297,51 +297,22 @@
   }
 }
 
-/// Translates expressions, statements, and other constructs into [ast] nodes.
-///
-/// Naming convention:
-/// - `buildX` may not be given null as argument (it may crash the compiler).
-/// - `buildOptionalX` returns null or an empty list if given null
-/// - `buildMandatoryX` returns an invalid node if given null.
-class MemberScope extends TypeScope {
+class ExpressionScope extends TypeScope {
+  /// The library containing the code, currently at body level.
+  ast.Library currentLibrary;
   final Map<LocalElement, ast.VariableDeclaration> localVariables =
       <LocalElement, ast.VariableDeclaration>{};
 
-  /// A reference to the member currently being upgraded to body level.
-  final ast.Member currentMember;
-
   ExpressionBuilder _expressionBuilder;
   StatementBuilder _statementBuilder;
 
-  MemberScope(ReferenceLevelLoader loader, this.currentMember) : super(loader) {
-    assert(currentMember != null);
+  ExpressionScope(ReferenceLevelLoader loader, this.currentLibrary)
+      : super(loader) {
     _expressionBuilder = new ExpressionBuilder(this);
     _statementBuilder = new StatementBuilder(this);
   }
 
-  /// The library containing the code, currently at body level.
-  ast.Library get currentLibrary => currentMember.enclosingLibrary;
-
-  ast.Class get currentClass => currentMember.enclosingClass;
-
-  bool get allowThis => _memberHasThis(currentMember);
-
-  /// Returns a string for debugging use, indicating the location of the member
-  /// being built.
-  String get location {
-    var library = currentMember.enclosingLibrary?.importUri ?? '<No Library>';
-    var className = currentMember.enclosingClass == null
-        ? null
-        : (currentMember.enclosingClass?.name ?? '<Anonymous Class>');
-    var member =
-        currentMember.name?.name ?? '<Anonymous ${currentMember.runtimeType}>';
-    return [library, className, member].join('::');
-  }
-
-  bool _memberHasThis(ast.Member member) {
-    return member is ast.Procedure && !member.isStatic ||
-        member is ast.Constructor;
-  }
+  bool get allowThis => false; // Overridden by MemberScope.
 
   ast.Name buildName(SimpleIdentifier node) {
     return new ast.Name(node.name, currentLibrary);
@@ -478,6 +449,63 @@
     }
     return declaration;
   }
+
+  ast.Expression buildAnnotation(Annotation annotation) {
+    Element element = annotation.element;
+    if (annotation.arguments == null) {
+      var target = resolveGet(element, null);
+      return target == null
+          ? new ast.InvalidExpression()
+          : new ast.StaticGet(target);
+    } else if (element is ConstructorElement && element.isConst) {
+      var target = resolveConstructor(element);
+      return target == null
+          ? new ast.InvalidExpression()
+          : new ast.ConstructorInvocation(
+              target, _expressionBuilder.buildArguments(annotation.arguments),
+              isConst: true);
+    } else {
+      return new ast.InvalidExpression();
+    }
+  }
+}
+
+/// Translates expressions, statements, and other constructs into [ast] nodes.
+///
+/// Naming convention:
+/// - `buildX` may not be given null as argument (it may crash the compiler).
+/// - `buildOptionalX` returns null or an empty list if given null
+/// - `buildMandatoryX` returns an invalid node if given null.
+class MemberScope extends ExpressionScope {
+  /// A reference to the member currently being upgraded to body level.
+  final ast.Member currentMember;
+
+  MemberScope(ReferenceLevelLoader loader, ast.Member currentMember)
+      : currentMember = currentMember,
+        super(loader, currentMember.enclosingLibrary) {
+    assert(currentMember != null);
+  }
+
+  ast.Class get currentClass => currentMember.enclosingClass;
+
+  bool get allowThis => _memberHasThis(currentMember);
+
+  /// Returns a string for debugging use, indicating the location of the member
+  /// being built.
+  String get location {
+    var library = currentMember.enclosingLibrary?.importUri ?? '<No Library>';
+    var className = currentMember.enclosingClass == null
+        ? null
+        : (currentMember.enclosingClass?.name ?? '<Anonymous Class>');
+    var member =
+        currentMember.name?.name ?? '<Anonymous ${currentMember.runtimeType}>';
+    return [library, className, member].join('::');
+  }
+
+  bool _memberHasThis(ast.Member member) {
+    return member is ast.Procedure && !member.isStatic ||
+        member is ast.Constructor;
+  }
 }
 
 class LabelStack {
@@ -495,7 +523,7 @@
 }
 
 class StatementBuilder extends GeneralizingAstVisitor<ast.Statement> {
-  final MemberScope scope;
+  final ExpressionScope scope;
   final LabelStack breakStack, continueStack;
 
   StatementBuilder(this.scope, [this.breakStack, this.continueStack]);
@@ -851,7 +879,7 @@
 
 class ExpressionBuilder
     extends GeneralizingAstVisitor /* <ast.Expression | Accessor> */ {
-  final MemberScope scope;
+  final ExpressionScope scope;
   final ast.VariableDeclaration cascadeReceiver;
   ExpressionBuilder(this.scope, [this.cascadeReceiver]);
 
@@ -1447,7 +1475,7 @@
 }
 
 class StringLiteralPartBuilder extends GeneralizingAstVisitor<Null> {
-  final MemberScope scope;
+  final ExpressionScope scope;
   final List<ast.Expression> output;
   StringLiteralPartBuilder(this.scope, this.output);
 
@@ -1698,13 +1726,15 @@
 ///
 /// The enclosing library is assumed to be at body level already.
 class ClassBodyBuilder extends GeneralizingAstVisitor<Null> {
-  final TypeScope scope;
+  final ExpressionScope scope;
   final ast.Class currentClass;
   final ClassElement element;
   ast.Library get currentLibrary => currentClass.enclosingLibrary;
 
-  ClassBodyBuilder(ReferenceLevelLoader loader, this.currentClass, this.element)
-      : scope = new TypeScope(loader);
+  ClassBodyBuilder(
+      ReferenceLevelLoader loader, ast.Class currentClass, this.element)
+      : this.currentClass = currentClass,
+        scope = new ExpressionScope(loader, currentClass.enclosingLibrary);
 
   void build(CompilationUnitMember node) {
     if (node == null) {
@@ -1713,6 +1743,12 @@
     node.accept(this);
   }
 
+  void addAnnotations(List<Annotation> annotations) {
+    for (var annotation in annotations) {
+      currentClass.addAnnotation(scope.buildAnnotation(annotation));
+    }
+  }
+
   void addTypeParameterBounds(TypeParameterList typeParameters) {
     if (typeParameters == null) return;
     int index = 0;
@@ -1760,6 +1796,7 @@
   }
 
   visitClassDeclaration(ClassDeclaration node) {
+    addAnnotations(node.metadata);
     ast.NormalClass classNode = currentClass;
     addTypeParameterBounds(node.typeParameters);
     // Build the super class reference and expand the 'with' clause into
@@ -1810,6 +1847,7 @@
   static bool _isValuesField(FieldElement field) => field.name == 'values';
 
   visitEnumDeclaration(EnumDeclaration node) {
+    addAnnotations(node.metadata);
     ast.NormalClass classNode = currentClass;
     classNode.supertype = new ast.InterfaceType(scope.getRootClassReference());
     var intType =
@@ -1856,6 +1894,7 @@
   }
 
   visitClassTypeAlias(ClassTypeAlias node) {
+    addAnnotations(node.metadata);
     assert(node.withClause != null && node.withClause.mixinTypes.isNotEmpty);
     ast.MixinClass classNode = currentClass;
     addTypeParameterBounds(node.typeParameters);
@@ -1986,6 +2025,12 @@
             types: typeArguments))..parent = constructor);
   }
 
+  void addAnnotations(List<Annotation> annotations) {
+    for (var annotation in annotations) {
+      currentMember.addAnnotation(scope.buildAnnotation(annotation));
+    }
+  }
+
   visitConstructorDeclaration(ConstructorDeclaration node) {
     if (node.factoryKeyword != null) {
       buildFactoryConstructor(node);
@@ -1995,6 +2040,7 @@
   }
 
   void buildGenerativeConstructor(ConstructorDeclaration node) {
+    addAnnotations(node.metadata);
     ast.Constructor constructor = currentMember;
     constructor.function = scope.buildFunctionNode(node.parameters, node.body,
         inferredReturnType: const ast.VoidType())..parent = constructor;
@@ -2030,6 +2076,7 @@
   }
 
   void buildFactoryConstructor(ConstructorDeclaration node) {
+    addAnnotations(node.metadata);
     ast.Procedure procedure = currentMember;
     ClassElement classElement = node.element.enclosingElement;
     ast.NormalClass classNode = procedure.enclosingClass;
@@ -2074,6 +2121,7 @@
   }
 
   visitMethodDeclaration(MethodDeclaration node) {
+    addAnnotations(node.metadata);
     ast.Procedure procedure = currentMember;
     procedure.function = scope.buildFunctionNode(node.parameters, node.body,
         returnType: node.returnType,
@@ -2084,6 +2132,7 @@
   }
 
   visitVariableDeclaration(VariableDeclaration node) {
+    addAnnotations(node.metadata);
     ast.Field field = currentMember;
     field.type = scope.buildType(node.element.type);
     if (node.initializer != null) {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 4f73438..349a5d3 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -261,6 +261,12 @@
 /// should treat the two kinds of classes separately, but otherwise it is
 /// recommended to interface against [Class].
 abstract class Class extends TreeNode {
+  /// List of metadata annotations on the class.
+  ///
+  /// This defaults to an immutable empty list. Use [addAnnotation] to add
+  /// annotations if needed.
+  List<Expression> annotations = const <Expression>[];
+
   String name; // Cosmetic name.
   bool isAbstract;
   final List<TypeParameter> typeParameters;
@@ -343,6 +349,14 @@
     }
   }
 
+  void addAnnotation(Expression node) {
+    if (annotations.isEmpty) {
+      annotations = <Expression>[];
+    }
+    annotations.add(node);
+    node.parent = this;
+  }
+
   accept(ClassVisitor v);
   acceptReference(ClassReferenceVisitor v);
 
@@ -387,6 +401,7 @@
   acceptReference(ClassReferenceVisitor v) => v.visitNormalClassReference(this);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     visitList(typeParameters, v);
     supertype?.accept(v);
     visitList(implementedTypes, v);
@@ -396,6 +411,7 @@
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     transformList(typeParameters, v, this);
     transformList(constructors, v, this);
     transformList(procedures, v, this);
@@ -440,6 +456,7 @@
   acceptReference(ClassReferenceVisitor v) => v.visitMixinClassReference(this);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     visitList(typeParameters, v);
     supertype?.accept(v);
     mixedInType?.accept(v);
@@ -448,6 +465,7 @@
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     transformList(typeParameters, v, this);
     transformList(constructors, v, this);
   }
@@ -458,6 +476,11 @@
 // ------------------------------------------------------------------------
 
 abstract class Member extends TreeNode {
+  /// List of metadata annotations on the class.
+  ///
+  /// This defaults to an immutable empty list. Use [addAnnotation] to add
+  /// annotations if needed.
+  List<Expression> annotations = const <Expression>[];
   Name name;
 
   Member(this.name);
@@ -482,6 +505,14 @@
   /// Returns a possibly synthesized name for this member, consistent with
   /// the names used across all [toString] calls.
   String toString() => debugQualifiedMemberName(this);
+
+  void addAnnotation(Expression node) {
+    if (annotations.isEmpty) {
+      annotations = <Expression>[];
+    }
+    annotations.add(node);
+    node.parent = this;
+  }
 }
 
 /// A field declaration.
@@ -537,6 +568,7 @@
   acceptReference(MemberReferenceVisitor v) => v.visitFieldReference(this);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     type?.accept(v);
     inferredValue?.accept(v);
     name?.accept(v);
@@ -544,6 +576,7 @@
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     if (initializer != null) {
       initializer = initializer.accept(v);
       initializer?.parent = this;
@@ -600,12 +633,14 @@
       v.visitConstructorReference(this);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     name?.accept(v);
     function?.accept(v);
     visitList(initializers, v);
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     if (function != null) {
       function = function.accept(v);
       function?.parent = this;
@@ -689,11 +724,13 @@
   acceptReference(MemberReferenceVisitor v) => v.visitProcedureReference(this);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     name?.accept(v);
     function?.accept(v);
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     if (function != null) {
       function = function.accept(v);
       function?.parent = this;
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index e5fed33..0edb0b4 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -130,6 +130,16 @@
     }
   }
 
+  List<Expression> readAnnotationList(TreeNode parent) {
+    int length = readUInt();
+    if (length == 0) return const <Expression>[];
+    List<Expression> list = new List<Expression>(length);
+    for (int i = 0; i < length; ++i) {
+      list[i] = readExpression()..parent = parent;
+    }
+    return list;
+  }
+
   void _fillTreeNodeList(
       List<TreeNode> list, TreeNode buildObject(), TreeNode parent) {
     list.length = readUInt();
@@ -322,6 +332,7 @@
     int flags = readByte();
     node.isAbstract = flags & 0x1 != 0;
     node.name = readStringOrNullIfEmpty();
+    node.annotations = readAnnotationList(node);
     debugPath.add(node.name ?? 'normal-class');
     readAndPushTypeParameterList(node.typeParameters, node);
     node.supertype = readDartTypeOption();
@@ -343,6 +354,7 @@
     int flags = readByte();
     node.isAbstract = flags & 0x1 != 0;
     node.name = readStringOrNullIfEmpty();
+    node.annotations = readAnnotationList(node);
     debugPath.add(node.name ?? 'mixin-class');
     readAndPushTypeParameterList(node.typeParameters, node);
     node.supertype = readDartType();
@@ -362,6 +374,7 @@
     assert(tag == Tag.Field);
     node.flags = readByte();
     node.name = readName();
+    node.annotations = readAnnotationList(node);
     debugPath.add(node.name?.name ?? 'field');
     node.type = readDartType();
     node.inferredValue = readOptionalInferredValue();
@@ -374,6 +387,7 @@
     assert(tag == Tag.Constructor);
     node.flags = readByte();
     node.name = readName();
+    node.annotations = readAnnotationList(node);
     debugPath.add(node.name?.name ?? 'constructor');
     node.function = readFunctionNode()..parent = node;
     pushVariableDeclarations(node.function.positionalParameters);
@@ -389,6 +403,7 @@
     node.kind = ProcedureKind.values[kindIndex];
     node.flags = readByte();
     node.name = readName();
+    node.annotations = readAnnotationList(node);
     debugPath.add(node.name?.name ?? 'procedure');
     node.function = readFunctionNodeOption();
     node.function?.parent = node;
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index b8f79f2..f48130e 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -251,6 +251,7 @@
     writeByte(Tag.NormalClass);
     writeByte(node.isAbstract ? 1 : 0);
     writeStringReference(node.name ?? '');
+    writeNodeList(node.annotations);
     _typeParameterIndexer.push(node.typeParameters);
     writeNodeList(node.typeParameters);
     writeOptionalNode(node.supertype);
@@ -265,6 +266,7 @@
     writeByte(Tag.MixinClass);
     writeByte(node.isAbstract ? 1 : 0);
     writeStringReference(node.name ?? '');
+    writeNodeList(node.annotations);
     _typeParameterIndexer.push(node.typeParameters);
     writeNodeList(node.typeParameters);
     writeNode(node.supertype);
@@ -279,6 +281,7 @@
     writeByte(Tag.Constructor);
     writeByte(node.flags);
     writeName(node.name ?? '');
+    writeNodeList(node.annotations);
     assert(node.function.typeParameters.isEmpty);
     writeNode(node.function);
     writeNodeList(node.initializers);
@@ -290,6 +293,7 @@
     writeByte(node.kind.index);
     writeByte(node.flags);
     writeName(node.name ?? '');
+    writeNodeList(node.annotations);
     writeOptionalNode(node.function);
   }
 
@@ -298,6 +302,7 @@
     writeByte(Tag.Field);
     writeByte(node.flags);
     writeName(node.name ?? '');
+    writeNodeList(node.annotations);
     writeNode(node.type);
     writeOptionalInferredValue(node.inferredValue);
     writeOptionalNode(node.initializer);
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 1d84502..c203d00 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -609,9 +609,28 @@
     }
   }
 
+  void writeAnnotation(Expression node) {
+    writeSymbol('@');
+    if (node is ConstructorInvocation) {
+      writeMemberReference(node.target);
+      visitArguments(node.arguments);
+    } else {
+      writeExpression(node);
+    }
+  }
+
+  void writeAnnotationList(List<Expression> nodes) {
+    for (Expression node in nodes) {
+      writeIndentation();
+      writeAnnotation(node);
+      endLine();
+    }
+  }
+
   visitLibrary(Library node) {}
 
   visitField(Field node) {
+    writeAnnotationList(node.annotations);
     writeIndentation();
     writeModifier(node.isStatic, 'static');
     writeModifier(node.isFinal, 'final');
@@ -627,6 +646,7 @@
   }
 
   visitProcedure(Procedure node) {
+    writeAnnotationList(node.annotations);
     writeIndentation();
     writeModifier(node.isExternal, 'external');
     writeModifier(node.isStatic, 'static');
@@ -636,6 +656,7 @@
   }
 
   visitConstructor(Constructor node) {
+    writeAnnotationList(node.annotations);
     writeIndentation();
     writeModifier(node.isExternal, 'external');
     writeModifier(node.isConst, 'const');
@@ -645,6 +666,7 @@
   }
 
   visitNormalClass(NormalClass node) {
+    writeAnnotationList(node.annotations);
     writeIndentation();
     writeModifier(node.isAbstract, 'abstract');
     writeWord('class');
@@ -669,6 +691,7 @@
   }
 
   visitMixinClass(MixinClass node) {
+    writeAnnotationList(node.annotations);
     writeIndentation();
     writeModifier(node.isAbstract, 'abstract');
     writeWord('mixin');