| Index: runtime/bin/process_impl.dart
|
| diff --git a/runtime/bin/process_impl.dart b/runtime/bin/process_impl.dart
|
| deleted file mode 100644
|
| index ab8dfd080f98c11c7a261e9f6b8eaf6020d48d30..0000000000000000000000000000000000000000
|
| --- a/runtime/bin/process_impl.dart
|
| +++ /dev/null
|
| @@ -1,359 +0,0 @@
|
| -// Copyright (c) 2012, 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.
|
| -
|
| -_exit(int status) native "Exit";
|
| -
|
| -class _ProcessStartStatus {
|
| - int _errorCode; // Set to OS error code if process start failed.
|
| - String _errorMessage; // Set to OS error message if process start failed.
|
| -}
|
| -
|
| -
|
| -class _Process extends NativeFieldWrapperClass1 implements Process {
|
| - static Future<ProcessResult> run(String path,
|
| - List<String> arguments,
|
| - [ProcessOptions options]) {
|
| - return new _NonInteractiveProcess(path, arguments, options)._result;
|
| - }
|
| -
|
| - static Future<Process> start(String path,
|
| - List<String> arguments,
|
| - ProcessOptions options) {
|
| - _Process process = new _Process(path, arguments, options);
|
| - return process._start();
|
| - }
|
| -
|
| - _Process(String path, List<String> arguments, ProcessOptions options) {
|
| - if (path is !String) {
|
| - throw new ArgumentError("Path is not a String: $path");
|
| - }
|
| - _path = path;
|
| -
|
| - if (arguments is !List) {
|
| - throw new ArgumentError("Arguments is not a List: $arguments");
|
| - }
|
| - int len = arguments.length;
|
| - _arguments = new List<String>(len);
|
| - for (int i = 0; i < len; i++) {
|
| - var arg = arguments[i];
|
| - if (arg is !String) {
|
| - throw new ArgumentError("Non-string argument: $arg");
|
| - }
|
| - _arguments[i] = arguments[i];
|
| - if (Platform.operatingSystem == 'windows') {
|
| - _arguments[i] = _windowsArgumentEscape(_arguments[i]);
|
| - }
|
| - }
|
| -
|
| - if (options !== null && options.workingDirectory !== null) {
|
| - _workingDirectory = options.workingDirectory;
|
| - if (_workingDirectory is !String) {
|
| - throw new ArgumentError(
|
| - "WorkingDirectory is not a String: $_workingDirectory");
|
| - }
|
| - }
|
| -
|
| - if (options !== null && options.environment !== null) {
|
| - var env = options.environment;
|
| - if (env is !Map) {
|
| - throw new ArgumentError("Environment is not a map: $env");
|
| - }
|
| - _environment = [];
|
| - env.forEach((key, value) {
|
| - if (key is !String || value is !String) {
|
| - throw new ArgumentError(
|
| - "Environment key or value is not a string: ($key, $value)");
|
| - }
|
| - _environment.add('$key=$value');
|
| - });
|
| - }
|
| -
|
| - _in = new _Socket._internalReadOnly(); // stdout coming from process.
|
| - _out = new _Socket._internalWriteOnly(); // stdin going to process.
|
| - _err = new _Socket._internalReadOnly(); // stderr coming from process.
|
| - _exitHandler = new _Socket._internalReadOnly();
|
| - _ended = false;
|
| - _started = false;
|
| - _onExit = null;
|
| - }
|
| -
|
| - String _windowsArgumentEscape(String argument) {
|
| - var result = argument;
|
| - if (argument.contains('\t') || 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 = '\\'.charCodeAt(0);
|
| - var sb = new StringBuffer();
|
| - var nextPos = 0;
|
| - var quotePos = argument.indexOf('"', nextPos);
|
| - while (quotePos != -1) {
|
| - var numBackslash = 0;
|
| - var pos = quotePos - 1;
|
| - while (pos >= 0 && argument.charCodeAt(pos) == backslash) {
|
| - numBackslash++;
|
| - pos--;
|
| - }
|
| - sb.add(argument.substring(nextPos, quotePos - numBackslash));
|
| - for (var i = 0; i < numBackslash; i++) {
|
| - sb.add(r'\\');
|
| - }
|
| - sb.add(r'\"');
|
| - nextPos = quotePos + 1;
|
| - quotePos = argument.indexOf('"', nextPos);
|
| - }
|
| - sb.add(argument.substring(nextPos, argument.length));
|
| - result = sb.toString();
|
| -
|
| - // Add '"' at the beginning and end and replace all '\' at
|
| - // the end with two '\'.
|
| - sb = new StringBuffer('"');
|
| - sb.add(result);
|
| - nextPos = argument.length - 1;
|
| - while (argument.charCodeAt(nextPos) == backslash) {
|
| - sb.add('\\');
|
| - nextPos--;
|
| - }
|
| - sb.add('"');
|
| - 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 = new Completer();
|
| - // TODO(ager): Make the actual process starting really async instead of
|
| - // simulating it with a timer.
|
| - new Timer(0, (_) {
|
| - var status = new _ProcessStartStatus();
|
| - bool success = _startNative(_path,
|
| - _arguments,
|
| - _workingDirectory,
|
| - _environment,
|
| - _in,
|
| - _out,
|
| - _err,
|
| - _exitHandler,
|
| - status);
|
| - if (!success) {
|
| - _in.close();
|
| - _out.close();
|
| - _err.close();
|
| - _exitHandler.close();
|
| - completer.completeException(
|
| - new ProcessException(status._errorMessage, status._errorCode));
|
| - return;
|
| - }
|
| - _started = true;
|
| -
|
| - _in._closed = false;
|
| - _out._closed = false;
|
| - _err._closed = false;
|
| - _exitHandler._closed = false;
|
| -
|
| - // Make sure to activate socket handlers now that the file
|
| - // descriptors have been set.
|
| - _in._activateHandlers();
|
| - _out._activateHandlers();
|
| - _err._activateHandlers();
|
| -
|
| - // Setup an exit handler to handle internal cleanup and possible
|
| - // callback when a process terminates.
|
| - int exitDataRead = 0;
|
| - final int EXIT_DATA_SIZE = 8;
|
| - List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE);
|
| - _exitHandler.inputStream.onData = () {
|
| -
|
| - 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;
|
| - if (_onExit !== null) {
|
| - _onExit(exitCode(exitDataBuffer));
|
| - }
|
| - _out.close();
|
| - }
|
| -
|
| - exitDataRead += _exitHandler.inputStream.readInto(
|
| - exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead);
|
| - if (exitDataRead == EXIT_DATA_SIZE) handleExit();
|
| - };
|
| -
|
| - completer.complete(this);
|
| - });
|
| - return completer.future;
|
| - }
|
| -
|
| - bool _startNative(String path,
|
| - List<String> arguments,
|
| - String workingDirectory,
|
| - List<String> environment,
|
| - Socket input,
|
| - Socket output,
|
| - Socket error,
|
| - Socket exitHandler,
|
| - _ProcessStartStatus status) native "Process_Start";
|
| -
|
| - InputStream get stdout {
|
| - return _in.inputStream;
|
| - }
|
| -
|
| - InputStream get stderr {
|
| - return _err.inputStream;
|
| - }
|
| -
|
| - OutputStream get stdin {
|
| - return _out.outputStream;
|
| - }
|
| -
|
| - bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) {
|
| - if (signal is! ProcessSignal) {
|
| - throw new ArgumentError(
|
| - "Argument 'signal' must be a ProcessSignal");
|
| - }
|
| - assert(_started);
|
| - if (_ended) return false;
|
| - return _kill(this, signal._signalNumber);
|
| - }
|
| -
|
| - bool _kill(Process p, int signal) native "Process_Kill";
|
| -
|
| - void set onExit(void callback(int exitCode)) {
|
| - if (_ended) {
|
| - throw new ProcessException("Process killed");
|
| - }
|
| - _onExit = callback;
|
| - }
|
| -
|
| - String _path;
|
| - List<String> _arguments;
|
| - String _workingDirectory;
|
| - List<String> _environment;
|
| - // Private methods of _Socket are used by _in, _out, and _err.
|
| - _Socket _in;
|
| - _Socket _out;
|
| - _Socket _err;
|
| - Socket _exitHandler;
|
| - bool _ended;
|
| - bool _started;
|
| - Function _onExit;
|
| -}
|
| -
|
| -
|
| -// _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.
|
| -class _NonInteractiveProcess {
|
| - _NonInteractiveProcess(String path,
|
| - List<String> arguments,
|
| - ProcessOptions options) {
|
| - _completer = new Completer<ProcessResult>();
|
| - // Extract output encoding options and verify arguments.
|
| - var stdoutEncoding = Encoding.UTF_8;
|
| - var stderrEncoding = Encoding.UTF_8;
|
| - if (options !== null) {
|
| - if (options.stdoutEncoding !== null) {
|
| - stdoutEncoding = options.stdoutEncoding;
|
| - if (stdoutEncoding is !Encoding) {
|
| - throw new ArgumentError(
|
| - 'stdoutEncoding option is not an encoding: $stdoutEncoding');
|
| - }
|
| - }
|
| - if (options.stderrEncoding !== null) {
|
| - stderrEncoding = options.stderrEncoding;
|
| - if (stderrEncoding is !Encoding) {
|
| - throw new ArgumentError(
|
| - 'stderrEncoding option is not an encoding: $stderrEncoding');
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Start the underlying process.
|
| - var processFuture = new _Process(path, arguments, options)._start();
|
| -
|
| - processFuture.then((Process p) {
|
| - // Make sure the process stdin is closed.
|
| - p.stdin.close;
|
| -
|
| - // Setup process exit handling.
|
| - p.onExit = (exitCode) {
|
| - _exitCode = exitCode;
|
| - _checkDone();
|
| - };
|
| -
|
| - // Setup stdout handling.
|
| - _stdoutBuffer = new StringBuffer();
|
| - var stdoutStream = new StringInputStream(p.stdout, stdoutEncoding);
|
| - stdoutStream.onData = () {
|
| - var data = stdoutStream.read();
|
| - if (data != null) _stdoutBuffer.add(data);
|
| - };
|
| - stdoutStream.onClosed = () {
|
| - _stdoutClosed = true;
|
| - _checkDone();
|
| - };
|
| -
|
| - // Setup stderr handling.
|
| - _stderrBuffer = new StringBuffer();
|
| - var stderrStream = new StringInputStream(p.stderr, stderrEncoding);
|
| - stderrStream.onData = () {
|
| - var data = stderrStream.read();
|
| - if (data != null) _stderrBuffer.add(data);
|
| - };
|
| - stderrStream.onClosed = () {
|
| - _stderrClosed = true;
|
| - _checkDone();
|
| - };
|
| - });
|
| -
|
| - processFuture.handleException((error) {
|
| - _completer.completeException(error);
|
| - return true;
|
| - });
|
| - }
|
| -
|
| - void _checkDone() {
|
| - if (_exitCode != null && _stderrClosed && _stdoutClosed) {
|
| - _completer.complete(new _ProcessResult(_exitCode,
|
| - _stdoutBuffer.toString(),
|
| - _stderrBuffer.toString()));
|
| - }
|
| - }
|
| -
|
| - Future<ProcessResult> get _result => _completer.future;
|
| -
|
| - Completer<ProcessResult> _completer;
|
| - StringBuffer _stdoutBuffer;
|
| - StringBuffer _stderrBuffer;
|
| - int _exitCode;
|
| - bool _stdoutClosed = false;
|
| - bool _stderrClosed = false;
|
| -}
|
| -
|
| -
|
| -class _ProcessResult implements ProcessResult {
|
| - const _ProcessResult(int this.exitCode,
|
| - String this.stdout,
|
| - String this.stderr);
|
| -
|
| - final int exitCode;
|
| - final String stdout;
|
| - final String stderr;
|
| -}
|
|
|