// 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 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 _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 _dillPut(Object? argumentObject) async { final arguments = argumentObject.argumentAsMap; final uriStr = arguments.required('uri'); final bytes = arguments.required('bytes'); final uri = Uri.parse(uriStr); _dills[uri] = bytes; } Future _dillRemove(Object? argumentObject) async { final arguments = argumentObject.argumentAsMap; final uriStr = arguments.required('uri'); final uri = Uri.parse(uriStr); _dills.remove(uri); } Future _exit(Object? argument) async { io.exit(0); } fe.CompilerOptions _getCompilerOptions(Map arguments) { final sdkSummaryUriStr = arguments.required('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(); if (additionalDills != null) { compilerOptions.additionalDills.addAll( additionalDills.map(Uri.parse), ); } return compilerOptions; } Future _kernelForModule(Object? argumentObject) async { final arguments = argumentObject.argumentAsMap; final packagesFileUriStr = arguments.required('packagesFileUri'); final compilerOptions = _getCompilerOptions(arguments) ..packagesFileUri = Uri.parse(packagesFileUriStr); final uriStrList = arguments['uris'].asListOf(); if (uriStrList == null) { throw ArgumentError('Missing field: uris'); } final compilationResults = await fe.kernelForModule( uriStrList.map(Uri.parse).toList(), compilerOptions, ); return _serializeComponentWithoutPlatform( compilationResults.component!, ); } Future _kernelForProgram(Object? argumentObject) async { final arguments = argumentObject.argumentAsMap; final compilerOptions = _getCompilerOptions(arguments); final packagesFileUriStr = arguments.optional('packagesFileUri'); if (packagesFileUriStr != null) { compilerOptions.packagesFileUri = Uri.parse(packagesFileUriStr); } final uriStr = arguments.required('uri'); final compilationResults = await vm.compileToKernel( Uri.parse(uriStr), compilerOptions, environmentDefines: {}, ); return _serializeComponentWithoutPlatform( compilationResults.component!, ); } Future _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 _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 exists() async { if (_fileSystem._dills.containsKey(uri)) { return true; } return _channel.sendRequest('file.exists', _uriStr); } @override Future existsAsyncIfPossible() => exists(); @override Future> readAsBytes() async { final storedBytes = _fileSystem._dills[uri]; if (storedBytes != null) { return storedBytes; } return _channel.sendRequest('file.readAsBytes', _uriStr); } @override Future> readAsBytesAsyncIfPossible() => readAsBytes(); @override Future readAsString() async { return _channel.sendRequest('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 get argumentAsMap { final self = this; if (self is Map) { return self; } throw ArgumentError('The argument must be a map.'); } } extension on Map { T? optional(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(String name) { final value = this[name]; if (value is T) { return value; } throw ArgumentError('Must be $T: $name'); } } extension on Object? { List? asListOf() { final self = this; if (self is List) { final result = []; for (final element in self) { if (element is T) { result.add(element); } else { return null; } } return result; } return null; } }