Fix MCD.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Wed, 2 Feb 2022 10:56:46 +0000 (02:56 -0800)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Thu, 17 Feb 2022 15:39:26 +0000 (18:39 +0300)
45 files changed:
src/CMakeLists.txt
src/managed/interop.cpp
src/managed/interop.h
src/metadata/modules.cpp
src/protocols/completions.h
src/protocols/escaped_string.cpp [new file with mode: 0644]
src/protocols/escaped_string.h [new file with mode: 0644]
src/protocols/miprotocol.h
src/protocols/vscodeprotocol.cpp
src/unittests/CMakeLists.txt
src/unittests/escaped_string_test.cpp
src/unix/dynlibs_unix.cpp [deleted file]
src/unix/filesystem_unix.cpp [deleted file]
src/unix/filesystem_unix.h [deleted file]
src/unix/interop_unix.cpp [deleted file]
src/unix/iosystem_unix.cpp [deleted file]
src/unix/iosystem_unix.h [deleted file]
src/unix/platform_unix.cpp [deleted file]
src/utility.h [deleted file]
src/utils/dynlibs_unix.cpp [new file with mode: 0644]
src/utils/dynlibs_win32.cpp [new file with mode: 0644]
src/utils/escaped_string.cpp [deleted file]
src/utils/escaped_string.h [deleted file]
src/utils/filesystem_unix.cpp [new file with mode: 0644]
src/utils/filesystem_unix.h [new file with mode: 0644]
src/utils/filesystem_win32.cpp [new file with mode: 0644]
src/utils/filesystem_win32.h [new file with mode: 0644]
src/utils/interop_unix.cpp [new file with mode: 0644]
src/utils/interop_win32.cpp [new file with mode: 0644]
src/utils/iosystem_unix.cpp [new file with mode: 0644]
src/utils/iosystem_unix.h [new file with mode: 0644]
src/utils/iosystem_win32.cpp [new file with mode: 0644]
src/utils/iosystem_win32.h [new file with mode: 0644]
src/utils/literal_string.h
src/utils/platform_unix.cpp [new file with mode: 0644]
src/utils/platform_win32.cpp [new file with mode: 0644]
src/utils/rwlock.h
src/utils/utility.h [new file with mode: 0644]
src/windows/dynlibs_win32.cpp [deleted file]
src/windows/filesystem_win32.cpp [deleted file]
src/windows/filesystem_win32.h [deleted file]
src/windows/interop_win32.cpp [deleted file]
src/windows/iosystem_win32.cpp [deleted file]
src/windows/iosystem_win32.h [deleted file]
src/windows/platform_win32.cpp [deleted file]

index b62617cc3aaaa7664e0c761c5328b7dc010b4be0..fe87a525385ae8520543af0a5d133a7ace45ce57 100644 (file)
@@ -109,6 +109,7 @@ set(netcoredbg_SRC
     metadata/modules.cpp
     metadata/typeprinter.cpp
     protocols/cliprotocol.cpp
+    protocols/escaped_string.cpp
     protocols/protocol_utils.cpp
     protocols/miprotocol.cpp
     protocols/tokenizer.cpp
@@ -118,20 +119,19 @@ set(netcoredbg_SRC
     errormessage.cpp
     main.cpp
     buildinfo.cpp
-    utils/escaped_string.cpp
+    utils/dynlibs_unix.cpp
+    utils/dynlibs_win32.cpp
     utils/filesystem.cpp
+    utils/filesystem_unix.cpp
+    utils/filesystem_win32.cpp
     utils/ioredirect.cpp
+    utils/iosystem_unix.cpp
+    utils/iosystem_win32.cpp
+    utils/interop_unix.cpp
+    utils/interop_win32.cpp
+    utils/platform_unix.cpp
+    utils/platform_win32.cpp
     utils/streams.cpp
-    unix/interop_unix.cpp
-    unix/dynlibs_unix.cpp
-    unix/filesystem_unix.cpp
-    unix/iosystem_unix.cpp
-    unix/platform_unix.cpp
-    windows/interop_win32.cpp
-    windows/dynlibs_win32.cpp
-    windows/filesystem_win32.cpp
-    windows/iosystem_win32.cpp
-    windows/platform_win32.cpp
     )
 
 set(CMAKE_INCLUDE_CURRENT_DIR OFF)
index b07e4cbf10a98a4d63f007bfbf38cc34cd5330e8..e12387b6259e0df5c45a668788ade7e739978296 100644 (file)
@@ -114,6 +114,8 @@ int ReadMemoryForSymbols(uint64_t address, char *buffer, int cb)
     return 0;
 }
 
+} // unnamed namespace
+
 HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize,
                                   ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle)
 {
@@ -138,42 +140,11 @@ HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory
     return S_OK;
 }
 
-} // unnamed namespace
-
-
 SequencePoint::~SequencePoint() noexcept
 {
     Interop::SysFreeString(document);
 }
 
-HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle)
-{
-    HRESULT Status = S_OK;
-    BOOL isDynamic = FALSE;
-    BOOL isInMemory = FALSE;
-    IfFailRet(pModule->IsDynamic(&isDynamic));
-    IfFailRet(pModule->IsInMemory(&isInMemory));
-
-    if (isDynamic)
-        return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now
-
-    ULONG64 peAddress = 0;
-    ULONG32 peSize = 0;
-    IfFailRet(pModule->GetBaseAddress(&peAddress));
-    IfFailRet(pModule->GetSize(&peSize));
-
-    return LoadSymbolsForPortablePDB(
-        GetModuleFileName(pModule),
-        isInMemory,
-        isInMemory, // isFileLayout
-        peAddress,
-        peSize,
-        0,          // inMemoryPdbAddress
-        0,          // inMemoryPdbSize
-        ppSymbolReaderHandle
-    );
-}
-
 void DisposeSymbols(PVOID pSymbolReaderHandle)
 {
     std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
index cceb7648726fecb3166045a5ac37d3352f258a1d..20cd60235483702ebd6c34ed91a8ab4a8b5c2f54 100644 (file)
@@ -83,7 +83,8 @@ namespace Interop
     // WARNING! Due to CoreCLR limitations, Shutdown() can't be called out of the Main() scope, for example, from global object destructor.
     void Shutdown();
 
-    HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle);
+    HRESULT LoadSymbolsForPortablePDB(const std::string &modulePath, BOOL isInMemory, BOOL isFileLayout, ULONG64 peAddress, ULONG64 peSize,
+                                      ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle);
     void DisposeSymbols(PVOID pSymbolReaderHandle);
     HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint);
     HRESULT GetNextSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilCloseOffset, bool *noUserCodeFound);
index 0943233b9e5b07daeaad619311dabdaeef877f12..176a60084683da8265b7e5e24ed71ec17173f8b5 100644 (file)
@@ -717,6 +717,34 @@ bool Modules::FindLastIlOffsetAwaitInfo(CORDB_ADDRESS modAddress, mdMethodDef me
     return true;
 }
 
+static HRESULT LoadSymbols(IMetaDataImport *pMD, ICorDebugModule *pModule, VOID **ppSymbolReaderHandle)
+{
+    HRESULT Status = S_OK;
+    BOOL isDynamic = FALSE;
+    BOOL isInMemory = FALSE;
+    IfFailRet(pModule->IsDynamic(&isDynamic));
+    IfFailRet(pModule->IsInMemory(&isInMemory));
+
+    if (isDynamic)
+        return E_FAIL; // Dynamic and in memory assemblies are a special case which we will ignore for now
+
+    ULONG64 peAddress = 0;
+    ULONG32 peSize = 0;
+    IfFailRet(pModule->GetBaseAddress(&peAddress));
+    IfFailRet(pModule->GetSize(&peSize));
+
+    return Interop::LoadSymbolsForPortablePDB(
+        GetModuleFileName(pModule),
+        isInMemory,
+        isInMemory, // isFileLayout
+        peAddress,
+        peSize,
+        0,          // inMemoryPdbAddress
+        0,          // inMemoryPdbSize
+        ppSymbolReaderHandle
+    );
+}
+
 HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module, bool needJMC, std::string &outputText)
 {
     HRESULT Status;
@@ -730,7 +758,7 @@ HRESULT Modules::TryLoadModuleSymbols(ICorDebugModule *pModule, Module &module,
     module.name = GetFileName(module.path);
 
     PVOID pSymbolReaderHandle = nullptr;
-    Interop::LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle);
+    LoadSymbols(pMDImport, pModule, &pSymbolReaderHandle);
     module.symbolStatus = pSymbolReaderHandle != nullptr ? SymbolsLoaded : SymbolsNotFound;
 
     if (module.symbolStatus == SymbolsLoaded)
index ebd848590850eee0532b0853b328403f8b2d0048..20a1eaefacc5a86752e13a901c9c0551f074a0fc 100644 (file)
@@ -5,7 +5,6 @@
 #include <utility>
 #include <type_traits>
 #include <tuple>
-#include "utility.h"
 #include "utils/string_view.h"
 #include "utils/limits.h"
 
diff --git a/src/protocols/escaped_string.cpp b/src/protocols/escaped_string.cpp
new file mode 100644 (file)
index 0000000..523e3c7
--- /dev/null
@@ -0,0 +1,130 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+#include <algorithm>
+#include <string>
+#include "escaped_string.h"
+#include "assert.h"
+
+namespace netcoredbg
+{
+
+EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring)
+: 
+    m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false)
+{
+    ref.set(this, &EscapedStringImpl::transform);
+}
+
+// This function performs input string transformation according to specified (via `m_params) rules.
+// Result will be passed to supplied callback function.
+void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view))
+{
+    // always have transformed result
+    if (m_isresult)
+        return func(thiz, {&m_result[0], m_result.size()});
+
+    // case, when no conversion needed
+    if (m_size == m_input.size())
+        return func(thiz, m_input);
+
+    // perform transformation and compute result size
+    size_t size = 0;
+    string_view src = m_input;
+    while (!src.empty())
+    {
+        // try to find first forbidden character
+        auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end());
+        size_t prefix_size = it - src.begin();
+        if (prefix_size)
+        {
+            // output any other charactes that preceede first forbidden character
+            func(thiz, src.substr(0, prefix_size));
+            size += prefix_size;
+        }
+
+        if (it != src.end())
+        {
+            // find right substitution for forbidden character and output substituting pair of characters
+            auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it);
+            string_view subst = m_params.subst[ir - m_params.forbidden.begin()];
+            func(thiz, subst);
+            size += subst.size();
+            prefix_size++;
+        }
+
+        src.remove_prefix(prefix_size);
+    }
+
+    // remember output size (to avoid computations in future)
+    if (m_size == UndefinedSize)
+        m_size = size;
+}
+
+// function computes output size (but not produces the output)
+size_t EscapedStringInternal::EscapedStringImpl::size() noexcept
+{
+    if (m_size == UndefinedSize)
+        (*this)(nullptr, [](void *, string_view){});
+
+    return m_size;
+}
+
+// function allocates memory and transforms string in all cases,
+// except of case, when transformation isn't required at all and
+// input arguments still not destroyed.
+EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept
+{
+    if (! m_isresult)
+    {
+        if (size() == m_input.size())
+            return m_input;
+
+        transform();
+    }
+    return {&m_result[0], m_result.size()};
+}
+
+// function allocates memory and transforms string.
+EscapedStringInternal::EscapedStringImpl::operator const std::string&()
+{
+    if (!m_isresult)
+        transform();
+
+    return m_result;
+}
+
+// function allocates memory and transforms string in all cases,
+// except of case, when transformation isn't required at all, and
+// input arguments still not destroyed, and input argument contains
+// terminating zero.
+const char* EscapedStringInternal::EscapedStringImpl::c_str()
+{
+    if (m_isstring && !m_isresult && size() == m_input.size())
+        return m_input.data();
+    else
+        return static_cast<const std::string&>(*this).c_str();
+}
+
+// function performs string transformation to allocated memory
+void EscapedStringInternal::EscapedStringImpl::transform()
+{
+    m_ref->reset();
+    m_ref = nullptr;
+
+    m_result.resize(size(), 0);
+    auto it = m_result.begin();
+
+    auto func = [&](string_view str)
+    {
+        m_result.replace(it, it + str.size(), str.begin(), str.end());
+        it += str.size();
+    };
+
+    (*this)(&func, [](void *fp, string_view str) { (*static_cast<decltype(func)*>(fp))(str); });
+
+    m_isresult = true;
+    m_isstring = true;
+}
+
+} // ::netcoredbg::Utility
diff --git a/src/protocols/escaped_string.h b/src/protocols/escaped_string.h
new file mode 100644 (file)
index 0000000..3a9a48e
--- /dev/null
@@ -0,0 +1,222 @@
+// Copyright (C) 2021 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+#pragma once
+#include <utility>
+#include <string>
+#include "utils/utility.h"
+#include "utils/span.h"
+#include "utils/string_view.h"
+
+namespace netcoredbg
+{
+
+// This namespace contains implementation details for few following classes,
+// contents of this namespace isn't designated for direct use.
+namespace EscapedStringInternal
+{
+    using Utility::string_view;
+
+    // This class allows to perform some predefined actions in moment of time,
+    // when temporary object of type TempReference is destroyed. Typically it might
+    // be used to perform some sort of lazy evaluation, when some other class accepts
+    // it's arguments and temporary TempReference object. And it's expected, that
+    // later some evaluation on arguments might be performed. But arguments might
+    // be temporary objects, which will be deleted at end of full expression.
+    // So arguments should be copied, or evaluation performed, prior deleting
+    // the arguments.
+    //
+    // When const TempReference& used as last (default) argument to
+    // class constructor, it is guaranteed, that ~TempReference will be called
+    // exactly prior to deleting other temporary arguments, so some required actions
+    // might be performed before deleting other arguments.
+    //
+    // CAUTION:
+    // When TempReference is passed to ordinary function (not a class constructor),
+    // additionaly cautions is needed: in this case construction order of function
+    // arguments isn't predefined, as destruction order (which is reverse). In such
+    // situation TempReference might be used only and only in case, when no other 
+    // arguments is strictly not a temporary variables. To ensure this, you should
+    // delete functions accepting r-values: void f(const Type&&) = delete.
+    //
+    template <typename T>
+    struct TempReference
+    {
+        // This function should be called from constructor of T, and should pass
+        // reference to T's method, which be called on TempReference destruction.
+        void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; }
+
+        // This function might be called from T to cancel callback which is set with `set`
+        // function. Especially, `reset` might be called from T's destructor.
+        void reset() const noexcept { m_ptr = nullptr; }
+
+        ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); }
+
+    private:
+        mutable T* m_ptr;
+        mutable void (T::*m_func)();
+    };
+
+
+    // This is actual implementation of `EscapedString` class.
+    struct EscapedStringImpl
+    {
+        // EscapeStrinct class performs type erasure, and information on
+        // it's template parameters should be stored in `Params` class.
+        struct Params
+        {
+            string_view forbidden;                  // characters which must be replaced
+            Utility::span<const string_view> subst; // strings to which `forbidden` characters must be replaced
+            char escape;                            // character, which preceedes each substitution
+        };
+
+        using TempRef = TempReference<EscapedStringImpl>;
+
+        // `str` is the source string, in which all `forbidden` characters must be replaced,
+        // `isstring` must be set to true only in case, when `str` contains terminating zero
+        // (to which `str->end()` points).
+        EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring);
+
+        ~EscapedStringImpl() { if (m_ref) m_ref->reset(); }
+
+        // see comments in `EscapeString` class below
+        void operator()(void *thiz, void (*func)(void*, string_view));
+        size_t size() noexcept;
+        explicit operator const std::string&();
+        operator string_view() noexcept;
+        const char* c_str();
+
+    private:
+        // This function performs input string (`m_input`) transformation and stores the
+        // result in (`m_result`). This function might be called in two cases: from
+        // destructor of `TempRef` object (before deleting function argument `str`,
+        // to which `m_input` currently points), or in case when result is needed
+        // in form of data (on call of any getter function, except of operator()).
+        void transform();
+
+        static const size_t UndefinedSize = size_t(0)-1;
+
+        // This TempReference structure was passed as arument to EscapedString class
+        // constructor and continue to exist until end of full expression (till ';').
+        // At end of full expression all temporary variables destroyed and this structure too.
+        // When this happens, TempReference calls transform() method from it's destructor.
+        // In transform() method the strings, which was passed as arguments to EscapedString
+        // constructor, is transformed and copied in allocated memory, so arguments might
+        // be safely deleted. In case, if such transformation and copying occurs before
+        // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset()
+        // to cancel callback from TempReference class.
+        const TempReference<EscapedStringImpl>* m_ref;
+
+        const Params&     m_params;     // character substitution rules
+        const string_view m_input;      // points to input string
+        std::string       m_result;     // might contain result string (lazy evaluated)
+        size_t            m_size;       // size of result stirng (lazy evaluated)
+        bool              m_isstring;   // true if m_input->end() points to terminating zero
+        bool              m_isresult;   // true if m_result contain result string
+    };
+} // namespace EscapedStringInternal
+
+
+/// This class allows lazy transformation of the given source string by substituting
+/// set of "forbidden" characters with escape symbol and other substituted character.
+/// The main idea is to avoid unwanted memory allocation: when it is possible no
+/// memory allocation performed. To achieve this lazy evaluation is used: string
+/// transformation is perfomed only if it is needed.
+///
+/// This class should be used in two steps: first -- creation of class instance from given
+/// arguments, at this time neither memory allocation, nor string transformation is
+/// performed. Only arguments is remembered. And second step -- calling one of the
+/// member functions (see below). At this time string transformation is performed
+/// and memory might be allocated. The latter depends on string content, which
+/// function and when was called... Memory allocation avoided in cases when no
+/// transformation is required (string doesn't contain forbidden characters) and
+/// when arguments, given to class constructor, still not destroyed. Also memory
+/// allocation always avoided in case of call to `operator()`:  in this case
+/// output generated on the fly, but again only if constructor arguments still
+/// exists. String is always transformed and copy saved to allocated memory in
+/// moment, when constructor arguments destroyed (if `EscapedString` class instance
+/// is not temporary variable itself, but continue to exist after end of full expression).
+///
+template <typename Traits> class EscapedString
+{
+    using string_view = Utility::string_view;
+    using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl;
+    static EscapedStringImpl::Params params;
+
+public:
+    /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view.
+    /// Second parameter must have default value (shouldn't be assigned explicitly).
+    EscapedString(string_view str,
+        const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef())
+    : 
+        impl(params, str, ref, false)
+    {}
+
+    // Note: there is no reasons to disable temporaries of type std::string, because
+    // temporary objects construction and destruction ordef for constructors are
+    // predefined, and TempRef always destructed prior to temporary string.
+
+    /// This function allows to avoid memory allocation: functor `func` given as argument
+    /// will consume parts of result (transformed string) which will be generated on the fly.
+    /// Functor `func` must accept `string_view` as argument.
+    template <typename Func, typename = decltype(std::declval<Func>()(std::declval<string_view>()))>
+    void operator()(Func&& func) const
+    {
+        impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast<Func*>(thiz))(str); });
+    }
+
+    /// Function returns size of transformed string (no actual transformation performed).
+    size_t size() const noexcept { return impl.size(); }
+
+    /// Function transforms string (if it was not transformed earlier) and allocates memory.
+    explicit operator const std::string&() const& { return static_cast<const std::string&>(impl); }
+
+    /// Function transforms string and allocates memory.
+    operator string_view() const noexcept { return static_cast<string_view>(impl); }
+
+    /// Function transforms string to allocated memory.
+    const char* c_str() const    { return impl.c_str(); }
+
+private:
+    mutable EscapedStringImpl impl;
+};
+
+
+// instantiation of Params structure for particular Traits template parameter
+template <typename Traits> EscapedStringInternal::EscapedStringImpl::Params EscapedString<Traits>::params =
+{
+    { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars),
+        "forbidden_chars and subst_chars must have same size!");}),
+      string_view(Traits::forbidden_chars)) },
+    { Traits::subst_chars },
+    Traits::escape_char
+};
+
+
+/// Implementation of `operator<<`, which allows write `EscapedString` contents to
+/// the supplied `std::ostream` avoiding memory allocations.
+template <typename Traits>
+std::ostream& operator<<(std::ostream& os, const EscapedString<Traits>& estr)
+{
+    using string_view = Utility::string_view;
+    estr([&](string_view str) -> void { os.write(str.data(), str.size()); });
+    return os;
+}
+
+/// Overloading of `operator+` for `EscapedString`, which allows concatenation
+/// of `EscapedString` instances with ordinary strings.
+template <typename Traits, typename T>
+std::string operator+(const EscapedString<Traits>& left, T&& right)
+{
+    return static_cast<const std::string&>(left) + std::forward<T>(right);
+}
+
+/// Overloading of `operator+` for `EscapedString`, which allows concatenation
+/// of `EscapedString` instances with ordinary strings.
+template <typename Traits, typename T>
+std::string operator+(T&& left, const EscapedString<Traits>& right)
+{
+    return std::forward<T>(left) + static_cast<const std::string&>(right);
+}
+
+} // ::netcoredbg
index bdfd4d229badd838089d9e26946b632e32b398db..b5719bcc1b0070defc5f982bba064e9205796688 100644 (file)
@@ -11,7 +11,7 @@
 #include <iostream>
 #include <memory>
 #include "utils/string_view.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
 #include "interfaces/idebugger.h"
 #include "interfaces/iprotocol.h"
 
