blob: 714dc053198983dd3b83da5c5f88f57559221fa5 [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.
// @dart = 2.15
import 'dart:async';
import 'dart:io' as io;
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/compiler/request_channel.dart';
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/api_prototype/kernel_generator.dart' as fe;
import 'package:front_end/src/fasta/kernel/utils.dart' as fe;
import 'package:kernel/ast.dart' as fe;
import 'package:kernel/target/targets.dart' as fe;
import 'package:vm/kernel_front_end.dart' as vm;
import 'package:vm/target/vm.dart' as vm;
Future<void> runBinaryProtocol(String addressStr) async {
final parsedAddress = _ParsedAddress.parse(addressStr);
final socket = await io.Socket.connect(
parsedAddress.host,
parsedAddress.port,
);
_Client(
RequestChannel(socket),
() {
socket.destroy();
},
);
}
class _Client {
final RequestChannel _channel;
final void Function() _stopHandler;
final Map<Uri, Uint8List> _dills = {};
_Client(this._channel, this._stopHandler) {
_channel.add('dill.put', _dillPut);
_channel.add('dill.remove', _dillRemove);
_channel.add('exit', _exit);
_channel.add('kernelForModule', _kernelForModule);
_channel.add('kernelForProgram', _kernelForProgram);
_channel.add('stop', _stop);
}
Future<void> _dillPut(Object? argumentObject) async {
final arguments = argumentObject.argumentAsMap;
final uriStr = arguments.required<String>('uri');
final bytes = arguments.required<Uint8List>('bytes');
final uri = Uri.parse(uriStr);
_dills[uri] = bytes;
}
Future<void> _dillRemove(Object? argumentObject) async {
final arguments = argumentObject.argumentAsMap;
final uriStr = arguments.required<String>('uri');
final uri = Uri.parse(uriStr);
_dills.remove(uri);
}
Future<void> _exit(Object? argument) async {
io.exit(0);
}
fe.CompilerOptions _getCompilerOptions(Map<Object?, Object?> arguments) {
final sdkSummaryUriStr = arguments.required<String>('sdkSummary');
final compilerOptions = fe.CompilerOptions()
..environmentDefines = {}
..fileSystem = _FileSystem(_channel, _dills)
..sdkSummary = Uri.parse(sdkSummaryUriStr)
..target = vm.VmTarget(fe.TargetFlags(enableNullSafety: true));
final additionalDills = arguments['additionalDills'].asListOf<String>();
if (additionalDills != null) {
compilerOptions.additionalDills.addAll(
additionalDills.map(Uri.parse),
);
}
return compilerOptions;
}
Future<Object?> _kernelForModule(Object? argumentObject) async {
final arguments = argumentObject.argumentAsMap;
final packagesFileUriStr = arguments.required<String>('packagesFileUri');
final compilerOptions = _getCompilerOptions(arguments)
..packagesFileUri = Uri.parse(packagesFileUriStr);
final uriStrList = arguments['uris'].asListOf<String>();
if (uriStrList == null) {
throw ArgumentError('Missing field: uris');
}
final compilationResults = await fe.kernelForModule(
uriStrList.map(Uri.parse).toList(),
compilerOptions,
);
return _serializeComponentWithoutPlatform(
compilationResults.component!,
);
}
Future<Object?> _kernelForProgram(Object? argumentObject) async {
final arguments = argumentObject.argumentAsMap;
final compilerOptions = _getCompilerOptions(arguments);
final packagesFileUriStr = arguments.optional<String>('packagesFileUri');
if (packagesFileUriStr != null) {
compilerOptions.packagesFileUri = Uri.parse(packagesFileUriStr);
}
final uriStr = arguments.required<String>('uri');
final compilationResults = await vm.compileToKernel(
Uri.parse(uriStr),
compilerOptions,
environmentDefines: {},
);
return _serializeComponentWithoutPlatform(
compilationResults.component!,
);
}
Future<void> _stop(Object? argument) async {
_stopHandler();
}
static Uint8List _serializeComponentWithoutPlatform(fe.Component component) {
return fe.serializeComponent(
component,
filter: (library) {
return !library.importUri.isScheme('dart');
},
includeSources: true,
);
}
}
class _FileSystem implements fe.FileSystem {
final RequestChannel _channel;
final Map<Uri, Uint8List> _dills;
_FileSystem(this._channel, this._dills);
@override
fe.FileSystemEntity entityForUri(Uri uri) {
return _FileSystemEntity(this, uri);
}
}
class _FileSystemEntity implements fe.FileSystemEntity {
final _FileSystem _fileSystem;
@override
final Uri uri;
_FileSystemEntity(this._fileSystem, this.uri);
RequestChannel get _channel => _fileSystem._channel;
String get _uriStr => uri.toString();
@override
Future<bool> exists() async {
if (_fileSystem._dills.containsKey(uri)) {
return true;
}
return _channel.sendRequest<bool>('file.exists', _uriStr);
}
@override
Future<bool> existsAsyncIfPossible() => exists();
@override
Future<List<int>> readAsBytes() async {
final storedBytes = _fileSystem._dills[uri];
if (storedBytes != null) {
return storedBytes;
}
return _channel.sendRequest<Uint8List>('file.readAsBytes', _uriStr);
}
@override
Future<List<int>> readAsBytesAsyncIfPossible() => readAsBytes();
@override
Future<String> readAsString() async {
return _channel.sendRequest<String>('file.readAsString', _uriStr);
}
}
class _ParsedAddress {
final String host;
final int port;
factory _ParsedAddress.parse(String str) {
final colonOffset = str.lastIndexOf(':');
if (colonOffset == -1) {
throw FormatException("Expected ':' in: $str");
}
return _ParsedAddress._(
str.substring(0, colonOffset),
int.parse(str.substring(colonOffset + 1)),
);
}
_ParsedAddress._(this.host, this.port);
}
extension on Object? {
Map<Object?, Object?> get argumentAsMap {
final self = this;
if (self is Map<Object?, Object?>) {
return self;
}
throw ArgumentError('The argument must be a map.');
}
}
extension on Map<Object?, Object?> {
T? optional<T extends Object>(String name) {
final value = this[name];
if (value == null) {
return null;
}
if (value is T) {
return value;
}
throw ArgumentError('Must be null or $T: $name');
}
T required<T>(String name) {
final value = this[name];
if (value is T) {
return value;
}
throw ArgumentError('Must be $T: $name');
}
}
extension on Object? {
List<T>? asListOf<T>() {
final self = this;
if (self is List<Object?>) {
final result = <T>[];
for (final element in self) {
if (element is T) {
result.add(element);
} else {
return null;
}
}
return result;
}
return null;
}
}