| // Copyright (c) 2021, 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/macros/api.dart' as macro; |
| import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro; |
| import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart' |
| as macro; |
| import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart' |
| as macro; |
| import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart' |
| as macro; |
| import 'package:front_end/src/fasta/kernel/benchmarker.dart' |
| show BenchmarkSubdivides, Benchmarker; |
| import 'package:kernel/ast.dart' show DartType; |
| import 'package:kernel/src/types.dart'; |
| import 'package:kernel/type_environment.dart' show SubtypeCheckMode; |
| |
| import '../../../base/common.dart'; |
| import '../../builder/builder.dart'; |
| import '../../builder/class_builder.dart'; |
| import '../../builder/formal_parameter_builder.dart'; |
| import '../../builder/library_builder.dart'; |
| import '../../builder/member_builder.dart'; |
| import '../../builder/named_type_builder.dart'; |
| import '../../builder/nullability_builder.dart'; |
| import '../../builder/type_alias_builder.dart'; |
| import '../../builder/type_builder.dart'; |
| import '../../builder/type_declaration_builder.dart'; |
| import '../../identifiers.dart'; |
| import '../../source/source_class_builder.dart'; |
| import '../../source/source_constructor_builder.dart'; |
| import '../../source/source_factory_builder.dart'; |
| import '../../source/source_field_builder.dart'; |
| import '../../source/source_library_builder.dart'; |
| import '../../source/source_loader.dart'; |
| import '../../source/source_procedure_builder.dart'; |
| import '../hierarchy/hierarchy_builder.dart'; |
| import '../hierarchy/hierarchy_node.dart'; |
| import 'identifiers.dart'; |
| |
| bool enableMacros = false; |
| |
| /// Enables macros whether the Macro class actually exists in the transitive |
| /// deps or not. This allows for easier experimentation. |
| /// |
| /// TODO: Remove this once it is no longer necessary. |
| bool forceEnableMacros = false; |
| |
| const String augmentationScheme = 'org-dartlang-augmentation'; |
| |
| final Uri macroLibraryUri = |
| Uri.parse('package:_fe_analyzer_shared/src/macros/api.dart'); |
| const String macroClassName = 'Macro'; |
| final IdentifierImpl omittedTypeIdentifier = |
| new OmittedTypeIdentifier(id: macro.RemoteInstance.uniqueId); |
| |
| class MacroDeclarationData { |
| bool macrosAreAvailable = false; |
| Map<Uri, List<String>> macroDeclarations = {}; |
| List<List<Uri>>? compilationSequence; |
| List<Map<Uri, Map<String, List<String>>>> neededPrecompilations = []; |
| } |
| |
| class MacroApplication { |
| final ClassBuilder classBuilder; |
| final String constructorName; |
| final macro.Arguments arguments; |
| |
| // TODO(johnniwinther): Add support for arguments. |
| |
| MacroApplication(this.classBuilder, this.constructorName, this.arguments); |
| |
| late macro.MacroInstanceIdentifier instanceIdentifier; |
| |
| @override |
| String toString() { |
| return '${classBuilder.name}.' |
| '${constructorName.isEmpty ? 'new' : constructorName}()'; |
| } |
| } |
| |
| class MacroApplicationDataForTesting { |
| Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData = {}; |
| Map<SourceLibraryBuilder, String> libraryTypesResult = {}; |
| Map<SourceLibraryBuilder, String> libraryDefinitionResult = {}; |
| Map<SourceClassBuilder, List<macro.MacroExecutionResult>> classTypesResults = |
| {}; |
| Map<SourceClassBuilder, List<macro.MacroExecutionResult>> |
| classDeclarationsResults = {}; |
| Map<SourceClassBuilder, List<macro.MacroExecutionResult>> |
| classDefinitionsResults = {}; |
| Map<MemberBuilder, List<macro.MacroExecutionResult>> memberTypesResults = {}; |
| Map<MemberBuilder, List<macro.MacroExecutionResult>> |
| memberDeclarationsResults = {}; |
| Map<MemberBuilder, List<macro.MacroExecutionResult>> |
| memberDefinitionsResults = {}; |
| } |
| |
| class LibraryMacroApplicationData { |
| Map<SourceClassBuilder, ClassMacroApplicationData> classData = {}; |
| Map<MemberBuilder, List<MacroApplication>> memberApplications = {}; |
| } |
| |
| class ClassMacroApplicationData { |
| List<MacroApplication>? classApplications; |
| Map<MemberBuilder, List<MacroApplication>> memberApplications = {}; |
| } |
| |
| /// Macro classes that need to be precompiled. |
| class NeededPrecompilations { |
| /// Map from library uris to macro class names and the names of constructor |
| /// their constructors is returned for macro classes that need to be |
| /// precompiled. |
| final Map<Uri, Map<String, List<String>>> macroDeclarations; |
| |
| NeededPrecompilations(this.macroDeclarations); |
| } |
| |
| class MacroApplications { |
| final macro.MacroExecutor _macroExecutor; |
| final Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData; |
| final MacroApplicationDataForTesting? dataForTesting; |
| List<_ApplicationData>? _applicationDataCache; |
| |
| MacroApplications( |
| this._macroExecutor, this.libraryData, this.dataForTesting) { |
| dataForTesting?.libraryData.addAll(libraryData); |
| } |
| |
| static Future<MacroApplications> loadMacroIds( |
| macro.MultiMacroExecutor macroExecutor, |
| Map<SourceLibraryBuilder, LibraryMacroApplicationData> libraryData, |
| MacroApplicationDataForTesting? dataForTesting, |
| Benchmarker? benchmarker) async { |
| Map<MacroApplication, macro.MacroInstanceIdentifier> instanceIdCache = {}; |
| |
| Future<void> ensureMacroClassIds( |
| List<MacroApplication>? applications) async { |
| if (applications != null) { |
| for (MacroApplication application in applications) { |
| Uri libraryUri = application.classBuilder.libraryBuilder.importUri; |
| String macroClassName = application.classBuilder.name; |
| try { |
| benchmarker?.beginSubdivide( |
| BenchmarkSubdivides.macroApplications_macroExecutorLoadMacro); |
| benchmarker?.endSubdivide(); |
| try { |
| benchmarker?.beginSubdivide(BenchmarkSubdivides |
| .macroApplications_macroExecutorInstantiateMacro); |
| application.instanceIdentifier = instanceIdCache[application] ??= |
| await macroExecutor.instantiateMacro( |
| libraryUri, |
| macroClassName, |
| application.constructorName, |
| application.arguments); |
| benchmarker?.endSubdivide(); |
| } catch (e) { |
| throw "Error instantiating macro `${application}`: $e"; |
| } |
| } catch (e) { |
| throw "Error loading macro class " |
| "'${application.classBuilder.name}' from " |
| "'${application.classBuilder.libraryBuilder.importUri}': $e"; |
| } |
| } |
| } |
| } |
| |
| for (LibraryMacroApplicationData libraryData in libraryData.values) { |
| for (ClassMacroApplicationData classData |
| in libraryData.classData.values) { |
| await ensureMacroClassIds(classData.classApplications); |
| for (List<MacroApplication> applications |
| in classData.memberApplications.values) { |
| await ensureMacroClassIds(applications); |
| } |
| } |
| for (List<MacroApplication> applications |
| in libraryData.memberApplications.values) { |
| await ensureMacroClassIds(applications); |
| } |
| } |
| return new MacroApplications(macroExecutor, libraryData, dataForTesting); |
| } |
| |
| Map<ClassBuilder, macro.ClassDeclaration> _classDeclarations = {}; |
| Map<macro.ClassDeclaration, ClassBuilder> _classBuilders = {}; |
| Map<TypeAliasBuilder, macro.TypeAliasDeclaration> _typeAliasDeclarations = {}; |
| Map<MemberBuilder, macro.Declaration?> _memberDeclarations = {}; |
| |
| // TODO(johnniwinther): Support all members. |
| macro.Declaration? _getMemberDeclaration(MemberBuilder memberBuilder) { |
| return _memberDeclarations[memberBuilder] ??= |
| _createMemberDeclaration(memberBuilder); |
| } |
| |
| macro.ClassDeclaration getClassDeclaration(ClassBuilder builder) { |
| return _classDeclarations[builder] ??= _createClassDeclaration(builder); |
| } |
| |
| macro.TypeAliasDeclaration getTypeAliasDeclaration(TypeAliasBuilder builder) { |
| return _typeAliasDeclarations[builder] ??= |
| _createTypeAliasDeclaration(builder); |
| } |
| |
| ClassBuilder _getClassBuilder(macro.ClassDeclaration declaration) { |
| return _classBuilders[declaration]!; |
| } |
| |
| macro.Declaration _createMemberDeclaration(MemberBuilder memberBuilder) { |
| if (memberBuilder is SourceProcedureBuilder) { |
| return _createFunctionDeclaration(memberBuilder); |
| } else if (memberBuilder is SourceFieldBuilder) { |
| return _createVariableDeclaration(memberBuilder); |
| } else if (memberBuilder is SourceConstructorBuilder) { |
| return _createConstructorDeclaration(memberBuilder); |
| } else if (memberBuilder is SourceFactoryBuilder) { |
| return _createFactoryDeclaration(memberBuilder); |
| } else { |
| // TODO(johnniwinther): Throw when all members are supported. |
| throw new UnimplementedError( |
| 'Unsupported member ${memberBuilder} (${memberBuilder.runtimeType})'); |
| } |
| } |
| |
| macro.ResolvedIdentifier _resolveIdentifier(macro.Identifier identifier) { |
| if (identifier is IdentifierImpl) { |
| return identifier.resolveIdentifier(); |
| } else { |
| throw new UnsupportedError( |
| 'Unsupported identifier ${identifier} (${identifier.runtimeType})'); |
| } |
| } |
| |
| macro.TypeAnnotation _inferOmittedType( |
| macro.OmittedTypeAnnotation omittedType) { |
| throw new UnimplementedError('This is not yet supported!'); |
| } |
| |
| Iterable<_ApplicationData> get _applicationData { |
| if (_applicationDataCache == null) { |
| List<_ApplicationData> data = _applicationDataCache = []; |
| for (MapEntry<SourceLibraryBuilder, |
| LibraryMacroApplicationData> libraryEntry in libraryData.entries) { |
| SourceLibraryBuilder libraryBuilder = libraryEntry.key; |
| LibraryMacroApplicationData libraryMacroApplicationData = |
| libraryEntry.value; |
| for (MapEntry<MemberBuilder, List<MacroApplication>> memberEntry |
| in libraryMacroApplicationData.memberApplications.entries) { |
| MemberBuilder memberBuilder = memberEntry.key; |
| macro.Declaration? declaration = _getMemberDeclaration(memberBuilder); |
| if (declaration != null) { |
| data.add(new _ApplicationData( |
| libraryBuilder, memberBuilder, declaration, memberEntry.value)); |
| } |
| } |
| for (MapEntry<SourceClassBuilder, ClassMacroApplicationData> classEntry |
| in libraryMacroApplicationData.classData.entries) { |
| SourceClassBuilder classBuilder = classEntry.key; |
| ClassMacroApplicationData classData = classEntry.value; |
| List<MacroApplication>? classApplications = |
| classData.classApplications; |
| if (classApplications != null) { |
| macro.ClassDeclaration classDeclaration = |
| getClassDeclaration(classBuilder); |
| data.add(new _ApplicationData(libraryBuilder, classBuilder, |
| classDeclaration, classApplications)); |
| } |
| for (MapEntry<MemberBuilder, List<MacroApplication>> memberEntry |
| in classData.memberApplications.entries) { |
| MemberBuilder memberBuilder = memberEntry.key; |
| macro.Declaration? declaration = |
| _getMemberDeclaration(memberBuilder); |
| if (declaration != null) { |
| data.add(new _ApplicationData(libraryBuilder, memberBuilder, |
| declaration, memberEntry.value)); |
| } |
| } |
| } |
| } |
| } |
| return _applicationDataCache!; |
| } |
| |
| Future<List<macro.MacroExecutionResult>> _applyTypeMacros( |
| _ApplicationData applicationData) async { |
| macro.Declaration declaration = applicationData.declaration; |
| List<macro.MacroExecutionResult> results = []; |
| for (MacroApplication macroApplication |
| in applicationData.macroApplications) { |
| if (macroApplication.instanceIdentifier |
| .shouldExecute(_declarationKind(declaration), macro.Phase.types)) { |
| macro.MacroExecutionResult result = |
| await _macroExecutor.executeTypesPhase( |
| macroApplication.instanceIdentifier, |
| declaration, |
| identifierResolver); |
| if (result.isNotEmpty) { |
| results.add(result); |
| } |
| } |
| } |
| |
| if (retainDataForTesting) { |
| Builder builder = applicationData.builder; |
| if (builder is SourceClassBuilder) { |
| dataForTesting?.classTypesResults[builder] = results; |
| } else { |
| dataForTesting?.memberTypesResults[builder as MemberBuilder] = results; |
| } |
| } |
| return results; |
| } |
| |
| late macro.IdentifierResolver identifierResolver; |
| late SourceLoader sourceLoader; |
| |
| Future<List<SourceLibraryBuilder>> applyTypeMacros( |
| SourceLoader sourceLoader) async { |
| identifierResolver = new _IdentifierResolver(sourceLoader); |
| List<SourceLibraryBuilder> augmentationLibraries = []; |
| Map<SourceLibraryBuilder, List<macro.MacroExecutionResult>> results = {}; |
| for (_ApplicationData macroApplication in _applicationData) { |
| List<macro.MacroExecutionResult> executionResults = |
| await _applyTypeMacros(macroApplication); |
| if (executionResults.isNotEmpty) { |
| (results[macroApplication.libraryBuilder] ??= []) |
| .addAll(executionResults); |
| } |
| } |
| for (MapEntry<SourceLibraryBuilder, List<macro.MacroExecutionResult>> entry |
| in results.entries) { |
| SourceLibraryBuilder sourceLibraryBuilder = entry.key; |
| assert(entry.value.isNotEmpty); |
| String result = _macroExecutor |
| .buildAugmentationLibrary( |
| entry.value, _resolveIdentifier, _inferOmittedType) |
| .trim(); |
| assert( |
| result.trim().isNotEmpty, |
| "Empty types phase augmentation library source for " |
| "$sourceLibraryBuilder}"); |
| if (result.isNotEmpty) { |
| if (retainDataForTesting) { |
| dataForTesting?.libraryTypesResult[sourceLibraryBuilder] = result; |
| } |
| augmentationLibraries |
| .add(await sourceLibraryBuilder.createAugmentationLibrary(result)); |
| } |
| } |
| return augmentationLibraries; |
| } |
| |
| Future<void> _applyDeclarationsMacros(_ApplicationData applicationData, |
| Future<void> Function(SourceLibraryBuilder) onAugmentationLibrary) async { |
| List<macro.MacroExecutionResult> results = []; |
| macro.Declaration declaration = applicationData.declaration; |
| for (MacroApplication macroApplication |
| in applicationData.macroApplications) { |
| if (macroApplication.instanceIdentifier.shouldExecute( |
| _declarationKind(declaration), macro.Phase.declarations)) { |
| macro.MacroExecutionResult result = |
| await _macroExecutor.executeDeclarationsPhase( |
| macroApplication.instanceIdentifier, |
| declaration, |
| identifierResolver, |
| typeResolver, |
| classIntrospector); |
| if (result.isNotEmpty) { |
| String source = _macroExecutor.buildAugmentationLibrary( |
| [result], _resolveIdentifier, _inferOmittedType); |
| SourceLibraryBuilder augmentationLibrary = await applicationData |
| .libraryBuilder |
| .createAugmentationLibrary(source); |
| await onAugmentationLibrary(augmentationLibrary); |
| if (retainDataForTesting) { |
| results.add(result); |
| } |
| } |
| } |
| } |
| if (retainDataForTesting) { |
| Builder builder = applicationData.builder; |
| if (builder is SourceClassBuilder) { |
| dataForTesting?.classDeclarationsResults[builder] = results; |
| } else { |
| dataForTesting?.memberDeclarationsResults[builder as MemberBuilder] = |
| results; |
| } |
| } |
| } |
| |
| late Types types; |
| late macro.TypeResolver typeResolver; |
| late macro.ClassIntrospector classIntrospector; |
| |
| Future<void> applyDeclarationsMacros(ClassHierarchyBuilder classHierarchy, |
| Future<void> Function(SourceLibraryBuilder) onAugmentationLibrary) async { |
| types = new Types(classHierarchy); |
| typeResolver = new _TypeResolver(this); |
| classIntrospector = new _ClassIntrospector(this, classHierarchy); |
| for (_ApplicationData macroApplication in _applicationData) { |
| await _applyDeclarationsMacros(macroApplication, onAugmentationLibrary); |
| } |
| } |
| |
| Future<List<macro.MacroExecutionResult>> _applyDefinitionMacros( |
| _ApplicationData applicationData) async { |
| List<macro.MacroExecutionResult> results = []; |
| macro.Declaration declaration = applicationData.declaration; |
| for (MacroApplication macroApplication |
| in applicationData.macroApplications) { |
| if (macroApplication.instanceIdentifier.shouldExecute( |
| _declarationKind(declaration), macro.Phase.definitions)) { |
| macro.MacroExecutionResult result = |
| await _macroExecutor.executeDefinitionsPhase( |
| macroApplication.instanceIdentifier, |
| declaration, |
| identifierResolver, |
| typeResolver, |
| classIntrospector, |
| typeDeclarationResolver, |
| typeInferrer); |
| if (result.isNotEmpty) { |
| results.add(result); |
| } |
| } |
| } |
| if (retainDataForTesting) { |
| Builder builder = applicationData.builder; |
| if (builder is SourceClassBuilder) { |
| dataForTesting?.classDefinitionsResults[builder] = results; |
| } else { |
| dataForTesting?.memberDefinitionsResults[builder as MemberBuilder] = |
| results; |
| } |
| } |
| return results; |
| } |
| |
| late macro.TypeDeclarationResolver typeDeclarationResolver; |
| late macro.TypeInferrer typeInferrer; |
| |
| Future<List<SourceLibraryBuilder>> applyDefinitionMacros() async { |
| typeDeclarationResolver = new _TypeDeclarationResolver(this); |
| typeInferrer = new _TypeInferrer(this); |
| List<SourceLibraryBuilder> augmentationLibraries = []; |
| Map<SourceLibraryBuilder, List<macro.MacroExecutionResult>> results = {}; |
| for (_ApplicationData macroApplication in _applicationData) { |
| List<macro.MacroExecutionResult> executionResults = |
| await _applyDefinitionMacros(macroApplication); |
| if (executionResults.isNotEmpty) { |
| (results[macroApplication.libraryBuilder] ??= []) |
| .addAll(executionResults); |
| } |
| } |
| for (MapEntry<SourceLibraryBuilder, List<macro.MacroExecutionResult>> entry |
| in results.entries) { |
| SourceLibraryBuilder sourceLibraryBuilder = entry.key; |
| String result = _macroExecutor.buildAugmentationLibrary( |
| entry.value, _resolveIdentifier, _inferOmittedType); |
| assert( |
| result.trim().isNotEmpty, |
| "Empty definitions phase augmentation library source for " |
| "$sourceLibraryBuilder}"); |
| if (retainDataForTesting) { |
| dataForTesting?.libraryDefinitionResult[sourceLibraryBuilder] = result; |
| } |
| augmentationLibraries |
| .add(await sourceLibraryBuilder.createAugmentationLibrary(result)); |
| } |
| return augmentationLibraries; |
| } |
| |
| void close() { |
| _macroExecutor.close(); |
| _staticTypeCache.clear(); |
| _typeAnnotationCache.clear(); |
| _applicationDataCache?.clear(); |
| } |
| |
| macro.ClassDeclaration _createClassDeclaration(ClassBuilder builder) { |
| macro.ClassDeclaration declaration = new macro.ClassDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new TypeDeclarationBuilderIdentifier( |
| typeDeclarationBuilder: builder, |
| libraryBuilder: builder.libraryBuilder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: [], |
| // TODO(johnniwinther): Support interfaces |
| interfaces: [], |
| isAbstract: builder.isAbstract, |
| isExternal: builder.isExternal, |
| // TODO(johnniwinther): Support mixins |
| mixins: [], |
| // TODO(johnniwinther): Support superclass |
| superclass: null); |
| _classBuilders[declaration] = builder; |
| return declaration; |
| } |
| |
| macro.TypeAliasDeclaration _createTypeAliasDeclaration( |
| TypeAliasBuilder builder) { |
| macro.TypeAliasDeclaration declaration = new macro.TypeAliasDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new TypeDeclarationBuilderIdentifier( |
| typeDeclarationBuilder: builder, |
| libraryBuilder: builder.libraryBuilder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: [], |
| aliasedType: |
| _computeTypeAnnotation(builder.libraryBuilder, builder.type)); |
| return declaration; |
| } |
| |
| List<List<macro.ParameterDeclarationImpl>> _createParameters( |
| MemberBuilder builder, List<FormalParameterBuilder>? formals) { |
| List<macro.ParameterDeclarationImpl>? positionalParameters; |
| List<macro.ParameterDeclarationImpl>? namedParameters; |
| if (formals == null) { |
| positionalParameters = namedParameters = const []; |
| } else { |
| positionalParameters = []; |
| namedParameters = []; |
| for (FormalParameterBuilder formal in formals) { |
| macro.TypeAnnotationImpl type = |
| computeTypeAnnotation(builder.libraryBuilder, formal.type); |
| macro.IdentifierImpl identifier = new FormalParameterBuilderIdentifier( |
| id: macro.RemoteInstance.uniqueId, |
| name: formal.name, |
| parameterBuilder: formal, |
| libraryBuilder: builder.libraryBuilder); |
| if (formal.isNamed) { |
| namedParameters.add(new macro.ParameterDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: identifier, |
| isRequired: formal.isRequiredNamed, |
| isNamed: true, |
| type: type, |
| )); |
| } else { |
| positionalParameters.add(new macro.ParameterDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: identifier, |
| isRequired: formal.isRequiredPositional, |
| isNamed: false, |
| type: type, |
| )); |
| } |
| } |
| } |
| return [positionalParameters, namedParameters]; |
| } |
| |
| macro.ConstructorDeclaration _createConstructorDeclaration( |
| SourceConstructorBuilder builder) { |
| List<FormalParameterBuilder>? formals = null; |
| // TODO(johnniwinther): Support formals for other constructors. |
| if (builder is DeclaredSourceConstructorBuilder) { |
| formals = builder.formals; |
| } |
| List<List<macro.ParameterDeclarationImpl>> parameters = |
| _createParameters(builder, formals); |
| macro.ClassDeclaration definingClass = |
| getClassDeclaration(builder.classBuilder as SourceClassBuilder); |
| return new macro.ConstructorDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| definingClass: definingClass.identifier as macro.IdentifierImpl, |
| isFactory: builder.isFactory, |
| isAbstract: builder.isAbstract, |
| isExternal: builder.isExternal, |
| isGetter: builder.isGetter, |
| isOperator: builder.isOperator, |
| isSetter: builder.isSetter, |
| positionalParameters: parameters[0], |
| namedParameters: parameters[1], |
| // TODO(johnniwinther): Support constructor return type. |
| returnType: computeTypeAnnotation(builder.libraryBuilder, null), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: const [], |
| ); |
| } |
| |
| macro.ConstructorDeclaration _createFactoryDeclaration( |
| SourceFactoryBuilder builder) { |
| List<List<macro.ParameterDeclarationImpl>> parameters = |
| _createParameters(builder, builder.formals); |
| macro.ClassDeclaration definingClass = |
| getClassDeclaration(builder.classBuilder as SourceClassBuilder); |
| |
| return new macro.ConstructorDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| definingClass: definingClass.identifier as macro.IdentifierImpl, |
| isFactory: builder.isFactory, |
| isAbstract: builder.isAbstract, |
| isExternal: builder.isExternal, |
| isGetter: builder.isGetter, |
| isOperator: builder.isOperator, |
| isSetter: builder.isSetter, |
| positionalParameters: parameters[0], |
| namedParameters: parameters[1], |
| // TODO(johnniwinther): Support constructor return type. |
| returnType: computeTypeAnnotation(builder.libraryBuilder, null), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: const [], |
| ); |
| } |
| |
| macro.FunctionDeclaration _createFunctionDeclaration( |
| SourceProcedureBuilder builder) { |
| List<List<macro.ParameterDeclarationImpl>> parameters = |
| _createParameters(builder, builder.formals); |
| |
| macro.ClassDeclaration? definingClass = null; |
| if (builder.classBuilder != null) { |
| definingClass = |
| getClassDeclaration(builder.classBuilder as SourceClassBuilder); |
| } |
| if (definingClass != null) { |
| // TODO(johnniwinther): Should static fields be field or variable |
| // declarations? |
| return new macro.MethodDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| definingClass: definingClass.identifier as macro.IdentifierImpl, |
| isAbstract: builder.isAbstract, |
| isExternal: builder.isExternal, |
| isGetter: builder.isGetter, |
| isOperator: builder.isOperator, |
| isSetter: builder.isSetter, |
| isStatic: builder.isStatic, |
| positionalParameters: parameters[0], |
| namedParameters: parameters[1], |
| returnType: |
| computeTypeAnnotation(builder.libraryBuilder, builder.returnType), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: const []); |
| } else { |
| return new macro.FunctionDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| isAbstract: builder.isAbstract, |
| isExternal: builder.isExternal, |
| isGetter: builder.isGetter, |
| isOperator: builder.isOperator, |
| isSetter: builder.isSetter, |
| positionalParameters: parameters[0], |
| namedParameters: parameters[1], |
| returnType: |
| computeTypeAnnotation(builder.libraryBuilder, builder.returnType), |
| // TODO(johnniwinther): Support typeParameters |
| typeParameters: const []); |
| } |
| } |
| |
| macro.VariableDeclaration _createVariableDeclaration( |
| SourceFieldBuilder builder) { |
| macro.ClassDeclaration? definingClass = null; |
| if (builder.classBuilder != null) { |
| definingClass = |
| getClassDeclaration(builder.classBuilder as SourceClassBuilder); |
| } |
| if (definingClass != null) { |
| // TODO(johnniwinther): Should static fields be field or variable |
| // declarations? |
| return new macro.FieldDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| definingClass: definingClass.identifier as macro.IdentifierImpl, |
| isExternal: builder.isExternal, |
| isFinal: builder.isFinal, |
| isLate: builder.isLate, |
| isStatic: builder.isStatic, |
| type: computeTypeAnnotation(builder.libraryBuilder, builder.type)); |
| } else { |
| return new macro.VariableDeclarationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: builder.name), |
| isExternal: builder.isExternal, |
| isFinal: builder.isFinal, |
| isLate: builder.isLate, |
| type: computeTypeAnnotation(builder.libraryBuilder, builder.type)); |
| } |
| } |
| |
| Map<TypeBuilder?, macro.TypeAnnotationImpl> _typeAnnotationCache = {}; |
| |
| List<macro.TypeAnnotationImpl> computeTypeAnnotations( |
| LibraryBuilder library, List<TypeBuilder>? typeBuilders) { |
| if (typeBuilders == null) return const []; |
| return new List.generate(typeBuilders.length, |
| (int index) => computeTypeAnnotation(library, typeBuilders[index])); |
| } |
| |
| macro.TypeAnnotationImpl _computeTypeAnnotation( |
| LibraryBuilder libraryBuilder, TypeBuilder? typeBuilder) { |
| if (typeBuilder != null) { |
| if (typeBuilder is NamedTypeBuilder) { |
| Object name = typeBuilder.name; |
| List<macro.TypeAnnotationImpl> typeArguments = |
| computeTypeAnnotations(libraryBuilder, typeBuilder.arguments); |
| bool isNullable = typeBuilder.nullabilityBuilder.isNullable; |
| if (name is String) { |
| return new macro.NamedTypeAnnotationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new TypeBuilderIdentifier( |
| typeBuilder: typeBuilder, |
| libraryBuilder: libraryBuilder, |
| id: macro.RemoteInstance.uniqueId, |
| name: name), |
| typeArguments: typeArguments, |
| isNullable: isNullable); |
| } else if (name is QualifiedName) { |
| assert(name.qualifier is String); |
| return new macro.NamedTypeAnnotationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: new TypeBuilderIdentifier( |
| typeBuilder: typeBuilder, |
| libraryBuilder: libraryBuilder, |
| id: macro.RemoteInstance.uniqueId, |
| name: name.name), |
| typeArguments: typeArguments, |
| isNullable: isNullable); |
| } |
| } |
| } |
| return new macro.NamedTypeAnnotationImpl( |
| id: macro.RemoteInstance.uniqueId, |
| identifier: omittedTypeIdentifier, |
| isNullable: false, |
| typeArguments: const []); |
| } |
| |
| macro.TypeAnnotationImpl computeTypeAnnotation( |
| LibraryBuilder libraryBuilder, TypeBuilder? typeBuilder) { |
| return _typeAnnotationCache[typeBuilder] ??= |
| _computeTypeAnnotation(libraryBuilder, typeBuilder); |
| } |
| |
| DartType _typeForAnnotation(macro.TypeAnnotationCode typeAnnotation) { |
| NullabilityBuilder nullabilityBuilder; |
| if (typeAnnotation is macro.NullableTypeAnnotationCode) { |
| nullabilityBuilder = const NullabilityBuilder.nullable(); |
| typeAnnotation = typeAnnotation.underlyingType; |
| } else { |
| nullabilityBuilder = const NullabilityBuilder.omitted(); |
| } |
| |
| if (typeAnnotation is macro.NamedTypeAnnotationCode) { |
| macro.NamedTypeAnnotationCode namedTypeAnnotation = typeAnnotation; |
| IdentifierImpl typeIdentifier = typeAnnotation.name as IdentifierImpl; |
| List<DartType> arguments = new List<DartType>.generate( |
| namedTypeAnnotation.typeArguments.length, |
| (int index) => |
| _typeForAnnotation(namedTypeAnnotation.typeArguments[index])); |
| return typeIdentifier.buildType(nullabilityBuilder, arguments); |
| } |
| // TODO: Implement support for function types. |
| throw new UnimplementedError( |
| 'Unimplemented type annotation kind ${typeAnnotation.kind}'); |
| } |
| |
| macro.StaticType resolveTypeAnnotation( |
| macro.TypeAnnotationCode typeAnnotation) { |
| return createStaticType(_typeForAnnotation(typeAnnotation)); |
| } |
| |
| Map<DartType, _StaticTypeImpl> _staticTypeCache = {}; |
| |
| macro.StaticType createStaticType(DartType dartType) { |
| return _staticTypeCache[dartType] ??= new _StaticTypeImpl(this, dartType); |
| } |
| } |
| |
| class _StaticTypeImpl extends macro.StaticType { |
| final MacroApplications macroApplications; |
| final DartType type; |
| |
| _StaticTypeImpl(this.macroApplications, this.type); |
| |
| @override |
| Future<bool> isExactly(covariant _StaticTypeImpl other) { |
| return new Future.value(type == other.type); |
| } |
| |
| @override |
| Future<bool> isSubtypeOf(covariant _StaticTypeImpl other) { |
| return new Future.value(macroApplications.types |
| .isSubtypeOf(type, other.type, SubtypeCheckMode.withNullabilities)); |
| } |
| } |
| |
| class _IdentifierResolver implements macro.IdentifierResolver { |
| final SourceLoader sourceLoader; |
| |
| _IdentifierResolver(this.sourceLoader); |
| |
| @override |
| Future<macro.Identifier> resolveIdentifier(Uri library, String name) { |
| LibraryBuilder? libraryBuilder = sourceLoader.lookupLibraryBuilder(library); |
| if (libraryBuilder == null) { |
| return new Future.error( |
| new ArgumentError('Library at uri $library could not be resolved.'), |
| StackTrace.current); |
| } |
| bool isSetter = false; |
| String memberName = name; |
| if (name.endsWith('=')) { |
| memberName = name.substring(0, name.length - 1); |
| isSetter = true; |
| } |
| Builder? builder = |
| libraryBuilder.scope.lookupLocalMember(memberName, setter: isSetter); |
| if (builder == null) { |
| return new Future.error( |
| new ArgumentError( |
| 'Unable to find top level identifier "$name" in $library'), |
| StackTrace.current); |
| } else if (builder is TypeDeclarationBuilder) { |
| return new Future.value(new TypeDeclarationBuilderIdentifier( |
| typeDeclarationBuilder: builder, |
| libraryBuilder: libraryBuilder, |
| id: macro.RemoteInstance.uniqueId, |
| name: name)); |
| } else if (builder is MemberBuilder) { |
| return new Future.value(new MemberBuilderIdentifier( |
| memberBuilder: builder, |
| id: macro.RemoteInstance.uniqueId, |
| name: name)); |
| } else { |
| return new Future.error( |
| new UnsupportedError('Unsupported identifier kind $builder'), |
| StackTrace.current); |
| } |
| } |
| } |
| |
| class _TypeResolver implements macro.TypeResolver { |
| final MacroApplications macroApplications; |
| |
| _TypeResolver(this.macroApplications); |
| |
| @override |
| Future<macro.StaticType> resolve(macro.TypeAnnotationCode typeAnnotation) { |
| return new Future.value( |
| macroApplications.resolveTypeAnnotation(typeAnnotation)); |
| } |
| } |
| |
| class _ClassIntrospector implements macro.ClassIntrospector { |
| final MacroApplications macroApplications; |
| final ClassHierarchyBuilder classHierarchy; |
| |
| _ClassIntrospector(this.macroApplications, this.classHierarchy); |
| |
| @override |
| Future<List<macro.ConstructorDeclaration>> constructorsOf( |
| macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| List<macro.ConstructorDeclaration> result = []; |
| classBuilder.forEachConstructor((_, MemberBuilder memberBuilder) { |
| if (memberBuilder is DeclaredSourceConstructorBuilder) { |
| // TODO(johnniwinther): Should we support synthesized constructors? |
| result.add(macroApplications._getMemberDeclaration(memberBuilder) |
| as macro.ConstructorDeclaration); |
| } else if (memberBuilder is SourceFactoryBuilder) { |
| result.add(macroApplications._getMemberDeclaration(memberBuilder) |
| as macro.ConstructorDeclaration); |
| } |
| }); |
| return new Future.value(result); |
| } |
| |
| @override |
| Future<List<macro.FieldDeclaration>> fieldsOf(macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| List<macro.FieldDeclaration> result = []; |
| classBuilder.forEach((_, Builder memberBuilder) { |
| if (memberBuilder is SourceFieldBuilder) { |
| result.add(macroApplications._getMemberDeclaration(memberBuilder) |
| as macro.FieldDeclaration); |
| } |
| }); |
| return new Future.value(result); |
| } |
| |
| @override |
| Future<List<macro.ClassDeclaration>> interfacesOf( |
| macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| ClassHierarchyNode node = |
| classHierarchy.getNodeFromClassBuilder(classBuilder); |
| List<ClassHierarchyNode>? directInterfaceNodes = node.directInterfaceNodes; |
| if (directInterfaceNodes != null) { |
| List<macro.ClassDeclaration> list = []; |
| for (ClassHierarchyNode interfaceNode in directInterfaceNodes) { |
| list.add( |
| macroApplications.getClassDeclaration(interfaceNode.classBuilder)); |
| } |
| return new Future.value(list); |
| } |
| return new Future.value(const []); |
| } |
| |
| @override |
| Future<List<macro.MethodDeclaration>> methodsOf( |
| macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| List<macro.MethodDeclaration> result = []; |
| classBuilder.forEach((_, Builder memberBuilder) { |
| if (memberBuilder is SourceProcedureBuilder) { |
| result.add(macroApplications._getMemberDeclaration(memberBuilder) |
| as macro.MethodDeclaration); |
| } |
| }); |
| return new Future.value(result); |
| } |
| |
| @override |
| Future<List<macro.ClassDeclaration>> mixinsOf(macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| ClassHierarchyNode node = |
| classHierarchy.getNodeFromClassBuilder(classBuilder); |
| ClassHierarchyNode? superNode = node.directSuperClassNode; |
| List<macro.ClassDeclaration>? list; |
| while (superNode != null && superNode.isMixinApplication) { |
| (list ??= []).add(macroApplications |
| .getClassDeclaration(superNode.mixedInNode!.classBuilder)); |
| superNode = superNode.directSuperClassNode; |
| } |
| return new Future.value(list?.reversed.toList() ?? const []); |
| } |
| |
| @override |
| Future<macro.ClassDeclaration?> superclassOf(macro.ClassDeclaration clazz) { |
| ClassBuilder classBuilder = macroApplications._getClassBuilder(clazz); |
| ClassHierarchyNode node = |
| classHierarchy.getNodeFromClassBuilder(classBuilder); |
| ClassHierarchyNode? superNode = node.directSuperClassNode; |
| while (superNode != null && |
| superNode.classBuilder.isAnonymousMixinApplication) { |
| superNode = superNode.directSuperClassNode; |
| } |
| if (superNode != null) { |
| return new Future.value( |
| macroApplications.getClassDeclaration(superNode.classBuilder)); |
| } |
| return new Future.value(); |
| } |
| } |
| |
| class _TypeDeclarationResolver implements macro.TypeDeclarationResolver { |
| final MacroApplications macroApplications; |
| |
| _TypeDeclarationResolver(this.macroApplications); |
| |
| @override |
| Future<macro.TypeDeclaration> declarationOf(macro.Identifier identifier) { |
| if (identifier is IdentifierImpl) { |
| return identifier.resolveTypeDeclaration(macroApplications); |
| } |
| throw new UnsupportedError( |
| 'Unsupported identifier $identifier (${identifier.runtimeType})'); |
| } |
| } |
| |
| class _TypeInferrer implements macro.TypeInferrer { |
| final MacroApplications _macroApplications; |
| |
| _TypeInferrer(this._macroApplications); |
| |
| @override |
| Future<macro.TypeAnnotation> inferType( |
| macro.OmittedTypeAnnotation omittedType) => |
| new Future.value(_macroApplications._inferOmittedType(omittedType)); |
| } |
| |
| macro.DeclarationKind _declarationKind(macro.Declaration declaration) { |
| if (declaration is macro.ConstructorDeclaration) { |
| return macro.DeclarationKind.constructor; |
| } else if (declaration is macro.MethodDeclaration) { |
| return macro.DeclarationKind.method; |
| } else if (declaration is macro.FunctionDeclaration) { |
| return macro.DeclarationKind.function; |
| } else if (declaration is macro.FieldDeclaration) { |
| return macro.DeclarationKind.field; |
| } else if (declaration is macro.VariableDeclaration) { |
| return macro.DeclarationKind.variable; |
| } else if (declaration is macro.ClassDeclaration) { |
| return macro.DeclarationKind.clazz; |
| } |
| throw new UnsupportedError( |
| "Unexpected declaration ${declaration} (${declaration.runtimeType})"); |
| } |
| |
| /// Data needed to apply a list of macro applications to a class or member. |
| class _ApplicationData { |
| final SourceLibraryBuilder libraryBuilder; |
| final Builder builder; |
| final macro.Declaration declaration; |
| final List<MacroApplication> macroApplications; |
| |
| _ApplicationData(this.libraryBuilder, this.builder, this.declaration, |
| this.macroApplications); |
| } |
| |
| extension on macro.MacroExecutionResult { |
| bool get isNotEmpty => |
| libraryAugmentations.isNotEmpty || classAugmentations.isNotEmpty; |
| } |