| // Copyright (c) 2022, 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. |
| |
| // ignore: deprecated_member_use |
| import 'dart:cli' as cli; |
| import 'dart:convert'; |
| import 'dart:io' as io; |
| import 'dart:typed_data'; |
| |
| import 'package:_fe_analyzer_shared/src/macros/bootstrap.dart'; |
| import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/file_system/memory_file_system.dart'; |
| import 'package:analyzer/file_system/physical_file_system.dart'; |
| import 'package:analyzer/src/summary2/macro.dart'; |
| import 'package:analyzer/src/util/uri.dart'; |
| import 'package:analyzer_utilities/package_root.dart' as package_root; |
| import 'package:front_end/src/api_prototype/compiler_options.dart' as fe; |
| import 'package:front_end/src/api_prototype/file_system.dart' as fe; |
| import 'package:front_end/src/fasta/kernel/utils.dart' as fe; |
| import 'package:kernel/target/targets.dart' as fe; |
| import 'package:path/path.dart' as package_path; |
| import 'package:vm/kernel_front_end.dart' as fe; |
| import 'package:vm/target/vm.dart' as fe; |
| |
| final Uri _platformDillUri = Uri.parse('org-dartlang-sdk://vm.dill'); |
| |
| /// Implementation of [MacroKernelBuilder] that can run in Dart SDK repository. |
| /// |
| /// This is a temporary implementation, to be replaced with a more stable |
| /// approach, e.g. a `dart:` API for compilation, shipping `front_end` |
| /// with SDK, etc. |
| class DartRepositoryMacroKernelBuilder implements MacroKernelBuilder { |
| final Uint8List platformDillBytes; |
| |
| DartRepositoryMacroKernelBuilder(this.platformDillBytes); |
| |
| @override |
| Uint8List build({ |
| required MacroFileSystem fileSystem, |
| required List<MacroLibrary> libraries, |
| }) { |
| var options = fe.CompilerOptions() |
| ..sdkSummary = _platformDillUri |
| ..target = fe.VmTarget(fe.TargetFlags(enableNullSafety: true)); |
| |
| var macroMainContent = bootstrapMacroIsolate( |
| { |
| for (var library in libraries) |
| library.uri.toString(): { |
| for (var c in library.classes) c.name: c.constructors |
| }, |
| }, |
| SerializationMode.byteDataClient, |
| ); |
| |
| var macroMainBytes = utf8.encode(macroMainContent) as Uint8List; |
| var macroMainPath = '${libraries.first.path}.macro'; |
| var macroMainUri = fileSystem.pathContext.toUri(macroMainPath); |
| |
| options.fileSystem = _FileSystem( |
| fileSystem, |
| platformDillBytes, |
| macroMainUri, |
| macroMainBytes, |
| ); |
| |
| // TODO(scheglov) For now we convert async into sync. |
| // ignore: deprecated_member_use |
| var compilationResults = cli.waitFor( |
| fe.compileToKernel( |
| macroMainUri, |
| options, |
| environmentDefines: {}, |
| ), |
| ); |
| |
| return fe.serializeComponent( |
| compilationResults.component!, |
| filter: (library) { |
| return !library.importUri.isScheme('dart'); |
| }, |
| includeSources: false, |
| ); |
| } |
| } |
| |
| /// Environment for compiling macros to kernels, expecting that we run |
| /// a test in the Dart SDK repository. |
| /// |
| /// Just like [DartRepositoryMacroKernelBuilder], this is a temporary |
| /// implementation. |
| class MacrosEnvironment { |
| static late final instance = MacrosEnvironment._(); |
| |
| final _resourceProvider = MemoryResourceProvider(context: package_path.posix); |
| late final Uint8List platformDillBytes; |
| |
| MacrosEnvironment._() { |
| var physical = PhysicalResourceProvider.INSTANCE; |
| |
| var packageRoot = physical.pathContext.normalize(package_root.packageRoot); |
| physical |
| .getFolder(packageRoot) |
| .getChildAssumingFolder('_fe_analyzer_shared/lib/src/macros') |
| .copyTo( |
| packageSharedFolder.getChildAssumingFolder('lib/src'), |
| ); |
| |
| platformDillBytes = physical |
| .getFile(io.Platform.resolvedExecutable) |
| .parent |
| .parent |
| .getChildAssumingFolder('lib') |
| .getChildAssumingFolder('_internal') |
| .getChildAssumingFile('vm_platform_strong.dill') |
| .readAsBytesSync(); |
| } |
| |
| Folder get packageSharedFolder { |
| return _resourceProvider.getFolder('/packages/_fe_analyzer_shared'); |
| } |
| } |
| |
| class _BytesFileSystemEntity implements fe.FileSystemEntity { |
| @override |
| final Uri uri; |
| |
| final Uint8List bytes; |
| |
| _BytesFileSystemEntity(this.uri, this.bytes); |
| |
| @override |
| Future<bool> exists() async => true; |
| |
| @override |
| Future<bool> existsAsyncIfPossible() => exists(); |
| |
| @override |
| Future<List<int>> readAsBytes() async => bytes; |
| |
| @override |
| Future<List<int>> readAsBytesAsyncIfPossible() => readAsBytes(); |
| |
| @override |
| Future<String> readAsString() async { |
| var bytes = await readAsBytes(); |
| return utf8.decode(bytes); |
| } |
| } |
| |
| class _FileSystem implements fe.FileSystem { |
| final MacroFileSystem fileSystem; |
| final Uint8List platformDillBytes; |
| final Uri macroMainUri; |
| final Uint8List macroMainBytes; |
| |
| _FileSystem( |
| this.fileSystem, |
| this.platformDillBytes, |
| this.macroMainUri, |
| this.macroMainBytes, |
| ); |
| |
| @override |
| fe.FileSystemEntity entityForUri(Uri uri) { |
| if (uri == _platformDillUri) { |
| return _BytesFileSystemEntity(uri, platformDillBytes); |
| } else if (uri == macroMainUri) { |
| return _BytesFileSystemEntity(uri, macroMainBytes); |
| } else if (uri.isScheme('file')) { |
| var path = fileUriToNormalizedPath(fileSystem.pathContext, uri); |
| return _FileSystemEntity( |
| uri, |
| fileSystem.getFile(path), |
| ); |
| } else { |
| throw fe.FileSystemException(uri, 'Only supports file: URIs'); |
| } |
| } |
| } |
| |
| class _FileSystemEntity implements fe.FileSystemEntity { |
| @override |
| final Uri uri; |
| |
| final MacroFileEntry file; |
| |
| _FileSystemEntity(this.uri, this.file); |
| |
| @override |
| Future<bool> exists() async => file.exists; |
| |
| @override |
| Future<bool> existsAsyncIfPossible() => exists(); |
| |
| @override |
| Future<List<int>> readAsBytes() async { |
| var string = await readAsString(); |
| return utf8.encode(string); |
| } |
| |
| @override |
| Future<List<int>> readAsBytesAsyncIfPossible() => readAsBytes(); |
| |
| @override |
| Future<String> readAsString() async => file.content; |
| } |