Commit 2c2c8a69 authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #464 from sandstorm-io/appveyor-tests

Have AppVeyor run the tests.
parents 4c169a09 6fb5003c
......@@ -7,9 +7,8 @@
# support for.
# - Use CMake to ...
# build Cap'n Proto with MinGW.
# build Cap'n Proto with VS2015 and the MinGW-built capnp tools.
# build the Cap'n Proto samples with VS2015 and the MinGW-built capnp tools.
# - Archive both Cap'n Proto builds in capnproto-c++-{mingw,vs2015}.zip artifacts.
# build Cap'n Proto with VS2015.
# build Cap'n Proto samples with VS2015.
version: "{build}"
......@@ -52,29 +51,23 @@ build_script:
-DCMAKE_BUILD_TYPE=%BUILD_TYPE%
-DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_MINGW%
- cmake --build build-mingw --target install -- -j%NUMBER_OF_PROCESSORS%
- set PATH=%INSTALL_PREFIX_MINGW%\bin;%PATH%
- echo "Building Cap'n Proto with Visual Studio 2015"
- >-
cmake -Hc++ -Bbuild-vs2015 -G "Visual Studio 14 2015" -A x64
-DEXTERNAL_CAPNP=ON
-DCMAKE_INSTALL_PREFIX=%INSTALL_PREFIX_VS2015%
- cmake --build build-vs2015 --config %BUILD_TYPE% --target install
# TODO(someday): pass `-- /maxcpucount` for a parallel build. Right now it occasionally expresses
# a filesystem-related race: capnp-capnpc++ complains that it can't create test.capnp.h.
- echo "Building Cap'n Proto samples with Visual Studio 2015"
- >-
cmake -Hc++/samples -Bbuild-samples -G "Visual Studio 14 2015" -A x64
cmake -Hc++/samples -Bbuild-vs2015-samples -G "Visual Studio 14 2015" -A x64
-DCMAKE_PREFIX_PATH=%INSTALL_PREFIX_VS2015%
-DCAPNP_EXECUTABLE=%INSTALL_PREFIX_MINGW%\bin\capnp.exe
-DCAPNPC_CXX_EXECUTABLE=%INSTALL_PREFIX_MINGW%\bin\capnpc-c++.exe
- cmake --build build-samples --config %BUILD_TYPE%
- cmake --build build-vs2015-samples --config %BUILD_TYPE%
# TODO(soon): Fix tests on Windows.
#
# Tests are not currently run, because until the tests start passing normally, it's more useful for
# us to only be notified of build errors. When they start passing, uncomment the code below.
#
#test_script:
# - timeout /t 2
# # Sleep a little to prevent interleaving test output with build output.
# - cd build-msvc\src
# - ctest -V -C %BUILD_TYPE%
test_script:
- timeout /t 2
# Sleep a little to prevent interleaving test output with build output.
- cd build-vs2015\src
- ctest -V -C %BUILD_TYPE%
......@@ -29,31 +29,11 @@ option(BUILD_TESTING "Build unit tests and enable CTest 'check' target." ON)
option(EXTERNAL_CAPNP "Use the system capnp binary, or the one specified in $CAPNP, instead of using the compiled one." OFF)
option(CAPNP_LITE "Compile Cap'n Proto in 'lite mode', in which all reflection APIs (schema.h, dynamic.h, etc.) are not included. Produces a smaller library at the cost of features. All programs built against the library must be compiled with -DCAPNP_LITE. Requires EXTERNAL_CAPNP." OFF)
if(MSVC)
option(CAPNP_BUILD_TOOLS "Build Cap'n Proto tools" OFF)
else()
option(CAPNP_BUILD_TOOLS "Build Cap'n Proto tools" ON)
endif()
mark_as_advanced(CAPNP_BUILD_TOOLS)
# TODO(msvc): This option exists purely to ease the MSVC port. As of this writing, the latest
# stable version of Visual C++ (VS2015U3) cannot build the Cap'n Proto tools, so this option
# defaults to OFF in that case. In all other cases it defaults to ON.
# Check for invalid combinations of build options
if(CAPNP_LITE AND BUILD_TESTING AND NOT EXTERNAL_CAPNP)
message(SEND_ERROR "You must set EXTERNAL_CAPNP when using CAPNP_LITE and BUILD_TESTING.")
endif()
if(NOT CAPNP_BUILD_TOOLS AND BUILD_TESTING AND NOT EXTERNAL_CAPNP)
message(SEND_ERROR "You must set EXTERNAL_CAPNP when using BUILD_TESTING but not CAPNP_BUILD_TOOLS")
if(MSVC)
message(SEND_ERROR "Note: CAPNP_BUILD_TOOLS is off because you are using MSVC, which cannot build the tools")
# If we're using MSVC, then we know the reason why CAPNP_BUILD_TOOLS is off and should help the
# user out. If we're NOT using MSVC, then the user must have explicitly set CAPNP_BUILD_TOOLS
# off, so they're on their own.
endif()
endif()
if(CAPNP_LITE)
set(CAPNP_LITE_FLAG "-DCAPNP_LITE")
# This flag is attached as PUBLIC target_compile_definition to kj target
......@@ -64,6 +44,14 @@ endif()
if(MSVC)
# TODO(cleanup): Enable higher warning level in MSVC, but make sure to test
# build with that warning level and clean out false positives.
add_compile_options(/wo4503)
# Only warn once on truncated decorated names. The maximum symbol length MSVC
# supports is 4k characters, which the parser framework regularly blows. The
# compiler likes to print out the entire type that went over the limit along
# with this warning, which gets unbearably spammy. That said, we don't want to
# just ignore it, so I'm letting it trigger once until we find some places to
# inject ParserRefs.
else()
# Note that it's important to add new CXXFLAGS before ones specified by the
# user, so that the user's flags override them. This is particularly
......
......@@ -12,10 +12,8 @@
set(CapnProto_VERSION @VERSION@)
if(@CAPNP_BUILD_TOOLS@)
set(CAPNP_EXECUTABLE $<TARGET_FILE:CapnProto::capnp_tool>)
set(CAPNPC_CXX_EXECUTABLE $<TARGET_FILE:CapnProto::capnpc_cpp>)
endif()
set(CAPNP_EXECUTABLE $<TARGET_FILE:CapnProto::capnp_tool>)
set(CAPNPC_CXX_EXECUTABLE $<TARGET_FILE:CapnProto::capnpc_cpp>)
set(CAPNP_INCLUDE_DIRECTORY "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
# work around http://public.kitware.com/Bug/view.php?id=15258
......
......@@ -124,60 +124,58 @@ endif()
# Tools/Compilers ==============================================================
if(CAPNP_BUILD_TOOLS)
set(capnpc_sources
compiler/md5.c++
compiler/error-reporter.c++
compiler/lexer.capnp.c++
compiler/lexer.c++
compiler/grammar.capnp.c++
compiler/parser.c++
compiler/node-translator.c++
compiler/compiler.c++
schema-parser.c++
serialize-text.c++
)
if(NOT CAPNP_LITE)
add_library(capnpc ${capnpc_sources})
target_link_libraries(capnpc capnp kj)
install(TARGETS capnpc ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES ${capnpc_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
endif()
set(capnpc_sources
compiler/md5.c++
compiler/error-reporter.c++
compiler/lexer.capnp.c++
compiler/lexer.c++
compiler/grammar.capnp.c++
compiler/parser.c++
compiler/node-translator.c++
compiler/compiler.c++
schema-parser.c++
serialize-text.c++
)
if(NOT CAPNP_LITE)
add_library(capnpc ${capnpc_sources})
target_link_libraries(capnpc capnp kj)
install(TARGETS capnpc ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES ${capnpc_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/capnp")
endif()
if(NOT CAPNP_LITE)
add_executable(capnp_tool
compiler/module-loader.c++
compiler/capnp.c++
)
target_link_libraries(capnp_tool capnpc capnp kj)
set_target_properties(capnp_tool PROPERTIES OUTPUT_NAME capnp)
set_target_properties(capnp_tool PROPERTIES CAPNP_INCLUDE_DIRECTORY
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/..>>
)
if(NOT CAPNP_LITE)
add_executable(capnp_tool
compiler/module-loader.c++
compiler/capnp.c++
)
target_link_libraries(capnp_tool capnpc capnp kj)
set_target_properties(capnp_tool PROPERTIES OUTPUT_NAME capnp)
set_target_properties(capnp_tool PROPERTIES CAPNP_INCLUDE_DIRECTORY
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/..>>
)
add_executable(capnpc_cpp
compiler/capnpc-c++.c++
)
target_link_libraries(capnpc_cpp capnp kj)
set_target_properties(capnpc_cpp PROPERTIES OUTPUT_NAME capnpc-c++)
#Capnp tool needs capnpc_cpp location. But cmake deprecated LOCATION property.
#So we use custom property to pass location
set_target_properties(capnpc_cpp PROPERTIES CAPNPC_CXX_EXECUTABLE
$<JOIN:$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/capnpc-c++>,$<INSTALL_INTERFACE:${CMAKE_INSTALL_BINDIR}/capnpc-c++>>
)
add_executable(capnpc_cpp
compiler/capnpc-c++.c++
)
target_link_libraries(capnpc_cpp capnp kj)
set_target_properties(capnpc_cpp PROPERTIES OUTPUT_NAME capnpc-c++)
#Capnp tool needs capnpc_cpp location. But cmake deprecated LOCATION property.
#So we use custom property to pass location
set_target_properties(capnpc_cpp PROPERTIES CAPNPC_CXX_EXECUTABLE
$<TARGET_FILE:capnpc_cpp>
)
add_executable(capnpc_capnp
compiler/capnpc-capnp.c++
)
target_link_libraries(capnpc_capnp capnp kj)
set_target_properties(capnpc_capnp PROPERTIES OUTPUT_NAME capnpc-capnp)
add_executable(capnpc_capnp
compiler/capnpc-capnp.c++
)
target_link_libraries(capnpc_capnp capnp kj)
set_target_properties(capnpc_capnp PROPERTIES OUTPUT_NAME capnpc-capnp)
install(TARGETS capnp_tool capnpc_cpp capnpc_capnp ${INSTALL_TARGETS_DEFAULT_ARGS})
install(TARGETS capnp_tool capnpc_cpp capnpc_capnp ${INSTALL_TARGETS_DEFAULT_ARGS})
# Symlink capnpc -> capnp
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink capnp \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/capnpc\")")
endif() # NOT CAPNP_LITE
endif() # CAPNP_BUILD_TOOLS
# Symlink capnpc -> capnp
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink capnp \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/capnpc\")")
endif() # NOT CAPNP_LITE
# Tests ========================================================================
......@@ -195,12 +193,10 @@ if(BUILD_TESTING)
capnp_generate_cpp(test_capnp_cpp_files test_capnp_h_files ${test_capnp_files})
set(test_libraries capnp kj-test kj)
if(NOT CAPNP_LITE)
list(APPEND test_libraries capnp-json capnp-rpc kj-async)
endif()
if(CAPNP_BUILD_TOOLS)
list(APPEND test_libraries capnpc)
if(CAPNP_LITE)
set(test_libraries capnp kj-test kj)
else()
set(test_libraries capnp-json capnp-rpc capnp capnpc kj-async kj-test kj)
endif()
add_executable(capnp-tests
......@@ -232,12 +228,16 @@ if(BUILD_TESTING)
membrane-test.c++
schema-test.c++
schema-loader-test.c++
schema-parser-test.c++
dynamic-test.c++
stringify-test.c++
serialize-async-test.c++
serialize-text-test.c++
rpc-test.c++
rpc-twoparty-test.c++
ez-rpc-test.c++
compiler/lexer-test.c++
compiler/md5-test.c++
test-util.c++
compat/json-test.c++
${test_capnp_cpp_files}
......@@ -253,29 +253,9 @@ if(BUILD_TESTING)
add_dependencies(check capnp-heavy-tests)
add_test(NAME capnp-heavy-tests-run COMMAND capnp-heavy-tests)
if(CAPNP_BUILD_TOOLS)
add_executable(capnp-parse-tests
schema-parser-test.c++
serialize-text-test.c++
compiler/md5-test.c++
compiler/lexer-test.c++
test-util.c++
${test_capnp_cpp_files}
${test_capnp_h_files}
)
target_link_libraries(capnp-parse-tests ${test_libraries})
if(NOT MSVC)
set_target_properties(capnp-parse-tests
PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations"
)
endif()
add_dependencies(check capnp-parse-tests)
add_test(NAME capnp-parse-tests-run COMMAND capnp-parse-tests)
add_executable(capnp-evolution-tests compiler/evolution-test.c++)
target_link_libraries(capnp-evolution-tests capnpc capnp kj)
add_dependencies(check capnp-evolution-tests)
add_test(NAME capnp-evolution-tests-run COMMAND capnp-evolution-tests)
endif() # CAPNP_BUILD_TOOLS
add_executable(capnp-evolution-tests compiler/evolution-test.c++)
target_link_libraries(capnp-evolution-tests capnpc capnp kj)
add_dependencies(check capnp-evolution-tests)
add_test(NAME capnp-evolution-tests-run COMMAND capnp-evolution-tests)
endif() # NOT CAPNP_LITE
endif() # BUILD_TESTING
......@@ -57,7 +57,7 @@ protected:
}
};
constexpr MmapDisposer mmapDisposer = MmapDisposer();
KJ_CONSTEXPR(static const) MmapDisposer mmapDisposer = MmapDisposer();
kj::Array<const byte> mmapForRead(kj::StringPtr filename) {
int fd;
......
......@@ -1703,7 +1703,7 @@ TEST(Encoding, Embeds) {
auto root = builder.getRoot<TestAllTypes>();
initTestMessage(root);
kj::StringPtr text = test::EMBEDDED_TEXT;
EXPECT_EQ(kj::str(root, '\n').size(), text.size());
EXPECT_EQ(kj::str(root, text.endsWith("\r\n") ? "\r\n" : "\n"), text);
}
#endif // CAPNP_LITE
......
......@@ -19,6 +19,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if !_MSC_VER
// (MSVC targets only little-endian platforms and so we haven't implemented any byte swapping
// intrinsics for it. So, this test would fail there.)
// Test that the code for the opposite endianness of our CPU works. E.g. on x86 this will test
// the bswap-based code.
#define CAPNP_REVERSE_ENDIAN 1
......@@ -104,3 +108,5 @@ TEST(EndianReverse, EightBytes) {
} // namespace
} // namespace _ (private)
} // namespace capnp
#endif // !_MSC_VER
......@@ -28,12 +28,6 @@
#include <kj/miniposix.h>
#include "test-util.h"
#if !_WIN32
// This test is super-slow on Windows seemingly due to generating exception stack traces being
// expensive.
//
// TODO(perf): Maybe create an API to disable stack traces, and use it here.
namespace capnp {
namespace _ { // private
namespace {
......@@ -47,6 +41,16 @@ bool skipFuzzTest() {
}
}
class DisableStackTraces: public kj::ExceptionCallback {
// This test generates a lot of exceptions. Performing a backtrace on each one can be slow,
// especially on Windows (where it is very, very slow). So, disable them.
public:
StackTraceMode stackTraceMode() override {
return StackTraceMode::NONE;
}
};
uint64_t traverse(AnyPointer::Reader reader);
uint64_t traverse(AnyStruct::Reader reader);
uint64_t traverse(AnyList::Reader reader);
......@@ -163,6 +167,7 @@ struct StructChecker {
KJ_TEST("fuzz-test struct pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
builder.getRoot<TestAllTypes>().setTextField("foo");
......@@ -183,6 +188,7 @@ struct ListChecker {
KJ_TEST("fuzz-test list pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<uint32_t>>(2);
......@@ -207,6 +213,7 @@ struct StructListChecker {
KJ_TEST("fuzz-test struct list pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2);
......@@ -230,6 +237,7 @@ struct TextChecker {
KJ_TEST("fuzz-test text pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder;
builder.template getRoot<AnyPointer>().setAs<Text>("foo");
......@@ -238,6 +246,7 @@ KJ_TEST("fuzz-test text pointer") {
KJ_TEST("fuzz-test far pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
initTestMessage(builder.getRoot<TestAllTypes>());
......@@ -251,6 +260,7 @@ KJ_TEST("fuzz-test far pointer") {
KJ_TEST("fuzz-test double-far pointer") {
if (skipFuzzTest()) return;
DisableStackTraces disableStackTraces;
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
......@@ -274,5 +284,3 @@ KJ_TEST("fuzz-test double-far pointer") {
} // namespace
} // namespace _ (private)
} // namespace capnp
#endif
......@@ -42,7 +42,7 @@ public:
}
#endif
};
static constexpr DummyCapTableReader dummyCapTableReader = DummyCapTableReader();
static KJ_CONSTEXPR(const) DummyCapTableReader dummyCapTableReader = DummyCapTableReader();
} // namespace
......
......@@ -242,7 +242,7 @@ protected:
}
};
constexpr MmapDisposer mmapDisposer = MmapDisposer();
KJ_CONSTEXPR(static const) MmapDisposer mmapDisposer = MmapDisposer();
static char* canonicalizePath(char* path) {
// Taken from some old C code of mine.
......
......@@ -19,16 +19,12 @@ set(kj_sources_heavy
units.c++
refcount.c++
string-tree.c++
)
set(kj-parse_sources
parse/char.c++
)
set(kj_sources ${kj_sources_lite})
if(NOT CAPNP_LITE)
list(APPEND kj_sources ${kj_sources_heavy})
endif()
if(CAPNP_BUILD_TOOLS)
list(APPEND kj_sources ${kj-parse_sources})
set(kj_sources ${kj_sources_lite} ${kj_sources_heavy})
else()
set(kj_sources ${kj_sources_lite})
endif()
set(kj_headers
......@@ -177,20 +173,12 @@ if(BUILD_TESTING)
one-of-test.c++
function-test.c++
threadlocal-pthread-test.c++
parse/common-test.c++
parse/char-test.c++
compat/http-test.c++
)
target_link_libraries(kj-heavy-tests kj-http kj-async kj-test kj)
add_dependencies(check kj-heavy-tests)
add_test(NAME kj-heavy-tests-run COMMAND kj-heavy-tests)
if(CAPNP_BUILD_TOOLS)
add_executable(kj-parse-tests
parse/common-test.c++
parse/char-test.c++
)
target_link_libraries(kj-parse-tests kj-test kj)
add_dependencies(check kj-parse-tests)
add_test(NAME kj-parse-tests-run COMMAND kj-parse-tests)
endif() # CAPNP_BUILD_TOOLS
endif() # NOT CAPNP_LITE
endif() # BUILD_TESTING
......@@ -26,9 +26,12 @@
namespace kj {
namespace {
#if !_MSC_VER
// TODO(msvc): GetFunctorStartAddress is not supported on MSVC currently, so skip the test.
TEST(Async, GetFunctorStartAddress) {
EXPECT_TRUE(nullptr != _::GetFunctorStartAddress<>::apply([](){return 0;}));
}
#endif
TEST(Async, EvalVoid) {
EventLoop loop;
......
This diff is collapsed.
......@@ -150,6 +150,7 @@ Exception::Type typeOfWin32Error(DWORD error) {
case WSAENETDOWN:
case WSAENETRESET:
case WSAENETUNREACH:
case WSAESHUTDOWN:
return Exception::Type::DISCONNECTED;
case WSAEOPNOTSUPP:
......
......@@ -28,6 +28,9 @@ namespace _ { // private
namespace {
TEST(Exception, TrimSourceFilename) {
#if _WIN32
if (trimSourceFilename(__FILE__) != "kj\\exception-test.c++")
#endif
EXPECT_EQ(trimSourceFilename(__FILE__), "kj/exception-test.c++");
}
......
......@@ -45,7 +45,7 @@
#include <dbghelp.h>
#endif
#if (__linux__ || __APPLE__) && defined(KJ_DEBUG)
#if (__linux__ || __APPLE__)
#include <stdio.h>
#include <pthread.h>
#endif
......@@ -156,6 +156,10 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
#endif
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) {
return nullptr;
}
#if _WIN32 && _M_X64
CONTEXT context;
RtlCaptureContext(&context);
......@@ -170,11 +174,11 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
String stringifyStackTrace(ArrayPtr<void* const> trace) {
if (trace.size() == 0) return nullptr;
if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) {
return nullptr;
}
#ifndef KJ_DEBUG
return nullptr;
#elif _WIN32 && _M_X64 && _MSC_VER
#if _WIN32 && _M_X64 && _MSC_VER
// Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
// this requires MSVC debug info.
......@@ -407,17 +411,29 @@ kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
// To deal with all this, we look for directory names in the path which we recognize to be
// locations that represent roots of the source tree. We strip said root and everything before
// it.
//
// On Windows, we often get filenames containing backslashes. Since we aren't allowed to allocate
// a new string here, we can't do much about this, so our returned "canonical" name will
// unfortunately end up with backslashes.
static constexpr const char* ROOTS[] = {
"ekam-provider/canonical/", // Ekam source file.
"ekam-provider/c++header/", // Ekam include file.
"src/", // Non-Ekam source root.
"tmp/" // Non-Ekam generated code.
"tmp/", // Non-Ekam generated code.
#if _WIN32
"src\\", // Win32 source root.
"tmp\\", // Win32 generated code.
#endif
};
retry:
for (size_t i: kj::indices(filename)) {
if (i == 0 || filename[i-1] == '/') {
if (i == 0 || filename[i-1] == '/'
#if _WIN32
|| filename[i-1] == '\\'
#endif
) {
// We're at the start of a directory name. Check for valid prefixes.
for (kj::StringPtr root: ROOTS) {
if (filename.slice(i).startsWith(root)) {
......@@ -629,6 +645,10 @@ void ExceptionCallback::logMessage(
next.logMessage(severity, file, line, contextDepth, mv(text));
}
ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() {
return next.stackTraceMode();
}
class ExceptionCallback::RootExceptionCallback: public ExceptionCallback {
public:
RootExceptionCallback(): ExceptionCallback(*this) {}
......@@ -675,6 +695,14 @@ public:
}
}
StackTraceMode stackTraceMode() override {
#ifdef KJ_DEBUG
return StackTraceMode::FULL;
#else
return StackTraceMode::ADDRESS_ONLY;
#endif
}
private:
void logException(LogSeverity severity, Exception&& e) {
// We intentionally go back to the top exception callback on the stack because we don't want to
......
......@@ -193,6 +193,29 @@ public:
//
// The global default implementation writes the text to stderr.
enum class StackTraceMode {
FULL,
// Stringifying a stack trace will attempt to determine source file and line numbers. This may
// be expensive. For example, on Linux, this shells out to `addr2line`.
//
// This is the default in debug builds.
ADDRESS_ONLY,
// Stringifying a stack trace will only generate a list of code addresses.
//
// This is the default in release builds.
NONE
// Generating a stack trace will always return an empty array.
//
// This avoids ever unwinding the stack. On Windows in particular, the stack unwinding library
// has been observed to be pretty slow, so exception-heavy code might benefit significantly
// from this setting. (But exceptions should be rare...)
};
virtual StackTraceMode stackTraceMode();
// Returns the current preferred stack trace mode.
protected:
ExceptionCallback& next;
......
......@@ -45,6 +45,9 @@
#include "../array.h"
#include "../tuple.h"
#include "../vector.h"
#if _MSC_VER
#include <type_traits> // result_of_t
#endif
namespace kj {
namespace parse {
......@@ -100,7 +103,15 @@ private:
template <typename T> struct OutputType_;
template <typename T> struct OutputType_<Maybe<T>> { typedef T Type; };
template <typename Parser, typename Input>
using OutputType = typename OutputType_<decltype(instance<Parser&>()(instance<Input&>()))>::Type;
using OutputType = typename OutputType_<
#if _MSC_VER
std::result_of_t<Parser(Input)>
// The instance<T&>() based version below results in:
// C2064: term does not evaluate to a function taking 1 arguments
#else
decltype(instance<Parser&>()(instance<Input&>()))
#endif
>::Type;
// Synonym for the output type of a parser, given the parser type and the input type.
// =======================================================================================
......@@ -122,12 +133,12 @@ public:
template <typename Other>
constexpr ParserRef(Other&& other)
: parser(&other), wrapper(&WrapperImplInstance<Decay<Other>>::instance) {
static_assert(kj::isReference<Other>(), "ParseRef should not be assigned to a temporary.");
static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary.");
}
template <typename Other>
inline ParserRef& operator=(Other&& other) {
static_assert(kj::isReference<Other>(), "ParseRef should not be assigned to a temporary.");
static_assert(kj::isReference<Other>(), "ParserRef should not be assigned to a temporary.");
parser = &other;
wrapper = &WrapperImplInstance<Decay<Other>>::instance;
return *this;
......@@ -151,7 +162,13 @@ private:
};
template <typename ParserImpl>
struct WrapperImplInstance {
#if _MSC_VER
// TODO(msvc): MSVC currently fails to initialize vtable pointers for constexpr values so
// we have to make this just const instead.
static const WrapperImpl<ParserImpl> instance;
#else
static constexpr WrapperImpl<ParserImpl> instance = WrapperImpl<ParserImpl>();
#endif
};
const void* parser;
......@@ -160,8 +177,13 @@ private:
template <typename Input, typename Output>
template <typename ParserImpl>
#if _MSC_VER
const typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl>
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance = WrapperImpl<ParserImpl>();
#else
constexpr typename ParserRef<Input, Output>::template WrapperImpl<ParserImpl>
ParserRef<Input, Output>::WrapperImplInstance<ParserImpl>::instance;
#endif
template <typename Input, typename ParserImpl>
constexpr ParserRef<Input, OutputType<ParserImpl, Input>> ref(ParserImpl& impl) {
......@@ -298,29 +320,46 @@ public:
explicit constexpr Sequence_(T&& firstSubParser, U&&... rest)
: first(kj::fwd<T>(firstSubParser)), rest(kj::fwd<U>(rest)...) {}
// TODO(msvc): MSVC ICEs on the return types of operator() and parseNext() unless SubParsers is
// wrapped in `decltype(instance<SubParsers>())`. This is similar to the workaround in
// KJ_CONTEXT().
// TODO(msvc): The trailing return types on `operator()` and `parseNext()` expose at least two
// bugs in MSVC:
//
// 1. An ICE.
// 2. 'error C2672: 'operator __surrogate_func': no matching overloaded function found)',
// which crops up in numerous places when trying to build the capnp command line tools.
//
// The only workaround I found for both bugs is to omit the trailing return types and instead
// rely on C++14's return type deduction.
template <typename Input>
auto operator()(Input& input) const ->
Maybe<decltype(tuple(
auto operator()(Input& input) const
#ifndef _MSC_VER
-> Maybe<decltype(tuple(
instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<decltype(instance<SubParsers>()), Input>>()...))> {
instance<OutputType<SubParsers, Input>>()...))>
#endif
{
return parseNext(input);
}
template <typename Input, typename... InitialParams>
auto parseNext(Input& input, InitialParams&&... initialParams) const ->
Maybe<decltype(tuple(
auto parseNext(Input& input, InitialParams&&... initialParams) const
#ifndef _MSC_VER
-> Maybe<decltype(tuple(
kj::fwd<InitialParams>(initialParams)...,
instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<decltype(instance<SubParsers>()), Input>>()...))> {
instance<OutputType<SubParsers, Input>>()...))>
#endif
{
KJ_IF_MAYBE(firstResult, first(input)) {
return rest.parseNext(input, kj::fwd<InitialParams>(initialParams)...,
kj::mv(*firstResult));
} else {
return nullptr;
// TODO(msvc): MSVC depends on return type deduction to compile this function, so we need to
// help it deduce the right type on this code path.
return Maybe<decltype(tuple(
kj::fwd<InitialParams>(initialParams)...,
instance<OutputType<FirstSubParser, Input>>(),
instance<OutputType<SubParsers, Input>>()...))>{nullptr};
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment