diff --git a/pkg/analysis_server/lib/src/server/stdio_server.dart b/pkg/analysis_server/lib/src/server/stdio_server.dart index 6eab23018ff..d28dbde86db 100644 --- a/pkg/analysis_server/lib/src/server/stdio_server.dart +++ b/pkg/analysis_server/lib/src/server/stdio_server.dart @@ -36,7 +36,7 @@ class StdioAnalysisServer { Future serveStdio() { ByteStreamServerChannel serverChannel = new ByteStreamServerChannel( stdin, - stdout.nonBlocking, + stdout, socketServer.instrumentationService); socketServer.createAnalysisServer(serverChannel); return serverChannel.closed; diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc index b2ec83e909f..b8050cfb4ee 100644 --- a/runtime/bin/io_natives.cc +++ b/runtime/bin/io_natives.cc @@ -90,7 +90,7 @@ namespace bin { V(Stdin_SetEchoMode, 1) \ V(Stdin_GetLineMode, 0) \ V(Stdin_SetLineMode, 1) \ - V(Stdout_GetTerminalSize, 1) \ + V(Stdout_GetTerminalSize, 0) \ V(StringToSystemEncoding, 1) \ V(SystemEncodingToString, 1) diff --git a/runtime/bin/stdio.cc b/runtime/bin/stdio.cc index 2ed93fb51a3..ab3b8a2f448 100644 --- a/runtime/bin/stdio.cc +++ b/runtime/bin/stdio.cc @@ -45,13 +45,8 @@ void FUNCTION_NAME(Stdin_SetLineMode)(Dart_NativeArguments args) { void FUNCTION_NAME(Stdout_GetTerminalSize)(Dart_NativeArguments args) { - intptr_t fd = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0)); - if (fd != 1 && fd != 2) { - Dart_PropagateError(Dart_NewApiError("Terminal fd must be 1 or 2")); - } - int size[2]; - if (Stdout::GetTerminalSize(fd, size)) { + if (Stdout::GetTerminalSize(size)) { Dart_Handle list = Dart_NewList(2); Dart_ListSetAt(list, 0, Dart_NewInteger(size[0])); Dart_ListSetAt(list, 1, Dart_NewInteger(size[1])); diff --git a/runtime/bin/stdio.h b/runtime/bin/stdio.h index 99e2cd587d4..646629d299a 100644 --- a/runtime/bin/stdio.h +++ b/runtime/bin/stdio.h @@ -32,7 +32,7 @@ class Stdin { class Stdout { public: - static bool GetTerminalSize(intptr_t fd, int size[2]); + static bool GetTerminalSize(int size[2]); private: DISALLOW_ALLOCATION(); @@ -43,3 +43,4 @@ class Stdout { } // namespace dart #endif // BIN_STDIO_H_ + diff --git a/runtime/bin/stdio_linux.cc b/runtime/bin/stdio_linux.cc index c01a7718067..dee8c389e36 100644 --- a/runtime/bin/stdio_linux.cc +++ b/runtime/bin/stdio_linux.cc @@ -65,9 +65,9 @@ void Stdin::SetLineMode(bool enabled) { } -bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { +bool Stdout::GetTerminalSize(int size[2]) { struct winsize w; - if (NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)) == 0 && + if (NO_RETRY_EXPECTED(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w)) == 0 && (w.ws_col != 0 || w.ws_row != 0)) { size[0] = w.ws_col; size[1] = w.ws_row; @@ -80,3 +80,4 @@ bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { } // namespace dart #endif // defined(TARGET_OS_LINUX) + diff --git a/runtime/bin/stdio_macos.cc b/runtime/bin/stdio_macos.cc index 822e445c69b..be736bd0d0a 100644 --- a/runtime/bin/stdio_macos.cc +++ b/runtime/bin/stdio_macos.cc @@ -65,9 +65,9 @@ void Stdin::SetLineMode(bool enabled) { } -bool Stdout::GetTerminalSize(int fd, int size[2]) { +bool Stdout::GetTerminalSize(int size[2]) { struct winsize w; - if (NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w) == 0) && + if (NO_RETRY_EXPECTED(ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) && (w.ws_col != 0 || w.ws_row != 0)) { size[0] = w.ws_col; size[1] = w.ws_row; diff --git a/runtime/bin/stdio_patch.dart b/runtime/bin/stdio_patch.dart index ffc47031ba0..d400306afac 100644 --- a/runtime/bin/stdio_patch.dart +++ b/runtime/bin/stdio_patch.dart @@ -17,13 +17,20 @@ patch class _StdIOUtils { } static _getStdioOutputStream(int fd) { + wrap(sink) { + if (fd == 1) { + return new Stdout._(sink); + } else { + return new _StdSink(sink); + } + } assert(fd == 1 || fd == 2); switch (_getStdioHandleType(fd)) { case _STDIO_HANDLE_TYPE_TERMINAL: case _STDIO_HANDLE_TYPE_PIPE: case _STDIO_HANDLE_TYPE_SOCKET: case _STDIO_HANDLE_TYPE_FILE: - return new Stdout._(new IOSink(new _StdConsumer(fd)), fd); + return wrap(new IOSink(new _FileStreamConsumer.fromStdio(fd))); default: throw new FileSystemException("Unsupported stdin type"); } @@ -56,27 +63,27 @@ patch class Stdin { } patch class Stdout { - /* patch */ bool _hasTerminal(int fd) { + /* patch */ bool get hasTerminal { try { - _terminalSize(fd); + _terminalSize; return true; } catch (_) { return false; } } - /* patch */ int _terminalColumns(int fd) => _terminalSize(fd)[0]; - /* patch */ int _terminalLines(int fd) => _terminalSize(fd)[1]; + /* patch */ int get terminalColumns => _terminalSize[0]; + /* patch */ int get terminalLines => _terminalSize[1]; - static List _terminalSize(int fd) { - var size = _getTerminalSize(fd); + static List get _terminalSize { + var size = _getTerminalSize(); if (size is! List) { throw new StdoutException("Could not get terminal size", size); } return size; } - static _getTerminalSize(int fd) native "Stdout_GetTerminalSize"; + static _getTerminalSize() native "Stdout_GetTerminalSize"; } diff --git a/runtime/bin/stdio_win.cc b/runtime/bin/stdio_win.cc index 86fd939f425..24ceb325cab 100644 --- a/runtime/bin/stdio_win.cc +++ b/runtime/bin/stdio_win.cc @@ -65,13 +65,8 @@ void Stdin::SetLineMode(bool enabled) { } -bool Stdout::GetTerminalSize(int fd, int size[2]) { - HANDLE h; - if (fd == 1) { - h = GetStdHandle(STD_OUTPUT_HANDLE); - } else { - h = GetStdHandle(STD_ERROR_HANDLE); - } +bool Stdout::GetTerminalSize(int size[2]) { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO info; if (!GetConsoleScreenBufferInfo(h, &info)) return false; size[0] = info.srWindow.Right - info.srWindow.Left + 1; diff --git a/sdk/lib/_internal/compiler/js_lib/io_patch.dart b/sdk/lib/_internal/compiler/js_lib/io_patch.dart index b6400f3c693..c2c218a0544 100644 --- a/sdk/lib/_internal/compiler/js_lib/io_patch.dart +++ b/sdk/lib/_internal/compiler/js_lib/io_patch.dart @@ -495,15 +495,15 @@ class Stdin { @patch class Stdout { @patch - bool _hasTerminal(int fd) { + bool get hasTerminal { throw new UnsupportedError("Stdout.hasTerminal"); } @patch - int _terminalColumns(int fd) { + int get terminalColumns { throw new UnsupportedError("Stdout.terminalColumns"); } @patch - int _terminalLines(int fd) { + int get terminalLines { throw new UnsupportedError("Stdout.terminalLines"); } } diff --git a/sdk/lib/io/console.dart b/sdk/lib/io/console.dart new file mode 100644 index 00000000000..c4ab7f27f0d --- /dev/null +++ b/sdk/lib/io/console.dart @@ -0,0 +1,97 @@ +// 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 dart.io; + +Console _console; + +Console get console { + if (_console == null) { + _console = new Console._(); + } + return _console; +} + +/** + * [Console] provides synchronous write access to stdout and stderr. + * + * The direct access to stdout and stderr through [stdout] and [stderr] + * provides non-blocking async operations. + */ +class Console { + final ConsoleSink _stdout; + final ConsoleSink _stderr; + + Console._() + : _stdout = new ConsoleSink._(1), + _stderr = new ConsoleSink._(2); + + /** + * Write to stdout. + */ + ConsoleSink get log => _stdout; + + /** + * Write to stderr. + */ + ConsoleSink get error => _stderr; +} + +/** + * Sink class used for console writing. + * + * This class has a call method so you can call it directly. Calling + * it directly is the same as calling its `writeln` method. + */ +class ConsoleSink implements Sink>, StringSink { + IOSink _sink; + + ConsoleSink._(int fd) { + _sink = new IOSink(new _ConsoleConsumer(fd)); + } + + void call([Object message = ""]) => _sink.writeln(message); + + void add(List data) => _sink.add(data); + + void close() {} + + void write(Object obj) => _sink.write(obj); + + void writeAll(Iterable objects, [String separator=""]) => + _sink.writeAll(objects, separator); + + void writeCharCode(int charCode) => _sink.writeCharCode(charCode); + + void writeln([Object obj=""]) => _sink.writeln(obj); +} + +class _ConsoleConsumer implements StreamConsumer> { + final _file; + + _ConsoleConsumer(int fd) : _file = _File._openStdioSync(fd); + + Future addStream(Stream> stream) { + var completer = new Completer(); + var sub; + sub = stream.listen( + (data) { + try { + _file.writeFromSync(data); + } catch (e, s) { + sub.cancel(); + completer.completeError(e, s); + } + }, + onError: completer.completeError, + onDone: completer.complete, + cancelOnError: true); + return completer.future; + } + + Future close() { + _file.closeSync(); + return new Future.value(); + } +} diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart index 43650355765..7bad1e02436 100644 --- a/sdk/lib/io/io.dart +++ b/sdk/lib/io/io.dart @@ -212,6 +212,7 @@ import 'dart:typed_data'; part 'bytes_builder.dart'; part 'common.dart'; +part 'console.dart'; part 'crypto.dart'; part 'data_transformer.dart'; part 'directory.dart'; diff --git a/sdk/lib/io/iolib_sources.gypi b/sdk/lib/io/iolib_sources.gypi index 5b821471236..21952474927 100644 --- a/sdk/lib/io/iolib_sources.gypi +++ b/sdk/lib/io/iolib_sources.gypi @@ -6,6 +6,7 @@ 'sources': [ 'bytes_builder.dart', 'common.dart', + 'console.dart', 'crypto.dart', 'data_transformer.dart', 'directory.dart', diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart index 9888d951688..e4bdf01ff05 100644 --- a/sdk/lib/io/stdio.dart +++ b/sdk/lib/io/stdio.dart @@ -149,29 +149,17 @@ class Stdin extends _StdStream implements Stream> { /** - * [Stdout] represents the [IOSink] for either `stdout` or `stderr`. + * [Stdout] exposes methods to query the terminal for properties. * - * It provides a *blocking* `IOSink`, so using this to write will block until - * the output is written. - * - * In some situations this blocking behavior is undesirable as it does not - * provide the same non-blocking behavior as dart:io in general exposes. - * Use the property [nonBlocking] to get an `IOSink` which has the non-blocking - * behavior. - * - * This class can also be used to check whether `stdout` or `stderr` is - * connected to a terminal and query some terminal properties. + * Use [hasTerminal] to test if there is a terminal associated to stdout. */ class Stdout extends _StdSink implements IOSink { - final int _fd; - IOSink _nonBlocking; - - Stdout._(IOSink sink, this._fd) : super(sink); + Stdout._(IOSink sink) : super(sink); /** * Returns true if there is a terminal attached to stdout. */ - bool get hasTerminal => _hasTerminal(_fd); + external bool get hasTerminal; /** * Get the number of columns of the terminal. @@ -179,7 +167,7 @@ class Stdout extends _StdSink implements IOSink { * If no terminal is attached to stdout, a [StdoutException] is thrown. See * [hasTerminal] for more info. */ - int get terminalColumns => _terminalColumns(_fd); + external int get terminalColumns; /** * Get the number of lines of the terminal. @@ -187,21 +175,7 @@ class Stdout extends _StdSink implements IOSink { * If no terminal is attached to stdout, a [StdoutException] is thrown. See * [hasTerminal] for more info. */ - int get terminalLines => _terminalLines(_fd); - - external bool _hasTerminal(int fd); - external int _terminalColumns(int fd); - external int _terminalLines(int fd); - - /** - * Get a non-blocking `IOSink`. - */ - IOSink get nonBlocking { - if (_nonBlocking == null) { - _nonBlocking = new IOSink(new _FileStreamConsumer.fromStdio(_fd)); - } - return _nonBlocking; - } + external int get terminalLines; } @@ -216,34 +190,6 @@ class StdoutException implements IOException { } } -class _StdConsumer implements StreamConsumer> { - final _file; - - _StdConsumer(int fd) : _file = _File._openStdioSync(fd); - - Future addStream(Stream> stream) { - var completer = new Completer(); - var sub; - sub = stream.listen( - (data) { - try { - _file.writeFromSync(data); - } catch (e, s) { - sub.cancel(); - completer.completeError(e, s); - } - }, - onError: completer.completeError, - onDone: completer.complete, - cancelOnError: true); - return completer.future; - } - - Future close() { - _file.closeSync(); - return new Future.value(); - } -} class _StdSink implements IOSink { final IOSink _sink; @@ -281,7 +227,7 @@ class StdioType { Stdin _stdin; Stdout _stdout; -Stdout _stderr; +IOSink _stderr; /// The standard input stream of data read by this program. @@ -303,7 +249,7 @@ Stdout get stdout { /// The standard output stream of errors written by this program. -Stdout get stderr { +IOSink get stderr { if (_stderr == null) { _stderr = _StdIOUtils._getStdioOutputStream(2); } diff --git a/tests/standalone/io/console_script.dart b/tests/standalone/io/console_script.dart new file mode 100644 index 00000000000..130031ec2d9 --- /dev/null +++ b/tests/standalone/io/console_script.dart @@ -0,0 +1,31 @@ +// Copyright (c) 2014, 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. + +import "dart:io"; + +class Message { + final message; + Message(this.message); + toString() => message; +} + +void test(ConsoleSink sink) { + sink.add([65, 66, 67]); + sink.write('DEF'); + sink.writeAll(['GH', 'I']); + sink.writeCharCode(74); + sink.writeln('KLM'); +} + +void main(List arguments) { + console.log('stdout'); + console.error('stderr'); + console.log(); + console.error(); + console.log(new Message('tuodts')); + console.error(new Message('rredts')); + test(console.log); + test(console.error); + exit(1); +} diff --git a/tests/standalone/io/stdio_nonblocking_test.dart b/tests/standalone/io/console_test.dart similarity index 85% rename from tests/standalone/io/stdio_nonblocking_test.dart rename to tests/standalone/io/console_test.dart index 0740dbe7267..30d1c9f31d9 100644 --- a/tests/standalone/io/stdio_nonblocking_test.dart +++ b/tests/standalone/io/console_test.dart @@ -1,4 +1,4 @@ -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// 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. @@ -8,8 +8,7 @@ import "dart:io"; import "package:expect/expect.dart"; void main() { - var script = - Platform.script.resolve("stdio_nonblocking_script.dart").toFilePath(); + var script = Platform.script.resolve("console_script.dart").toFilePath(); Process.run(Platform.executable, ['--checked', script], stdoutEncoding: ASCII, diff --git a/tests/standalone/io/stdio_nonblocking_script.dart b/tests/standalone/io/stdio_nonblocking_script.dart deleted file mode 100644 index 773f837cda1..00000000000 --- a/tests/standalone/io/stdio_nonblocking_script.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2015, 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. - -import "dart:async"; -import "dart:io"; - -class Message { - final message; - Message(this.message); - toString() => message; -} - -void test(IOSink sink) { - sink.add([65, 66, 67]); - sink.write('DEF'); - sink.writeAll(['GH', 'I']); - sink.writeCharCode(74); - sink.writeln('KLM'); -} - -void main(List arguments) { - stdout.nonBlocking.writeln('stdout'); - stderr.nonBlocking.writeln('stderr'); - stdout.nonBlocking.writeln(); - stderr.nonBlocking.writeln(); - stdout.nonBlocking.writeln(new Message('tuodts')); - stderr.nonBlocking.writeln(new Message('rredts')); - test(stdout.nonBlocking); - test(stderr.nonBlocking); - Future.wait([stdout.nonBlocking.close(), - stderr.nonBlocking.close()]) - .then((_) => exit(1)); -} diff --git a/tests/standalone/io/stdout_stderr_non_blocking_test.dart b/tests/standalone/io/stdout_stderr_non_blocking_test.dart deleted file mode 100644 index 061e8557d3d..00000000000 --- a/tests/standalone/io/stdout_stderr_non_blocking_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2014, 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. - -import "package:expect/expect.dart"; -import "dart:async"; -import "dart:convert"; -import "dart:io"; - -callIOSink(IOSink sink) { - // Call all methods on IOSink. - sink.encoding = ASCII; - Expect.equals(ASCII, sink.encoding); - sink.write("Hello\n"); - sink.writeln("Hello"); - sink.writeAll(["H", "e", "l", "lo\n"]); - sink.writeCharCode(72); - sink.add([101, 108, 108, 111, 10]); - - var controller = new StreamController(sync: true); - var future = sink.addStream(controller.stream); - controller.add([72, 101, 108]); - controller.add([108, 111, 10]); - controller.close(); - - future.then((_) { - controller = new StreamController(sync: true); - controller.stream.pipe(sink); - controller.add([72, 101, 108]); - controller.add([108, 111, 10]); - controller.close(); - }); -} - -main() { - callIOSink(stdout.nonBlocking); - stdout.nonBlocking.done.then((_) { - callIOSink(stderr.nonBlocking); - stderr.nonBlocking.done.then((_) { - stdout.close(); - stderr.close(); - }); - }); -} diff --git a/tests/standalone/io/stdout_stderr_terminal_test.dart b/tests/standalone/io/stdout_stderr_terminal_test.dart deleted file mode 100644 index 0d880787baf..00000000000 --- a/tests/standalone/io/stdout_stderr_terminal_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -import "dart:io"; - -import "package:expect/expect.dart"; - -void testTerminalSize(std) { - if (std.hasTerminal) { - Expect.notEquals(0, std.terminalColumns); - Expect.notEquals(0, std.terminalLines); - } else { - Expect.throws(() => std.terminalColumns, (e) => e is StdoutException); - Expect.throws(() => std.terminalLines, (e) => e is StdoutException); - } -} - - -void main() { - testTerminalSize(stdout); - testTerminalSize(stderr); -} diff --git a/tests/standalone/io/stdout_test.dart b/tests/standalone/io/stdout_test.dart new file mode 100644 index 00000000000..57a7e1c5ee2 --- /dev/null +++ b/tests/standalone/io/stdout_test.dart @@ -0,0 +1,22 @@ +// 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. + +import "dart:io"; + +import "package:expect/expect.dart"; + +void testTerminalSize() { + if (stdout.hasTerminal) { + Expect.notEquals(0, stdout.terminalColumns); + Expect.notEquals(0, stdout.terminalLines); + } else { + Expect.throws(() => stdout.terminalColumns, (e) => e is StdoutException); + Expect.throws(() => stdout.terminalLines, (e) => e is StdoutException); + } +} + + +void main() { + testTerminalSize(); +}