Skip to content

Centralise code to map between UTF-8 and UTF-16 on Windows. #62605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions include/swift/Runtime/Win32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===--- Win32.h - Win32 utility functions ----------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Utility functions that are specific to the Windows port.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_RUNTIME_WIN32_H
#define SWIFT_RUNTIME_WIN32_H

#ifdef _WIN32

#include "swift/shims/Visibility.h"

#include <functional>
#include <type_traits>

// For HANDLE
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>

#include <wchar.h>

/// Convert a wide string to UTF-8.
///
/// @param str The string to convert.
///
/// @returns The string, converted to UTF-8. The caller is responsible
/// for freeing this string with @c free() when done with it.
///
/// If @a str cannot be converted to UTF-8, @c nullptr is returned.
SWIFT_RUNTIME_STDLIB_SPI
char *_swift_win32_copyUTF8FromWide(const wchar_t *str);

/// Convert a UTF-8 string to a wide string.
///
/// @param str The string to convert.
///
/// @returns The string, converted to UTF-16. The caller is responsible
/// for freeing this string with @c free() when done with it.
///
/// If @a str cannot be converted to UTF-16, @c nullptr is returned.
SWIFT_RUNTIME_STDLIB_SPI
wchar_t *_swift_win32_copyWideFromUTF8(const char *str);

/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
/// \param context A caller-supplied value to pass to \a body.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
SWIFT_RUNTIME_STDLIB_SPI
void _swift_win32_withDbgHelpLibrary(
void (* body)(HANDLE hProcess, void *context), void *context);

/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
static inline void _swift_win32_withDbgHelpLibrary(
const std::function<void(HANDLE /*hProcess*/)> &body) {
_swift_win32_withDbgHelpLibrary([](HANDLE hProcess, void *context) {
auto bodyp = reinterpret_cast<std::function<void(HANDLE)> *>(context);
(* bodyp)(hProcess);
}, const_cast<void *>(reinterpret_cast<const void *>(&body)));
}

/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
///
/// \returns Whatever is returned from \a body.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
template <
typename F,
typename R = typename std::result_of_t<F&(HANDLE /*hProcess*/)>,
typename = typename std::enable_if_t<!std::is_same<void, R>::value>
>
static inline R _swift_win32_withDbgHelpLibrary(const F& body) {
R result;

_swift_win32_withDbgHelpLibrary([&body, &result] (HANDLE hProcess) {
result = body(hProcess);
});

return result;
}

#endif // defined(_WIN32)

#endif // SWIFT_RUNTIME_WIN32_H
58 changes: 13 additions & 45 deletions stdlib/public/CommandLineSupport/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <string>

#include "swift/Runtime/Debug.h"
#include "swift/Runtime/Win32.h"

#include "swift/shims/GlobalObjects.h"
#include "swift/shims/RuntimeStubs.h"
Expand Down Expand Up @@ -193,48 +194,6 @@ static void swift::enumerateUnsafeArgv(const F& body) {
#elif defined(_WIN32)
#include <stdlib.h>

namespace swift {
/// Convert an argument, represented by a wide string, to UTF-8.
///
/// @param str The string to convert.
///
/// @returns The string, converted to UTF-8. The caller is responsible for
/// freeing this string when done with it.
///
/// If @a str cannot be converted to UTF-8, a fatal error occurs.
static char *copyUTF8FromWide(wchar_t *str) {
char *result = nullptr;

int resultLength = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str,
-1, nullptr, 0, nullptr, nullptr);
if (resultLength <= 0) {
swift::fatalError(0,
"Fatal error: Could not get length of commandline "
"argument '%ls': %lu\n",
str, GetLastError());
}

result = reinterpret_cast<char *>(malloc(resultLength));
if (!result) {
swift::fatalError(0,
"Fatal error: Could not allocate space for commandline "
"argument '%ls': %d\n",
str, errno);
}

resultLength = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str, -1,
result, resultLength, nullptr, nullptr);
if (resultLength <= 0) {
swift::fatalError(0,
"Fatal error: Conversion to UTF-8 failed for "
"commandline argument '%ls': %lu\n",
str, GetLastError());
}

return result;
}
}

static char **swift::getUnsafeArgvArgc(int *outArgLen) {
return nullptr;
}
Expand All @@ -244,9 +203,18 @@ static void swift::enumerateUnsafeArgv(const F& body) {
int argc = 0;
if (LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc)) {
std::for_each(wargv, wargv + argc, [=] (wchar_t *warg) {
auto arg = copyUTF8FromWide(warg);
body(argc, arg);
free(arg);
if (char *arg = _swift_win32_copyUTF8FromWide(warg)) {
body(argc, arg);
free(arg);
} else {
// Note that GetLastError() and errno may not be so useful here,
// as in the error case we may have called free(), which might reset
// either or both of them.
swift::fatalError(0,
"Fatal error: Unable to convert argument '%ls' to "
"UTF-8: %lx, %d.\n",
warg, ::GetLastError(), errno);
}
});

LocalFree(wargv);
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ set(swift_runtime_sources
SwiftTLSContext.cpp
ThreadingError.cpp
AccessibleFunction.cpp
RuntimeAttribute.cpp)
RuntimeAttribute.cpp
Win32.cpp)

# Acknowledge that the following sources are known.
set(LLVM_OPTIONAL_SOURCES
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/runtime/Errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "swift/Demangling/Demangle.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/Portability.h"
#include "swift/Runtime/Win32.h"
#include "swift/Threading/Errors.h"
#include "swift/Threading/Mutex.h"
#include "llvm/ADT/StringRef.h"
Expand Down
77 changes: 0 additions & 77 deletions stdlib/public/runtime/ImageInspection.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,83 +92,6 @@ void addImageRuntimeAttributesBlockCallbackUnsafe(const void *baseAddress,
const void *start,
uintptr_t size);

#if defined(_WIN32)
/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
/// \param context A caller-supplied value to pass to \a body.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
SWIFT_RUNTIME_STDLIB_SPI
void _swift_win32_withDbgHelpLibrary(
void (* body)(HANDLE hProcess, void *context), void *context);

/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
static inline void _swift_win32_withDbgHelpLibrary(
const std::function<void(HANDLE /*hProcess*/)> &body) {
_swift_win32_withDbgHelpLibrary([](HANDLE hProcess, void *context) {
auto bodyp = reinterpret_cast<std::function<void(HANDLE)> *>(context);
(* bodyp)(hProcess);
}, const_cast<void *>(reinterpret_cast<const void *>(&body)));
}

/// Configure the environment to allow calling into the Debug Help library.
///
/// \param body A function to invoke. This function attempts to first initialize
/// the Debug Help library. If it did so successfully, the handle used during
/// initialization is passed to this function and should be used with
/// subsequent calls to the Debug Help library. Do not close this handle.
///
/// \returns Whatever is returned from \a body.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
///
/// This function sets the Debug Help library's options by calling
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
/// their old value before returning. \a body can also call \c SymSetOptions()
/// if needed.
template <
typename F,
typename R = typename std::result_of_t<F&(HANDLE /*hProcess*/)>,
typename = typename std::enable_if_t<!std::is_same<void, R>::value>
>
static inline R _swift_win32_withDbgHelpLibrary(const F& body) {
R result;

_swift_win32_withDbgHelpLibrary([&body, &result] (HANDLE hProcess) {
result = body(hProcess);
});

return result;
}
#endif

} // end namespace swift

#endif
3 changes: 2 additions & 1 deletion stdlib/public/runtime/ImageInspectionCOFF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <DbgHelp.h>
#endif

#include "swift/Runtime/Win32.h"
#include "swift/Threading/Mutex.h"

using namespace swift;
Expand All @@ -31,7 +32,7 @@ using namespace swift;
static LazyMutex mutex;
static HANDLE dbgHelpHandle = nullptr;

void swift::_swift_win32_withDbgHelpLibrary(
void _swift_win32_withDbgHelpLibrary(
void (* body)(HANDLE hProcess, void *context), void *context) {
mutex.withLock([=] () {
// If we have not previously created a handle to use with the library, do so
Expand Down
2 changes: 2 additions & 0 deletions stdlib/public/runtime/SymbolInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <dlfcn.h>
#endif

#include "swift/Runtime/Win32.h"

#include "ImageInspection.h"

using namespace swift;
Expand Down
Loading