blob: 615a927769eb8e764dc6ad9ce2d91741bac46733 [file] [log] [blame]
// 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;
}