[dart:io] Adds Platform.ansiSupported

This is so that flutter_tool can determine whether it can print
ANSI codes to the terminal on windows.

fixes #28614,#28984

R=asiva@google.com

Review-Url: https://codereview.chromium.org/2739683005 .
This commit is contained in:
Zach Anderson 2017-03-15 08:51:50 -07:00
parent ae8e6705c0
commit a8bf498e56
14 changed files with 142 additions and 23 deletions

View file

@ -92,6 +92,7 @@ namespace bin {
V(Platform_Environment, 0) \
V(Platform_ExecutableArguments, 0) \
V(Platform_GetVersion, 0) \
V(Platform_AnsiSupported, 0) \
V(Process_Start, 11) \
V(Process_Wait, 5) \
V(Process_KillPid, 2) \

View file

@ -109,6 +109,11 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, Dart_NewStringFromCString(Dart_VersionString()));
}
void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) {
Dart_SetBooleanReturnValue(args, Platform::AnsiSupported());
}
} // namespace bin
} // namespace dart

View file

@ -83,6 +83,8 @@ class Platform {
static int GetScriptIndex() { return script_index_; }
static char** GetArgv() { return argv_; }
static bool AnsiSupported();
static DART_NORETURN void Exit(int exit_code);
private:

View file

@ -85,6 +85,11 @@ const char* Platform::ResolveExecutablePath() {
}
bool Platform::AnsiSupported() {
return true;
}
void Platform::Exit(int exit_code) {
exit(exit_code);
}

View file

@ -109,6 +109,11 @@ const char* Platform::ResolveExecutablePath() {
}
bool Platform::AnsiSupported() {
return true;
}
void Platform::Exit(int exit_code) {
exit(exit_code);
}

View file

@ -110,6 +110,11 @@ const char* Platform::ResolveExecutablePath() {
}
bool Platform::AnsiSupported() {
return true;
}
void Platform::Exit(int exit_code) {
exit(exit_code);
}

View file

@ -154,6 +154,11 @@ const char* Platform::ResolveExecutablePath() {
}
bool Platform::AnsiSupported() {
return true;
}
void Platform::Exit(int exit_code) {
exit(exit_code);
}

View file

@ -21,6 +21,8 @@
native "Platform_ExecutableArguments";
@patch static String _version()
native "Platform_GetVersion";
@patch static bool _ansiSupported()
native "Platform_AnsiSupported";
@patch static String _packageRoot()
=> VMLibraryHooks.packageRootString;

View file

@ -64,6 +64,12 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) {
"Platform is not supported on this platform"));
}
void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"Platform is not supported on this platform"));
}
} // namespace bin
} // namespace dart

View file

