instantiateAnnotation function

dynamic instantiateAnnotation(
  1. AnnotatedNode member,
  2. Type annotationType, {
  3. dynamic onUnsupportedArgument(
    1. Expression argument
    )?,
})

Uses reflection to instantiate and returns the first annotation on member of type annotationType, or null if no matching annotations are found.

Annotation constructors are currently limited to the values supported by getValue.

Naively assumes that the name of the annotationType class is canonical.

Implementation

dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType,
    {dynamic onUnsupportedArgument(Expression argument)?}) {
  final matchingAnnotation = getMatchingAnnotation(member, annotationType);

  // If no annotation is found, return null.
  if (matchingAnnotation == null) {
    return null;
  }

  final matchingAnnotationArgs = matchingAnnotation.arguments;
  if (matchingAnnotationArgs == null) {
    throw 'Annotation not invocation of constructor: `$matchingAnnotation`. '
        'This is likely due to invalid usage of the annotation class, but could'
        'also be a name conflict with the specified type `$annotationType`';
  }

  // Get the parameters from the annotation's AST.
  Map<Symbol, dynamic> namedParameters = {};
  List positionalParameters = [];

  matchingAnnotationArgs.arguments.forEach((argument) {
    var onUnsupportedExpression = onUnsupportedArgument == null
        ? null
        : (_) => onUnsupportedArgument(argument);

    if (argument is NamedExpression) {
      var name = argument.name.label.name;
      var value = getValue(argument.expression,
          onUnsupportedExpression: onUnsupportedExpression);

      namedParameters[Symbol(name)] = value;
    } else {
      var value =
          getValue(argument, onUnsupportedExpression: onUnsupportedExpression);

      positionalParameters.add(value);
    }
  });

  // Instantiate and return an instance of the annotation using reflection.
  String constructorName = _getConstructorName(matchingAnnotation) ?? '';

  // Be sure to use `originalDeclaration` so that generic parameters work.
  final classMirror = mirrors.reflectClass(annotationType).originalDeclaration
      as mirrors.ClassMirror;

  try {
    var instanceMirror = classMirror.newInstance(
        Symbol(constructorName), positionalParameters, namedParameters);
    return instanceMirror.reflectee;
  } catch (e, stacktrace) {
    throw 'Unable to instantiate annotation: $matchingAnnotation. This is '
        'likely due to improper usage, or a naming conflict with '
        'annotationType $annotationType. Original error: $e. Stacktrace: $stacktrace';
  }
}