| // Copyright (c) 2017, 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 'dart:io' show File, IOSink; |
| import 'dart:typed_data' show BytesBuilder, Uint8List; |
| |
| import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart'; |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token; |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' |
| show SyntheticToken, TokenType; |
| import 'package:front_end/src/base/lookup_result.dart'; |
| import 'package:front_end/src/base/scope.dart'; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/binary/ast_to_binary.dart'; |
| import 'package:kernel/clone.dart'; |
| import 'package:kernel/text/ast_to_text.dart'; |
| |
| import '../base/combinator.dart'; |
| import '../base/configuration.dart'; |
| import '../base/identifiers.dart'; |
| import '../base/modifiers.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/fixed_type_builder.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/omitted_type_builder.dart'; |
| import '../builder/record_type_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../fragment/fragment.dart'; |
| import '../source/fragment_factory.dart'; |
| import '../source/source_type_parameter_builder.dart'; |
| import 'body_builder.dart'; |
| |
| /// The name for the synthesized field used to store information of |
| /// unserializable exports in a [Library]. |
| /// |
| /// For instance, if a [Library] tries to export two declarations with the same |
| /// name, the unserializable exports will map this name to the corresponding |
| /// error message. |
| const String unserializableExportName = '_exports#'; |
| |
| /// Sentinel value used in unserializable exports to signal an export of |
| /// 'dynamic' from 'dart:core'. |
| const String exportDynamicSentinel = '<dynamic>'; |
| |
| /// Sentinel value used in unserializable exports to signal an export of |
| /// 'Never' from 'dart:core'. |
| const String exportNeverSentinel = '<Never>'; |
| |
| // Coverage-ignore(suite): Not run. |
| void printNodeOn(Node? node, StringSink sink, {NameSystem? syntheticNames}) { |
| if (node == null) { |
| sink.write("null"); |
| } else { |
| syntheticNames ??= new NameSystem(); |
| new Printer(sink, syntheticNames: syntheticNames).writeNode(node); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| void printQualifiedNameOn(Member? member, StringSink sink) { |
| if (member == null) { |
| sink.write("null"); |
| } else { |
| sink.write(member.enclosingLibrary.importUri); |
| sink.write("::"); |
| Class? cls = member.enclosingClass; |
| if (cls != null) { |
| sink.write(cls.name); |
| sink.write("::"); |
| } |
| sink.write(member.name.text); |
| } |
| } |
| |
| void bindCoreType( |
| LibraryBuilder coreLibrary, |
| NamedTypeBuilder typeBuilder, { |
| bool isNullClass = false, |
| }) { |
| TypeDeclarationBuilder typeDeclarationBuilder = |
| coreLibrary.lookupRequiredLocalMember(typeBuilder.typeName.name) |
| as TypeDeclarationBuilder; |
| typeBuilder.bind(coreLibrary, typeDeclarationBuilder); |
| if (isNullClass) { |
| (typeDeclarationBuilder as ClassBuilder).isNullClass = true; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| /// Print the given [component]. Do nothing if it is `null`. If the |
| /// [libraryFilter] is provided, then only libraries that satisfy it are |
| /// printed. |
| void printComponentText( |
| Component? component, { |
| bool Function(Library library)? libraryFilter, |
| bool showOffsets = false, |
| }) { |
| if (component == null) return; |
| StringBuffer sb = new StringBuffer(); |
| Printer printer = new Printer(sb, showOffsets: showOffsets); |
| printer.writeComponentProblems(component); |
| for (Library library in component.libraries) { |
| if (libraryFilter != null && !libraryFilter(library)) continue; |
| printer.writeLibraryFile(library); |
| } |
| printer.writeConstantTable(component); |
| print(sb); |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| /// Write [component] to file only including libraries that match [filter]. |
| Future<Null> writeComponentToFile( |
| Component component, |
| Uri uri, { |
| bool Function(Library library)? filter, |
| }) async { |
| File output = new File.fromUri(uri); |
| IOSink sink = output.openWrite(); |
| try { |
| BinaryPrinter printer = new BinaryPrinter(sink, libraryFilter: filter); |
| printer.writeComponentFile(component); |
| } finally { |
| await sink.close(); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| /// Serialize the libraries in [component] that match [filter]. |
| Uint8List serializeComponent( |
| Component component, { |
| bool Function(Library library)? filter, |
| bool includeSources = true, |
| bool includeOffsets = true, |
| }) { |
| ByteSink byteSink = new ByteSink(); |
| BinaryPrinter printer = new BinaryPrinter( |
| byteSink, |
| libraryFilter: filter, |
| includeSources: includeSources, |
| includeOffsets: includeOffsets, |
| ); |
| printer.writeComponentFile(component); |
| return byteSink.builder.takeBytes(); |
| } |
| |
| const String kDebugClassName = "#DebugClass"; |
| |
| // Coverage-ignore(suite): Not run. |
| class _CollectLibraryDependencies extends RecursiveVisitor { |
| Set<LibraryDependency> foundLibraryDependencies = {}; |
| |
| @override |
| void visitLoadLibrary(LoadLibrary node) { |
| foundLibraryDependencies.add(node.import); |
| } |
| |
| @override |
| void visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) { |
| foundLibraryDependencies.add(node.import); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| Component createExpressionEvaluationComponent(Procedure procedure) { |
| Library realLibrary = procedure.enclosingLibrary; |
| |
| Uri uri = new Uri(scheme: 'evaluate', path: 'source'); |
| Library fakeLibrary = new Library(uri, fileUri: uri) |
| ..setLanguageVersion(realLibrary.languageVersion); |
| |
| // Add deferred library dependencies. They are needed for serializing |
| // references to deferred libraries. We can just claim ownership of the ones |
| // we find as they were created when doing the expression compilation. |
| _CollectLibraryDependencies collectLibraryDependencies = |
| new _CollectLibraryDependencies(); |
| procedure.accept(collectLibraryDependencies); |
| for (LibraryDependency libraryDependency |
| in collectLibraryDependencies.foundLibraryDependencies) { |
| fakeLibrary.addDependency(libraryDependency); |
| } |
| |
| TreeNode? realClass = procedure.parent; |
| if (realClass is Class) { |
| Class fakeClass = new Class(name: kDebugClassName, fileUri: uri) |
| ..parent = fakeLibrary; |
| Map<TypeParameter, TypeParameter> typeParams = |
| <TypeParameter, TypeParameter>{}; |
| Map<TypeParameter, DartType> typeSubstitution = <TypeParameter, DartType>{}; |
| for (TypeParameter typeParam in realClass.typeParameters) { |
| TypeParameter newNode = new TypeParameter(typeParam.name) |
| ..declaration = fakeClass; |
| typeParams[typeParam] = newNode; |
| typeSubstitution[typeParam] = new TypeParameterType( |
| newNode, |
| typeParam.computeNullabilityFromBound(), |
| ); |
| } |
| CloneVisitorWithMembers cloner = new CloneVisitorWithMembers( |
| typeSubstitution: typeSubstitution, |
| typeParams: typeParams, |
| ); |
| |
| for (TypeParameter typeParam in realClass.typeParameters) { |
| fakeClass.typeParameters.add( |
| typeParam.accept<TreeNode>(cloner) as TypeParameter, |
| ); |
| } |
| |
| if (realClass.supertype != null) { |
| // supertype is null for Object. |
| fakeClass.supertype = new Supertype.byReference( |
| realClass.supertype!.className, |
| realClass.supertype!.typeArguments.map(cloner.visitType).toList(), |
| ); |
| } |
| |
| // Rebind the type parameters in the procedure. |
| procedure = cloner.cloneProcedure(procedure, null); |
| procedure.parent = fakeClass; |
| fakeClass.procedures.add(procedure); |
| fakeLibrary.classes.add(fakeClass); |
| } else { |
| fakeLibrary.procedures.add(procedure); |
| procedure.parent = fakeLibrary; |
| } |
| |
| // TODO(vegorov) find a way to preserve metadata. |
| Component component = new Component(libraries: [fakeLibrary]); |
| component.setMainMethodAndMode(null, false); |
| return component; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| List<int> serializeProcedure(Procedure procedure) { |
| return serializeComponent(createExpressionEvaluationComponent(procedure)); |
| } |
| |
| /// A [Sink] that directly writes data into a byte builder. |
| class ByteSink implements Sink<List<int>> { |
| final BytesBuilder builder = new BytesBuilder(); |
| |
| @override |
| void add(List<int> data) { |
| builder.add(data); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void close() {} |
| } |
| |
| int compareProcedures(Procedure a, Procedure b) { |
| int i = "${a.fileUri}".compareTo("${b.fileUri}"); |
| if (i != 0) return i; |
| return a.fileOffset.compareTo(b.fileOffset); |
| } |
| |
| List<Combinator>? toCombinators(List<CombinatorBuilder>? combinatorBuilders) { |
| if (combinatorBuilders == null) { |
| // Note: it's safe to return null here as Kernel's LibraryDependency will |
| // convert null to an empty list. |
| return null; |
| } |
| |
| return new List<Combinator>.generate(combinatorBuilders.length, (int i) { |
| CombinatorBuilder combinator = combinatorBuilders[i]; |
| List<String> nameList = combinator.names.toList(); |
| return combinator.isShow |
| ? new Combinator.show(nameList) |
| : new Combinator.hide(nameList); |
| }, growable: true); |
| } |
| |
| final Token dummyToken = new SyntheticToken(TokenType.AT, -1); |
| final Identifier dummyIdentifier = new SimpleIdentifier(dummyToken); |
| final CombinatorBuilder dummyCombinator = new CombinatorBuilder( |
| false, |
| {}, |
| -1, |
| dummyUri, |
| ); |
| final MetadataBuilder dummyMetadataBuilder = new MetadataBuilder( |
| dummyToken, |
| dummyUri, |
| ); |
| final TypeBuilder dummyTypeBuilder = new FixedTypeBuilderImpl( |
| dummyDartType, |
| dummyUri, |
| -1, |
| ); |
| final FormalParameterBuilder dummyFormalParameterBuilder = |
| new FormalParameterBuilder( |
| FormalParameterKind.requiredPositional, |
| Modifiers.empty, |
| const ImplicitTypeBuilder(), |
| '', |
| -1, |
| fileUri: dummyUri, |
| hasImmediatelyDeclaredInitializer: false, |
| ); |
| final FunctionTypeParameterBuilder dummyFunctionTypeParameterBuilder = |
| new FunctionTypeParameterBuilder( |
| FormalParameterKind.requiredPositional, |
| const ImplicitTypeBuilder(), |
| '', |
| ); |
| final NominalParameterBuilder dummyNominalVariableBuilder = |
| new SourceNominalParameterBuilder( |
| new DirectNominalParameterDeclaration( |
| name: NominalParameterBuilder.noNameSentinel, |
| kind: TypeParameterKind.function, |
| isWildcard: false, |
| fileOffset: -1, |
| fileUri: dummyUri, |
| ), |
| ); |
| final TypeParameterFragment dummyTypeParameterFragment = |
| new TypeParameterFragment( |
| metadata: null, |
| name: '', |
| nameOffset: -1, |
| fileUri: dummyUri, |
| kind: TypeParameterKind.function, |
| isWildcard: false, |
| variableName: '', |
| typeParameterScope: dummyLookupScope, |
| ); |
| final SourceStructuralParameterBuilder dummyStructuralVariableBuilder = |
| new SourceStructuralParameterBuilder( |
| new RegularStructuralParameterDeclaration( |
| metadata: null, |
| name: StructuralParameterBuilder.noNameSentinel, |
| fileOffset: -1, |
| fileUri: dummyUri, |
| isWildcard: false, |
| ), |
| ); |
| final Label dummyLabel = new Label('', -1); |
| final RecordTypeFieldBuilder dummyRecordTypeFieldBuilder = |
| new RecordTypeFieldBuilder(null, dummyTypeBuilder, null, -1); |
| final FieldInfo dummyFieldInfo = new FieldInfo( |
| dummyIdentifier, |
| null, |
| dummyToken, |
| -1, |
| ); |
| final Configuration dummyConfiguration = new Configuration(-1, '', '', ''); |
| final LookupScope dummyLookupScope = new _DummyLookupScope(); |
| |
| // Coverage-ignore(suite): Not run. |
| class _DummyLookupScope implements LookupScope { |
| @override |
| void forEachExtension(void Function(ExtensionBuilder p1) f) {} |
| |
| @override |
| ScopeKind get kind => ScopeKind.library; |
| |
| @override |
| LookupResult? lookup(String name) => null; |
| } |