| // 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 '../api_prototype/experimental_flags.dart'; | |
| import '../api_prototype/external_effect.dart' show ExternalEffect; | |
| import '../base/extension_scope.dart'; | |
| import '../base/loader.dart'; | |
| import '../base/scope.dart' show LookupScope; | |
| import '../kernel/body_builder_context.dart'; | |
| import '../kernel/external_ast_helper.dart' as extern; | |
| import '../kernel/kernel_constants.dart' show KernelConstantErrorReporter; | |
| 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; | |
| new( | |
| this.metadataBuilder, | |
| this.atToken, { | |
| required this.createFileUriExpression, | |
| }); | |
| 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; | |
| new(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, | |
| libraryBuilder.libraryFeatures, | |
| ); | |
| if (delaySharedExpressionLookupForTesting) { | |
| annotationBuilder._unresolvedSharedExpressionForTesting = | |
| _parseSharedExpression( | |
| libraryBuilder.loader, | |
| beginToken, | |
| libraryBuilder.importUri, | |
| annotationBuilder.fileUri, | |
| scope, | |
| libraryBuilder.libraryFeatures, | |
| 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!, | |
| ); | |
| if (createFileUriExpression && annotation is! FileUriExpression) { | |
| // Coverage-ignore-block(suite): Not run. | |
| annotation = extern.createFileUriExpression( | |
| expression: annotation, | |
| fileUri: 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; | |
| } | |
| validateAnnotations(annotatable, libraryBuilder); | |
| } | |
| static void validateAnnotations( | |
| Annotatable annotatable, | |
| SourceLibraryBuilder libraryBuilder, | |
| ) { | |
| if (ExternalEffect.isOutlineAnnotatedWithExternalEffect( | |
| annotatable, | |
| libraryBuilder.loader.coreTypes, | |
| )) { | |
| ExternalEffect.validatePragma( | |
| annotatable, | |
| libraryBuilder.loader.coreTypes, | |
| new KernelConstantErrorReporter(libraryBuilder.loader), | |
| checkHasFlag: false, | |
| ); | |
| } | |
| } | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| shared.Expression _parseSharedExpression( | |
| Loader loader, | |
| Token atToken, | |
| Uri importUri, | |
| Uri fileUri, | |
| LookupScope scope, | |
| LibraryFeatures libraryFeatures, { | |
| bool delayLookupForTesting = false, | |
| }) { | |
| return parseAnnotation( | |
| loader, | |
| atToken, | |
| importUri, | |
| fileUri, | |
| scope, | |
| libraryFeatures, | |
| delayLookupForTesting: delayLookupForTesting, | |
| ); | |
| } |