index 5098a4983f5fe8ece68967c72ca4d978ea80d004..4a71713d07a7305ed186bed3128b18d8496f3bc8 100644 (file)
@@ -23,7 +23,7 @@
 #include "utils/torelease.h"
 #include "utils/utf.h"
 #include "utils/logger.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
 
 // for convenience
 using json = nlohmann::json;
index 35561990b6b20366afb0bd83266e690b65428a6c..44ee84bc75f5f97563450397cc2d64d40aba344c 100644 (file)
@@ -29,26 +29,26 @@ find_package (Threads)
 # currently defined unit tests
 deftest(string_view string_view_test.cpp)
 deftest(span span_test.cpp)
-deftest(escaped_string ../utils/escaped_string.cpp escaped_string_test.cpp)
+deftest(escaped_string ../protocols/escaped_string.cpp escaped_string_test.cpp)
 
 deftest(iosystem
     iosystem_test.cpp
-    ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
-    ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
 )
 
 deftest(streams
     streams_test.cpp
     ${PROJECT_SOURCE_DIR}/src/utils/streams.cpp
-    ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
-    ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
 )
 
 deftest(ioredirect
     ioredirect_test.cpp
     ${PROJECT_SOURCE_DIR}/src/utils/ioredirect.cpp
     ${PROJECT_SOURCE_DIR}/src/utils/streams.cpp
-    ${PROJECT_SOURCE_DIR}/src/windows/iosystem_win32.cpp
-    ${PROJECT_SOURCE_DIR}/src/unix/iosystem_unix.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_win32.cpp
+    ${PROJECT_SOURCE_DIR}/src/utils/iosystem_unix.cpp
     ${PROJECT_SOURCE_DIR}/src/utils/logger.cpp
 )
index f5672f5f8caf68b7ff7dd5d9dad059524a71687a..9923c9ea4f6af690e169f945da638e9cc09a911f 100644 (file)
@@ -4,7 +4,7 @@
 #include <catch2/catch.hpp>
 #include <string>
 #include "utils/string_view.h"
-#include "utils/escaped_string.h"
+#include "protocols/escaped_string.h"
 #include "compile_test.h"
 
 using namespace netcoredbg;
diff --git a/src/unix/dynlibs_unix.cpp b/src/unix/dynlibs_unix.cpp
deleted file mode 100644 (file)
index e7f745a..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file dynlibsi_unix.h  This file contains unix-specific function definitions
-/// required to work with dynamically loading libraries.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-
-#if defined(__APPLE__)
-#include <mach-o/dyld.h>
-#include <crt_externs.h>
-#endif
-
-#include <dlfcn.h>
-#include "utils/limits.h"
-#include "utils/dynlibs.h"
-
-namespace netcoredbg
-{
-
-// This functon load specified library and returns handle (which then
-// can be passed to DLSym and DLCLose functions).
-// In case of error function returns NULL.
-DLHandle DLOpen(const std::string &path)
-{
-    return reinterpret_cast<DLHandle>(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW));
-}
-
-// This function resolves symbol address within library specified by handle,
-// and returns it's address, in case of error function returns NULL.
-void* DLSym(DLHandle handle, string_view name)
-{
-    char str[LINE_MAX];
-    if (name.size() >= sizeof(str))
-        return {};
-
-    name.copy(str, name.size());
-    str[name.size()] = 0;
-    return ::dlsym(handle, str);
-}
-
-/// This function unloads previously loadded library, specified by handle.
-/// In case of error this function returns `false'.
-bool DLClose(DLHandle handle)
-{
-    return ::dlclose(handle);
-}
-
-}  // ::netcoredbg
-#endif // __unix__
diff --git a/src/unix/filesystem_unix.cpp b/src/unix/filesystem_unix.cpp
deleted file mode 100644 (file)
index 2006e7b..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_unix.cpp
-/// This file contains definitions of unix-specific functions related to file system.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#ifdef __APPLE__
-#include <mach-o/dyld.h>
-#endif
-#include <stdlib.h>
-#include <unistd.h>
-#include <array>
-#include <string>
-#include "utils/filesystem.h"
-#include "utils/string_view.h"
-#include "utils/span.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-using Utility::string_view;
-template <typename T> using span = Utility::span<T>;
-
-const char* FileSystemTraits<UnixPlatformTag>::PathSeparatorSymbols = "/";
-
-namespace
-{
-#ifdef __linux__
-    std::string get_exe_path()
-    {
-        static const char self_link[] = "/proc/self/exe";
-        char buffer[PATH_MAX];
-        ssize_t r = readlink(self_link, buffer, PATH_MAX);
-        return std::string(buffer, r < 0 ? 0 : r);
-    }
-#elif defined(__APPLE__)
-    std::string get_exe_path()
-    {
-        uint32_t lenActualPath = 0;
-        if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
-        {
-            // OSX has placed the actual path length in lenActualPath,
-            // so re-attempt the operation
-            std::string resizedPath(lenActualPath, '\0');
-            char *pResizedPath = const_cast<char *>(resizedPath.data());
-            if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
-                return pResizedPath;
-        }
-        return std::string();
-    }
-#endif
-}
-
-// Function returns absolute path to currently running executable.
-std::string GetExeAbsPath()
-{
-    static const std::string result(get_exe_path());
-    return result;
-}
-
-// Function returns path to directory, which should be used for creation of
-// temporary files. Typically this is `/tmp` on Unix and something like
-// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
-string_view GetTempDir()
-{
-    auto get_tmpdir = []()
-    {
-        const char *pPath = getenv("TMPDIR");
-        if (pPath != nullptr)
-            return pPath;
-        else
-            return P_tmpdir;
-    };
-
-    static const std::string result {get_tmpdir()};
-    return result;
-}
-
-
-// Function changes current working directory. Return value is `false` in case of error.
-bool SetWorkDir(const std::string &path)
-{
-    return chdir(path.c_str()) == 0;
-}
-
-}  // ::netcoredbg
-#endif __unix__
diff --git a/src/unix/filesystem_unix.h b/src/unix/filesystem_unix.h
deleted file mode 100644 (file)
index b27882e..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_unix.h  This file contains unix-specific details to FileSystem class.
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#pragma once
-#include <cstddef>
-#include "utils/limits.h"
-#include "utils/platform.h"
-
-namespace netcoredbg
-{
-    template <> struct FileSystemTraits<UnixPlatformTag>
-    {
-        const static size_t PathMax = PATH_MAX;
-        const static size_t NameMax = NAME_MAX;
-        const static char PathSeparator = '/';
-        const static char* PathSeparatorSymbols;
-    };
-} 
-#endif
diff --git a/src/unix/interop_unix.cpp b/src/unix/interop_unix.cpp
deleted file mode 100644 (file)
index 1a22e57..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file interop_unix.h  This file contains unix-specific functions for Interop class defined in interop.h
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#include <dirent.h>
-#include <stddef.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <string>
-#include <set>
-
-#include "managed/interop.h"
-#include "utils/filesystem.h"
-
-namespace netcoredbg
-{
-
-// This function searches *.dll files in specified directory and adds full paths to files
-// to colon-separated list `tpaList'.
-template <>
-void InteropTraits<UnixPlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
-{
-    const char * const tpaExtensions[] = {
-                ".ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
-                ".dll",
-                ".ni.exe",
-                ".exe",
-                };
-
-    DIR* dir = opendir(directory.c_str());
-    if (dir == nullptr)
-        return;
-
-    std::set<std::string> addedAssemblies;
-
-    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
-    // then files with .dll extension, etc.
-    for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
-    {
-        const char* ext = tpaExtensions[extIndex];
-        int extLength = strlen(ext);
-
-        struct dirent* entry;
-
-        // For all entries in the directory
-        while ((entry = readdir(dir)) != nullptr)
-        {
-            // We are interested in files only
-            switch (entry->d_type)
-            {
-            case DT_REG:
-                break;
-
-            // Handle symlinks and file systems that do not support d_type
-            case DT_LNK:
-            case DT_UNKNOWN:
-            {
-                std::string fullFilename;
-
-                fullFilename.append(directory);
-                fullFilename += FileSystem::PathSeparator;
-                fullFilename.append(entry->d_name);
-
-                struct stat sb;
-                if (stat(fullFilename.c_str(), &sb) == -1)
-                    continue;
-
-                if (!S_ISREG(sb.st_mode))
-                    continue;
-            }
-            break;
-
-            default:
-                continue;
-            }
-
-            std::string filename(entry->d_name);
-
-            // Check if the extension matches the one we are looking for
-            int extPos = filename.length() - extLength;
-            if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
-            {
-                continue;
-            }
-
-            std::string filenameWithoutExt(filename.substr(0, extPos));
-
-            // Make sure if we have an assembly with multiple extensions present,
-            // we insert only one version of it.
-            if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
-            {
-                addedAssemblies.insert(filenameWithoutExt);
-
-                tpaList.append(directory);
-                tpaList += FileSystem::PathSeparator;
-                tpaList.append(filename);
-                tpaList.append(":");
-            }
-        }
-
-        // Rewind the directory stream to be able to iterate over it for the next extension
-        rewinddir(dir);
-    }
-
-    closedir(dir);
-}
-
-// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
-template <>
-void InteropTraits<UnixPlatformTag>::UnsetCoreCLREnv()
-{
-    unsetenv("CORECLR_ENABLE_PROFILING");
-}
-
-// Returns the length of a BSTR.
-template <>
-UINT InteropTraits<UnixPlatformTag>::SysStringLen(BSTR bstrString)
-{
-    if (bstrString == NULL)
-        return 0;
-    return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR));
-}
-
-}  // ::netcoredbg
-#endif  // __unix__
diff --git a/src/unix/iosystem_unix.cpp b/src/unix/iosystem_unix.cpp
deleted file mode 100644 (file)
index 04831cc..0000000
+++ /dev/null
@@ -1,405 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_unix.cpp  This file contains unix-specific definitions of
-/// IOSystem class members (see iosystem.h).
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#include <cstdlib>
-#include <cassert>
-#include <cstdio>
-#include <cstring>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <stdexcept>
-#include <algorithm>
-
-#include "iosystem_unix.h"
-
-namespace
-{
-    // short alias for full class name
-    typedef netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag> Class;
-    
-    
-    struct AsyncRead
-    {
-        int    fd;
-        void*  buffer;
-        size_t size;
-
-        AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
-
-        Class::IOResult operator()()
-        {
-            // TODO need to optimize code to left only one syscall.
-            fd_set set;
-            FD_ZERO(&set);
-            FD_SET(fd, &set);
-            struct timeval tv = {0, 0};
-            ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv);
-            if (result == 0)
-                return {Class::IOResult::Pending, 0};
-
-            if (result >= 0)
-                result = read(fd, buffer, size);
-
-            if (result < 0)
-            {
-                if (errno == EAGAIN)
-                    return {Class::IOResult::Pending, 0};
-
-                // TODO make exception class
-                char msg[256];
-                snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
-                throw std::runtime_error(msg);
-            }
-
-            return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)};
-        }
-
-        int poll(fd_set* read, fd_set *, fd_set* except) const
-        {
-            FD_SET(fd, read);
-            FD_SET(fd, except);
-            return fd;
-        }
-    };
-
-    struct AsyncWrite
-    {
-        int         fd;
-        void const* buffer;
-        size_t      size;
-
-        AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
-
-        Class::IOResult operator()()
-        {
-            fd_set set;
-            FD_ZERO(&set);
-            FD_SET(fd, &set);
-            struct timeval tv = {0, 0};
-            ssize_t result = select(fd + 1, NULL, &set, NULL, &tv);
-            if (result == 0)
-                return {Class::IOResult::Pending, 0};
-
-            if (result >= 0)
-                result = write(fd, buffer, size);
-
-            if (result < 0)
-            {
-                if (errno == EAGAIN)
-                    return {Class::IOResult::Pending, 0};
-
-                char msg[256];
-                snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
-                throw std::runtime_error(msg);
-            }
-
-            return {Class::IOResult::Success, size_t(result)};
-        }
-
-
-        int poll(fd_set *, fd_set *write, fd_set *) const
-        {
-            FD_SET(fd, write);
-            return fd;
-        }
-    };
-}
-
-
-template <typename T> Class::AsyncHandle::Traits  Class::AsyncHandle::TraitsImpl<T>::traits =
-{
-    [](void *thiz) 
-        -> Class::IOResult { return reinterpret_cast<T*>(thiz)->operator()(); },
-
-    [](void *thiz, fd_set* read, fd_set* write, fd_set* except)
-        -> int { return reinterpret_cast<T*>(thiz)->poll(read, write, except); },
-
-    [](void *src, void *dst)
-        -> void { *reinterpret_cast<T*>(dst) = *reinterpret_cast<T*>(src); },
-
-    [](void *thiz)
-        -> void { reinterpret_cast<T*>(thiz)->~T(); }
-};
-
-
-// Function should create unnamed pipe and return two file handles
-// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
-std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
-{
-    int fds[2];
-    if (::pipe(fds) < 0)
-    {
-        perror("pipe");
-        return {};
-    }
-
-    // TODO what to do with this?
-    signal(SIGPIPE, SIG_IGN);
-
-    return { fds[0], fds[1] };
-}
-
-
-// Function creates listening TCP socket on given port, waits, accepts single
-// connection, and return file descriptor related to the accepted connection.
-// In case of error, empty file handle will be returned.
-Class::FileHandle Class::listen_socket(unsigned port)
-{
-    assert(port > 0 && port < 65536);
-
-    int newsockfd;
-    socklen_t clilen;
-    struct sockaddr_in serv_addr, cli_addr;
-
-    int sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
-    if (sockFd < 0)
-    {
-        perror("can't create socket");
-        return {};
-    }
-
-    int enable = 1;
-    if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
-    {
-        ::close(sockFd);
-        perror("can't set socket options");
-        return {};
-    }
-    memset(&serv_addr, 0, sizeof(serv_addr));
-
-    serv_addr.sin_family = AF_INET;
-    serv_addr.sin_addr.s_addr = INADDR_ANY;
-    serv_addr.sin_port = htons(port);
-
-    if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
-    {
-        ::close(sockFd);
-        perror("can't bind to specified port");
-        return {};
-    }
-
-    ::listen(sockFd, 1);
-
-#ifdef DEBUGGER_FOR_TIZEN
-    // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed.
-    // But Visual Studio initiates the connection only after launch_app termination,
-    // therefore we need to close the descriptors before the call to accept().
-    int fd_null = open("/dev/null", O_WRONLY | O_APPEND);
-    if (fd_null < 0)
-    {
-        ::close(sockFd);
-        perror("can't open /dev/null");
-        return {};
-    }
-
-    // Silently close previous stdin/stdout/stderr and reuse fds.
-    // Don't allow stdin read (EOF), but allow stdout/stderr write.
-    if (dup2(fd_null, STDIN_FILENO) == -1 ||
-        dup2(fd_null, STDOUT_FILENO) == -1 ||
-        dup2(fd_null, STDERR_FILENO) == -1)
-    {
-        ::close(sockFd);
-        perror("can't dup2");
-        return {};
-    }
-
-    close(fd_null);
-
-    //TODO on Tizen redirect stderr/stdout output into dlog
-#endif
-
-    clilen = sizeof(cli_addr);
-    newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen);
-    ::close(sockFd);
-    if (newsockfd < 0)
-    {
-        perror("accept");
-        return {};
-    }
-
-    return newsockfd;
-}
-
-// Enable/disable handle inheritance for child processes.
-Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit)
-{
-    int flags = fcntl(fh.fd, F_GETFD);
-    if (flags < 0)
-        return {IOResult::Error, 0};
-
-    if (inherit)
-        flags &= ~FD_CLOEXEC;
-    else
-        flags |= FD_CLOEXEC;
-
-    if (fcntl(fh.fd, F_SETFD, flags) < 0)
-        return {IOResult::Error, 0};
-
-    return {IOResult::Success, 0};
-}
-
-// Function perform reading from the file: it may read up to `count' bytes to `buf'.
-Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count)
-{
-    ssize_t rsize = ::read(fh.fd, buf, count);
-    if (rsize < 0)
-        return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
-    else
-        return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) };
-}
-
-
-// Function perform writing to the file: it may write up to `count' byte from `buf'.
-Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count)
-{
-    ssize_t wsize = ::write(fh.fd, buf, count);
-    if (wsize < 0)
-        return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
-    else
-        return { IOResult::Success, size_t(wsize) };
-}
-
-
-Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
-{
-    return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncRead>(fh.fd, buf, count);
-}
-
-Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
-{
-    return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncWrite>(fh.fd, buf, count);
-}
-
-
-bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
-{
-    fd_set read_set, write_set, except_set;
-    FD_ZERO(&read_set);
-    FD_ZERO(&write_set);
-    FD_ZERO(&except_set);
-
-    int maxfd = -1;
-    for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it)
-    {
-        if (*it)
-            maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd);
-    }
-
-    struct timeval tv;
-    std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
-    tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000;
-
-    int result;
-    do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv);
-    while (result < 0 && errno == EINTR);
-
-    if (result < 0)
-    {
-        char msg[256];
-        snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
-        throw std::runtime_error(msg);
-    }
-
-    return result > 0;
-}
-
-Class::IOResult Class::async_cancel(Class::AsyncHandle& handle)
-{
-    if (!handle)
-        return {Class::IOResult::Error, 0};
-
-    handle = {};
-    return {Class::IOResult::Success, 0};
-}
-
-Class::IOResult Class::async_result(Class::AsyncHandle& handle)
-{
-    if (!handle)
-        return {Class::IOResult::Error, 0};
-
-    auto result = handle();
-    if (result.status != Class::IOResult::Pending)
-        handle = {};
-
-    return result;
-}
-
-
-// Function closes the file represented by file handle.
-Class::IOResult Class::close(const FileHandle &fh)
-{
-    return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 };
-}
-
-
-// This function returns triplet of currently selected standard files.
-netcoredbg::IOSystem::StdFiles Class::get_std_files()
-{
-    static const IOSystem::FileHandle handles[std::tuple_size<IOSystem::StdFiles>::value] = {
-        FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO)
-    };
-    return {handles[0], handles[1], handles[2]};
-}
-
-
-// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
-// Substitution exists only during life time of StsIOSwap instance.
-Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
-{
-    const static unsigned NFD = std::tuple_size<StdFiles>::value;
-    static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
-
-    const int newfd[NFD] = {
-        std::get<StdFileType::Stdin>(files).handle.fd,
-        std::get<StdFileType::Stdout>(files).handle.fd,
-        std::get<StdFileType::Stderr>(files).handle.fd };
-
-    for (unsigned n = 0; n < NFD; n++)
-    {
-        m_orig_fd[n] = ::dup(oldfd[n]);
-        if (m_orig_fd[n] == -1)
-        {
-            char msg[256];
-            snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno));
-            throw std::runtime_error(msg);
-        }
-
-        if (::dup2(newfd[n], oldfd[n]) == -1)
-        {
-            char msg[256];
-            snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno));
-            throw std::runtime_error(msg);
-        }
-    }
-}
-
-
-Class::StdIOSwap::~StdIOSwap()
-{
-    if (!m_valid)
-        return;
-
-    const static unsigned NFD = std::tuple_size<StdFiles>::value;
-    static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
-    for (unsigned n = 0; n < NFD; n++)
-    {
-        if (::dup2(m_orig_fd[n], oldfd[n]) == -1)
-        {
-            abort();
-        }
-        
-        ::close(m_orig_fd[n]);
-    }
-}
-
-#endif  // __unix__
diff --git a/src/unix/iosystem_unix.h b/src/unix/iosystem_unix.h
deleted file mode 100644 (file)
index a8e4402..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_unix.h  This file contains unix-specific declaration of IOSystem class (see iosystem.h).
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#pragma once
-#include <cstdlib>
-#include <cassert>
-#include <sys/select.h>
-#include <tuple>
-#include <new>
-
-#include "utils/platform.h"
-#include "utils/iosystem.h"
-
-template <> struct netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag>
-{
-    using IOSystem = typename netcoredbg::IOSystemImpl<IOSystemTraits<UnixPlatformTag> >;
-    using IOResult = IOSystem::IOResult;
-
-    struct FileHandle
-    {
-        FileHandle() : fd(-1) {}
-        FileHandle(int n) : fd(n) {}
-        explicit operator bool() const { return fd != -1; }
-
-        int fd;
-    };
-
-    struct AsyncHandle
-    {
-        struct Traits
-        {
-            IOResult (*oper)(void *thiz);
-            int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *);
-            void (*move)(void* src, void *dst);
-            void (*destr)(void *thiz);
-        };
-        
-        template <typename T> struct TraitsImpl
-        {
-            static struct Traits traits;
-        };
-
-        const Traits *traits;
-        mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4];
-
-        explicit operator bool() const { return !!traits; }
-
-        IOResult operator()() { assert(*this); return traits->oper(data); }
-
-        int poll(fd_set* read, fd_set* write, fd_set* except)
-        {
-            assert(*this);
-            return traits->poll(data, read, write, except);
-        }
-
-        AsyncHandle() : traits(nullptr) {}
-
-        template <typename InstanceType, typename... Args>
-        static AsyncHandle create(Args&&... args)
-        {
-            static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size");
-            AsyncHandle result;
-            result.traits = &TraitsImpl<InstanceType>::traits;
-            new (result.data) InstanceType(std::forward<Args>(args)...);
-            return result;
-        }
-
-        AsyncHandle(AsyncHandle&& other) : traits(other.traits)
-        {
-            if (other) traits->move(other.data, data);
-            other.traits = nullptr;
-        }
-
-        AsyncHandle& operator=(AsyncHandle&& other)
-        {
-            this->~AsyncHandle();
-            return *new (this) AsyncHandle(std::move(other));
-        }
-
-        ~AsyncHandle() { if (*this) traits->destr(data); }
-    };
-
-    static std::pair<FileHandle, FileHandle> unnamed_pipe();
-    static FileHandle listen_socket(unsigned tcp_port);
-    static IOResult set_inherit(const FileHandle&, bool);
-    static IOResult read(const FileHandle&, void *buf, size_t count);
-    static IOResult write(const FileHandle&, const void *buf, size_t count);
-    static AsyncHandle async_read(const FileHandle&, void *buf, size_t count);
-    static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count);
-    static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
-    static IOResult async_cancel(AsyncHandle&);
-    static IOResult async_result(AsyncHandle&);
-    static IOResult close(const FileHandle&);
-
-    struct StdIOSwap
-    {
-        using StdFiles = IOSystem::StdFiles;
-        using StdFileType = IOSystem::StdFileType;
-        StdIOSwap(const StdFiles &);
-        ~StdIOSwap();
-
-        StdIOSwap(StdIOSwap&& other)
-        {
-            m_valid = other.m_valid;
-            if (!m_valid)
-                return;
-
-            other.m_valid = false;
-            for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
-                m_orig_fd[n] = other.m_orig_fd[n];
-        }
-
-        bool m_valid;
-        int m_orig_fd[std::tuple_size<StdFiles>::value];
-    };
-
-    static IOSystem::StdFiles get_std_files();
-};
-
-#endif // __unix__
diff --git a/src/unix/platform_unix.cpp b/src/unix/platform_unix.cpp
deleted file mode 100644 (file)
index 9d799c5..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file platform_unix.cpp  This file contains unix-specific function definitions,
-/// for functions defined in platform.h
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#ifdef __APPLE__
-#include <crt_externs.h>
-#endif
-#include <unistd.h>
-#include "utils/platform.h"
-
-extern char** environ;
-
-namespace netcoredbg
-{
-
-// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
-unsigned long OSPageSize()
-{
-    static unsigned long pageSize = sysconf(_SC_PAGESIZE);
-    return pageSize;
-}
-
-
-// Function suspends process execution for specified amount of time (in microseconds)
-void USleep(unsigned long usec)
-{
-    usleep(usec);
-}
-
-
-// Function returns list of environment variables (like char **environ).
-char** GetSystemEnvironment()
-{
-#if __APPLE__
-    return *(_NSGetEnviron());
-#else   // __APPLE__
-    return environ;
-#endif  // __APPLE__
-}
-
-}  // ::netcoredbg
-#endif  // __unix__
diff --git a/src/utility.h b/src/utility.h
deleted file mode 100644 (file)
index 6dd50dc..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2020 Samsung Electronics Co., LTD
-// Distributed under the MIT License.
-// See the LICENSE file in the project root for more information.
-
-// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace.
-
-#pragma once
-#include <stddef.h>
-
-namespace netcoredbg
-{
-
-namespace Utility
-{
-
-/// @{ This function is similar to `std::size()` from C++17 and allows to determine
-/// the object size as number of elements, which might be stored within object
-/// (opposed to sizeof(), which returns object sizes in bytes). Typically these
-/// functions applicable to arrays and to classes like std::array.
-template <typename T> constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); }
-template <class T, size_t N> constexpr size_t Size(const T (&)[N]) noexcept { return N; }
-/// @}
-
-  
-/// This type is similar to `std::index_sequence` from C++14 and typically
-/// should be used in pair with `MakeSequence` type to create sequence of
-/// indices usable in template metaprogramming techniques.
-template <size_t... Index> struct Sequence {};
-
-// Actual implementation of MakeSequence type.
-namespace Internals
-{
-    template <size_t Size, size_t... Index> struct MakeSequence : MakeSequence<Size-1, Size-1, Index...> {};
-    template <size_t... Index> struct MakeSequence<0, Index...> { typedef Sequence<Index...> type; };
-}
-
-/// MakeSequence type is similar to `std::make_index_sequence<N>` from C++14.
-/// Instantination of this type exapands to `Sequence<size_t...N>` type, where
-/// `N` have values from 0 to `Size-1`.
-template <size_t Size> using MakeSequence = typename Internals::MakeSequence<Size>::type;
-
-
-/// @{ This is similar to std:void_t which is defined in c++17.
-template <typename... Args> struct MakeVoid { typedef void type; };
-template <typename... Args> using Void = typename MakeVoid<Args...>::type;
-/// @}
-
-
-/// This template is similar to `offsetof` macros in plain C. It allows
-/// to get offset of specified member in some particular class.
-template <typename Owner, typename Member>
-static inline constexpr size_t offset_of(const Member Owner::*mem)
-{
-    return reinterpret_cast<size_t>(&(reinterpret_cast<Owner*>(0)->*mem));
-}
-
-/// This template is similar to well known `container_of` macros. It allows
-/// to get pointer to owner class from pointer to member.
-template <typename Owner, typename Member>
-static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem)
-{
-    return reinterpret_cast<Owner*>(reinterpret_cast<size_t>(ptr) - offset_of(mem));
-}
-
-
-// This is helper class which simplifies implementation of singleton classes.
-//
-// Usage example:
-//   1) define dictinct type of singleton: typedef Singleton<YourType> YourSingleton;
-//   2) to access your singleton use expression: YourSingleton::instance().operations...
-//
-template <typename T> struct Singleton
-{
-    static T& instance()
-    {
-        static T val;
-        return val;
-    }
-};
-
-} // Utility namespace
-} // namespace netcoredbg
diff --git a/src/utils/dynlibs_unix.cpp b/src/utils/dynlibs_unix.cpp
new file mode 100644 (file)
index 0000000..e7f745a
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file dynlibsi_unix.h  This file contains unix-specific function definitions
+/// required to work with dynamically loading libraries.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#include <crt_externs.h>
+#endif
+
+#include <dlfcn.h>
+#include "utils/limits.h"
+#include "utils/dynlibs.h"
+
+namespace netcoredbg
+{
+
+// This functon load specified library and returns handle (which then
+// can be passed to DLSym and DLCLose functions).
+// In case of error function returns NULL.
+DLHandle DLOpen(const std::string &path)
+{
+    return reinterpret_cast<DLHandle>(::dlopen(path.c_str(), RTLD_GLOBAL | RTLD_NOW));
+}
+
+// This function resolves symbol address within library specified by handle,
+// and returns it's address, in case of error function returns NULL.
+void* DLSym(DLHandle handle, string_view name)
+{
+    char str[LINE_MAX];
+    if (name.size() >= sizeof(str))
+        return {};
+
+    name.copy(str, name.size());
+    str[name.size()] = 0;
+    return ::dlsym(handle, str);
+}
+
+/// This function unloads previously loadded library, specified by handle.
+/// In case of error this function returns `false'.
+bool DLClose(DLHandle handle)
+{
+    return ::dlclose(handle);
+}
+
+}  // ::netcoredbg
+#endif // __unix__
diff --git a/src/utils/dynlibs_win32.cpp b/src/utils/dynlibs_win32.cpp
new file mode 100644 (file)
index 0000000..7a1fcb0
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file dynlibsi_win32.h  This file contains windows-specific function definitions
+/// required to work with dynamically loading libraries.
+
+#ifdef WIN32
+#include <windows.h>
+#include "utils/dynlibs.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+// This functon load specified library and returns handle (which then
+// can be passed to DLSym and DLCLose functions).
+// In case of error function returns NULL.
+DLHandle DLOpen(const std::string &path)
+{
+    return reinterpret_cast<DLHandle>(::LoadLibraryExA(path.c_str(), NULL, 0));
+}
+
+// This function resolves symbol address within library specified by handle,
+// and returns it's address, in case of error function returns NULL.
+void* DLSym(DLHandle handle, string_view name)
+{
+    char str[LINE_MAX];
+    if (name.size() >= sizeof(str))
+        return {};
+
+    name.copy(str, name.size());
+    str[name.size()] = 0;
+    return ::GetProcAddress((HMODULE)handle, str);
+}
+
+/// This function unloads previously loadded library, specified by handle.
+/// In case of error this function returns `false'.
+bool DLClose(DLHandle handle)
+{
+    return ::FreeLibrary(reinterpret_cast<HMODULE>(handle));
+}
+
+}  // ::netcoredbg
+#endif
diff --git a/src/utils/escaped_string.cpp b/src/utils/escaped_string.cpp
deleted file mode 100644 (file)
index 523e3c7..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-#include <algorithm>
-#include <string>
-#include "escaped_string.h"
-#include "assert.h"
-
-namespace netcoredbg
-{
-
-EscapedStringInternal::EscapedStringImpl::EscapedStringImpl(const EscapedStringInternal::EscapedStringImpl::Params& params, Utility::string_view str, const TempRef& ref, bool isstring)
-: 
-    m_ref(&ref), m_params(params), m_input(str), m_result(), m_size(UndefinedSize), m_isstring(isstring), m_isresult(false)
-{
-    ref.set(this, &EscapedStringImpl::transform);
-}
-
-// This function performs input string transformation according to specified (via `m_params) rules.
-// Result will be passed to supplied callback function.
-void EscapedStringInternal::EscapedStringImpl::operator()(void *thiz, void (*func)(void*, Utility::string_view))
-{
-    // always have transformed result
-    if (m_isresult)
-        return func(thiz, {&m_result[0], m_result.size()});
-
-    // case, when no conversion needed
-    if (m_size == m_input.size())
-        return func(thiz, m_input);
-
-    // perform transformation and compute result size
-    size_t size = 0;
-    string_view src = m_input;
-    while (!src.empty())
-    {
-        // try to find first forbidden character
-        auto it = std::find_first_of(src.begin(), src.end(), m_params.forbidden.begin(), m_params.forbidden.end());
-        size_t prefix_size = it - src.begin();
-        if (prefix_size)
-        {
-            // output any other charactes that preceede first forbidden character
-            func(thiz, src.substr(0, prefix_size));
-            size += prefix_size;
-        }
-
-        if (it != src.end())
-        {
-            // find right substitution for forbidden character and output substituting pair of characters
-            auto ir = std::find(m_params.forbidden.begin(), m_params.forbidden.end(), *it);
-            string_view subst = m_params.subst[ir - m_params.forbidden.begin()];
-            func(thiz, subst);
-            size += subst.size();
-            prefix_size++;
-        }
-
-        src.remove_prefix(prefix_size);
-    }
-
-    // remember output size (to avoid computations in future)
-    if (m_size == UndefinedSize)
-        m_size = size;
-}
-
-// function computes output size (but not produces the output)
-size_t EscapedStringInternal::EscapedStringImpl::size() noexcept
-{
-    if (m_size == UndefinedSize)
-        (*this)(nullptr, [](void *, string_view){});
-
-    return m_size;
-}
-
-// function allocates memory and transforms string in all cases,
-// except of case, when transformation isn't required at all and
-// input arguments still not destroyed.
-EscapedStringInternal::EscapedStringImpl::operator Utility::string_view() noexcept
-{
-    if (! m_isresult)
-    {
-        if (size() == m_input.size())
-            return m_input;
-
-        transform();
-    }
-    return {&m_result[0], m_result.size()};
-}
-
-// function allocates memory and transforms string.
-EscapedStringInternal::EscapedStringImpl::operator const std::string&()
-{
-    if (!m_isresult)
-        transform();
-
-    return m_result;
-}
-
-// function allocates memory and transforms string in all cases,
-// except of case, when transformation isn't required at all, and
-// input arguments still not destroyed, and input argument contains
-// terminating zero.
-const char* EscapedStringInternal::EscapedStringImpl::c_str()
-{
-    if (m_isstring && !m_isresult && size() == m_input.size())
-        return m_input.data();
-    else
-        return static_cast<const std::string&>(*this).c_str();
-}
-
-// function performs string transformation to allocated memory
-void EscapedStringInternal::EscapedStringImpl::transform()
-{
-    m_ref->reset();
-    m_ref = nullptr;
-
-    m_result.resize(size(), 0);
-    auto it = m_result.begin();
-
-    auto func = [&](string_view str)
-    {
-        m_result.replace(it, it + str.size(), str.begin(), str.end());
-        it += str.size();
-    };
-
-    (*this)(&func, [](void *fp, string_view str) { (*static_cast<decltype(func)*>(fp))(str); });
-
-    m_isresult = true;
-    m_isstring = true;
-}
-
-} // ::netcoredbg::Utility
diff --git a/src/utils/escaped_string.h b/src/utils/escaped_string.h
deleted file mode 100644 (file)
index e8f8466..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright (C) 2021 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-#pragma once
-#include <utility>
-#include <string>
-#include "utility.h"
-#include "utils/span.h"
-#include "utils/string_view.h"
-
-namespace netcoredbg
-{
-
-// This namespace contains implementation details for few following classes,
-// contents of this namespace isn't designated for direct use.
-namespace EscapedStringInternal
-{
-    using Utility::string_view;
-
-    // This class allows to perform some predefined actions in moment of time,
-    // when temporary object of type TempReference is destroyed. Typically it might
-    // be used to perform some sort of lazy evaluation, when some other class accepts
-    // it's arguments and temporary TempReference object. And it's expected, that
-    // later some evaluation on arguments might be performed. But arguments might
-    // be temporary objects, which will be deleted at end of full expression.
-    // So arguments should be copied, or evaluation performed, prior deleting
-    // the arguments.
-    //
-    // When const TempReference& used as last (default) argument to
-    // class constructor, it is guaranteed, that ~TempReference will be called
-    // exactly prior to deleting other temporary arguments, so some required actions
-    // might be performed before deleting other arguments.
-    //
-    // CAUTION:
-    // When TempReference is passed to ordinary function (not a class constructor),
-    // additionaly cautions is needed: in this case construction order of function
-    // arguments isn't predefined, as destruction order (which is reverse). In such
-    // situation TempReference might be used only and only in case, when no other 
-    // arguments is strictly not a temporary variables. To ensure this, you should
-    // delete functions accepting r-values: void f(const Type&&) = delete.
-    //
-    template <typename T>
-    struct TempReference
-    {
-        // This function should be called from constructor of T, and should pass
-        // reference to T's method, which be called on TempReference destruction.
-        void set(T* ptr, void (T::*func)()) const noexcept { m_ptr = ptr, m_func = func; }
-
-        // This function might be called from T to cancel callback which is set with `set`
-        // function. Especially, `reset` might be called from T's destructor.
-        void reset() const noexcept { m_ptr = nullptr; }
-
-        ~TempReference() { if (m_ptr) (m_ptr->*m_func)(); }
-
-    private:
-        mutable T* m_ptr;
-        mutable void (T::*m_func)();
-    };
-
-
-    // This is actual implementation of `EscapedString` class.
-    struct EscapedStringImpl
-    {
-        // EscapeStrinct class performs type erasure, and information on
-        // it's template parameters should be stored in `Params` class.
-        struct Params
-        {
-            string_view forbidden;                  // characters which must be replaced
-            Utility::span<const string_view> subst; // strings to which `forbidden` characters must be replaced
-            char escape;                            // character, which preceedes each substitution
-        };
-
-        using TempRef = TempReference<EscapedStringImpl>;
-
-        // `str` is the source string, in which all `forbidden` characters must be replaced,
-        // `isstring` must be set to true only in case, when `str` contains terminating zero
-        // (to which `str->end()` points).
-        EscapedStringImpl(const Params& params, string_view str, const TempRef& ref, bool isstring);
-
-        ~EscapedStringImpl() { if (m_ref) m_ref->reset(); }
-
-        // see comments in `EscapeString` class below
-        void operator()(void *thiz, void (*func)(void*, string_view));
-        size_t size() noexcept;
-        explicit operator const std::string&();
-        operator string_view() noexcept;
-        const char* c_str();
-
-    private:
-        // This function performs input string (`m_input`) transformation and stores the
-        // result in (`m_result`). This function might be called in two cases: from
-        // destructor of `TempRef` object (before deleting function argument `str`,
-        // to which `m_input` currently points), or in case when result is needed
-        // in form of data (on call of any getter function, except of operator()).
-        void transform();
-
-        static const size_t UndefinedSize = size_t(0)-1;
-
-        // This TempReference structure was passed as arument to EscapedString class
-        // constructor and continue to exist until end of full expression (till ';').
-        // At end of full expression all temporary variables destroyed and this structure too.
-        // When this happens, TempReference calls transform() method from it's destructor.
-        // In transform() method the strings, which was passed as arguments to EscapedString
-        // constructor, is transformed and copied in allocated memory, so arguments might
-        // be safely deleted. In case, if such transformation and copying occurs before
-        // calling of temporary destructors, EscapedStringImpl::transform() calls m_ref->reset()
-        // to cancel callback from TempReference class.
-        const TempReference<EscapedStringImpl>* m_ref;
-
-        const Params&     m_params;     // character substitution rules
-        const string_view m_input;      // points to input string
-        std::string       m_result;     // might contain result string (lazy evaluated)
-        size_t            m_size;       // size of result stirng (lazy evaluated)
-        bool              m_isstring;   // true if m_input->end() points to terminating zero
-        bool              m_isresult;   // true if m_result contain result string
-    };
-} // namespace EscapedStringInternal
-
-
-/// This class allows lazy transformation of the given source string by substituting
-/// set of "forbidden" characters with escape symbol and other substituted character.
-/// The main idea is to avoid unwanted memory allocation: when it is possible no
-/// memory allocation performed. To achieve this lazy evaluation is used: string
-/// transformation is perfomed only if it is needed.
-///
-/// This class should be used in two steps: first -- creation of class instance from given
-/// arguments, at this time neither memory allocation, nor string transformation is
-/// performed. Only arguments is remembered. And second step -- calling one of the
-/// member functions (see below). At this time string transformation is performed
-/// and memory might be allocated. The latter depends on string content, which
-/// function and when was called... Memory allocation avoided in cases when no
-/// transformation is required (string doesn't contain forbidden characters) and
-/// when arguments, given to class constructor, still not destroyed. Also memory
-/// allocation always avoided in case of call to `operator()`:  in this case
-/// output generated on the fly, but again only if constructor arguments still
-/// exists. String is always transformed and copy saved to allocated memory in
-/// moment, when constructor arguments destroyed (if `EscapedString` class instance
-/// is not temporary variable itself, but continue to exist after end of full expression).
-///
-template <typename Traits> class EscapedString
-{
-    using string_view = Utility::string_view;
-    using EscapedStringImpl = EscapedStringInternal::EscapedStringImpl;
-    static EscapedStringImpl::Params params;
-
-public:
-    /// Construct `EscapedString` from c-strings (via implicit conversion) or string_view.
-    /// Second parameter must have default value (shouldn't be assigned explicitly).
-    EscapedString(string_view str,
-        const EscapedStringImpl::TempRef& ref = EscapedStringImpl::TempRef())
-    : 
-        impl(params, str, ref, false)
-    {}
-
-    // Note: there is no reasons to disable temporaries of type std::string, because
-    // temporary objects construction and destruction ordef for constructors are
-    // predefined, and TempRef always destructed prior to temporary string.
-
-    /// This function allows to avoid memory allocation: functor `func` given as argument
-    /// will consume parts of result (transformed string) which will be generated on the fly.
-    /// Functor `func` must accept `string_view` as argument.
-    template <typename Func, typename = decltype(std::declval<Func>()(std::declval<string_view>()))>
-    void operator()(Func&& func) const
-    {
-        impl.operator()(&func, [](void *thiz, string_view str) { (*static_cast<Func*>(thiz))(str); });
-    }
-
-    /// Function returns size of transformed string (no actual transformation performed).
-    size_t size() const noexcept { return impl.size(); }
-
-    /// Function transforms string (if it was not transformed earlier) and allocates memory.
-    explicit operator const std::string&() const& { return static_cast<const std::string&>(impl); }
-
-    /// Function transforms string and allocates memory.
-    operator string_view() const noexcept { return static_cast<string_view>(impl); }
-
-    /// Function transforms string to allocated memory.
-    const char* c_str() const    { return impl.c_str(); }
-
-private:
-    mutable EscapedStringImpl impl;
-};
-
-
-// instantiation of Params structure for particular Traits template parameter
-template <typename Traits> EscapedStringInternal::EscapedStringImpl::Params EscapedString<Traits>::params =
-{
-    { ((void)([]() -> void {static_assert(sizeof(Traits::forbidden_chars)-1 == Utility::Size(Traits::subst_chars),
-        "forbidden_chars and subst_chars must have same size!");}),
-      string_view(Traits::forbidden_chars)) },
-    { Traits::subst_chars },
-    Traits::escape_char
-};
-
-
-/// Implementation of `operator<<`, which allows write `EscapedString` contents to
-/// the supplied `std::ostream` avoiding memory allocations.
-template <typename Traits>
-std::ostream& operator<<(std::ostream& os, const EscapedString<Traits>& estr)
-{
-    using string_view = Utility::string_view;
-    estr([&](string_view str) -> void { os.write(str.data(), str.size()); });
-    return os;
-}
-
-/// Overloading of `operator+` for `EscapedString`, which allows concatenation
-/// of `EscapedString` instances with ordinary strings.
-template <typename Traits, typename T>
-std::string operator+(const EscapedString<Traits>& left, T&& right)
-{
-    return static_cast<const std::string&>(left) + std::forward<T>(right);
-}
-
-/// Overloading of `operator+` for `EscapedString`, which allows concatenation
-/// of `EscapedString` instances with ordinary strings.
-template <typename Traits, typename T>
-std::string operator+(T&& left, const EscapedString<Traits>& right)
-{
-    return std::forward<T>(left) + static_cast<const std::string&>(right);
-}
-
-} // ::netcoredbg
diff --git a/src/utils/filesystem_unix.cpp b/src/utils/filesystem_unix.cpp
new file mode 100644 (file)
index 0000000..2006e7b
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_unix.cpp
+/// This file contains definitions of unix-specific functions related to file system.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <array>
+#include <string>
+#include "utils/filesystem.h"
+#include "utils/string_view.h"
+#include "utils/span.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+using Utility::string_view;
+template <typename T> using span = Utility::span<T>;
+
+const char* FileSystemTraits<UnixPlatformTag>::PathSeparatorSymbols = "/";
+
+namespace
+{
+#ifdef __linux__
+    std::string get_exe_path()
+    {
+        static const char self_link[] = "/proc/self/exe";
+        char buffer[PATH_MAX];
+        ssize_t r = readlink(self_link, buffer, PATH_MAX);
+        return std::string(buffer, r < 0 ? 0 : r);
+    }
+#elif defined(__APPLE__)
+    std::string get_exe_path()
+    {
+        uint32_t lenActualPath = 0;
+        if (_NSGetExecutablePath(nullptr, &lenActualPath) == -1)
+        {
+            // OSX has placed the actual path length in lenActualPath,
+            // so re-attempt the operation
+            std::string resizedPath(lenActualPath, '\0');
+            char *pResizedPath = const_cast<char *>(resizedPath.data());
+            if (_NSGetExecutablePath(pResizedPath, &lenActualPath) == 0)
+                return pResizedPath;
+        }
+        return std::string();
+    }
+#endif
+}
+
+// Function returns absolute path to currently running executable.
+std::string GetExeAbsPath()
+{
+    static const std::string result(get_exe_path());
+    return result;
+}
+
+// Function returns path to directory, which should be used for creation of
+// temporary files. Typically this is `/tmp` on Unix and something like
+// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
+string_view GetTempDir()
+{
+    auto get_tmpdir = []()
+    {
+        const char *pPath = getenv("TMPDIR");
+        if (pPath != nullptr)
+            return pPath;
+        else
+            return P_tmpdir;
+    };
+
+    static const std::string result {get_tmpdir()};
+    return result;
+}
+
+
+// Function changes current working directory. Return value is `false` in case of error.
+bool SetWorkDir(const std::string &path)
+{
+    return chdir(path.c_str()) == 0;
+}
+
+}  // ::netcoredbg
+#endif __unix__
diff --git a/src/utils/filesystem_unix.h b/src/utils/filesystem_unix.h
new file mode 100644 (file)
index 0000000..b27882e
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_unix.h  This file contains unix-specific details to FileSystem class.
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#pragma once
+#include <cstddef>
+#include "utils/limits.h"
+#include "utils/platform.h"
+
+namespace netcoredbg
+{
+    template <> struct FileSystemTraits<UnixPlatformTag>
+    {
+        const static size_t PathMax = PATH_MAX;
+        const static size_t NameMax = NAME_MAX;
+        const static char PathSeparator = '/';
+        const static char* PathSeparatorSymbols;
+    };
+} 
+#endif
diff --git a/src/utils/filesystem_win32.cpp b/src/utils/filesystem_win32.cpp
new file mode 100644 (file)
index 0000000..c5380c4
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_win32.cpp
+/// This file contains definitions of windows-specific functions related to file system.
+
+#ifdef WIN32
+#include <windows.h>
+#include <string>
+#include "utils/filesystem.h"
+#include "utils/limits.h"
+
+
+namespace netcoredbg
+{
+
+const char* FileSystemTraits<Win32PlatformTag>::PathSeparatorSymbols = "/\\";
+
+// Function returns absolute path to currently running executable.
+std::string GetExeAbsPath()
+{
+    const size_t MAX_LONGPATH = 1024;
+    char hostPath[MAX_LONGPATH + 1];
+    static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH));
+    return result;
+}
+
+
+// Function returns path to directory, which should be used for creation of
+// temporary files. Typically this is `/tmp` on Unix and something like
+// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
+string_view GetTempDir()
+{
+    CHAR path[MAX_PATH + 1];
+    static const std::string result(path, GetTempPathA(MAX_PATH, path));
+    return result;
+}
+
+
+// Function changes current working directory. Return value is `false` in case of error.
+bool SetWorkDir(const std::string &path)
+{
+    // In the ANSI version of this function, the name is limited to MAX_PATH characters.
+    // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory
+    if (path.size() >= MAX_PATH)
+        return false;
+
+    return SetCurrentDirectoryA(path.c_str());
+}
+
+}  // ::netcoredbg
+#endif
diff --git a/src/utils/filesystem_win32.h b/src/utils/filesystem_win32.h
new file mode 100644 (file)
index 0000000..ac14fcf
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file filesystem_win32.h  This file contains windows-specific details to FileSystem class.
+
+#ifdef WIN32
+#pragma once
+#include <cstddef>
+#include <windows.h>
+
+namespace netcoredbg
+{
+    template <> struct FileSystemTraits<Win32PlatformTag>
+    {
+        const static size_t PathMax = MAX_PATH;
+        const static size_t NameMax = MAX_PATH - 1; // not include terminal null.
+        const static char PathSeparator = '\\';
+        const static char* PathSeparatorSymbols;
+    };
+} 
+#endif
diff --git a/src/utils/interop_unix.cpp b/src/utils/interop_unix.cpp
new file mode 100644 (file)
index 0000000..1a22e57
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file interop_unix.h  This file contains unix-specific functions for Interop class defined in interop.h
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <dirent.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <set>
+
+#include "managed/interop.h"
+#include "utils/filesystem.h"
+
+namespace netcoredbg
+{
+
+// This function searches *.dll files in specified directory and adds full paths to files
+// to colon-separated list `tpaList'.
+template <>
+void InteropTraits<UnixPlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
+{
+    const char * const tpaExtensions[] = {
+                ".ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+                ".dll",
+                ".ni.exe",
+                ".exe",
+                };
+
+    DIR* dir = opendir(directory.c_str());
+    if (dir == nullptr)
+        return;
+
+    std::set<std::string> addedAssemblies;
+
+    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+    // then files with .dll extension, etc.
+    for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+    {
+        const char* ext = tpaExtensions[extIndex];
+        int extLength = strlen(ext);
+
+        struct dirent* entry;
+
+        // For all entries in the directory
+        while ((entry = readdir(dir)) != nullptr)
+        {
+            // We are interested in files only
+            switch (entry->d_type)
+            {
+            case DT_REG:
+                break;
+
+            // Handle symlinks and file systems that do not support d_type
+            case DT_LNK:
+            case DT_UNKNOWN:
+            {
+                std::string fullFilename;
+
+                fullFilename.append(directory);
+                fullFilename += FileSystem::PathSeparator;
+                fullFilename.append(entry->d_name);
+
+                struct stat sb;
+                if (stat(fullFilename.c_str(), &sb) == -1)
+                    continue;
+
+                if (!S_ISREG(sb.st_mode))
+                    continue;
+            }
+            break;
+
+            default:
+                continue;
+            }
+
+            std::string filename(entry->d_name);
+
+            // Check if the extension matches the one we are looking for
+            int extPos = filename.length() - extLength;
+            if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0))
+            {
+                continue;
+            }
+
+            std::string filenameWithoutExt(filename.substr(0, extPos));
+
+            // Make sure if we have an assembly with multiple extensions present,
+            // we insert only one version of it.
+            if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+            {
+                addedAssemblies.insert(filenameWithoutExt);
+
+                tpaList.append(directory);
+                tpaList += FileSystem::PathSeparator;
+                tpaList.append(filename);
+                tpaList.append(":");
+            }
+        }
+
+        // Rewind the directory stream to be able to iterate over it for the next extension
+        rewinddir(dir);
+    }
+
+    closedir(dir);
+}
+
+// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
+template <>
+void InteropTraits<UnixPlatformTag>::UnsetCoreCLREnv()
+{
+    unsetenv("CORECLR_ENABLE_PROFILING");
+}
+
+// Returns the length of a BSTR.
+template <>
+UINT InteropTraits<UnixPlatformTag>::SysStringLen(BSTR bstrString)
+{
+    if (bstrString == NULL)
+        return 0;
+    return (unsigned int)((((DWORD FAR*)bstrString)[-1]) / sizeof(OLECHAR));
+}
+
+}  // ::netcoredbg
+#endif  // __unix__
diff --git a/src/utils/interop_win32.cpp b/src/utils/interop_win32.cpp
new file mode 100644 (file)
index 0000000..a0d06f8
--- /dev/null
@@ -0,0 +1,95 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file interop_win32.h  This file contains windows-specific functions for Interop class defined in interop.h
+
+#ifdef WIN32
+#include <windows.h>
+#include <stddef.h>
+#include <string.h>
+#include <string>
+#include <set>
+
+#include "managed/interop.h"
+#include "utils/filesystem.h"
+#include "utils/limits.h"
+
+
+namespace netcoredbg
+{
+
+// This function searches *.dll files in specified directory and adds full paths to files
+// to semicolon-separated list `tpaList'.
+template <>
+void InteropTraits<Win32PlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
+{
+    const char * const tpaExtensions[] = {
+        "*.ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+        "*.dll",
+        "*.ni.exe",
+        "*.exe",
+    };
+
+    std::set<std::string> addedAssemblies;
+
+    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+    // then files with .dll extension, etc.
+    for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+    {
+        const char* ext = tpaExtensions[extIndex];
+        size_t extLength = strlen(ext);
+
+        std::string assemblyPath(directory);
+        assemblyPath += FileSystem::PathSeparator;
+        assemblyPath.append(tpaExtensions[extIndex]);
+
+        WIN32_FIND_DATAA data;
+        HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+        if (findHandle != INVALID_HANDLE_VALUE)
+        {
+            do
+            {
+                if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                {
+
+                    std::string filename(data.cFileName);
+                    size_t extPos = filename.length() - extLength;
+                    std::string filenameWithoutExt(filename.substr(0, extPos));
+
+                    // Make sure if we have an assembly with multiple extensions present,
+                    // we insert only one version of it.
+                    if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+                    {
+                        addedAssemblies.insert(filenameWithoutExt);
+
+                        tpaList.append(directory);
+                        tpaList += FileSystem::PathSeparator;
+                        tpaList.append(filename);
+                        tpaList.append(";");
+                    }
+                }
+            }
+            while (0 != FindNextFileA(findHandle, &data));
+
+            FindClose(findHandle);
+        }
+    }
+}
+
+// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
+template <>
+void InteropTraits<Win32PlatformTag>::UnsetCoreCLREnv()
+{
+    _putenv("CORECLR_ENABLE_PROFILING=");
+}
+
+// Returns the length of a BSTR.
+template <>
+UINT InteropTraits<Win32PlatformTag>::SysStringLen(BSTR bstrString)
+{
+    return ::SysStringLen(bstrString);
+}
+
+}  // ::netcoredbg
+#endif  // WIN32
diff --git a/src/utils/iosystem_unix.cpp b/src/utils/iosystem_unix.cpp
new file mode 100644 (file)
index 0000000..04831cc
--- /dev/null
@@ -0,0 +1,405 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_unix.cpp  This file contains unix-specific definitions of
+/// IOSystem class members (see iosystem.h).
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <cstdlib>
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdexcept>
+#include <algorithm>
+
+#include "iosystem_unix.h"
+
+namespace
+{
+    // short alias for full class name
+    typedef netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag> Class;
+    
+    
+    struct AsyncRead
+    {
+        int    fd;
+        void*  buffer;
+        size_t size;
+
+        AsyncRead(int fd, void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
+
+        Class::IOResult operator()()
+        {
+            // TODO need to optimize code to left only one syscall.
+            fd_set set;
+            FD_ZERO(&set);
+            FD_SET(fd, &set);
+            struct timeval tv = {0, 0};
+            ssize_t result = ::select(fd + 1, &set, NULL, &set, &tv);
+            if (result == 0)
+                return {Class::IOResult::Pending, 0};
+
+            if (result >= 0)
+                result = read(fd, buffer, size);
+
+            if (result < 0)
+            {
+                if (errno == EAGAIN)
+                    return {Class::IOResult::Pending, 0};
+
+                // TODO make exception class
+                char msg[256];
+                snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+                throw std::runtime_error(msg);
+            }
+
+            return {result == 0 ? Class::IOResult::Eof : Class::IOResult::Success, size_t(result)};
+        }
+
+        int poll(fd_set* read, fd_set *, fd_set* except) const
+        {
+            FD_SET(fd, read);
+            FD_SET(fd, except);
+            return fd;
+        }
+    };
+
+    struct AsyncWrite
+    {
+        int         fd;
+        void const* buffer;
+        size_t      size;
+
+        AsyncWrite(int fd, const void *buf, size_t size) : fd(fd), buffer(buf), size(size) {}
+
+        Class::IOResult operator()()
+        {
+            fd_set set;
+            FD_ZERO(&set);
+            FD_SET(fd, &set);
+            struct timeval tv = {0, 0};
+            ssize_t result = select(fd + 1, NULL, &set, NULL, &tv);
+            if (result == 0)
+                return {Class::IOResult::Pending, 0};
+
+            if (result >= 0)
+                result = write(fd, buffer, size);
+
+            if (result < 0)
+            {
+                if (errno == EAGAIN)
+                    return {Class::IOResult::Pending, 0};
+
+                char msg[256];
+                snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+                throw std::runtime_error(msg);
+            }
+
+            return {Class::IOResult::Success, size_t(result)};
+        }
+
+
+        int poll(fd_set *, fd_set *write, fd_set *) const
+        {
+            FD_SET(fd, write);
+            return fd;
+        }
+    };
+}
+
+
+template <typename T> Class::AsyncHandle::Traits  Class::AsyncHandle::TraitsImpl<T>::traits =
+{
+    [](void *thiz) 
+        -> Class::IOResult { return reinterpret_cast<T*>(thiz)->operator()(); },
+
+    [](void *thiz, fd_set* read, fd_set* write, fd_set* except)
+        -> int { return reinterpret_cast<T*>(thiz)->poll(read, write, except); },
+
+    [](void *src, void *dst)
+        -> void { *reinterpret_cast<T*>(dst) = *reinterpret_cast<T*>(src); },
+
+    [](void *thiz)
+        -> void { reinterpret_cast<T*>(thiz)->~T(); }
+};
+
+
+// Function should create unnamed pipe and return two file handles
+// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
+std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
+{
+    int fds[2];
+    if (::pipe(fds) < 0)
+    {
+        perror("pipe");
+        return {};
+    }
+
+    // TODO what to do with this?
+    signal(SIGPIPE, SIG_IGN);
+
+    return { fds[0], fds[1] };
+}
+
+
+// Function creates listening TCP socket on given port, waits, accepts single
+// connection, and return file descriptor related to the accepted connection.
+// In case of error, empty file handle will be returned.
+Class::FileHandle Class::listen_socket(unsigned port)
+{
+    assert(port > 0 && port < 65536);
+
+    int newsockfd;
+    socklen_t clilen;
+    struct sockaddr_in serv_addr, cli_addr;
+
+    int sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (sockFd < 0)
+    {
+        perror("can't create socket");
+        return {};
+    }
+
+    int enable = 1;
+    if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
+    {
+        ::close(sockFd);
+        perror("can't set socket options");
+        return {};
+    }
+    memset(&serv_addr, 0, sizeof(serv_addr));
+
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = INADDR_ANY;
+    serv_addr.sin_port = htons(port);
+
+    if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+    {
+        ::close(sockFd);
+        perror("can't bind to specified port");
+        return {};
+    }
+
+    ::listen(sockFd, 1);
+
+#ifdef DEBUGGER_FOR_TIZEN
+    // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed.
+    // But Visual Studio initiates the connection only after launch_app termination,
+    // therefore we need to close the descriptors before the call to accept().
+    int fd_null = open("/dev/null", O_WRONLY | O_APPEND);
+    if (fd_null < 0)
+    {
+        ::close(sockFd);
+        perror("can't open /dev/null");
+        return {};
+    }
+
+    // Silently close previous stdin/stdout/stderr and reuse fds.
+    // Don't allow stdin read (EOF), but allow stdout/stderr write.
+    if (dup2(fd_null, STDIN_FILENO) == -1 ||
+        dup2(fd_null, STDOUT_FILENO) == -1 ||
+        dup2(fd_null, STDERR_FILENO) == -1)
+    {
+        ::close(sockFd);
+        perror("can't dup2");
+        return {};
+    }
+
+    close(fd_null);
+
+    //TODO on Tizen redirect stderr/stdout output into dlog
+#endif
+
+    clilen = sizeof(cli_addr);
+    newsockfd = ::accept(sockFd, (struct sockaddr *) &cli_addr, &clilen);
+    ::close(sockFd);
+    if (newsockfd < 0)
+    {
+        perror("accept");
+        return {};
+    }
+
+    return newsockfd;
+}
+
+// Enable/disable handle inheritance for child processes.
+Class::IOResult Class::set_inherit(const FileHandle &fh, bool inherit)
+{
+    int flags = fcntl(fh.fd, F_GETFD);
+    if (flags < 0)
+        return {IOResult::Error, 0};
+
+    if (inherit)
+        flags &= ~FD_CLOEXEC;
+    else
+        flags |= FD_CLOEXEC;
+
+    if (fcntl(fh.fd, F_SETFD, flags) < 0)
+        return {IOResult::Error, 0};
+
+    return {IOResult::Success, 0};
+}
+
+// Function perform reading from the file: it may read up to `count' bytes to `buf'.
+Class::IOResult Class::read(const FileHandle &fh, void *buf, size_t count)
+{
+    ssize_t rsize = ::read(fh.fd, buf, count);
+    if (rsize < 0)
+        return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
+    else
+        return { (rsize == 0 ? IOResult::Eof : IOResult::Success), size_t(rsize) };
+}
+
+
+// Function perform writing to the file: it may write up to `count' byte from `buf'.
+Class::IOResult Class::write(const FileHandle &fh, const void *buf, size_t count)
+{
+    ssize_t wsize = ::write(fh.fd, buf, count);
+    if (wsize < 0)
+        return { (errno == EAGAIN ? IOResult::Pending : IOResult::Error), 0 };
+    else
+        return { IOResult::Success, size_t(wsize) };
+}
+
+
+Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
+{
+    return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncRead>(fh.fd, buf, count);
+}
+
+Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
+{
+    return fh.fd == -1 ? AsyncHandle() : AsyncHandle::create<AsyncWrite>(fh.fd, buf, count);
+}
+
+
+bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
+{
+    fd_set read_set, write_set, except_set;
+    FD_ZERO(&read_set);
+    FD_ZERO(&write_set);
+    FD_ZERO(&except_set);
+
+    int maxfd = -1;
+    for (IOSystem::AsyncHandleIterator it = begin; it != end; ++it)
+    {
+        if (*it)
+            maxfd = std::max(it->handle.poll(&read_set, &write_set, &except_set), maxfd);
+    }
+
+    struct timeval tv;
+    std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
+    tv.tv_sec = us.count() / 1000000, tv.tv_usec = us.count() % 1000000;
+
+    int result;
+    do result = ::select(maxfd + 1, &read_set, &write_set, &except_set, &tv);
+    while (result < 0 && errno == EINTR);
+
+    if (result < 0)
+    {
+        char msg[256];
+        snprintf(msg, sizeof(msg), "select: %s", strerror(errno));
+        throw std::runtime_error(msg);
+    }
+
+    return result > 0;
+}
+
+Class::IOResult Class::async_cancel(Class::AsyncHandle& handle)
+{
+    if (!handle)
+        return {Class::IOResult::Error, 0};
+
+    handle = {};
+    return {Class::IOResult::Success, 0};
+}
+
+Class::IOResult Class::async_result(Class::AsyncHandle& handle)
+{
+    if (!handle)
+        return {Class::IOResult::Error, 0};
+
+    auto result = handle();
+    if (result.status != Class::IOResult::Pending)
+        handle = {};
+
+    return result;
+}
+
+
+// Function closes the file represented by file handle.
+Class::IOResult Class::close(const FileHandle &fh)
+{
+    return { (::close(fh.fd) == 0 ? IOResult::Success : IOResult::Error), 0 };
+}
+
+
+// This function returns triplet of currently selected standard files.
+netcoredbg::IOSystem::StdFiles Class::get_std_files()
+{
+    static const IOSystem::FileHandle handles[std::tuple_size<IOSystem::StdFiles>::value] = {
+        FileHandle(STDIN_FILENO), FileHandle(STDOUT_FILENO), FileHandle(STDERR_FILENO)
+    };
+    return {handles[0], handles[1], handles[2]};
+}
+
+
+// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
+// Substitution exists only during life time of StsIOSwap instance.
+Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
+{
+    const static unsigned NFD = std::tuple_size<StdFiles>::value;
+    static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
+
+    const int newfd[NFD] = {
+        std::get<StdFileType::Stdin>(files).handle.fd,
+        std::get<StdFileType::Stdout>(files).handle.fd,
+        std::get<StdFileType::Stderr>(files).handle.fd };
+
+    for (unsigned n = 0; n < NFD; n++)
+    {
+        m_orig_fd[n] = ::dup(oldfd[n]);
+        if (m_orig_fd[n] == -1)
+        {
+            char msg[256];
+            snprintf(msg, sizeof(msg), "dup(%d): %s", oldfd[n], strerror(errno));
+            throw std::runtime_error(msg);
+        }
+
+        if (::dup2(newfd[n], oldfd[n]) == -1)
+        {
+            char msg[256];
+            snprintf(msg, sizeof(msg), "dup2(%d, %d): %s", newfd[n], oldfd[n], strerror(errno));
+            throw std::runtime_error(msg);
+        }
+    }
+}
+
+
+Class::StdIOSwap::~StdIOSwap()
+{
+    if (!m_valid)
+        return;
+
+    const static unsigned NFD = std::tuple_size<StdFiles>::value;
+    static const int oldfd[NFD] = { StdFileType::Stdin, StdFileType::Stdout, StdFileType::Stderr };
+    for (unsigned n = 0; n < NFD; n++)
+    {
+        if (::dup2(m_orig_fd[n], oldfd[n]) == -1)
+        {
+            abort();
+        }
+        
+        ::close(m_orig_fd[n]);
+    }
+}
+
+#endif  // __unix__
diff --git a/src/utils/iosystem_unix.h b/src/utils/iosystem_unix.h
new file mode 100644 (file)
index 0000000..a8e4402
--- /dev/null
@@ -0,0 +1,123 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_unix.h  This file contains unix-specific declaration of IOSystem class (see iosystem.h).
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#pragma once
+#include <cstdlib>
+#include <cassert>
+#include <sys/select.h>
+#include <tuple>
+#include <new>
+
+#include "utils/platform.h"
+#include "utils/iosystem.h"
+
+template <> struct netcoredbg::IOSystemTraits<netcoredbg::UnixPlatformTag>
+{
+    using IOSystem = typename netcoredbg::IOSystemImpl<IOSystemTraits<UnixPlatformTag> >;
+    using IOResult = IOSystem::IOResult;
+
+    struct FileHandle
+    {
+        FileHandle() : fd(-1) {}
+        FileHandle(int n) : fd(n) {}
+        explicit operator bool() const { return fd != -1; }
+
+        int fd;
+    };
+
+    struct AsyncHandle
+    {
+        struct Traits
+        {
+            IOResult (*oper)(void *thiz);
+            int (*poll)(void *thiz, fd_set *, fd_set *, fd_set *);
+            void (*move)(void* src, void *dst);
+            void (*destr)(void *thiz);
+        };
+        
+        template <typename T> struct TraitsImpl
+        {
+            static struct Traits traits;
+        };
+
+        const Traits *traits;
+        mutable char data alignas(__BIGGEST_ALIGNMENT__) [sizeof(void*) * 4];
+
+        explicit operator bool() const { return !!traits; }
+
+        IOResult operator()() { assert(*this); return traits->oper(data); }
+
+        int poll(fd_set* read, fd_set* write, fd_set* except)
+        {
+            assert(*this);
+            return traits->poll(data, read, write, except);
+        }
+
+        AsyncHandle() : traits(nullptr) {}
+
+        template <typename InstanceType, typename... Args>
+        static AsyncHandle create(Args&&... args)
+        {
+            static_assert(sizeof(InstanceType) <= sizeof(data), "insufficiend data size");
+            AsyncHandle result;
+            result.traits = &TraitsImpl<InstanceType>::traits;
+            new (result.data) InstanceType(std::forward<Args>(args)...);
+            return result;
+        }
+
+        AsyncHandle(AsyncHandle&& other) : traits(other.traits)
+        {
+            if (other) traits->move(other.data, data);
+            other.traits = nullptr;
+        }
+
+        AsyncHandle& operator=(AsyncHandle&& other)
+        {
+            this->~AsyncHandle();
+            return *new (this) AsyncHandle(std::move(other));
+        }
+
+        ~AsyncHandle() { if (*this) traits->destr(data); }
+    };
+
+    static std::pair<FileHandle, FileHandle> unnamed_pipe();
+    static FileHandle listen_socket(unsigned tcp_port);
+    static IOResult set_inherit(const FileHandle&, bool);
+    static IOResult read(const FileHandle&, void *buf, size_t count);
+    static IOResult write(const FileHandle&, const void *buf, size_t count);
+    static AsyncHandle async_read(const FileHandle&, void *buf, size_t count);
+    static AsyncHandle async_write(const FileHandle&, const void *buf, size_t count);
+    static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
+    static IOResult async_cancel(AsyncHandle&);
+    static IOResult async_result(AsyncHandle&);
+    static IOResult close(const FileHandle&);
+
+    struct StdIOSwap
+    {
+        using StdFiles = IOSystem::StdFiles;
+        using StdFileType = IOSystem::StdFileType;
+        StdIOSwap(const StdFiles &);
+        ~StdIOSwap();
+
+        StdIOSwap(StdIOSwap&& other)
+        {
+            m_valid = other.m_valid;
+            if (!m_valid)
+                return;
+
+            other.m_valid = false;
+            for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
+                m_orig_fd[n] = other.m_orig_fd[n];
+        }
+
+        bool m_valid;
+        int m_orig_fd[std::tuple_size<StdFiles>::value];
+    };
+
+    static IOSystem::StdFiles get_std_files();
+};
+
+#endif // __unix__
diff --git a/src/utils/iosystem_win32.cpp b/src/utils/iosystem_win32.cpp
new file mode 100644 (file)
index 0000000..861cb1e
--- /dev/null
@@ -0,0 +1,615 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_win32.cpp  This file contains windows-specific definitions of
+/// IOSystem class members (see iosystem.h).
+
+#ifdef WIN32
+#include <io.h>
+#include <fcntl.h>
+#include <ws2tcpip.h>
+#include <afunix.h>
+#include <stdexcept>
+#include <new>
+#include <memory>
+#include <atomic>
+#include <string.h>
+#include <assert.h>
+#include "utils/iosystem.h"
+#include "utils/limits.h"
+
+// short alias for full class name
+namespace { typedef netcoredbg::IOSystemTraits<netcoredbg::Win32PlatformTag> Class; }
+
+namespace
+{
+    class Win32Exception : public std::runtime_error
+    {
+        struct Msg
+        {
+            mutable char buf[2 * LINE_MAX];
+        };
+
+        static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg())
+        {
+            int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0;
+
+            if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                    NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
+                    msg.buf + len, sizeof(msg.buf) - len, NULL))
+            {
+                return msg.buf;
+            }
+            
+            snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error);
+            return msg.buf;
+        }
+
+    public:
+        /// Specify Win32 error code and, optionally, error message prefix.
+        Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {}
+
+        /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError().
+        Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {}
+
+        /// Specify explicitly error message prefix and error code.
+        Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {}
+    };
+
+    struct Initializer
+    {
+        Initializer()
+        {
+            WSADATA wsa;
+            int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa);
+            if (wsa_error != 0)
+                throw Win32Exception("WSAStartup failed", wsa_error);
+        }
+
+        ~Initializer()
+        {
+            WSACleanup();
+        }
+    };
+
+    static Initializer initializer;
+
+#if 0 
+    // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0
+    int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2])
+    {
+
+        SOCKET serv = ::socket(domain, type, protocol);
+        if (serv == INVALID_SOCKET)
+            throw Win32Exception("can't create socket", WSAGetLastError());
+
+        // TODO
+        char name[] = "netcoredbg";
+        size_t namelen = sizeof(name)-1;
+
+        SOCKADDR_UN sa;
+        sa.sun_family = domain;
+        assert(namelen <= sizeof(sa.sun_path));
+        memcpy(sa.sun_path, name, namelen); 
+        if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            throw Win32Exception("can't bind socket", err);
+        }
+
+        u_long mode = 1;
+        if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            throw Win32Exception("ioctlsocket(FIONBIO)", err);
+        }
+
+        if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            throw Win32Exception("ioctlsocket(FIONBIO)", err);
+        }
+
+        SOCKET conn = ::socket(domain, type, protocol);
+        if (conn == INVALID_SOCKET)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            throw Win32Exception("can't create socket", err);
+        }
+
+        sa.sun_family = domain;
+        memcpy(sa.sun_path, name, namelen);
+        if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            ::closesocket(conn);
+            throw Win32Exception("can't bind socket", err);
+        }
+
+        mode = 0;
+        if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            ::closesocket(conn);
+            throw Win32Exception("ioctlsocket(FIONBIO)", err);
+        }
+
+        SOCKET newsock = ::accept(serv, NULL, NULL);
+        if (newsock == INVALID_SOCKET)
+        {
+            auto err = WSAGetLastError();
+            ::closesocket(serv);
+            ::closesocket(conn);
+            throw Win32Exception("accept on socket", err);
+        }
+
+        ::closesocket(serv);
+
+        sv[0] = newsock, sv[1] = conn;
+        return 0;
+    }
+#endif
+}
+
+
+// Function should create unnamed pipe and return two file handles
+// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
+std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
+{
+#if 0
+    SOCKET sv[2];
+    if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
+        return {FileHandle(), FileHandle()};
+#endif
+
+    static const size_t PipeSize = 32 * LINE_MAX;
+
+    SECURITY_ATTRIBUTES saAttr;
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+
+    HANDLE reading_fd, writing_fd;
+
+    static std::atomic<long> pipe_num;
+    char pipe_name[MAX_PATH + 1];
+    snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x",
+                GetCurrentProcessId(), pipe_num++);
+
+    reading_fd = CreateNamedPipeA(pipe_name,
+                    PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+                    PIPE_TYPE_BYTE | PIPE_WAIT,
+                    1,  // number of pipes
+                    PipeSize, PipeSize,
+                    0,  // 50ms default timeout
+                    &saAttr);
+                
+    if (reading_fd == INVALID_HANDLE_VALUE)
+    {
+        perror("CreateNamedPipeA");
+        return { FileHandle(), FileHandle() };
+    }
+
+    writing_fd = CreateFileA(pipe_name,
+                    GENERIC_WRITE,
+                    0,  // no sharing
+                    &saAttr,
+                    OPEN_EXISTING,
+                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+                    NULL);
+
+    if (writing_fd == INVALID_HANDLE_VALUE)
+    {
+        auto err = GetLastError();
+        ::CloseHandle(writing_fd);
+        fprintf(stderr, "CreateFile pipe error: %#x\n", err);
+        return { FileHandle(), FileHandle() };
+    }
+
+    if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0))
+    {
+        fprintf(stderr, "SetHandleInformation failed!\n");
+        return { FileHandle(), FileHandle() };
+    }
+
+    if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0))
+    {
+        fprintf(stderr, "SetHandleInformation failed!\n");
+        return { FileHandle(), FileHandle() };
+    }
+
+    return { FileHandle(reading_fd), FileHandle(writing_fd) };
+}
+
+
+// Function creates listening TCP socket on given port, waits, accepts single
+// connection, and return file descriptor related to the accepted connection.
+// In case of error, empty file handle will be returned.
+Class::FileHandle Class::listen_socket(unsigned port)
+{
+    assert(port > 0 && port < 65536);
+
+    SOCKET newsockfd;
+    int clilen;
+    struct sockaddr_in serv_addr, cli_addr;
+
+    SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (sockFd == INVALID_SOCKET)
+    {
+        fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError());
+        return {};
+    }
+
+    BOOL enable = 1;
+    if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR)
+    {
+        ::closesocket(sockFd);
+        fprintf(stderr, "setsockopt failed\n");
+        return {};
+    }
+    memset(&serv_addr, 0, sizeof(serv_addr));
+
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = INADDR_ANY;
+    serv_addr.sin_port = htons(port);
+
+    if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
+    {
+        ::closesocket(sockFd);
+        fprintf(stderr, "can't bind to specified port!\n");
+        return {};
+    }
+
+    ::listen(sockFd, 1);
+
+    clilen = sizeof(cli_addr);
+    newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen);
+    ::closesocket(sockFd);
+    if (newsockfd == INVALID_SOCKET)
+    {
+        fprintf(stderr, "can't accept connection\n");
+        return {};
+    }
+
+    return FileHandle(newsockfd);
+}
+
+// Function enables or disables inheritance of file handle for child processes.
+Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit)
+{
+    DWORD flags;
+    if (!GetHandleInformation(fh.handle, &flags))
+        return {IOResult::Error};
+
+    if (inherit)
+        flags |= HANDLE_FLAG_INHERIT;
+    else
+        flags &= ~HANDLE_FLAG_INHERIT;
+
+    if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags))
+        return {IOResult::Error};
+
+    return {IOResult::Success};
+}
+
+// Function perform reading from the file: it may read up to `count' bytes to `buf'.
+Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count)
+{
+    DWORD dwRead = 0;
+    OVERLAPPED ov = {};
+    if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov))
+        return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead };
+    else
+        return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead };
+}
+
+
+// Function perform writing to the file: it may write up to `count' byte from `buf'.
+Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count)
+{
+    // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error
+    DWORD dwWritten = 0;
+    OVERLAPPED ov = {};
+    if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov))
+        return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten };
+    else
+        return { IOResult::Success, dwWritten };
+}
+
+
+Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
+{
+    if (fh.handle == INVALID_HANDLE_VALUE)
+        return {};
+
+    AsyncHandle result; 
+    result.check_eof = true;
+    result.handle = fh.handle;
+    result.overlapped.reset(new OVERLAPPED);
+    memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
+    result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
+        return {};
+
+    DWORD val;
+    DWORD bytesRead;
+
+    if (GetConsoleMode(fh.handle, &val))
+    {   // file handle is the console
+        // first, remove all events before the first key event, if exists
+        while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val)
+        {
+            INPUT_RECORD event;
+            if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead))
+                return {};
+            if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown))
+            {
+                if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead))
+                    return {};
+            }
+            else
+                break;
+        }
+        if (!val)
+        {
+            // nothing to read from the console -- defer call to ReadFile
+            result.buf = buf, result.count = count;
+            return result;
+        }
+    }
+
+    if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
+    {
+        if (GetLastError() != ERROR_IO_PENDING)
+            return {};
+    }
+
+    return result;
+}
+
+Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
+{
+    if (fh.handle == INVALID_HANDLE_VALUE)
+        return {};
+
+    AsyncHandle result;
+    result.handle = fh.handle;
+    result.overlapped.reset(new OVERLAPPED);
+    memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
+    result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
+        return {};
+
+    if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
+    {
+        if (GetLastError() != ERROR_IO_PENDING)
+            return {};
+    }
+
+    return result;
+}
+
+bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
+{
+    // console workaround
+    for (auto it = begin; it != end; ++it)
+    {
+        if (it->handle.buf)
+        {
+            DWORD val;
+            if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val)
+                SetEvent(it->handle.overlapped->hEvent);
+        }
+    }
+
+    // count number of active handles
+    unsigned count = 0;
+    for (auto it = begin; it != end; ++it)
+        if (*it) ++count;
+
+    // allocate memory for events array
+    HANDLE *events = static_cast<HANDLE*>(alloca(count * sizeof(HANDLE)));
+    unsigned n = 0;
+    for (auto it = begin; it != end; ++it)
+    {
+        if (*it)
+            events[n++] = it->handle.overlapped->hEvent;
+    }
+
+    assert(n == count);
+    DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count()));
+    return result != WAIT_FAILED && result != WAIT_TIMEOUT;
+}
+
+Class::IOResult Class::async_cancel(AsyncHandle& h)
+{
+    if (!h)
+        return {IOResult::Error};
+    
+    if (!CloseHandle(h.overlapped->hEvent))
+        perror("CloseHandle(event) error");
+
+    // console workaround -- canceling deffered operation
+    if (h.buf)
+    {
+        h = AsyncHandle();
+        return {IOResult::Success};
+    }
+
+    IOResult result;
+    if (!CancelIoEx(h.handle, h.overlapped.get()))
+        result = {IOResult::Error};
+    else 
+        result = {IOResult::Success};
+
+    h = AsyncHandle();
+    return result;
+}
+
+Class::IOResult Class::async_result(AsyncHandle& h)
+{
+    if (!h)
+        return {IOResult::Error};
+
+    DWORD bytes = 1;
+    bool finished;
+
+    if (h.buf)
+    {
+        // workaround for the console
+        finished = true;
+    }
+    else
+    {
+        // pipes, normal files, etc...
+        finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE);
+        if (!finished)
+        {
+            DWORD error = GetLastError();
+            if (error == ERROR_IO_INCOMPLETE)
+                return {IOResult::Pending};
+        }
+    }
+
+    if (!CloseHandle(h.overlapped->hEvent))
+        perror("CloseHandle(event) error");
+
+    bool check_eof = h.check_eof;
+
+    h = AsyncHandle();
+
+    if (!finished)
+        return {IOResult::Error};
+
+    if (check_eof && bytes == 0)
+        return {IOResult::Eof, bytes};
+        
+    return {IOResult::Success, bytes};
+}
+
+// Function closes the file represented by file handle.
+Class::IOResult Class::close(const FileHandle& fh)
+{
+    assert(fh);
+    if (fh.type == FileHandle::Socket)
+        return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error };
+    else
+        return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error };
+}
+
+
+// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix.
+// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for
+// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set
+// isn't specified.
+// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles.
+// If function returns value greater than zero, at least one of the sets, passed in arguments,
+// is not empty and contains file handles ready to read/write/etc...
+
+
+// This function returns triplet of currently selected standard files.
+Class::IOSystem::StdFiles Class::get_std_files()
+{
+    using Handles = std::tuple<IOSystem::FileHandle, IOSystem::FileHandle, IOSystem::FileHandle>;
+    /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)];  // TODO
+    Handles& handles = *new (mem) Handles {
+        FileHandle(GetStdHandle(STD_INPUT_HANDLE)),
+        FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)),
+        FileHandle(GetStdHandle(STD_ERROR_HANDLE))
+    };
+    return { std::get<IOSystem::Stdin>(handles),
+             std::get<IOSystem::Stdout>(handles),
+             std::get<IOSystem::Stderr>(handles) };
+}
+
+
+// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
+// Substitution exists only during life time of StsIOSwap instance.
+Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
+{
+    const static unsigned NFD = std::tuple_size<StdFiles>::value;
+    static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
+    static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY};
+    const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
+
+    const FileHandle new_handles[NFD] = {
+        std::get<IOSystem::Stdin>(files).handle,
+        std::get<IOSystem::Stdout>(files).handle,
+        std::get<IOSystem::Stderr>(files).handle };
+
+    fflush(stdout);
+    fflush(stderr);
+
+    for (unsigned n = 0; n < NFD; n++)
+    {
+        if (new_handles[n].type != FileHandle::FileOrPipe)
+            throw std::runtime_error("can't use socket handle for stdin/stdout/stderr");
+    }
+
+    for (unsigned n = 0; n < NFD; n++)
+    {
+        m_orig_handle[n] = GetStdHandle(std_handles[n]);
+        if (m_orig_handle[n] == INVALID_HANDLE_VALUE)
+        {
+            char msg[256];
+            snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]);
+            throw std::runtime_error(msg);
+        }
+
+        if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1))
+            fprintf(stderr, "SetHandleInformation failed!\n");
+
+        if (!SetStdHandle(std_handles[n], new_handles[n].handle))
+        {
+            char msg[256];
+            snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle);
+            throw std::runtime_error(msg);
+        } 
+
+
+        int fd = _open_osfhandle(reinterpret_cast<intptr_t>(new_handles[n].handle), open_flags[n]); 
+        if (fd == -1)
+            throw Win32Exception("_open_osfhandle");
+
+        m_orig_fd[n] = _dup(open_fds[n]);
+        if (m_orig_fd[n] == -1)
+            throw Win32Exception("_dup");
+
+        if (_dup2(fd, open_fds[n]) == -1)
+            throw Win32Exception("_dup2");
+
+        close(fd);
+    }
+}
+
+Class::StdIOSwap::~StdIOSwap()
+{
+    if (!m_valid)
+        return;
+
+    const static unsigned NFD = std::tuple_size<StdFiles>::value;
+    static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
+    const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
+
+    fflush(stdout);
+    fflush(stderr);
+
+    for (unsigned n = 0; n < NFD; n++)
+    {
+        if (!SetStdHandle(std_handles[n], m_orig_handle[n]))
+        {
+            abort();
+        }
+
+        _dup2(m_orig_fd[n], open_fds[n]);
+        close(m_orig_fd[n]);
+    }
+}
+
+#endif  // WIN32
diff --git a/src/utils/iosystem_win32.h b/src/utils/iosystem_win32.h
new file mode 100644 (file)
index 0000000..424d367
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file iosystem_win32.h  This file contains windows-specific declaration of IOSystem class (see iosystem.h).
+//
+#ifdef _WIN32
+#pragma once
+#include <winsock2.h>
+#include <windows.h> // TODO
+#include <assert.h>
+#include <tuple>
+
+namespace netcoredbg
+{
+
+template <> struct IOSystemTraits<Win32PlatformTag>
+{
+    struct FileHandle
+    {
+        FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {}
+
+        explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
+
+        enum FileType
+        {
+            FileOrPipe,
+            Socket
+        };
+
+        FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {}
+        FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {}
+
+        HANDLE handle;
+        enum FileType type;
+    };
+
+    struct AsyncHandle
+    {
+        HANDLE handle;
+        std::unique_ptr<OVERLAPPED> overlapped;
+        bool check_eof;
+
+        // workaround: non-blocking reading from console
+        void *buf;
+        size_t count;
+
+        AsyncHandle()
+        : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0)
+        {}
+
+        AsyncHandle(AsyncHandle&& other) noexcept 
+        : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof),
+          buf(other.buf), count(other.count)
+        {
+            other.handle = INVALID_HANDLE_VALUE;
+        }
+
+        AsyncHandle& operator=(AsyncHandle&& other) noexcept
+        {
+            return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other));
+        }
+
+        explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
+    };
+
+    using IOSystem = typename IOSystemImpl<IOSystemTraits<Win32PlatformTag> >;
+    using IOResult = IOSystem::IOResult;
+
+    static std::pair<FileHandle, FileHandle> unnamed_pipe();
+    static FileHandle listen_socket(unsigned tcp_port);
+    static IOResult set_inherit(const FileHandle &, bool);
+    static IOResult read(const FileHandle &, void *buf, size_t count);
+    static IOResult write(const FileHandle &, const void *buf, size_t count);
+    static AsyncHandle async_read(const FileHandle &, void *buf, size_t count);
+    static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count);
+    static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
+    static IOResult async_cancel(AsyncHandle &);
+    static IOResult async_result(AsyncHandle &);
+    static IOResult close(const FileHandle &);
+
+    static IOSystem::StdFiles get_std_files();
+
+    struct StdIOSwap
+    {
+        using StdFiles = IOSystem::StdFiles;
+        using StdFileType = IOSystem::StdFileType;
+        StdIOSwap(const StdFiles &);
+        ~StdIOSwap();
+
+        StdIOSwap(StdIOSwap&& other)
+        {
+            m_valid = other.m_valid;
+            if (!m_valid)
+                return;
+
+            other.m_valid = false;
+            for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
+            {
+                m_orig_handle[n] = other.m_orig_handle[n];
+                m_orig_fd[n] = other.m_orig_fd[n];
+            }
+        }
+
+        bool m_valid;
+        HANDLE m_orig_handle[std::tuple_size<StdFiles>::value];
+        int m_orig_fd[std::tuple_size<StdFiles>::value];
+    };
+};
+
+} // ::netcoredbg
+#endif // WIN32
index 1aebd999c9e8888923964a9a5d2de89f1a8d54c9..e0b7965289d7bfe71ef5d69bbc1565210a1f7797 100644 (file)
@@ -2,7 +2,7 @@
 // See the LICENSE file in the project root for more information.
 
 #pragma once
-#include "utility.h"
+#include "utils/utility.h"
 #include "utils/string_view.h"
 
 namespace netcoredbg
diff --git a/src/utils/platform_unix.cpp b/src/utils/platform_unix.cpp
new file mode 100644 (file)
index 0000000..9d799c5
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file platform_unix.cpp  This file contains unix-specific function definitions,
+/// for functions defined in platform.h
+
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#ifdef __APPLE__
+#include <crt_externs.h>
+#endif
+#include <unistd.h>
+#include "utils/platform.h"
+
+extern char** environ;
+
+namespace netcoredbg
+{
+
+// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
+unsigned long OSPageSize()
+{
+    static unsigned long pageSize = sysconf(_SC_PAGESIZE);
+    return pageSize;
+}
+
+
+// Function suspends process execution for specified amount of time (in microseconds)
+void USleep(unsigned long usec)
+{
+    usleep(usec);
+}
+
+
+// Function returns list of environment variables (like char **environ).
+char** GetSystemEnvironment()
+{
+#if __APPLE__
+    return *(_NSGetEnviron());
+#else   // __APPLE__
+    return environ;
+#endif  // __APPLE__
+}
+
+}  // ::netcoredbg
+#endif  // __unix__
diff --git a/src/utils/platform_win32.cpp b/src/utils/platform_win32.cpp
new file mode 100644 (file)
index 0000000..6e08f7b
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 Samsung Electronics Co., Ltd.
+// See the LICENSE file in the project root for more information.
+
+/// \file platform_win32.cpp  This file contains windows-specific function definitions,
+/// for functions defined in platform.h
+
+#ifdef WIN32
+#include <windows.h>
+#include <stdlib.h>  // char **environ
+#include "utils/platform.h"
+#include "utils/limits.h"
+
+namespace netcoredbg
+{
+
+// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
+unsigned long OSPageSize()
+{
+    static unsigned long pageSize = []{
+            SYSTEM_INFO si;
+            GetSystemInfo(&si);
+            return si.dwPageSize;
+        }();
+
+    return pageSize;
+}
+
+
+// Function suspends process execution for specified amount of time (in microseconds)
+void USleep(unsigned long usec)
+{
+    HANDLE timer;
+    LARGE_INTEGER ft;
+
+    ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time
+
+    timer = CreateWaitableTimer(NULL, TRUE, NULL);
+    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
+    WaitForSingleObject(timer, INFINITE);
+    CloseHandle(timer);
+}
+
+
+// Function returns list of environment variables (like char **environ).
+char** GetSystemEnvironment()
+{
+    return environ;
+}
+
+}  // ::netcoredbg
+#endif
index 27973a440ed24691a4b6eeed0418397913b132c1..da6bfa0c59567398e1f1bb1f089515ab6528317a 100644 (file)
@@ -2,7 +2,7 @@
 // See the LICENSE file in the project root for more information.
 
 #pragma once
