| // 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; |
| } |
| } |