@ -46,6 +46,7 @@ class PlatformWin {
static void InitOnce() {
platform_win_mutex_ = new Mutex();
saved_output_cp_ = -1;
saved_input_cp_ = -1;
// Set up a no-op handler so that CRT functions return an error instead of
// hitting an assertion failure.
// See: https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
@ -61,50 +62,89 @@ class PlatformWin {
static void SaveAndConfigureConsole() {
MutexLocker ml(platform_win_mutex_);
// Set both the input and output code pages to UTF8.
ASSERT(saved_output_cp_ == -1);
ASSERT(saved_input_cp_ == -1);
saved_output_cp_ = GetConsoleOutputCP();
saved_input_cp_ = GetConsoleCP();
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
ansi_supported_ = true;
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
if ((out != INVALID_HANDLE_VALUE) &&
GetConsoleMode(out, &saved_console_out_mode_)) {
const DWORD request =
saved_console_out_mode_ | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(out, request);
DWORD out_mode;
if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) {
const DWORD request = out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
ansi_supported_ = ansi_supported_ && SetConsoleMode(out, request);
} else {
ansi_supported_ = false;
}
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
if ((in != INVALID_HANDLE_VALUE) &&
GetConsoleMode(in, &saved_console_in_mode_)) {
const DWORD request =
saved_console_in_mode_ | ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode(in, request);
DWORD in_mode;
if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) {
const DWORD request = in_mode | ENABLE_VIRTUAL_TERMINAL_INPUT;
ansi_supported_ = ansi_supported_ && SetConsoleMode(in, request);
} else {
ansi_supported_ = false;
}
}
static void RestoreConsole() {
MutexLocker ml(platform_win_mutex_);
// STD_OUTPUT_HANDLE and STD_INPUT_HANDLE may have been closed or
// redirected. Therefore, we explicitly open the CONOUT$ and CONIN$
// devices, so that we can be sure that we are really unsetting
// ENABLE_VIRTUAL_TERMINAL_PROCESSING and ENABLE_VIRTUAL_TERMINAL_INPUT
// respectively.
HANDLE out;
{
Utf8ToWideScope conin("CONOUT$");
out = CreateFileW(conin.wide(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (out != INVALID_HANDLE_VALUE) {
SetStdHandle(STD_OUTPUT_HANDLE, out);
}
}
DWORD out_mode;
if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) {
DWORD request = out_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode(out, request);
}
HANDLE in;
{
Utf8ToWideScope conin("CONIN$");
in = CreateFileW(conin.wide(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (in != INVALID_HANDLE_VALUE) {
SetStdHandle(STD_INPUT_HANDLE, in);
}
}
DWORD in_mode;
if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) {
DWORD request = in_mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT;
SetConsoleMode(in, request);
}
if (saved_output_cp_ != -1) {
SetConsoleOutputCP(saved_output_cp_);
saved_output_cp_ = -1;
}
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
if (out != INVALID_HANDLE_VALUE) {
SetConsoleMode(out, saved_console_out_mode_);
saved_console_out_mode_ = 0;
}
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
if (in != INVALID_HANDLE_VALUE) {
SetConsoleMode(in, saved_console_in_mode_);
saved_console_in_mode_ = 0;
if (saved_input_cp_ != -1) {
SetConsoleCP(saved_input_cp_);
saved_input_cp_ = -1;
}
}
static bool ansi_supported() { return ansi_supported_; }
private:
static Mutex* platform_win_mutex_;
static int saved_output_cp_;
static DWORD saved_console_out_mode_;
static DWORD saved_console_in_mode_;
static int saved_input_cp_;
static bool ansi_supported_;
static void InvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
@ -120,9 +160,9 @@ class PlatformWin {
};
int PlatformWin::saved_output_cp_ = -1;
DWORD PlatformWin::saved_console_out_mode_ = 0;
DWORD PlatformWin::saved_console_in_mode_ = 0;
int PlatformWin::saved_input_cp_ = -1;
Mutex* PlatformWin::platform_win_mutex_ = NULL;
bool PlatformWin::ansi_supported_ = false;
bool Platform::Initialize() {
PlatformWin::InitOnce();
@ -219,6 +259,11 @@ const char* Platform::ResolveExecutablePath() {
}
bool Platform::AnsiSupported() {
return PlatformWin::ansi_supported();
}
void Platform::Exit(int exit_code) {
// TODO(zra): Remove once VM shuts down cleanly.
::dart::private_flag_windows_run_tls_destructors = false;

View file

@ -218,6 +218,10 @@ class _Platform {
static String _version() {
throw new UnsupportedError("Platform._version");
}
@patch
static _ansiSupported() {
throw new UnsupportedError("Platform._ansiSupported");
}
}
@patch

View file

@ -71,6 +71,7 @@ class Platform {
static final _operatingSystem = _Platform.operatingSystem;
static final _localHostname = _Platform.localHostname;
static final _version = _Platform.version;
static final _ansiSupported = _Platform.ansiSupported;
/**
* Get the number of processors of the machine.
@ -124,6 +125,14 @@ class Platform {
*/
static final bool isFuchsia = (_operatingSystem == "fuchsia");
/**
* When stdio is connected to a terminal, whether ANSI codes are supported.
*
* This value is hard-coded to `true`, except on Windows where only more
* recent versions of Windows 10 support the codes.
*/
static final bool ansiSupported = _ansiSupported;
/**
* Get the environment for this process.
*

View file

@ -11,6 +11,7 @@ class _Platform {
external static _localHostname();
external static _executable();
external static _resolvedExecutable();
external static bool _ansiSupported();
/**
* Retrieve the entries of the process environment.
@ -45,6 +46,7 @@ class _Platform {
static int get numberOfProcessors => _numberOfProcessors();
static String get pathSeparator => _pathSeparator();
static String get operatingSystem => _operatingSystem();
static bool get ansiSupported => _ansiSupported();
static Uri script;
static String get localHostname {

View file

@ -0,0 +1,23 @@
// Copyright (c) 2017, 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";
main() {
try {
Platform.ansiSupported;
} catch (e, s) {
Expect.fail("Platform.ansiSupported threw: $e\n$s\n");
}
Expect.isNotNull(Platform.ansiSupported);
Expect.isTrue(Platform.ansiSupported is bool);
if (stdout.hasTerminal && Platform.ansiSupported) {
stdout.writeln('\x1b[31mThis text has a red foreground using SGR.31.');
stdout.writeln('\x1b[39mThis text has restored the foreground color.');
} else {
stdout.writeln('ANSI codes not supported on this platform');
}
}