|  | // Copyright (c) 2013, 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. | 
|  |  | 
|  | part of "common_patch.dart"; | 
|  |  | 
|  | @patch | 
|  | class _WindowsCodePageDecoder { | 
|  | @patch | 
|  | @pragma("vm:external-name", "SystemEncodingToString") | 
|  | external static String _decodeBytes(List<int> bytes); | 
|  | } | 
|  |  | 
|  | @patch | 
|  | class _WindowsCodePageEncoder { | 
|  | @patch | 
|  | @pragma("vm:external-name", "StringToSystemEncoding") | 
|  | external static List<int> _encodeString(String string); | 
|  | } | 
|  |  | 
|  | @patch | 
|  | class Process { | 
|  | @patch | 
|  | static Future<Process> start( | 
|  | String executable, | 
|  | List<String> arguments, { | 
|  | String? workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment = true, | 
|  | bool runInShell = false, | 
|  | ProcessStartMode mode = ProcessStartMode.normal, | 
|  | }) { | 
|  | _ProcessImpl process = _ProcessImpl( | 
|  | executable, | 
|  | arguments, | 
|  | workingDirectory, | 
|  | environment, | 
|  | includeParentEnvironment, | 
|  | runInShell, | 
|  | mode, | 
|  | ); | 
|  | return process._start(); | 
|  | } | 
|  |  | 
|  | @patch | 
|  | static Future<ProcessResult> run( | 
|  | String executable, | 
|  | List<String> arguments, { | 
|  | String? workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment = true, | 
|  | bool runInShell = false, | 
|  | Encoding? stdoutEncoding = systemEncoding, | 
|  | Encoding? stderrEncoding = systemEncoding, | 
|  | }) { | 
|  | return _runNonInteractiveProcess( | 
|  | executable, | 
|  | arguments, | 
|  | workingDirectory, | 
|  | environment, | 
|  | includeParentEnvironment, | 
|  | runInShell, | 
|  | stdoutEncoding, | 
|  | stderrEncoding, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @patch | 
|  | static ProcessResult runSync( | 
|  | String executable, | 
|  | List<String> arguments, { | 
|  | String? workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment = true, | 
|  | bool runInShell = false, | 
|  | Encoding? stdoutEncoding = systemEncoding, | 
|  | Encoding? stderrEncoding = systemEncoding, | 
|  | }) { | 
|  | return _runNonInteractiveProcessSync( | 
|  | executable, | 
|  | arguments, | 
|  | workingDirectory, | 
|  | environment, | 
|  | includeParentEnvironment, | 
|  | runInShell, | 
|  | stdoutEncoding, | 
|  | stderrEncoding, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @patch | 
|  | static bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) { | 
|  | // TODO(40614): Remove once non-nullability is sound. | 
|  | ArgumentError.checkNotNull(signal, "signal"); | 
|  | return _ProcessUtils._killPid(pid, signal.signalNumber); | 
|  | } | 
|  | } | 
|  |  | 
|  | List<_SignalController?> _signalControllers = List.filled(32, null); | 
|  |  | 
|  | class _SignalController { | 
|  | final ProcessSignal signal; | 
|  |  | 
|  | final _controller = StreamController<ProcessSignal>.broadcast(); | 
|  | var _id; | 
|  |  | 
|  | _SignalController(this.signal) { | 
|  | _controller | 
|  | ..onListen = _listen | 
|  | ..onCancel = _cancel; | 
|  | } | 
|  |  | 
|  | Stream<ProcessSignal> get stream => _controller.stream; | 
|  |  | 
|  | void _listen() { | 
|  | var id = _setSignalHandler(signal.signalNumber); | 
|  | if (id is! int) { | 
|  | _controller.addError(SignalException("Failed to listen for $signal", id)); | 
|  | return; | 
|  | } | 
|  | _id = id; | 
|  | var socket = _RawSocket(_NativeSocket._watchSignal(id)); | 
|  | socket.listen((event) { | 
|  | if (event == RawSocketEvent.read) { | 
|  | var bytes = socket.read()!; | 
|  | for (int i = 0; i < bytes.length; i++) { | 
|  | _controller.add(signal); | 
|  | } | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | void _cancel() { | 
|  | if (_id != null) { | 
|  | _clearSignalHandler(signal.signalNumber); | 
|  | _id = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | @pragma("vm:external-name", "Process_SetSignalHandler") | 
|  | external static _setSignalHandler(int signal); | 
|  | @pragma("vm:external-name", "Process_ClearSignalHandler") | 
|  | external static void _clearSignalHandler(int signal); | 
|  | } | 
|  |  | 
|  | @pragma("vm:entry-point", "call") | 
|  | Function _getWatchSignalInternal() => _ProcessUtils._watchSignalInternal; | 
|  |  | 
|  | @patch | 
|  | class _ProcessUtils { | 
|  | @patch | 
|  | @pragma("vm:external-name", "Process_Exit") | 
|  | external static Never _exit(int status); | 
|  | @patch | 
|  | @pragma("vm:external-name", "Process_SetExitCode") | 
|  | external static void _setExitCode(int status); | 
|  | @patch | 
|  | @pragma("vm:external-name", "Process_GetExitCode") | 
|  | external static int _getExitCode(); | 
|  | @patch | 
|  | @pragma("vm:external-name", "Process_Sleep") | 
|  | external static void _sleep(int millis); | 
|  | @patch | 
|  | @pragma("vm:external-name", "Process_Pid") | 
|  | external static int _pid(Process? process); | 
|  | @pragma("vm:external-name", "Process_KillPid") | 
|  | external static bool _killPid(int pid, int signal); | 
|  | @patch | 
|  | static Stream<ProcessSignal> _watchSignal(ProcessSignal signal) { | 
|  | if (signal != ProcessSignal.sighup && | 
|  | signal != ProcessSignal.sigint && | 
|  | signal != ProcessSignal.sigterm && | 
|  | (Platform.isWindows || | 
|  | (signal != ProcessSignal.sigusr1 && | 
|  | signal != ProcessSignal.sigusr2 && | 
|  | signal != ProcessSignal.sigwinch))) { | 
|  | throw SignalException("Listening for signal $signal is not supported"); | 
|  | } | 
|  | return _watchSignalInternal(signal); | 
|  | } | 
|  |  | 
|  | static Stream<ProcessSignal> _watchSignalInternal(ProcessSignal signal) { | 
|  | if (_signalControllers[signal.signalNumber] == null) { | 
|  | _signalControllers[signal.signalNumber] = _SignalController(signal); | 
|  | } | 
|  | return _signalControllers[signal.signalNumber]!.stream; | 
|  | } | 
|  | } | 
|  |  | 
|  | @patch | 
|  | class ProcessInfo { | 
|  | @patch | 
|  | static int get maxRss { | 
|  | var result = _maxRss(); | 
|  | if (result is OSError) { | 
|  | throw result; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | @patch | 
|  | static int get currentRss { | 
|  | var result = _currentRss(); | 
|  | if (result is OSError) { | 
|  | throw result; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | @pragma("vm:external-name", "ProcessInfo_MaxRSS") | 
|  | external static _maxRss(); | 
|  | @pragma("vm:external-name", "ProcessInfo_CurrentRSS") | 
|  | external static _currentRss(); | 
|  | } | 
|  |  | 
|  | @pragma("vm:entry-point") | 
|  | class _ProcessStartStatus { | 
|  | @pragma("vm:entry-point", "set") | 
|  | int? _errorCode; // Set to OS error code if process start failed. | 
|  | @pragma("vm:entry-point", "set") | 
|  | String? _errorMessage; // Set to OS error message if process start failed. | 
|  | } | 
|  |  | 
|  | // The NativeFieldWrapperClass1 can not be used with a mixin, due to missing | 
|  | // implicit constructor. | 
|  | base class _ProcessImplNativeWrapper extends NativeFieldWrapperClass1 {} | 
|  |  | 
|  | base class _ProcessImpl extends _ProcessImplNativeWrapper implements _Process { | 
|  | static bool connectedResourceHandler = false; | 
|  |  | 
|  | _ProcessImpl( | 
|  | String path, | 
|  | List<String> arguments, | 
|  | this._workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment, | 
|  | bool runInShell, | 
|  | this._mode, | 
|  | ) : super() { | 
|  | // TODO(40614): Remove once non-nullability is sound. | 
|  | ArgumentError.checkNotNull(path, "path"); | 
|  | ArgumentError.checkNotNull(arguments, "arguments"); | 
|  | for (int i = 0; i < arguments.length; i++) { | 
|  | ArgumentError.checkNotNull(arguments[i], "arguments[]"); | 
|  | } | 
|  | ArgumentError.checkNotNull(_mode, "mode"); | 
|  |  | 
|  | if (!const bool.fromEnvironment("dart.vm.product") && | 
|  | !connectedResourceHandler) { | 
|  | registerExtension( | 
|  | 'ext.dart.io.getSpawnedProcesses', | 
|  | _SpawnedProcessResourceInfo.getStartedProcesses, | 
|  | ); | 
|  | registerExtension( | 
|  | 'ext.dart.io.getSpawnedProcessById', | 
|  | _SpawnedProcessResourceInfo.getProcessInfoMapById, | 
|  | ); | 
|  | connectedResourceHandler = true; | 
|  | } | 
|  |  | 
|  | if (runInShell) { | 
|  | arguments = _getShellArguments(path, arguments); | 
|  | path = _getShellCommand(); | 
|  | } | 
|  |  | 
|  | if (Platform.isWindows && path.contains(' ') && !path.contains('"')) { | 
|  | // Escape paths that may contain spaces | 
|  | // Bug: https://github.com/dart-lang/sdk/issues/37751 | 
|  | _path = '"$path"'; | 
|  | } else { | 
|  | _path = path; | 
|  | } | 
|  |  | 
|  | _arguments = [ | 
|  | for (int i = 0; i < arguments.length; i++) | 
|  | Platform.isWindows | 
|  | ? _windowsArgumentEscape(arguments[i]) | 
|  | : arguments[i], | 
|  | ]; | 
|  |  | 
|  | _environment = []; | 
|  | // Ensure that we have a non-null environment. | 
|  | environment ??= const {}; | 
|  | environment.forEach((key, value) { | 
|  | _environment.add('$key=$value'); | 
|  | }); | 
|  | if (includeParentEnvironment) { | 
|  | Platform.environment.forEach((key, value) { | 
|  | // Do not override keys already set as part of environment. | 
|  | if (!environment!.containsKey(key)) { | 
|  | _environment.add('$key=$value'); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | if (_modeHasStdio(_mode)) { | 
|  | // stdin going to process. | 
|  | _stdin = _StdSink(_Socket._writePipe().._owner = this); | 
|  | // stdout coming from process. | 
|  | _stdout = _StdStream(_Socket._readPipe().._owner = this); | 
|  | // stderr coming from process. | 
|  | _stderr = _StdStream(_Socket._readPipe().._owner = this); | 
|  | } | 
|  | if (_modeIsAttached(_mode)) { | 
|  | _exitHandler = _Socket._readPipe(); | 
|  | } | 
|  | } | 
|  |  | 
|  | _NativeSocket get _stdinNativeSocket => | 
|  | (_stdin!._sink as _Socket)._nativeSocket; | 
|  | _NativeSocket get _stdoutNativeSocket => | 
|  | (_stdout!._stream as _Socket)._nativeSocket; | 
|  | _NativeSocket get _stderrNativeSocket => | 
|  | (_stderr!._stream as _Socket)._nativeSocket; | 
|  |  | 
|  | static bool _modeIsAttached(ProcessStartMode mode) { | 
|  | return (mode == ProcessStartMode.normal) || | 
|  | (mode == ProcessStartMode.inheritStdio); | 
|  | } | 
|  |  | 
|  | static bool _modeHasStdio(ProcessStartMode mode) { | 
|  | return (mode == ProcessStartMode.normal) || | 
|  | (mode == ProcessStartMode.detachedWithStdio); | 
|  | } | 
|  |  | 
|  | static String _getShellCommand() { | 
|  | if (Platform.isWindows) { | 
|  | return 'cmd.exe'; | 
|  | } | 
|  | return '/bin/sh'; | 
|  | } | 
|  |  | 
|  | static List<String> _getShellArguments( | 
|  | String executable, | 
|  | List<String> arguments, | 
|  | ) { | 
|  | List<String> shellArguments = []; | 
|  | if (Platform.isWindows) { | 
|  | shellArguments.add('/c'); | 
|  | shellArguments.add(executable); | 
|  | for (var arg in arguments) { | 
|  | shellArguments.add(arg); | 
|  | } | 
|  | } else { | 
|  | var commandLine = StringBuffer(); | 
|  | executable = executable.replaceAll("'", "'\"'\"'"); | 
|  | commandLine.write("'$executable'"); | 
|  | shellArguments.add("-c"); | 
|  | for (var arg in arguments) { | 
|  | arg = arg.replaceAll("'", "'\"'\"'"); | 
|  | commandLine.write(" '$arg'"); | 
|  | } | 
|  | shellArguments.add(commandLine.toString()); | 
|  | } | 
|  | return shellArguments; | 
|  | } | 
|  |  | 
|  | String _windowsArgumentEscape(String argument) { | 
|  | if (argument.isEmpty) { | 
|  | return '""'; | 
|  | } | 
|  | var result = argument; | 
|  | if (argument.contains('\t') || | 
|  | argument.contains(' ') || | 
|  | argument.contains('"')) { | 
|  | // Produce something that the C runtime on Windows will parse | 
|  | // back as this string. | 
|  |  | 
|  | // Replace any number of '\' followed by '"' with | 
|  | // twice as many '\' followed by '\"'. | 
|  | var backslash = '\\'.codeUnitAt(0); | 
|  | var sb = StringBuffer(); | 
|  | var nextPos = 0; | 
|  | var quotePos = argument.indexOf('"', nextPos); | 
|  | while (quotePos != -1) { | 
|  | var numBackslash = 0; | 
|  | var pos = quotePos - 1; | 
|  | while (pos >= 0 && argument.codeUnitAt(pos) == backslash) { | 
|  | numBackslash++; | 
|  | pos--; | 
|  | } | 
|  | sb.write(argument.substring(nextPos, quotePos - numBackslash)); | 
|  | for (var i = 0; i < numBackslash; i++) { | 
|  | sb.write(r'\\'); | 
|  | } | 
|  | sb.write(r'\"'); | 
|  | nextPos = quotePos + 1; | 
|  | quotePos = argument.indexOf('"', nextPos); | 
|  | } | 
|  | sb.write(argument.substring(nextPos, argument.length)); | 
|  | result = sb.toString(); | 
|  |  | 
|  | // Add '"' at the beginning and end and replace all '\' at | 
|  | // the end with two '\'. | 
|  | sb = StringBuffer('"'); | 
|  | sb.write(result); | 
|  | nextPos = argument.length - 1; | 
|  | while (argument.codeUnitAt(nextPos) == backslash) { | 
|  | sb.write('\\'); | 
|  | nextPos--; | 
|  | } | 
|  | sb.write('"'); | 
|  | result = sb.toString(); | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int _intFromBytes(List<int> bytes, int offset) { | 
|  | return (bytes[offset] + | 
|  | (bytes[offset + 1] << 8) + | 
|  | (bytes[offset + 2] << 16) + | 
|  | (bytes[offset + 3] << 24)); | 
|  | } | 
|  |  | 
|  | Future<Process> _start() { | 
|  | var completer = Completer<Process>(); | 
|  | var stackTrace = StackTrace.current; | 
|  | if (_modeIsAttached(_mode)) { | 
|  | _exitCode = Completer<int>(); | 
|  | } | 
|  | // TODO(ager): Make the actual process starting really async instead of | 
|  | // simulating it with a timer. | 
|  | Timer.run(() { | 
|  | var status = _ProcessStartStatus(); | 
|  | bool success = _startNative( | 
|  | _Namespace._namespace, | 
|  | _path, | 
|  | _arguments, | 
|  | _workingDirectory, | 
|  | _environment, | 
|  | _mode._mode, | 
|  | _modeHasStdio(_mode) ? _stdinNativeSocket : null, | 
|  | _modeHasStdio(_mode) ? _stdoutNativeSocket : null, | 
|  | _modeHasStdio(_mode) ? _stderrNativeSocket : null, | 
|  | _modeIsAttached(_mode) ? _exitHandler._nativeSocket : null, | 
|  | status, | 
|  | ); | 
|  | if (!success) { | 
|  | completer.completeError( | 
|  | ProcessException( | 
|  | _path, | 
|  | _arguments, | 
|  | status._errorMessage!, | 
|  | status._errorCode!, | 
|  | ), | 
|  | stackTrace, | 
|  | ); | 
|  | return; | 
|  | } | 
|  |  | 
|  | _started = true; | 
|  | final resourceInfo = _SpawnedProcessResourceInfo(this); | 
|  |  | 
|  | // Setup an exit handler to handle internal cleanup and possible | 
|  | // callback when a process terminates. | 
|  | if (_modeIsAttached(_mode)) { | 
|  | int exitDataRead = 0; | 
|  | final int EXIT_DATA_SIZE = 8; | 
|  | List<int> exitDataBuffer = List<int>.filled(EXIT_DATA_SIZE, 0); | 
|  | _exitHandler.listen((data) { | 
|  | int exitCode(List<int> ints) { | 
|  | var code = _intFromBytes(ints, 0); | 
|  | var negative = _intFromBytes(ints, 4); | 
|  | assert(negative == 0 || negative == 1); | 
|  | return (negative == 0) ? code : -code; | 
|  | } | 
|  |  | 
|  | void handleExit() { | 
|  | _ended = true; | 
|  | _exitCode!.complete(exitCode(exitDataBuffer)); | 
|  | // Kill stdin, helping hand if the user forgot to do it. | 
|  | if (_modeHasStdio(_mode)) { | 
|  | (_stdin!._sink as _Socket).destroy(); | 
|  | } | 
|  | resourceInfo.stopped(); | 
|  | } | 
|  |  | 
|  | exitDataBuffer.setRange( | 
|  | exitDataRead, | 
|  | exitDataRead + data.length, | 
|  | data, | 
|  | ); | 
|  | exitDataRead += data.length; | 
|  | if (exitDataRead == EXIT_DATA_SIZE) { | 
|  | handleExit(); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | completer.complete(this); | 
|  | }); | 
|  | return completer.future; | 
|  | } | 
|  |  | 
|  | ProcessResult _runAndWait( | 
|  | Encoding? stdoutEncoding, | 
|  | Encoding? stderrEncoding, | 
|  | ) { | 
|  | var status = _ProcessStartStatus(); | 
|  | _exitCode = Completer<int>(); | 
|  | bool success = _startNative( | 
|  | _Namespace._namespace, | 
|  | _path, | 
|  | _arguments, | 
|  | _workingDirectory, | 
|  | _environment, | 
|  | ProcessStartMode.normal._mode, | 
|  | _stdinNativeSocket, | 
|  | _stdoutNativeSocket, | 
|  | _stderrNativeSocket, | 
|  | _exitHandler._nativeSocket, | 
|  | status, | 
|  | ); | 
|  | if (!success) { | 
|  | throw ProcessException( | 
|  | _path, | 
|  | _arguments, | 
|  | status._errorMessage!, | 
|  | status._errorCode!, | 
|  | ); | 
|  | } | 
|  |  | 
|  | final resourceInfo = _SpawnedProcessResourceInfo(this); | 
|  |  | 
|  | var result = _wait( | 
|  | _stdinNativeSocket, | 
|  | _stdoutNativeSocket, | 
|  | _stderrNativeSocket, | 
|  | _exitHandler._nativeSocket, | 
|  | ); | 
|  |  | 
|  | getOutput(output, encoding) { | 
|  | if (encoding == null) return output; | 
|  | return encoding.decode(output); | 
|  | } | 
|  |  | 
|  | resourceInfo.stopped(); | 
|  |  | 
|  | return ProcessResult( | 
|  | result[0], | 
|  | result[1], | 
|  | getOutput(result[2], stdoutEncoding), | 
|  | getOutput(result[3], stderrEncoding), | 
|  | ); | 
|  | } | 
|  |  | 
|  | @pragma("vm:external-name", "Process_Start") | 
|  | external bool _startNative( | 
|  | _Namespace namespace, | 
|  | String path, | 
|  | List<String> arguments, | 
|  | String? workingDirectory, | 
|  | List<String> environment, | 
|  | int mode, | 
|  | _NativeSocket? stdin, | 
|  | _NativeSocket? stdout, | 
|  | _NativeSocket? stderr, | 
|  | _NativeSocket? exitHandler, | 
|  | _ProcessStartStatus status, | 
|  | ); | 
|  |  | 
|  | @pragma("vm:external-name", "Process_Wait") | 
|  | external _wait( | 
|  | _NativeSocket? stdin, | 
|  | _NativeSocket? stdout, | 
|  | _NativeSocket? stderr, | 
|  | _NativeSocket exitHandler, | 
|  | ); | 
|  |  | 
|  | Stream<List<int>> get stdout => | 
|  | _stdout ?? (throw StateError("stdio is not connected")); | 
|  |  | 
|  | Stream<List<int>> get stderr => | 
|  | _stderr ?? (throw StateError("stdio is not connected")); | 
|  |  | 
|  | IOSink get stdin => _stdin ?? (throw StateError("stdio is not connected")); | 
|  |  | 
|  | Future<int> get exitCode => | 
|  | _exitCode?.future ?? (throw StateError("Process is detached")); | 
|  |  | 
|  | bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { | 
|  | // TODO(40614): Remove once non-nullability is sound. | 
|  | ArgumentError.checkNotNull(kill, "kill"); | 
|  | assert(_started); | 
|  | if (_ended) return false; | 
|  | return _ProcessUtils._killPid(pid, signal.signalNumber); | 
|  | } | 
|  |  | 
|  | int get pid => _ProcessUtils._pid(this); | 
|  |  | 
|  | late String _path; | 
|  | late List<String> _arguments; | 
|  | String? _workingDirectory; | 
|  | late List<String> _environment; | 
|  | final ProcessStartMode _mode; | 
|  | // Private methods of Socket are used by _in, _out, and _err. | 
|  | _StdSink? _stdin; | 
|  | _StdStream? _stdout; | 
|  | _StdStream? _stderr; | 
|  | late _Socket _exitHandler; | 
|  | bool _ended = false; | 
|  | bool _started = false; | 
|  | Completer<int>? _exitCode; | 
|  | } | 
|  |  | 
|  | // _NonInteractiveProcess is a wrapper around an interactive process | 
|  | // that buffers output so it can be delivered when the process exits. | 
|  | // _NonInteractiveProcess is used to implement the Process.run | 
|  | // method. | 
|  | Future<ProcessResult> _runNonInteractiveProcess( | 
|  | String path, | 
|  | List<String> arguments, | 
|  | String? workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment, | 
|  | bool runInShell, | 
|  | Encoding? stdoutEncoding, | 
|  | Encoding? stderrEncoding, | 
|  | ) { | 
|  | // Start the underlying process. | 
|  | return Process.start( | 
|  | path, | 
|  | arguments, | 
|  | workingDirectory: workingDirectory, | 
|  | environment: environment, | 
|  | includeParentEnvironment: includeParentEnvironment, | 
|  | runInShell: runInShell, | 
|  | ).then((Process p) { | 
|  | int pid = p.pid; | 
|  |  | 
|  | // Make sure the process stdin is closed. | 
|  | p.stdin.close(); | 
|  |  | 
|  | // Setup stdout and stderr handling. | 
|  | Future foldStream(Stream<List<int>> stream, Encoding? encoding) { | 
|  | if (encoding == null) { | 
|  | return stream | 
|  | .fold<BytesBuilder>( | 
|  | BytesBuilder(), | 
|  | (builder, data) => builder..add(data), | 
|  | ) | 
|  | .then((builder) => builder.takeBytes()); | 
|  | } else { | 
|  | return stream | 
|  | .transform(encoding.decoder) | 
|  | .fold<StringBuffer>(StringBuffer(), (buf, data) { | 
|  | buf.write(data); | 
|  | return buf; | 
|  | }) | 
|  | .then((sb) => sb.toString()); | 
|  | } | 
|  | } | 
|  |  | 
|  | Future stdout = foldStream(p.stdout, stdoutEncoding); | 
|  | Future stderr = foldStream(p.stderr, stderrEncoding); | 
|  |  | 
|  | return Future.wait([p.exitCode, stdout, stderr]).then((result) { | 
|  | return ProcessResult(pid, result[0], result[1], result[2]); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | ProcessResult _runNonInteractiveProcessSync( | 
|  | String executable, | 
|  | List<String> arguments, | 
|  | String? workingDirectory, | 
|  | Map<String, String>? environment, | 
|  | bool includeParentEnvironment, | 
|  | bool runInShell, | 
|  | Encoding? stdoutEncoding, | 
|  | Encoding? stderrEncoding, | 
|  | ) { | 
|  | var process = _ProcessImpl( | 
|  | executable, | 
|  | arguments, | 
|  | workingDirectory, | 
|  | environment, | 
|  | includeParentEnvironment, | 
|  | runInShell, | 
|  | ProcessStartMode.normal, | 
|  | ); | 
|  | return process._runAndWait(stdoutEncoding, stderrEncoding); | 
|  | } |