blob: 07868bd5674835220f145e9e375129db92ac1417 [file] [log] [blame] [edit]
// Copyright (c) 2016, 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 'package:_fe_analyzer_shared/src/metadata/expressions.dart' as shared;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
import 'package:kernel/ast.dart';
import 'package:kernel/clone.dart';
import '../base/extension_scope.dart';
import '../base/loader.dart';
import '../base/scope.dart' show LookupScope;
import '../kernel/body_builder_context.dart';
import '../kernel/macro/metadata.dart' hide ExtensionScope;
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
bool computeSharedExpressionForTesting = false;
bool delaySharedExpressionLookupForTesting = false;
class Annotation {
final MetadataBuilder metadataBuilder;
final Token atToken;
final bool createFileUriExpression;
Annotation(
this.metadataBuilder,
this.atToken, {
required this.createFileUriExpression,
});
late int annotationIndex;
late Expression expression;
}
class MetadataBuilder {
/// Token for `@` for annotations that have not yet been parsed.
Token? _atToken;
final int atOffset;
/// `true` if the annotation begins with 'patch'.
///
/// This is used for detecting `@patch` annotations in patch libraries where
/// it can be assumed that it implies that it _is_ a `@patch` annotation.
final bool hasPatch;
/// Expression for an already parsed annotation.
Expression? _expression;
final Uri fileUri;
MetadataBuilder(Token this._atToken, this.fileUri)
: atOffset = _atToken.charOffset,
hasPatch = _atToken.next?.lexeme == 'patch';
// Coverage-ignore(suite): Not run.
Token? get beginToken => _atToken;
shared.Expression? _sharedExpression;
shared.Expression? _unresolvedSharedExpressionForTesting;
// Coverage-ignore(suite): Not run.
shared.Expression? get expression => _sharedExpression;
// Coverage-ignore(suite): Not run.
shared.Expression? get unresolvedExpressionForTesting =>
_unresolvedSharedExpressionForTesting;
static void buildAnnotations({
required Annotatable annotatable,
required Uri annotatableFileUri,
required List<MetadataBuilder>? metadata,
required Uri annotationsFileUri,
required BodyBuilderContext bodyBuilderContext,
required SourceLibraryBuilder libraryBuilder,
required ExtensionScope extensionScope,
required LookupScope scope,
}) {
if (metadata == null) return;
// Cloner used to clone already parsed annotations.
CloneVisitorNotMembers? cloner;
List<Annotation> annotations = [];
for (int i = 0; i < metadata.length; ++i) {
MetadataBuilder annotationBuilder = metadata[i];
bool createFileUriExpression =
annotatableFileUri != annotationBuilder.fileUri;
Token? beginToken = annotationBuilder._atToken;
annotationBuilder._atToken = null;
if (beginToken != null) {
if (computeSharedExpressionForTesting) {
// Coverage-ignore-block(suite): Not run.
annotationBuilder._sharedExpression = _parseSharedExpression(
libraryBuilder.loader,
beginToken,
libraryBuilder.importUri,
annotationBuilder.fileUri,
scope,
);
if (delaySharedExpressionLookupForTesting) {
annotationBuilder._unresolvedSharedExpressionForTesting =
_parseSharedExpression(
libraryBuilder.loader,
beginToken,
libraryBuilder.importUri,
annotationBuilder.fileUri,
scope,
delayLookupForTesting: true,
);
}
}
annotations.add(
new Annotation(
annotationBuilder,
beginToken,
createFileUriExpression: createFileUriExpression,
),
);
} else {
// The annotation is needed for multiple declarations so we need to
// clone the expression to use it more than once. For instance
//
// abstract class Class {
// @annotation
// abstract int field;
// }
//
// will be compiled to
//
// abstract class Class {
// @annotation
// int get field;
// @annotation
// void set field(int value);
// }
//
cloner ??= new CloneVisitorNotMembers();
Expression annotation = cloner.cloneInContext(
annotationBuilder._expression!,
);
// Coverage-ignore(suite): Not run.
if (createFileUriExpression && annotation is! FileUriExpression) {
annotation = new FileUriExpression(
annotation,
annotationBuilder.fileUri,
)..fileOffset = annotationBuilder.atOffset;
}
annotatable.addAnnotation(annotation);
}
}
libraryBuilder.loader.createResolver().buildAnnotations(
libraryBuilder: libraryBuilder,
bodyBuilderContext: bodyBuilderContext,
annotationsFileUri: annotationsFileUri,
extensionScope: extensionScope,
scope: scope,
annotatable: annotatable,
annotations: annotations,
);
for (Annotation annotation in annotations) {
annotation.metadataBuilder._expression = annotation.expression;
}
}
}
// Coverage-ignore(suite): Not run.
shared.Expression _parseSharedExpression(
Loader loader,
Token atToken,
Uri importUri,
Uri fileUri,
LookupScope scope, {
bool delayLookupForTesting = false,
}) {
return parseAnnotation(
loader,
atToken,
importUri,
fileUri,
scope,
delayLookupForTesting: delayLookupForTesting,
);
}