-#include "utility.h"
+#include "utils/utility.h"
 #include <mutex>
 #include <condition_variable>
 
diff --git a/src/utils/utility.h b/src/utils/utility.h
new file mode 100644 (file)
index 0000000..6dd50dc
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright (c) 2020 Samsung Electronics Co., LTD
+// Distributed under the MIT License.
+// See the LICENSE file in the project root for more information.
+
+// \file utility.h This file contains few supplimentar classes and functions residing in Utility namespace.
+
+#pragma once
+#include <stddef.h>
+
+namespace netcoredbg
+{
+
+namespace Utility
+{
+
+/// @{ This function is similar to `std::size()` from C++17 and allows to determine
+/// the object size as number of elements, which might be stored within object
+/// (opposed to sizeof(), which returns object sizes in bytes). Typically these
+/// functions applicable to arrays and to classes like std::array.
+template <typename T> constexpr auto Size(const T& v) -> decltype(v.size()) { return v.size(); }
+template <class T, size_t N> constexpr size_t Size(const T (&)[N]) noexcept { return N; }
+/// @}
+
+  
+/// This type is similar to `std::index_sequence` from C++14 and typically
+/// should be used in pair with `MakeSequence` type to create sequence of
+/// indices usable in template metaprogramming techniques.
+template <size_t... Index> struct Sequence {};
+
+// Actual implementation of MakeSequence type.
+namespace Internals
+{
+    template <size_t Size, size_t... Index> struct MakeSequence : MakeSequence<Size-1, Size-1, Index...> {};
+    template <size_t... Index> struct MakeSequence<0, Index...> { typedef Sequence<Index...> type; };
+}
+
+/// MakeSequence type is similar to `std::make_index_sequence<N>` from C++14.
+/// Instantination of this type exapands to `Sequence<size_t...N>` type, where
+/// `N` have values from 0 to `Size-1`.
+template <size_t Size> using MakeSequence = typename Internals::MakeSequence<Size>::type;
+
+
+/// @{ This is similar to std:void_t which is defined in c++17.
+template <typename... Args> struct MakeVoid { typedef void type; };
+template <typename... Args> using Void = typename MakeVoid<Args...>::type;
+/// @}
+
+
+/// This template is similar to `offsetof` macros in plain C. It allows
+/// to get offset of specified member in some particular class.
+template <typename Owner, typename Member>
+static inline constexpr size_t offset_of(const Member Owner::*mem)
+{
+    return reinterpret_cast<size_t>(&(reinterpret_cast<Owner*>(0)->*mem));
+}
+
+/// This template is similar to well known `container_of` macros. It allows
+/// to get pointer to owner class from pointer to member.
+template <typename Owner, typename Member>
+static inline constexpr Owner *container_of(Member *ptr, const Member Owner::*mem)
+{
+    return reinterpret_cast<Owner*>(reinterpret_cast<size_t>(ptr) - offset_of(mem));
+}
+
+
+// This is helper class which simplifies implementation of singleton classes.
+//
+// Usage example:
+//   1) define dictinct type of singleton: typedef Singleton<YourType> YourSingleton;
+//   2) to access your singleton use expression: YourSingleton::instance().operations...
+//
+template <typename T> struct Singleton
+{
+    static T& instance()
+    {
+        static T val;
+        return val;
+    }
+};
+
+} // Utility namespace
+} // namespace netcoredbg
diff --git a/src/windows/dynlibs_win32.cpp b/src/windows/dynlibs_win32.cpp
deleted file mode 100644 (file)
index 7a1fcb0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file dynlibsi_win32.h  This file contains windows-specific function definitions
-/// required to work with dynamically loading libraries.
-
-#ifdef WIN32
-#include <windows.h>
-#include "utils/dynlibs.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-// This functon load specified library and returns handle (which then
-// can be passed to DLSym and DLCLose functions).
-// In case of error function returns NULL.
-DLHandle DLOpen(const std::string &path)
-{
-    return reinterpret_cast<DLHandle>(::LoadLibraryExA(path.c_str(), NULL, 0));
-}
-
-// This function resolves symbol address within library specified by handle,
-// and returns it's address, in case of error function returns NULL.
-void* DLSym(DLHandle handle, string_view name)
-{
-    char str[LINE_MAX];
-    if (name.size() >= sizeof(str))
-        return {};
-
-    name.copy(str, name.size());
-    str[name.size()] = 0;
-    return ::GetProcAddress((HMODULE)handle, str);
-}
-
-/// This function unloads previously loadded library, specified by handle.
-/// In case of error this function returns `false'.
-bool DLClose(DLHandle handle)
-{
-    return ::FreeLibrary(reinterpret_cast<HMODULE>(handle));
-}
-
-}  // ::netcoredbg
-#endif
diff --git a/src/windows/filesystem_win32.cpp b/src/windows/filesystem_win32.cpp
deleted file mode 100644 (file)
index c5380c4..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_win32.cpp
-/// This file contains definitions of windows-specific functions related to file system.
-
-#ifdef WIN32
-#include <windows.h>
-#include <string>
-#include "utils/filesystem.h"
-#include "utils/limits.h"
-
-
-namespace netcoredbg
-{
-
-const char* FileSystemTraits<Win32PlatformTag>::PathSeparatorSymbols = "/\\";
-
-// Function returns absolute path to currently running executable.
-std::string GetExeAbsPath()
-{
-    const size_t MAX_LONGPATH = 1024;
-    char hostPath[MAX_LONGPATH + 1];
-    static const std::string result(hostPath, ::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH));
-    return result;
-}
-
-
-// Function returns path to directory, which should be used for creation of
-// temporary files. Typically this is `/tmp` on Unix and something like
-// `C:\Users\localuser\Appdata\Local\Temp` on Windows.
-string_view GetTempDir()
-{
-    CHAR path[MAX_PATH + 1];
-    static const std::string result(path, GetTempPathA(MAX_PATH, path));
-    return result;
-}
-
-
-// Function changes current working directory. Return value is `false` in case of error.
-bool SetWorkDir(const std::string &path)
-{
-    // In the ANSI version of this function, the name is limited to MAX_PATH characters.
-    // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcurrentdirectory
-    if (path.size() >= MAX_PATH)
-        return false;
-
-    return SetCurrentDirectoryA(path.c_str());
-}
-
-}  // ::netcoredbg
-#endif
diff --git a/src/windows/filesystem_win32.h b/src/windows/filesystem_win32.h
deleted file mode 100644 (file)
index ac14fcf..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file filesystem_win32.h  This file contains windows-specific details to FileSystem class.
-
-#ifdef WIN32
-#pragma once
-#include <cstddef>
-#include <windows.h>
-
-namespace netcoredbg
-{
-    template <> struct FileSystemTraits<Win32PlatformTag>
-    {
-        const static size_t PathMax = MAX_PATH;
-        const static size_t NameMax = MAX_PATH - 1; // not include terminal null.
-        const static char PathSeparator = '\\';
-        const static char* PathSeparatorSymbols;
-    };
-} 
-#endif
diff --git a/src/windows/interop_win32.cpp b/src/windows/interop_win32.cpp
deleted file mode 100644 (file)
index a0d06f8..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file interop_win32.h  This file contains windows-specific functions for Interop class defined in interop.h
-
-#ifdef WIN32
-#include <windows.h>
-#include <stddef.h>
-#include <string.h>
-#include <string>
-#include <set>
-
-#include "managed/interop.h"
-#include "utils/filesystem.h"
-#include "utils/limits.h"
-
-
-namespace netcoredbg
-{
-
-// This function searches *.dll files in specified directory and adds full paths to files
-// to semicolon-separated list `tpaList'.
-template <>
-void InteropTraits<Win32PlatformTag>::AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
-{
-    const char * const tpaExtensions[] = {
-        "*.ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
-        "*.dll",
-        "*.ni.exe",
-        "*.exe",
-    };
-
-    std::set<std::string> addedAssemblies;
-
-    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
-    // then files with .dll extension, etc.
-    for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
-    {
-        const char* ext = tpaExtensions[extIndex];
-        size_t extLength = strlen(ext);
-
-        std::string assemblyPath(directory);
-        assemblyPath += FileSystem::PathSeparator;
-        assemblyPath.append(tpaExtensions[extIndex]);
-
-        WIN32_FIND_DATAA data;
-        HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
-
-        if (findHandle != INVALID_HANDLE_VALUE)
-        {
-            do
-            {
-                if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
-                {
-
-                    std::string filename(data.cFileName);
-                    size_t extPos = filename.length() - extLength;
-                    std::string filenameWithoutExt(filename.substr(0, extPos));
-
-                    // Make sure if we have an assembly with multiple extensions present,
-                    // we insert only one version of it.
-                    if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
-                    {
-                        addedAssemblies.insert(filenameWithoutExt);
-
-                        tpaList.append(directory);
-                        tpaList += FileSystem::PathSeparator;
-                        tpaList.append(filename);
-                        tpaList.append(";");
-                    }
-                }
-            }
-            while (0 != FindNextFileA(findHandle, &data));
-
-            FindClose(findHandle);
-        }
-    }
-}
-
-// This function unsets `CORECLR_ENABLE_PROFILING' environment variable.
-template <>
-void InteropTraits<Win32PlatformTag>::UnsetCoreCLREnv()
-{
-    _putenv("CORECLR_ENABLE_PROFILING=");
-}
-
-// Returns the length of a BSTR.
-template <>
-UINT InteropTraits<Win32PlatformTag>::SysStringLen(BSTR bstrString)
-{
-    return ::SysStringLen(bstrString);
-}
-
-}  // ::netcoredbg
-#endif  // WIN32
diff --git a/src/windows/iosystem_win32.cpp b/src/windows/iosystem_win32.cpp
deleted file mode 100644 (file)
index 861cb1e..0000000
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_win32.cpp  This file contains windows-specific definitions of
-/// IOSystem class members (see iosystem.h).
-
-#ifdef WIN32
-#include <io.h>
-#include <fcntl.h>
-#include <ws2tcpip.h>
-#include <afunix.h>
-#include <stdexcept>
-#include <new>
-#include <memory>
-#include <atomic>
-#include <string.h>
-#include <assert.h>
-#include "utils/iosystem.h"
-#include "utils/limits.h"
-
-// short alias for full class name
-namespace { typedef netcoredbg::IOSystemTraits<netcoredbg::Win32PlatformTag> Class; }
-
-namespace
-{
-    class Win32Exception : public std::runtime_error
-    {
-        struct Msg
-        {
-            mutable char buf[2 * LINE_MAX];
-        };
-
-        static const char* getmsg(const char *prefix, DWORD error, const Msg& msg = Msg())
-        {
-            int len = prefix ? snprintf(msg.buf, sizeof(msg.buf), "%s: ", prefix) : 0;
-
-            if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                    NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
-                    msg.buf + len, sizeof(msg.buf) - len, NULL))
-            {
-                return msg.buf;
-            }
-            
-            snprintf(msg.buf + len, sizeof(msg.buf) - len, "error %#x", error);
-            return msg.buf;
-        }
-
-    public:
-        /// Specify Win32 error code and, optionally, error message prefix.
-        Win32Exception(DWORD error, const char* prefix = nullptr) : std::runtime_error(getmsg(prefix, error)) {}
-
-        /// Specify error message prefix (optionally). Win32 error code will be obtained via call to GetLastError().
-        Win32Exception(const char *prefix = nullptr) : Win32Exception(prefix, GetLastError()) {}
-
-        /// Specify explicitly error message prefix and error code.
-        Win32Exception(const char *prefix, DWORD error) : Win32Exception(error, prefix) {}
-    };
-
-    struct Initializer
-    {
-        Initializer()
-        {
-            WSADATA wsa;
-            int wsa_error = WSAStartup(MAKEWORD(2, 2), &wsa);
-            if (wsa_error != 0)
-                throw Win32Exception("WSAStartup failed", wsa_error);
-        }
-
-        ~Initializer()
-        {
-            WSACleanup();
-        }
-    };
-
-    static Initializer initializer;
-
-#if 0 
-    // assuming domain=AF_UNIX, type=SOCK_STREAM, protocol=0
-    int wsa_socketpair(int domain, int type, int protocol, SOCKET sv[2])
-    {
-
-        SOCKET serv = ::socket(domain, type, protocol);
-        if (serv == INVALID_SOCKET)
-            throw Win32Exception("can't create socket", WSAGetLastError());
-
-        // TODO
-        char name[] = "netcoredbg";
-        size_t namelen = sizeof(name)-1;
-
-        SOCKADDR_UN sa;
-        sa.sun_family = domain;
-        assert(namelen <= sizeof(sa.sun_path));
-        memcpy(sa.sun_path, name, namelen); 
-        if (::bind(serv, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            throw Win32Exception("can't bind socket", err);
-        }
-
-        u_long mode = 1;
-        if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            throw Win32Exception("ioctlsocket(FIONBIO)", err);
-        }
-
-        if (::listen(serv, 1) == SOCKET_ERROR && WSAGetLastError() != WSAEINPROGRESS)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            throw Win32Exception("ioctlsocket(FIONBIO)", err);
-        }
-
-        SOCKET conn = ::socket(domain, type, protocol);
-        if (conn == INVALID_SOCKET)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            throw Win32Exception("can't create socket", err);
-        }
-
-        sa.sun_family = domain;
-        memcpy(sa.sun_path, name, namelen);
-        if (::connect(conn, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            ::closesocket(conn);
-            throw Win32Exception("can't bind socket", err);
-        }
-
-        mode = 0;
-        if (::ioctlsocket(serv, FIONBIO, &mode) == SOCKET_ERROR)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            ::closesocket(conn);
-            throw Win32Exception("ioctlsocket(FIONBIO)", err);
-        }
-
-        SOCKET newsock = ::accept(serv, NULL, NULL);
-        if (newsock == INVALID_SOCKET)
-        {
-            auto err = WSAGetLastError();
-            ::closesocket(serv);
-            ::closesocket(conn);
-            throw Win32Exception("accept on socket", err);
-        }
-
-        ::closesocket(serv);
-
-        sv[0] = newsock, sv[1] = conn;
-        return 0;
-    }
-#endif
-}
-
-
-// Function should create unnamed pipe and return two file handles
-// (reading and writing pipe ends) or return empty file handles if pipe can't be created.
-std::pair<Class::FileHandle, Class::FileHandle> Class::unnamed_pipe()
-{
-#if 0
-    SOCKET sv[2];
-    if (wsa_socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
-        return {FileHandle(), FileHandle()};
-#endif
-
-    static const size_t PipeSize = 32 * LINE_MAX;
-
-    SECURITY_ATTRIBUTES saAttr;
-    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-    saAttr.bInheritHandle = TRUE;
-    saAttr.lpSecurityDescriptor = NULL;
-
-    HANDLE reading_fd, writing_fd;
-
-    static std::atomic<long> pipe_num;
-    char pipe_name[MAX_PATH + 1];
-    snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\Win32Pipes.%08x.%08x",
-                GetCurrentProcessId(), pipe_num++);
-
-    reading_fd = CreateNamedPipeA(pipe_name,
-                    PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
-                    PIPE_TYPE_BYTE | PIPE_WAIT,
-                    1,  // number of pipes
-                    PipeSize, PipeSize,
-                    0,  // 50ms default timeout
-                    &saAttr);
-                
-    if (reading_fd == INVALID_HANDLE_VALUE)
-    {
-        perror("CreateNamedPipeA");
-        return { FileHandle(), FileHandle() };
-    }
-
-    writing_fd = CreateFileA(pipe_name,
-                    GENERIC_WRITE,
-                    0,  // no sharing
-                    &saAttr,
-                    OPEN_EXISTING,
-                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
-                    NULL);
-
-    if (writing_fd == INVALID_HANDLE_VALUE)
-    {
-        auto err = GetLastError();
-        ::CloseHandle(writing_fd);
-        fprintf(stderr, "CreateFile pipe error: %#x\n", err);
-        return { FileHandle(), FileHandle() };
-    }
-
-    if (!SetHandleInformation(writing_fd, HANDLE_FLAG_INHERIT, 0))
-    {
-        fprintf(stderr, "SetHandleInformation failed!\n");
-        return { FileHandle(), FileHandle() };
-    }
-
-    if (!SetHandleInformation(reading_fd, HANDLE_FLAG_INHERIT, 0))
-    {
-        fprintf(stderr, "SetHandleInformation failed!\n");
-        return { FileHandle(), FileHandle() };
-    }
-
-    return { FileHandle(reading_fd), FileHandle(writing_fd) };
-}
-
-
-// Function creates listening TCP socket on given port, waits, accepts single
-// connection, and return file descriptor related to the accepted connection.
-// In case of error, empty file handle will be returned.
-Class::FileHandle Class::listen_socket(unsigned port)
-{
-    assert(port > 0 && port < 65536);
-
-    SOCKET newsockfd;
-    int clilen;
-    struct sockaddr_in serv_addr, cli_addr;
-
-    SOCKET sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
-    if (sockFd == INVALID_SOCKET)
-    {
-        fprintf(stderr, "can't create socket: %#x\n", WSAGetLastError());
-        return {};
-    }
-
-    BOOL enable = 1;
-    if (::setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR)
-    {
-        ::closesocket(sockFd);
-        fprintf(stderr, "setsockopt failed\n");
-        return {};
-    }
-    memset(&serv_addr, 0, sizeof(serv_addr));
-
-    serv_addr.sin_family = AF_INET;
-    serv_addr.sin_addr.s_addr = INADDR_ANY;
-    serv_addr.sin_port = htons(port);
-
-    if (::bind(sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
-    {
-        ::closesocket(sockFd);
-        fprintf(stderr, "can't bind to specified port!\n");
-        return {};
-    }
-
-    ::listen(sockFd, 1);
-
-    clilen = sizeof(cli_addr);
-    newsockfd = ::accept(sockFd, (struct sockaddr*)&cli_addr, &clilen);
-    ::closesocket(sockFd);
-    if (newsockfd == INVALID_SOCKET)
-    {
-        fprintf(stderr, "can't accept connection\n");
-        return {};
-    }
-
-    return FileHandle(newsockfd);
-}
-
-// Function enables or disables inheritance of file handle for child processes.
-Class::IOResult Class::set_inherit(const FileHandle& fh, bool inherit)
-{
-    DWORD flags;
-    if (!GetHandleInformation(fh.handle, &flags))
-        return {IOResult::Error};
-
-    if (inherit)
-        flags |= HANDLE_FLAG_INHERIT;
-    else
-        flags &= ~HANDLE_FLAG_INHERIT;
-
-    if (!SetHandleInformation(fh.handle, HANDLE_FLAG_INHERIT, flags))
-        return {IOResult::Error};
-
-    return {IOResult::Success};
-}
-
-// Function perform reading from the file: it may read up to `count' bytes to `buf'.
-Class::IOResult Class::read(const FileHandle& fh, void *buf, size_t count)
-{
-    DWORD dwRead = 0;
-    OVERLAPPED ov = {};
-    if (! ReadFile(fh.handle, buf, (DWORD)count, &dwRead, &ov))
-        return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwRead };
-    else
-        return { (dwRead == 0 ? IOResult::Eof : IOResult::Success), dwRead };
-}
-
-
-// Function perform writing to the file: it may write up to `count' byte from `buf'.
-Class::IOResult Class::write(const FileHandle& fh, const void *buf, size_t count)
-{
-    // see https://stackoverflow.com/questions/43939424/writefile-with-windows-sockets-returns-invalid-parameter-error
-    DWORD dwWritten = 0;
-    OVERLAPPED ov = {};
-    if (! WriteFile(fh.handle, buf, (DWORD)count, &dwWritten, &ov))
-        return { (GetLastError() == ERROR_IO_PENDING ? IOResult::Pending : IOResult::Error), dwWritten };
-    else
-        return { IOResult::Success, dwWritten };
-}
-
-
-Class::AsyncHandle Class::async_read(const FileHandle& fh, void *buf, size_t count)
-{
-    if (fh.handle == INVALID_HANDLE_VALUE)
-        return {};
-
-    AsyncHandle result; 
-    result.check_eof = true;
-    result.handle = fh.handle;
-    result.overlapped.reset(new OVERLAPPED);
-    memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
-    result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
-        return {};
-
-    DWORD val;
-    DWORD bytesRead;
-
-    if (GetConsoleMode(fh.handle, &val))
-    {   // file handle is the console
-        // first, remove all events before the first key event, if exists
-        while (GetNumberOfConsoleInputEvents(fh.handle, &val) && val)
-        {
-            INPUT_RECORD event;
-            if (!PeekConsoleInput(fh.handle, &event, 1, &bytesRead))
-                return {};
-            if (event.EventType != KEY_EVENT || (event.EventType == KEY_EVENT && !event.Event.KeyEvent.bKeyDown))
-            {
-                if (!ReadConsoleInput(fh.handle, &event, 1, &bytesRead))
-                    return {};
-            }
-            else
-                break;
-        }
-        if (!val)
-        {
-            // nothing to read from the console -- defer call to ReadFile
-            result.buf = buf, result.count = count;
-            return result;
-        }
-    }
-
-    if (! ReadFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
-    {
-        if (GetLastError() != ERROR_IO_PENDING)
-            return {};
-    }
-
-    return result;
-}
-
-Class::AsyncHandle Class::async_write(const FileHandle& fh, const void *buf, size_t count)
-{
-    if (fh.handle == INVALID_HANDLE_VALUE)
-        return {};
-
-    AsyncHandle result;
-    result.handle = fh.handle;
-    result.overlapped.reset(new OVERLAPPED);
-    memset(result.overlapped.get(), 0, sizeof(OVERLAPPED));
-    result.overlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (result.overlapped->hEvent == INVALID_HANDLE_VALUE)
-        return {};
-
-    if (! WriteFile(fh.handle, buf, (DWORD)count, nullptr, result.overlapped.get()))
-    {
-        if (GetLastError() != ERROR_IO_PENDING)
-            return {};
-    }
-
-    return result;
-}
-
-bool Class::async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds timeout)
-{
-    // console workaround
-    for (auto it = begin; it != end; ++it)
-    {
-        if (it->handle.buf)
-        {
-            DWORD val;
-            if (GetNumberOfConsoleInputEvents(it->handle.handle, &val) && val)
-                SetEvent(it->handle.overlapped->hEvent);
-        }
-    }
-
-    // count number of active handles
-    unsigned count = 0;
-    for (auto it = begin; it != end; ++it)
-        if (*it) ++count;
-
-    // allocate memory for events array
-    HANDLE *events = static_cast<HANDLE*>(alloca(count * sizeof(HANDLE)));
-    unsigned n = 0;
-    for (auto it = begin; it != end; ++it)
-    {
-        if (*it)
-            events[n++] = it->handle.overlapped->hEvent;
-    }
-
-    assert(n == count);
-    DWORD result = WaitForMultipleObjects(count, events, FALSE, DWORD(timeout.count()));
-    return result != WAIT_FAILED && result != WAIT_TIMEOUT;
-}
-
-Class::IOResult Class::async_cancel(AsyncHandle& h)
-{
-    if (!h)
-        return {IOResult::Error};
-    
-    if (!CloseHandle(h.overlapped->hEvent))
-        perror("CloseHandle(event) error");
-
-    // console workaround -- canceling deffered operation
-    if (h.buf)
-    {
-        h = AsyncHandle();
-        return {IOResult::Success};
-    }
-
-    IOResult result;
-    if (!CancelIoEx(h.handle, h.overlapped.get()))
-        result = {IOResult::Error};
-    else 
-        result = {IOResult::Success};
-
-    h = AsyncHandle();
-    return result;
-}
-
-Class::IOResult Class::async_result(AsyncHandle& h)
-{
-    if (!h)
-        return {IOResult::Error};
-
-    DWORD bytes = 1;
-    bool finished;
-
-    if (h.buf)
-    {
-        // workaround for the console
-        finished = true;
-    }
-    else
-    {
-        // pipes, normal files, etc...
-        finished = GetOverlappedResult(h.handle, h.overlapped.get(), &bytes, FALSE);
-        if (!finished)
-        {
-            DWORD error = GetLastError();
-            if (error == ERROR_IO_INCOMPLETE)
-                return {IOResult::Pending};
-        }
-    }
-
-    if (!CloseHandle(h.overlapped->hEvent))
-        perror("CloseHandle(event) error");
-
-    bool check_eof = h.check_eof;
-
-    h = AsyncHandle();
-
-    if (!finished)
-        return {IOResult::Error};
-
-    if (check_eof && bytes == 0)
-        return {IOResult::Eof, bytes};
-        
-    return {IOResult::Success, bytes};
-}
-
-// Function closes the file represented by file handle.
-Class::IOResult Class::close(const FileHandle& fh)
-{
-    assert(fh);
-    if (fh.type == FileHandle::Socket)
-        return { ::closesocket((SOCKET)fh.handle) == 0 ? IOResult::Success : IOResult::Error };
-    else
-        return { ::CloseHandle(fh.handle) ? IOResult::Success : IOResult::Error };
-}
-
-
-// Function allows non-blocking IO on files, it is similar with select(2) system call on Unix.
-// Arguments includes: pointers to three sets of file handles (for reading, for writing, and for
-// exceptions), and timeout value, in milliseconds. Any pointer might have NULL value if some set
-// isn't specified.
-// Function returns -1 on error, 0 on timeout or number of ready to read/write file handles.
-// If function returns value greater than zero, at least one of the sets, passed in arguments,
-// is not empty and contains file handles ready to read/write/etc...
-
-
-// This function returns triplet of currently selected standard files.
-Class::IOSystem::StdFiles Class::get_std_files()
-{
-    using Handles = std::tuple<IOSystem::FileHandle, IOSystem::FileHandle, IOSystem::FileHandle>;
-    /*thread_local*/ static alignas(alignof(Handles)) char mem[sizeof(Handles)];  // TODO
-    Handles& handles = *new (mem) Handles {
-        FileHandle(GetStdHandle(STD_INPUT_HANDLE)),
-        FileHandle(GetStdHandle(STD_OUTPUT_HANDLE)),
-        FileHandle(GetStdHandle(STD_ERROR_HANDLE))
-    };
-    return { std::get<IOSystem::Stdin>(handles),
-             std::get<IOSystem::Stdout>(handles),
-             std::get<IOSystem::Stderr>(handles) };
-}
-
-
-// StdIOSwap class allows to substitute set of standard IO files with one provided to constructor.
-// Substitution exists only during life time of StsIOSwap instance.
-Class::StdIOSwap::StdIOSwap(const StdFiles& files) : m_valid(true)
-{
-    const static unsigned NFD = std::tuple_size<StdFiles>::value;
-    static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
-    static const int open_flags[NFD] = {_O_RDONLY | _O_BINARY, _O_BINARY, _O_BINARY};
-    const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
-
-    const FileHandle new_handles[NFD] = {
-        std::get<IOSystem::Stdin>(files).handle,
-        std::get<IOSystem::Stdout>(files).handle,
-        std::get<IOSystem::Stderr>(files).handle };
-
-    fflush(stdout);
-    fflush(stderr);
-
-    for (unsigned n = 0; n < NFD; n++)
-    {
-        if (new_handles[n].type != FileHandle::FileOrPipe)
-            throw std::runtime_error("can't use socket handle for stdin/stdout/stderr");
-    }
-
-    for (unsigned n = 0; n < NFD; n++)
-    {
-        m_orig_handle[n] = GetStdHandle(std_handles[n]);
-        if (m_orig_handle[n] == INVALID_HANDLE_VALUE)
-        {
-            char msg[256];
-            snprintf(msg, sizeof(msg), "GetStdHandle(%#x): error", std_handles[n]);
-            throw std::runtime_error(msg);
-        }
-
-        if (!SetHandleInformation(new_handles[n].handle, HANDLE_FLAG_INHERIT, 1))
-            fprintf(stderr, "SetHandleInformation failed!\n");
-
-        if (!SetStdHandle(std_handles[n], new_handles[n].handle))
-        {
-            char msg[256];
-            snprintf(msg, sizeof(msg), "SetStdHandle(%#x, %p): error", std_handles[n], new_handles[n].handle);
-            throw std::runtime_error(msg);
-        } 
-
-
-        int fd = _open_osfhandle(reinterpret_cast<intptr_t>(new_handles[n].handle), open_flags[n]); 
-        if (fd == -1)
-            throw Win32Exception("_open_osfhandle");
-
-        m_orig_fd[n] = _dup(open_fds[n]);
-        if (m_orig_fd[n] == -1)
-            throw Win32Exception("_dup");
-
-        if (_dup2(fd, open_fds[n]) == -1)
-            throw Win32Exception("_dup2");
-
-        close(fd);
-    }
-}
-
-Class::StdIOSwap::~StdIOSwap()
-{
-    if (!m_valid)
-        return;
-
-    const static unsigned NFD = std::tuple_size<StdFiles>::value;
-    static const DWORD std_handles[NFD] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
-    const int open_fds[NFD] = {_fileno(stdin), _fileno(stdout), _fileno(stderr)};
-
-    fflush(stdout);
-    fflush(stderr);
-
-    for (unsigned n = 0; n < NFD; n++)
-    {
-        if (!SetStdHandle(std_handles[n], m_orig_handle[n]))
-        {
-            abort();
-        }
-
-        _dup2(m_orig_fd[n], open_fds[n]);
-        close(m_orig_fd[n]);
-    }
-}
-
-#endif  // WIN32
diff --git a/src/windows/iosystem_win32.h b/src/windows/iosystem_win32.h
deleted file mode 100644 (file)
index 424d367..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file iosystem_win32.h  This file contains windows-specific declaration of IOSystem class (see iosystem.h).
-//
-#ifdef _WIN32
-#pragma once
-#include <winsock2.h>
-#include <windows.h> // TODO
-#include <assert.h>
-#include <tuple>
-
-namespace netcoredbg
-{
-
-template <> struct IOSystemTraits<Win32PlatformTag>
-{
-    struct FileHandle
-    {
-        FileHandle() : handle(INVALID_HANDLE_VALUE), type(FileOrPipe) {}
-
-        explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
-
-        enum FileType
-        {
-            FileOrPipe,
-            Socket
-        };
-
-        FileHandle(HANDLE filefd) : handle(filefd), type(FileOrPipe) {}
-        FileHandle(SOCKET sockfd) : handle((HANDLE)sockfd), type(Socket) {}
-
-        HANDLE handle;
-        enum FileType type;
-    };
-
-    struct AsyncHandle
-    {
-        HANDLE handle;
-        std::unique_ptr<OVERLAPPED> overlapped;
-        bool check_eof;
-
-        // workaround: non-blocking reading from console
-        void *buf;
-        size_t count;
-
-        AsyncHandle()
-        : handle(INVALID_HANDLE_VALUE), overlapped(), check_eof(false), buf(nullptr), count(0)
-        {}
-
-        AsyncHandle(AsyncHandle&& other) noexcept 
-        : handle(other.handle), overlapped(std::move(other.overlapped)), check_eof(other.check_eof),
-          buf(other.buf), count(other.count)
-        {
-            other.handle = INVALID_HANDLE_VALUE;
-        }
-
-        AsyncHandle& operator=(AsyncHandle&& other) noexcept
-        {
-            return this->~AsyncHandle(), *new (this) AsyncHandle(std::move(other));
-        }
-
-        explicit operator bool() const { return handle != INVALID_HANDLE_VALUE; }
-    };
-
-    using IOSystem = typename IOSystemImpl<IOSystemTraits<Win32PlatformTag> >;
-    using IOResult = IOSystem::IOResult;
-
-    static std::pair<FileHandle, FileHandle> unnamed_pipe();
-    static FileHandle listen_socket(unsigned tcp_port);
-    static IOResult set_inherit(const FileHandle &, bool);
-    static IOResult read(const FileHandle &, void *buf, size_t count);
-    static IOResult write(const FileHandle &, const void *buf, size_t count);
-    static AsyncHandle async_read(const FileHandle &, void *buf, size_t count);
-    static AsyncHandle async_write(const FileHandle &, const void *buf, size_t count);
-    static bool async_wait(IOSystem::AsyncHandleIterator begin, IOSystem::AsyncHandleIterator end, std::chrono::milliseconds);
-    static IOResult async_cancel(AsyncHandle &);
-    static IOResult async_result(AsyncHandle &);
-    static IOResult close(const FileHandle &);
-
-    static IOSystem::StdFiles get_std_files();
-
-    struct StdIOSwap
-    {
-        using StdFiles = IOSystem::StdFiles;
-        using StdFileType = IOSystem::StdFileType;
-        StdIOSwap(const StdFiles &);
-        ~StdIOSwap();
-
-        StdIOSwap(StdIOSwap&& other)
-        {
-            m_valid = other.m_valid;
-            if (!m_valid)
-                return;
-
-            other.m_valid = false;
-            for (unsigned n = 0; n < std::tuple_size<StdFiles>::value; n++)
-            {
-                m_orig_handle[n] = other.m_orig_handle[n];
-                m_orig_fd[n] = other.m_orig_fd[n];
-            }
-        }
-
-        bool m_valid;
-        HANDLE m_orig_handle[std::tuple_size<StdFiles>::value];
-        int m_orig_fd[std::tuple_size<StdFiles>::value];
-    };
-};
-
-} // ::netcoredbg
-#endif // WIN32
diff --git a/src/windows/platform_win32.cpp b/src/windows/platform_win32.cpp
deleted file mode 100644 (file)
index 6e08f7b..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2020 Samsung Electronics Co., Ltd.
-// See the LICENSE file in the project root for more information.
-
-/// \file platform_win32.cpp  This file contains windows-specific function definitions,
-/// for functions defined in platform.h
-
-#ifdef WIN32
-#include <windows.h>
-#include <stdlib.h>  // char **environ
-#include "utils/platform.h"
-#include "utils/limits.h"
-
-namespace netcoredbg
-{
-
-// Function returns memory mapping page size (like sysconf(_SC_PAGESIZE) on Unix).
-unsigned long OSPageSize()
-{
-    static unsigned long pageSize = []{
-            SYSTEM_INFO si;
-            GetSystemInfo(&si);
-            return si.dwPageSize;
-        }();
-
-    return pageSize;
-}
-
-
-// Function suspends process execution for specified amount of time (in microseconds)
-void USleep(unsigned long usec)
-{
-    HANDLE timer;
-    LARGE_INTEGER ft;
-
-    ft.QuadPart = -(10*(long)usec); // Convert to 100 nanosecond interval, negative value indicates relative time
-
-    timer = CreateWaitableTimer(NULL, TRUE, NULL);
-    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
-    WaitForSingleObject(timer, INFINITE);
-    CloseHandle(timer);
-}
-
-
-// Function returns list of environment variables (like char **environ).
-char** GetSystemEnvironment()
-{
-    return environ;
-}
-
-}  // ::netcoredbg
-#endif