]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use WriteConsole to implement print and panic on windows
authorDaniel Theophanes <kardianos@gmail.com>
Fri, 6 Nov 2015 06:47:45 +0000 (22:47 -0800)
committerRuss Cox <rsc@golang.org>
Thu, 12 Nov 2015 20:00:34 +0000 (20:00 +0000)
Fixes #7864

Change-Id: Id13369352aeccac8387876f0b911e383c543c28e
Reviewed-on: https://go-review.googlesource.com/16714
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Russ Cox <rsc@golang.org>
src/runtime/os1_windows.go

index feece9fcc28b47a876093b9bac7ae39afeffb11b..f5301db1a44f9a0a48c76c1eecd4ae7f08b3a0a7 100644 (file)
@@ -21,6 +21,7 @@ import (
 //go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
 //go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
 //go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
 //go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll"
 //go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll"
 //go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll"
@@ -44,6 +45,7 @@ import (
 //go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
 //go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult%5 "ws2_32.dll"
 //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll"
+//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
 //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll"
 //go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod%1 "winmm.dll"
 
@@ -63,6 +65,7 @@ var (
        _DuplicateHandle,
        _ExitProcess,
        _FreeEnvironmentStringsW,
+       _GetConsoleMode,
        _GetEnvironmentStringsW,
        _GetProcAddress,
        _GetProcessAffinityMask,
@@ -86,6 +89,7 @@ var (
        _VirtualFree,
        _WSAGetOverlappedResult,
        _WaitForSingleObject,
+       _WriteConsoleW,
        _WriteFile,
        _timeBeginPeriod stdFunction
 
@@ -254,11 +258,90 @@ func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
                // assume fd is real windows handle.
                handle = fd
        }
+       isASCII := true
+       b := (*[1 << 30]byte)(buf)[:n]
+       for _, x := range b {
+               if x >= 0x80 {
+                       isASCII = false
+                       break
+               }
+       }
+
+       if !isASCII {
+               var m uint32
+               isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0
+               // If this is a console output, various non-unicode code pages can be in use.
+               // Use the dedicated WriteConsole call to ensure unicode is printed correctly.
+               if isConsole {
+                       return int32(writeConsole(handle, buf, n))
+               }
+       }
        var written uint32
        stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0)
        return int32(written)
 }
 
+var (
+       utf16ConsoleBack     [1000]uint16
+       utf16ConsoleBackLock mutex
+)
+
+// writeConsole writes bufLen bytes from buf to the console File.
+// It returns the number of bytes written.
+func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
+       const surr2 = (surrogateMin + surrogateMax + 1) / 2
+
+       // Do not use defer for unlock. May cause issues when printing a panic.
+       lock(&utf16ConsoleBackLock)
+
+       b := (*[1 << 30]byte)(buf)[:bufLen]
+       s := *(*string)(unsafe.Pointer(&b))
+
+       utf16tmp := utf16ConsoleBack[:]
+
+       total := len(s)
+       w := 0
+       for len(s) > 0 {
+               if w >= len(utf16tmp)-2 {
+                       writeConsoleUTF16(handle, utf16tmp[:w])
+                       w = 0
+               }
+               r, n := charntorune(s)
+               s = s[n:]
+               if r < 0x10000 {
+                       utf16tmp[w] = uint16(r)
+                       w++
+               } else {
+                       r -= 0x10000
+                       utf16tmp[w] = surrogateMin + uint16(r>>10)&0x3ff
+                       utf16tmp[w+1] = surr2 + uint16(r)&0x3ff
+                       w += 2
+               }
+       }
+       writeConsoleUTF16(handle, utf16tmp[:w])
+       unlock(&utf16ConsoleBackLock)
+       return total
+}
+
+// writeConsoleUTF16 is the dedicated windows calls that correctly prints
+// to the console regardless of the current code page. Input is utf-16 code points.
+// The handle must be a console handle.
+func writeConsoleUTF16(handle uintptr, b []uint16) {
+       l := uint32(len(b))
+       if l == 0 {
+               return
+       }
+       var written uint32
+       stdcall5(_WriteConsoleW,
+               handle,
+               uintptr(unsafe.Pointer(&b[0])),
+               uintptr(l),
+               uintptr(unsafe.Pointer(&written)),
+               0,
+       )
+       return
+}
+
 //go:nosplit
 func semasleep(ns int64) int32 {
        // store ms in ns to save stack space