ZScript DAP Debug server (#3009)
* Add STL-standard type traits and functions to TMap to enable for loop iteration * add third-party range-map library I have e-mailed the author for clarifcation on the license, will update this when they respond * vcpkg: Add cppdap and eventpp libraries, update baseline * DAP implementation * Add `FileSystem::FileHash()` to get the CRC32 hash * add `starting_offset` param to `VMDisasm()` for debugger disassembly view Defaults to `0`, should not change output if it's not set * Add `VMFrameStack::HasFrames()` to prevent assertions when inspecting in the debugger * Add `PC` field to VMFrame, ensure that it is updated whenever vm increments/decrements the pc Does not change alignment, the offsets used in VMFrame still work We need this for the debugger because we otherwise have no way to get the pc; it was a local in `ExecScriptFunc()` * `ZCCCompiler::CreateClassTypes()`: ensure SourceLumpName is set for classes derived from non-native classes * start debug server in d_main, add `vm_debug` cvars and `-debug` CLI arg * Add documentation for `vm_debug` cvars * vm_exec: Add debugger hooks for instruction execution events * c_console: Add debugger hooks for logging events in `PrintString` * add `.cache/` to gitignore * vendor cppdap on main @ 6464cd7 Patches: removed .gitmodules (submodules were thirdparty/json, thirdparty/googletest) removed thirdparty/googletest, not needed removed thirdparty/json/docs, thirdparty/json/test, thirdparty/json/benchmarks to prevent massive bloat * vendor eventpp on master @ 1224dd6 * build: use internal cppdap and eventpp by default * dap: fix Binary::GetFunctionLineRange() * fix bug in range_map::find_ranges * make Binary dap::Source dynamic * cache source code upon retrieval * refactor Binary into a class, cleanup PexCache * fix ending session gracefully * d_main: Stop debug server in D_Cleanup() * Fix connecting to debugger when session already started * cleanup unused stuff in ZScriptDebugger * always send TerminateEvents on disconnect if initialized * tweak color display * Don't cache disassembly lines when scanning scripts * Cache nodes when getting runtime state * WIP display locals * Fix display of static arrays * Ensure names display in proper order * dap: Fix struct locals display, add args display * Support `start` parameter * Support `filter` parameter * remove struct unbound native data display Practically useless for debugging zscript and didn't work properly anyway * d_main: fix vm_jit and vm_jit not being disabled soon enough * support breaking on abort exceptions * dap: refactor game event emitter functions into seperate header * dap: show native functions on stack * dap: Remove "Native" from exception handling, simplify exception event emit * dap: add instruction breakpoints * dap: fix display of locals not in scope yet * dap: Make disassembly view display invalid instructions for non-code addresses * dap: remove dot initializers * dap: fix local structs in scope * dap: don't parse the non-used options in the launch/attach requests * dap: fix local struct view * dap: Fix displaying objects that aren't their actual types * dap: Fix action and state handling * dap: stack display view * dap: fix object display view * d_dehacked: set qualified name in addition to the printable name * dap: fix displaying breakpoint errors when script isn't loaded * dap: remove debug print * dap: Display parameter names * dap: Turn down verbosity of logging * dap: fix disassembly view * dap: fix performance problems with arrays * c_console: emit event only if not PRINT_NODAPEVENT * dap: improve logging * dap: update upstream cppdap library to fix deadlocks on no bind * dap: Fix ending session on client socket closed * dap: prevent DebugServer.h from pulling in `dap` and `ZScriptDebugger.h` * dap: fix pause event not being emitted on pause * dap: remove eventpp emitters, way too slow * Remove eventpp dependency * dap: Display correct register names * dap: Show special inits in registers * dap: Add stack offset to VMLocalVariable * dap: fix display of static arrays and local variables on stack vs. registers * dap: fix displaying function pointers * dap: tweak color display * dap: fix scalar display < 4 * dap: add Globals display to debugger * dap: unify methods to get vmvalue * dap: rename free method to freeValue to avoid running afoul of macro defs * fix windows builds * fix compile on linux * cleanup * Fix display of function breakpoints * dap: Don't send back binary files * dap: include sbarinfo in script types (no debug support for anything but zscript yet, this is just for returning source info) * dap: don't show ending session message unless initialized * fix erroneous commit * dap: handle evaluate requests * Fix getting bitfield values * Add CVars Scope and evaluation * add running console commands from repl * dap: disable commands via repl for now * fix loading functions DECORATE scripts * Add source information to Dehacked VM functions, add debugging support * dap: cleanup * fix resolving archive paths * don't send source back on native stack frames * handle `modules` request * cleanup * allow evaluating cvars on hover * fix oob bpinfos * fix restarting the game blowing out the debug server * dap: process input events while paused to prevent deadlocks * fix getting local state * dap: fix LocalState alignment * dap: fix DumpStateHelper * update cppdap protocol version to 1.68.0 * remove cppdap from vcpkg deps We can't use the upstream version anyway because the maintainers are not merging our patches * dap: make named variable nodes derive from the same class * dap: make cvar scope available in native stack frames * handle local variables with conflicting names * add I_GetWindowEvent() to win32 to only process window events when debugging is paused * dap: fix evaluate * dap: fix display of `out` variables
This commit is contained in:
parent
fcf529590f
commit
45ac7c1d9e
235 changed files with 83166 additions and 62 deletions
4
libraries/cppdap/.gitattributes
vendored
Normal file
4
libraries/cppdap/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
* text=auto
|
||||
|
||||
*.sh eol=lf
|
||||
*.bat eol=crlf
|
||||
48
libraries/cppdap/.github/workflows/main.yml
vendored
Normal file
48
libraries/cppdap/.github/workflows/main.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: ci
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: ubuntu:22.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- CC: gcc
|
||||
CXX: g++
|
||||
- CC: clang
|
||||
CXX: clang++
|
||||
|
||||
# don't run pull requests from local branches twice
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
|
||||
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
steps:
|
||||
- name: Install packages
|
||||
run: |
|
||||
apt-get -q -y update
|
||||
apt-get -q -y install build-essential cmake git clang
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Build source
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=${{ matrix.CC }} CXX=${{ matrix.CXX }} cmake ..
|
||||
cmake --build .
|
||||
DESTDIR=../out cmake --install .
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cppdap-${{ matrix.CC }}
|
||||
path: out/usr/local/
|
||||
6
libraries/cppdap/.gitignore
vendored
Normal file
6
libraries/cppdap/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
build/
|
||||
fuzz/corpus
|
||||
fuzz/logs
|
||||
.vs/
|
||||
.vscode/settings.json
|
||||
CMakeSettings.json
|
||||
394
libraries/cppdap/CMakeLists.txt
Normal file
394
libraries/cppdap/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
# Copyright 2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
project(cppdap VERSION 1.68.0 LANGUAGES CXX C)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
###########################################################
|
||||
# Options
|
||||
###########################################################
|
||||
function (option_if_not_defined name description default)
|
||||
if(NOT DEFINED ${name})
|
||||
option(${name} ${description} ${default})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
option_if_not_defined(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
|
||||
option_if_not_defined(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF)
|
||||
option_if_not_defined(CPPDAP_BUILD_TESTS "Build tests" OFF)
|
||||
option_if_not_defined(CPPDAP_BUILD_FUZZER "Build fuzzer" OFF)
|
||||
option_if_not_defined(CPPDAP_ASAN "Build dap with address sanitizer" OFF)
|
||||
option_if_not_defined(CPPDAP_MSAN "Build dap with memory sanitizer" OFF)
|
||||
option_if_not_defined(CPPDAP_TSAN "Build dap with thread sanitizer" OFF)
|
||||
option_if_not_defined(CPPDAP_INSTALL_VSCODE_EXAMPLES "Build and install dap examples into vscode extensions directory" OFF)
|
||||
option_if_not_defined(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE "Use nlohmann_json with find_package() instead of building internal submodule" OFF)
|
||||
option_if_not_defined(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE "Use RapidJSON with find_package()" OFF)
|
||||
option_if_not_defined(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE "Use JsonCpp with find_package()" OFF)
|
||||
option_if_not_defined(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE "Use googletest with find_package()" OFF)
|
||||
|
||||
###########################################################
|
||||
# Directories
|
||||
###########################################################
|
||||
function (set_if_not_defined name value)
|
||||
if(NOT DEFINED ${name})
|
||||
set(${name} ${value} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
set(CPPDAP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
set(CPPDAP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
set_if_not_defined(CPPDAP_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party)
|
||||
set_if_not_defined(CPPDAP_JSON_DIR ${CPPDAP_THIRD_PARTY_DIR}/json)
|
||||
set_if_not_defined(CPPDAP_GOOGLETEST_DIR ${CPPDAP_THIRD_PARTY_DIR}/googletest)
|
||||
|
||||
###########################################################
|
||||
# Submodules
|
||||
###########################################################
|
||||
if(CPPDAP_BUILD_TESTS AND NOT CPPDAP_USE_EXTERNAL_GTEST_PACKAGE)
|
||||
if(NOT EXISTS ${CPPDAP_GOOGLETEST_DIR}/.git)
|
||||
message(WARNING "third_party/googletest submodule missing.")
|
||||
message(WARNING "Run: `git submodule update --init` to build tests.")
|
||||
set(CPPDAP_BUILD_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
###########################################################
|
||||
# JSON library
|
||||
###########################################################
|
||||
|
||||
if(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE)
|
||||
list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE")
|
||||
endif()
|
||||
if(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE)
|
||||
list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE")
|
||||
endif()
|
||||
if(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE)
|
||||
list(APPEND CPPDAP_EXTERNAL_JSON_PACKAGES "CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE")
|
||||
endif()
|
||||
list(LENGTH CPPDAP_EXTERNAL_JSON_PACKAGES CPPDAP_EXTERNAL_JSON_PACKAGES_COUNT)
|
||||
|
||||
if(CPPDAP_EXTERNAL_JSON_PACKAGES_COUNT GREATER 1)
|
||||
message(FATAL_ERROR "At most one of CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE, \
|
||||
CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE, and CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE can \
|
||||
be set, but ${CPPDAP_EXTERNAL_JSON_PACKAGES} were all set.")
|
||||
elseif(CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE)
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
set(CPPDAP_JSON_LIBRARY "nlohmann")
|
||||
elseif(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE)
|
||||
find_package(RapidJSON CONFIG REQUIRED)
|
||||
set(CPPDAP_JSON_LIBRARY "rapid")
|
||||
elseif(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE)
|
||||
find_package(jsoncpp CONFIG REQUIRED)
|
||||
set(CPPDAP_JSON_LIBRARY "jsoncpp")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CPPDAP_JSON_LIBRARY)
|
||||
# Attempt to detect JSON library from CPPDAP_JSON_DIR
|
||||
if(NOT EXISTS "${CPPDAP_JSON_DIR}")
|
||||
message(FATAL_ERROR "CPPDAP_JSON_DIR '${CPPDAP_JSON_DIR}' does not exist")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${CPPDAP_JSON_DIR}/include/nlohmann")
|
||||
set(CPPDAP_JSON_LIBRARY "nlohmann")
|
||||
elseif(EXISTS "${CPPDAP_JSON_DIR}/include/rapidjson")
|
||||
set(CPPDAP_JSON_LIBRARY "rapid")
|
||||
elseif(EXISTS "${CPPDAP_JSON_DIR}/include/json")
|
||||
set(CPPDAP_JSON_LIBRARY "jsoncpp")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not determine JSON library from ${CPPDAP_JSON_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
string(TOUPPER ${CPPDAP_JSON_LIBRARY} CPPDAP_JSON_LIBRARY_UPPER)
|
||||
|
||||
###########################################################
|
||||
# File lists
|
||||
###########################################################
|
||||
set(CPPDAP_LIST
|
||||
${CPPDAP_SRC_DIR}/content_stream.cpp
|
||||
${CPPDAP_SRC_DIR}/io.cpp
|
||||
${CPPDAP_SRC_DIR}/${CPPDAP_JSON_LIBRARY}_json_serializer.cpp
|
||||
${CPPDAP_SRC_DIR}/network.cpp
|
||||
${CPPDAP_SRC_DIR}/null_json_serializer.cpp
|
||||
${CPPDAP_SRC_DIR}/protocol_events.cpp
|
||||
${CPPDAP_SRC_DIR}/protocol_requests.cpp
|
||||
${CPPDAP_SRC_DIR}/protocol_response.cpp
|
||||
${CPPDAP_SRC_DIR}/protocol_types.cpp
|
||||
${CPPDAP_SRC_DIR}/session.cpp
|
||||
${CPPDAP_SRC_DIR}/socket.cpp
|
||||
${CPPDAP_SRC_DIR}/typeinfo.cpp
|
||||
${CPPDAP_SRC_DIR}/typeof.cpp
|
||||
)
|
||||
|
||||
###########################################################
|
||||
# OS libraries
|
||||
###########################################################
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
set(CPPDAP_OS_LIBS WS2_32)
|
||||
set(CPPDAP_OS_EXE_EXT ".exe")
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set(CPPDAP_OS_LIBS pthread)
|
||||
set(CPPDAP_OS_EXE_EXT "")
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
set(CPPDAP_OS_LIBS)
|
||||
set(CPPDAP_OS_EXE_EXT "")
|
||||
endif()
|
||||
|
||||
###########################################################
|
||||
# Functions
|
||||
###########################################################
|
||||
|
||||
function(cppdap_set_json_links target)
|
||||
if (CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE)
|
||||
target_link_libraries(${target} PRIVATE "$<BUILD_INTERFACE:nlohmann_json::nlohmann_json>")
|
||||
elseif(CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE)
|
||||
target_link_libraries(${target} PRIVATE rapidjson)
|
||||
elseif(CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE)
|
||||
target_link_libraries(${target} PRIVATE JsonCpp::JsonCpp)
|
||||
else()
|
||||
target_include_directories(${target} PRIVATE "${CPPDAP_JSON_DIR}/include/")
|
||||
endif()
|
||||
endfunction(cppdap_set_json_links)
|
||||
|
||||
function(cppdap_set_target_options target)
|
||||
# Enable all warnings
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE "-W4")
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE "-Wall")
|
||||
endif()
|
||||
|
||||
# Disable specific, pedantic warnings
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE
|
||||
"-D_CRT_SECURE_NO_WARNINGS"
|
||||
|
||||
# Warnings from nlohmann/json headers.
|
||||
"/wd4267" # 'argument': conversion from 'size_t' to 'int', possible loss of data
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add define for JSON library in use
|
||||
target_compile_definitions(${target}
|
||||
PRIVATE "CPPDAP_JSON_${CPPDAP_JSON_LIBRARY_UPPER}=1"
|
||||
)
|
||||
|
||||
# Treat all warnings as errors
|
||||
if(CPPDAP_WARNINGS_AS_ERRORS)
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE "/WX")
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE "-Werror")
|
||||
endif()
|
||||
endif(CPPDAP_WARNINGS_AS_ERRORS)
|
||||
|
||||
if(CPPDAP_ASAN)
|
||||
target_compile_options(${target} PUBLIC "-fsanitize=address")
|
||||
target_link_options(${target} PUBLIC "-fsanitize=address")
|
||||
elseif(CPPDAP_MSAN)
|
||||
target_compile_options(${target} PUBLIC "-fsanitize=memory")
|
||||
target_link_options(${target} PUBLIC "-fsanitize=memory")
|
||||
elseif(CPPDAP_TSAN)
|
||||
target_compile_options(${target} PUBLIC "-fsanitize=thread")
|
||||
target_link_options(${target} PUBLIC "-fsanitize=thread")
|
||||
endif()
|
||||
|
||||
# Error on undefined symbols
|
||||
# if(NOT MSVC)
|
||||
# target_compile_options(${target} PRIVATE "-Wl,--no-undefined")
|
||||
# endif()
|
||||
target_include_directories(
|
||||
"${target}"
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${CPPDAP_INCLUDE_DIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
cppdap_set_json_links(${target})
|
||||
target_link_libraries(${target} PRIVATE ${CPPDAP_OS_LIBS})
|
||||
endfunction(cppdap_set_target_options)
|
||||
|
||||
###########################################################
|
||||
# Targets
|
||||
###########################################################
|
||||
|
||||
# dap
|
||||
add_library(cppdap ${CPPDAP_LIST})
|
||||
set_target_properties(cppdap PROPERTIES POSITION_INDEPENDENT_CODE 1)
|
||||
|
||||
cppdap_set_target_options(cppdap)
|
||||
|
||||
set(CPPDAP_TARGET_NAME ${PROJECT_NAME})
|
||||
set(CPPDAP_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "")
|
||||
set(CPPDAP_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(CPPDAP_TARGETS_EXPORT_NAME "${PROJECT_NAME}-targets")
|
||||
set(CPPDAP_CMAKE_CONFIG_TEMPLATE "cmake/Config.cmake.in")
|
||||
set(CPPDAP_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(CPPDAP_CMAKE_VERSION_CONFIG_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(CPPDAP_CMAKE_PROJECT_CONFIG_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake")
|
||||
set(CPPDAP_CMAKE_PROJECT_TARGETS_FILE "${CPPDAP_CMAKE_CONFIG_DIR}/${PROJECT_NAME}-targets.cmake")
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
${CPPDAP_CMAKE_VERSION_CONFIG_FILE} COMPATIBILITY SameMinorVersion
|
||||
)
|
||||
configure_package_config_file(
|
||||
${CPPDAP_CMAKE_CONFIG_TEMPLATE}
|
||||
"${CPPDAP_CMAKE_PROJECT_CONFIG_FILE}"
|
||||
INSTALL_DESTINATION ${CPPDAP_CONFIG_INSTALL_DIR}
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS "${CPPDAP_TARGET_NAME}"
|
||||
EXPORT "${CPPDAP_TARGETS_EXPORT_NAME}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
INCLUDES DESTINATION "${CPPDAP_INCLUDE_INSTALL_DIR}"
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT "${CPPDAP_TARGETS_EXPORT_NAME}"
|
||||
NAMESPACE "${CPPDAP_TARGET_NAME}::"
|
||||
DESTINATION "${CPPDAP_CONFIG_INSTALL_DIR}"
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY
|
||||
"include/dap"
|
||||
DESTINATION "${CPPDAP_INCLUDE_INSTALL_DIR}"
|
||||
)
|
||||
install(FILES ${CPPDAP_CMAKE_VERSION_CONFIG_FILE} ${CPPDAP_CMAKE_PROJECT_CONFIG_FILE}
|
||||
DESTINATION ${CPPDAP_CONFIG_INSTALL_DIR})
|
||||
|
||||
# tests
|
||||
if(CPPDAP_BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
set(DAP_TEST_LIST
|
||||
${CPPDAP_SRC_DIR}/any_test.cpp
|
||||
${CPPDAP_SRC_DIR}/chan_test.cpp
|
||||
${CPPDAP_SRC_DIR}/content_stream_test.cpp
|
||||
${CPPDAP_SRC_DIR}/dap_test.cpp
|
||||
${CPPDAP_SRC_DIR}/json_serializer_test.cpp
|
||||
${CPPDAP_SRC_DIR}/network_test.cpp
|
||||
${CPPDAP_SRC_DIR}/optional_test.cpp
|
||||
${CPPDAP_SRC_DIR}/rwmutex_test.cpp
|
||||
${CPPDAP_SRC_DIR}/session_test.cpp
|
||||
${CPPDAP_SRC_DIR}/socket_test.cpp
|
||||
${CPPDAP_SRC_DIR}/traits_test.cpp
|
||||
${CPPDAP_SRC_DIR}/typeinfo_test.cpp
|
||||
${CPPDAP_SRC_DIR}/variant_test.cpp
|
||||
)
|
||||
|
||||
if(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE)
|
||||
find_package(GTest REQUIRED)
|
||||
else()
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${CPPDAP_GOOGLETEST_DIR})
|
||||
# googletest has -Werror=maybe-uninitialized problems.
|
||||
# Disable all warnings in googletest code.
|
||||
target_compile_options(gtest PRIVATE -w)
|
||||
# gmock has -Werror=deprecated-copy problems.
|
||||
target_compile_options(gmock PRIVATE -w)
|
||||
endif()
|
||||
|
||||
add_executable(cppdap-unittests ${DAP_TEST_LIST})
|
||||
add_test(NAME cppdap-unittests COMMAND cppdap-unittests)
|
||||
|
||||
set_target_properties(cppdap-unittests PROPERTIES
|
||||
FOLDER "Tests"
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
# googletest emits warning C4244: 'initializing': conversion from 'double' to 'testing::internal::BiggestInt', possible loss of data
|
||||
target_compile_options(cppdap-unittests PRIVATE "/wd4244")
|
||||
endif()
|
||||
|
||||
cppdap_set_target_options(cppdap-unittests)
|
||||
if(CPPDAP_USE_EXTERNAL_GTEST_PACKAGE)
|
||||
target_link_libraries(cppdap-unittests PRIVATE cppdap GTest::gtest)
|
||||
else()
|
||||
target_link_libraries(cppdap-unittests PRIVATE cppdap gtest gmock)
|
||||
endif()
|
||||
endif(CPPDAP_BUILD_TESTS)
|
||||
|
||||
# fuzzer
|
||||
if(CPPDAP_BUILD_FUZZER)
|
||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
message(FATAL_ERROR "CPPDAP_BUILD_FUZZER can currently only be used with the clang toolchain")
|
||||
endif()
|
||||
set(DAP_FUZZER_LIST
|
||||
${CPPDAP_LIST}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fuzz/fuzz.cpp
|
||||
)
|
||||
add_executable(cppdap-fuzzer ${DAP_FUZZER_LIST})
|
||||
if(CPPDAP_ASAN)
|
||||
target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address")
|
||||
target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address")
|
||||
elseif(CPPDAP_MSAN)
|
||||
target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,memory")
|
||||
target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,memory")
|
||||
elseif(CPPDAP_TSAN)
|
||||
target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,thread")
|
||||
target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,thread")
|
||||
else()
|
||||
target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer")
|
||||
target_link_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer")
|
||||
endif()
|
||||
target_include_directories(cppdap-fuzzer PUBLIC
|
||||
${CPPDAP_INCLUDE_DIR}
|
||||
${CPPDAP_SRC_DIR}
|
||||
)
|
||||
cppdap_set_json_links(cppdap-fuzzer)
|
||||
target_link_libraries(cppdap-fuzzer PRIVATE cppdap "${CPPDAP_OS_LIBS}")
|
||||
|
||||
endif(CPPDAP_BUILD_FUZZER)
|
||||
|
||||
# examples
|
||||
if(CPPDAP_BUILD_EXAMPLES)
|
||||
function(build_example target)
|
||||
add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp")
|
||||
set_target_properties(${target} PROPERTIES
|
||||
FOLDER "Examples"
|
||||
)
|
||||
cppdap_set_target_options(${target})
|
||||
target_link_libraries(${target} PRIVATE cppdap)
|
||||
|
||||
if(CPPDAP_INSTALL_VSCODE_EXAMPLES)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
set(extroot "$ENV{USERPROFILE}\\.vscode\\extensions")
|
||||
else()
|
||||
set(extroot "$ENV{HOME}/.vscode/extensions")
|
||||
endif()
|
||||
if(EXISTS ${extroot})
|
||||
set(extdir "${extroot}/google.cppdap-example-${target}-1.0.0")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examples/vscode/package.json ${extdir}/package.json)
|
||||
add_custom_command(TARGET ${target}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${extdir})
|
||||
else()
|
||||
message(WARNING "Could not install vscode example extension as '${extroot}' does not exist")
|
||||
endif()
|
||||
endif(CPPDAP_INSTALL_VSCODE_EXAMPLES)
|
||||
endfunction(build_example)
|
||||
|
||||
build_example(hello_debugger)
|
||||
build_example(simple_net_client_server)
|
||||
|
||||
endif(CPPDAP_BUILD_EXAMPLES)
|
||||
28
libraries/cppdap/CONTRIBUTING
Normal file
28
libraries/cppdap/CONTRIBUTING
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# How to Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution;
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
||||
|
||||
## Community Guidelines
|
||||
|
||||
This project follows
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
202
libraries/cppdap/LICENSE
Normal file
202
libraries/cppdap/LICENSE
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
79
libraries/cppdap/README.md
Normal file
79
libraries/cppdap/README.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# cppdap
|
||||
|
||||
## About
|
||||
|
||||
`cppdap` is a C++11 library (["SDK"](https://microsoft.github.io/debug-adapter-protocol/implementors/sdks/)) implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/), providing an API for implementing a DAP client or server.
|
||||
|
||||
`cppdap` provides C++ type-safe structures for the full [DAP specification](https://microsoft.github.io/debug-adapter-protocol/specification), and provides a simple way to add custom protocol messages.
|
||||
|
||||
## Fetching dependencies
|
||||
|
||||
`cppdap` provides CMake build files to build the library, unit tests and examples.
|
||||
|
||||
`cppdap` depends on the [`nlohmann/json` library](https://github.com/nlohmann/json), and the unit tests depend on the [`googletest` library](https://github.com/google/googletest). Both are referenced as a git submodules.
|
||||
|
||||
Before building, fetch the git submodules with:
|
||||
|
||||
```bash
|
||||
cd <path-to-cppdap>
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Alternatively, `cppdap` can use the [`RapidJSON` library](https://rapidjson.org/) or the [`JsonCpp` library](https://github.com/open-source-parsers/jsoncpp) for JSON serialization. Use the `CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE`, `CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE`, and `CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE` CMake cache variables to select which library to use.
|
||||
|
||||
## Building
|
||||
|
||||
### Linux and macOS
|
||||
|
||||
Next, generate the build files:
|
||||
|
||||
```bash
|
||||
cd <path-to-cppdap>
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```
|
||||
|
||||
You may wish to suffix the `cmake ..` line with any of the following flags:
|
||||
|
||||
* `-DCPPDAP_BUILD_TESTS=1` - Builds the `cppdap` unit tests
|
||||
* `-DCPPDAP_BUILD_EXAMPLES=1` - Builds the `cppdap` examples
|
||||
* `-DCPPDAP_INSTALL_VSCODE_EXAMPLES=1` - Installs the `cppdap` examples as Visual Studio Code extensions
|
||||
* `-DCPPDAP_WARNINGS_AS_ERRORS=1` - Treats all compiler warnings as errors.
|
||||
|
||||
Finally, build the project:
|
||||
|
||||
`make`
|
||||
|
||||
### Windows
|
||||
|
||||
`cppdap` can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019).
|
||||
|
||||
|
||||
### Using `cppdap` in your CMake project
|
||||
|
||||
You can build and link `cppdap` using `add_subdirectory()` in your project's `CMakeLists.txt` file:
|
||||
```cmake
|
||||
set(CPPDAP_DIR <path-to-cppdap>) # example <path-to-cppdap>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cppdap"
|
||||
add_subdirectory(${CPPDAP_DIR})
|
||||
```
|
||||
|
||||
This will define the `cppdap` library target, which you can pass to `target_link_libraries()`:
|
||||
|
||||
```cmake
|
||||
target_link_libraries(<target> cppdap) # replace <target> with the name of your project's target
|
||||
```
|
||||
|
||||
You may also wish to specify your own paths to the third party libraries used by `cppdap`.
|
||||
You can do this by setting any of the following variables before the call to `add_subdirectory()`:
|
||||
|
||||
```cmake
|
||||
set(CPPDAP_THIRD_PARTY_DIR <third-party-root-directory>) # defaults to ${CPPDAP_DIR}/third_party
|
||||
set(CPPDAP_JSON_DIR <path-to-nlohmann-json>) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/json
|
||||
set(CPPDAP_GOOGLETEST_DIR <path-to-googletest>) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/googletest
|
||||
add_subdirectory(${CPPDAP_DIR})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Note: This is not an officially supported Google product
|
||||
21
libraries/cppdap/clang-format-all.sh
Executable file
21
libraries/cppdap/clang-format-all.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
SRC_DIR=${ROOT_DIR}/src
|
||||
CLANG_FORMAT=${CLANG_FORMAT:-clang-format}
|
||||
|
||||
# Double clang-format, as it seems that one pass isn't always enough
|
||||
find ${SRC_DIR} -iname "*.h" -o -iname "*.cpp" | xargs ${CLANG_FORMAT} -i -style=file
|
||||
find ${SRC_DIR} -iname "*.h" -o -iname "*.cpp" | xargs ${CLANG_FORMAT} -i -style=file
|
||||
25
libraries/cppdap/cmake/Config.cmake.in
Normal file
25
libraries/cppdap/cmake/Config.cmake.in
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright 2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
@PACKAGE_INIT@
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@CPPDAP_TARGETS_EXPORT_NAME@.cmake")
|
||||
check_required_components("@CPPDAP_TARGET_NAME@")
|
||||
|
||||
if ( @CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGE@ )
|
||||
find_dependency(nlohmann_json CONFIG)
|
||||
elseif( @CPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE@ )
|
||||
find_dependency(RapidJSON CONFIG)
|
||||
endif()
|
||||
469
libraries/cppdap/examples/hello_debugger.cpp
Normal file
469
libraries/cppdap/examples/hello_debugger.cpp
Normal file
|
|
@ -0,0 +1,469 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// hello_debugger is an example DAP server that provides single line stepping
|
||||
// through a synthetic file.
|
||||
|
||||
#include "dap/io.h"
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/session.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define OS_WINDOWS 1
|
||||
#endif
|
||||
|
||||
// Uncomment the line below and change <path-to-log-file> to a file path to
|
||||
// write all DAP communications to the given path.
|
||||
//
|
||||
// #define LOG_TO_FILE "<path-to-log-file>"
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#include <fcntl.h> // _O_BINARY
|
||||
#include <io.h> // _setmode
|
||||
#endif // OS_WINDOWS
|
||||
|
||||
namespace {
|
||||
|
||||
// sourceContent holds the synthetic file source.
|
||||
constexpr char sourceContent[] = R"(// Hello Debugger!
|
||||
|
||||
This is a synthetic source file provided by the DAP debugger.
|
||||
|
||||
You can set breakpoints, and single line step.
|
||||
|
||||
You may also notice that the locals contains a single variable for the currently executing line number.)";
|
||||
|
||||
// Total number of newlines in source.
|
||||
constexpr int64_t numSourceLines = 7;
|
||||
|
||||
// Debugger holds the dummy debugger state and fires events to the EventHandler
|
||||
// passed to the constructor.
|
||||
class Debugger {
|
||||
public:
|
||||
enum class Event { BreakpointHit, Stepped, Paused };
|
||||
using EventHandler = std::function<void(Event)>;
|
||||
|
||||
Debugger(const EventHandler&);
|
||||
|
||||
// run() instructs the debugger to continue execution.
|
||||
void run();
|
||||
|
||||
// pause() instructs the debugger to pause execution.
|
||||
void pause();
|
||||
|
||||
// currentLine() returns the currently executing line number.
|
||||
int64_t currentLine();
|
||||
|
||||
// stepForward() instructs the debugger to step forward one line.
|
||||
void stepForward();
|
||||
|
||||
// clearBreakpoints() clears all set breakpoints.
|
||||
void clearBreakpoints();
|
||||
|
||||
// addBreakpoint() sets a new breakpoint on the given line.
|
||||
void addBreakpoint(int64_t line);
|
||||
|
||||
private:
|
||||
EventHandler onEvent;
|
||||
std::mutex mutex;
|
||||
int64_t line = 1;
|
||||
std::unordered_set<int64_t> breakpoints;
|
||||
};
|
||||
|
||||
Debugger::Debugger(const EventHandler& onEvent) : onEvent(onEvent) {}
|
||||
|
||||
void Debugger::run() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
for (int64_t i = 0; i < numSourceLines; i++) {
|
||||
int64_t l = ((line + i) % numSourceLines) + 1;
|
||||
if (breakpoints.count(l)) {
|
||||
line = l;
|
||||
lock.unlock();
|
||||
onEvent(Event::BreakpointHit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::pause() {
|
||||
onEvent(Event::Paused);
|
||||
}
|
||||
|
||||
int64_t Debugger::currentLine() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return line;
|
||||
}
|
||||
|
||||
void Debugger::stepForward() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
line = (line % numSourceLines) + 1;
|
||||
lock.unlock();
|
||||
onEvent(Event::Stepped);
|
||||
}
|
||||
|
||||
void Debugger::clearBreakpoints() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
this->breakpoints.clear();
|
||||
}
|
||||
|
||||
void Debugger::addBreakpoint(int64_t l) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
this->breakpoints.emplace(l);
|
||||
}
|
||||
|
||||
// Event provides a basic wait and signal synchronization primitive.
|
||||
class Event {
|
||||
public:
|
||||
// wait() blocks until the event is fired.
|
||||
void wait();
|
||||
|
||||
// fire() sets signals the event, and unblocks any calls to wait().
|
||||
void fire();
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool fired = false;
|
||||
};
|
||||
|
||||
void Event::wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return fired; });
|
||||
}
|
||||
|
||||
void Event::fire() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
fired = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// main() entry point to the DAP server.
|
||||
int main(int, char*[]) {
|
||||
#ifdef OS_WINDOWS
|
||||
// Change stdin & stdout from text mode to binary mode.
|
||||
// This ensures sequences of \r\n are not changed to \n.
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif // OS_WINDOWS
|
||||
|
||||
std::shared_ptr<dap::Writer> log;
|
||||
#ifdef LOG_TO_FILE
|
||||
log = dap::file(LOG_TO_FILE);
|
||||
#endif
|
||||
|
||||
// Create the DAP session.
|
||||
// This is used to implement the DAP server.
|
||||
auto session = dap::Session::create();
|
||||
|
||||
// Hard-coded identifiers for the one thread, frame, variable and source.
|
||||
// These numbers have no meaning, and just need to remain constant for the
|
||||
// duration of the service.
|
||||
const dap::integer threadId = 100;
|
||||
const dap::integer frameId = 200;
|
||||
const dap::integer variablesReferenceId = 300;
|
||||
const dap::integer sourceReferenceId = 400;
|
||||
|
||||
// Signal events
|
||||
Event configured;
|
||||
Event terminate;
|
||||
|
||||
// Event handlers from the Debugger.
|
||||
auto onDebuggerEvent = [&](Debugger::Event onEvent) {
|
||||
switch (onEvent) {
|
||||
case Debugger::Event::Stepped: {
|
||||
// The debugger has single-line stepped. Inform the client.
|
||||
dap::StoppedEvent event;
|
||||
event.reason = "step";
|
||||
event.threadId = threadId;
|
||||
session->send(event);
|
||||
break;
|
||||
}
|
||||
case Debugger::Event::BreakpointHit: {
|
||||
// The debugger has hit a breakpoint. Inform the client.
|
||||
dap::StoppedEvent event;
|
||||
event.reason = "breakpoint";
|
||||
event.threadId = threadId;
|
||||
session->send(event);
|
||||
break;
|
||||
}
|
||||
case Debugger::Event::Paused: {
|
||||
// The debugger has been suspended. Inform the client.
|
||||
dap::StoppedEvent event;
|
||||
event.reason = "pause";
|
||||
event.threadId = threadId;
|
||||
session->send(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Construct the debugger.
|
||||
Debugger debugger(onDebuggerEvent);
|
||||
|
||||
// Handle errors reported by the Session. These errors include protocol
|
||||
// parsing errors and receiving messages with no handler.
|
||||
session->onError([&](const char* msg) {
|
||||
if (log) {
|
||||
dap::writef(log, "dap::Session error: %s\n", msg);
|
||||
log->close();
|
||||
}
|
||||
terminate.fire();
|
||||
});
|
||||
|
||||
// The Initialize request is the first message sent from the client and
|
||||
// the response reports debugger capabilities.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
|
||||
session->registerHandler([](const dap::InitializeRequest&) {
|
||||
dap::InitializeResponse response;
|
||||
response.supportsConfigurationDoneRequest = true;
|
||||
return response;
|
||||
});
|
||||
|
||||
// When the Initialize response has been sent, we need to send the initialized
|
||||
// event.
|
||||
// We use the registerSentHandler() to ensure the event is sent *after* the
|
||||
// initialize response.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Events_Initialized
|
||||
session->registerSentHandler(
|
||||
[&](const dap::ResponseOrError<dap::InitializeResponse>&) {
|
||||
session->send(dap::InitializedEvent());
|
||||
});
|
||||
|
||||
// The Threads request queries the debugger's list of active threads.
|
||||
// This example debugger only exposes a single thread.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Threads
|
||||
session->registerHandler([&](const dap::ThreadsRequest&) {
|
||||
dap::ThreadsResponse response;
|
||||
dap::Thread thread;
|
||||
thread.id = threadId;
|
||||
thread.name = "TheThread";
|
||||
response.threads.push_back(thread);
|
||||
return response;
|
||||
});
|
||||
|
||||
// The StackTrace request reports the stack frames (call stack) for a given
|
||||
// thread. This example debugger only exposes a single stack frame for the
|
||||
// single thread.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StackTrace
|
||||
session->registerHandler(
|
||||
[&](const dap::StackTraceRequest& request)
|
||||
-> dap::ResponseOrError<dap::StackTraceResponse> {
|
||||
if (request.threadId != threadId) {
|
||||
return dap::Error("Unknown threadId '%d'", int(request.threadId));
|
||||
}
|
||||
|
||||
dap::Source source;
|
||||
source.sourceReference = sourceReferenceId;
|
||||
source.name = "HelloDebuggerSource";
|
||||
|
||||
dap::StackFrame frame;
|
||||
frame.line = debugger.currentLine();
|
||||
frame.column = 1;
|
||||
frame.name = "HelloDebugger";
|
||||
frame.id = frameId;
|
||||
frame.source = source;
|
||||
|
||||
dap::StackTraceResponse response;
|
||||
response.stackFrames.push_back(frame);
|
||||
return response;
|
||||
});
|
||||
|
||||
// The Scopes request reports all the scopes of the given stack frame.
|
||||
// This example debugger only exposes a single 'Locals' scope for the single
|
||||
// frame.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Scopes
|
||||
session->registerHandler([&](const dap::ScopesRequest& request)
|
||||
-> dap::ResponseOrError<dap::ScopesResponse> {
|
||||
if (request.frameId != frameId) {
|
||||
return dap::Error("Unknown frameId '%d'", int(request.frameId));
|
||||
}
|
||||
|
||||
dap::Scope scope;
|
||||
scope.name = "Locals";
|
||||
scope.presentationHint = "locals";
|
||||
scope.variablesReference = variablesReferenceId;
|
||||
|
||||
dap::ScopesResponse response;
|
||||
response.scopes.push_back(scope);
|
||||
return response;
|
||||
});
|
||||
|
||||
// The Variables request reports all the variables for the given scope.
|
||||
// This example debugger only exposes a single 'currentLine' variable for the
|
||||
// single 'Locals' scope.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Variables
|
||||
session->registerHandler([&](const dap::VariablesRequest& request)
|
||||
-> dap::ResponseOrError<dap::VariablesResponse> {
|
||||
if (request.variablesReference != variablesReferenceId) {
|
||||
return dap::Error("Unknown variablesReference '%d'",
|
||||
int(request.variablesReference));
|
||||
}
|
||||
|
||||
dap::Variable currentLineVar;
|
||||
currentLineVar.name = "currentLine";
|
||||
currentLineVar.value = std::to_string(debugger.currentLine());
|
||||
currentLineVar.type = "int";
|
||||
|
||||
dap::VariablesResponse response;
|
||||
response.variables.push_back(currentLineVar);
|
||||
return response;
|
||||
});
|
||||
|
||||
// The Pause request instructs the debugger to pause execution of one or all
|
||||
// threads.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Pause
|
||||
session->registerHandler([&](const dap::PauseRequest&) {
|
||||
debugger.pause();
|
||||
return dap::PauseResponse();
|
||||
});
|
||||
|
||||
// The Continue request instructs the debugger to resume execution of one or
|
||||
// all threads.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Continue
|
||||
session->registerHandler([&](const dap::ContinueRequest&) {
|
||||
debugger.run();
|
||||
return dap::ContinueResponse();
|
||||
});
|
||||
|
||||
// The Next request instructs the debugger to single line step for a specific
|
||||
// thread.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Next
|
||||
session->registerHandler([&](const dap::NextRequest&) {
|
||||
debugger.stepForward();
|
||||
return dap::NextResponse();
|
||||
});
|
||||
|
||||
// The StepIn request instructs the debugger to step-in for a specific thread.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepIn
|
||||
session->registerHandler([&](const dap::StepInRequest&) {
|
||||
// Step-in treated as step-over as there's only one stack frame.
|
||||
debugger.stepForward();
|
||||
return dap::StepInResponse();
|
||||
});
|
||||
|
||||
// The StepOut request instructs the debugger to step-out for a specific
|
||||
// thread.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepOut
|
||||
session->registerHandler([&](const dap::StepOutRequest&) {
|
||||
// Step-out is not supported as there's only one stack frame.
|
||||
return dap::StepOutResponse();
|
||||
});
|
||||
|
||||
// The SetBreakpoints request instructs the debugger to clear and set a number
|
||||
// of line breakpoints for a specific source file.
|
||||
// This example debugger only exposes a single source file.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints
|
||||
session->registerHandler([&](const dap::SetBreakpointsRequest& request) {
|
||||
dap::SetBreakpointsResponse response;
|
||||
|
||||
auto breakpoints = request.breakpoints.value({});
|
||||
if (request.source.sourceReference.value(0) == sourceReferenceId) {
|
||||
debugger.clearBreakpoints();
|
||||
response.breakpoints.resize(breakpoints.size());
|
||||
for (size_t i = 0; i < breakpoints.size(); i++) {
|
||||
debugger.addBreakpoint(breakpoints[i].line);
|
||||
response.breakpoints[i].verified = breakpoints[i].line < numSourceLines;
|
||||
}
|
||||
} else {
|
||||
response.breakpoints.resize(breakpoints.size());
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
// The SetExceptionBreakpoints request configures the debugger's handling of
|
||||
// thrown exceptions.
|
||||
// This example debugger does not use any exceptions, so this is a no-op.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetExceptionBreakpoints
|
||||
session->registerHandler([&](const dap::SetExceptionBreakpointsRequest&) {
|
||||
return dap::SetExceptionBreakpointsResponse();
|
||||
});
|
||||
|
||||
// The Source request retrieves the source code for a given source file.
|
||||
// This example debugger only exposes one synthetic source file.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Source
|
||||
session->registerHandler([&](const dap::SourceRequest& request)
|
||||
-> dap::ResponseOrError<dap::SourceResponse> {
|
||||
if (request.sourceReference != sourceReferenceId) {
|
||||
return dap::Error("Unknown source reference '%d'",
|
||||
int(request.sourceReference));
|
||||
}
|
||||
|
||||
dap::SourceResponse response;
|
||||
response.content = sourceContent;
|
||||
return response;
|
||||
});
|
||||
|
||||
// The Launch request is made when the client instructs the debugger adapter
|
||||
// to start the debuggee. This request contains the launch arguments.
|
||||
// This example debugger does nothing with this request.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Launch
|
||||
session->registerHandler(
|
||||
[&](const dap::LaunchRequest&) { return dap::LaunchResponse(); });
|
||||
|
||||
// Handler for disconnect requests
|
||||
session->registerHandler([&](const dap::DisconnectRequest& request) {
|
||||
if (request.terminateDebuggee.value(false)) {
|
||||
terminate.fire();
|
||||
}
|
||||
return dap::DisconnectResponse();
|
||||
});
|
||||
|
||||
// The ConfigurationDone request is made by the client once all configuration
|
||||
// requests have been made.
|
||||
// This example debugger uses this request to 'start' the debugger.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_ConfigurationDone
|
||||
session->registerHandler([&](const dap::ConfigurationDoneRequest&) {
|
||||
configured.fire();
|
||||
return dap::ConfigurationDoneResponse();
|
||||
});
|
||||
|
||||
// All the handlers we care about have now been registered.
|
||||
// We now bind the session to stdin and stdout to connect to the client.
|
||||
// After the call to bind() we should start receiving requests, starting with
|
||||
// the Initialize request.
|
||||
std::shared_ptr<dap::Reader> in = dap::file(stdin, false);
|
||||
std::shared_ptr<dap::Writer> out = dap::file(stdout, false);
|
||||
if (log) {
|
||||
session->bind(spy(in, log), spy(out, log));
|
||||
} else {
|
||||
session->bind(in, out);
|
||||
}
|
||||
|
||||
// Wait for the ConfigurationDone request to be made.
|
||||
configured.wait();
|
||||
|
||||
// Broadcast the existance of the single thread to the client.
|
||||
dap::ThreadEvent threadStartedEvent;
|
||||
threadStartedEvent.reason = "started";
|
||||
threadStartedEvent.threadId = threadId;
|
||||
session->send(threadStartedEvent);
|
||||
|
||||
// Start the debugger in a paused state.
|
||||
// This sends a stopped event to the client.
|
||||
debugger.pause();
|
||||
|
||||
// Block until we receive a 'terminateDebuggee' request or encounter a session
|
||||
// error.
|
||||
terminate.wait();
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
libraries/cppdap/examples/simple_net_client_server.cpp
Normal file
109
libraries/cppdap/examples/simple_net_client_server.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// simple_net_client_server demonstrates a minimal DAP network connection
|
||||
// between a server and client.
|
||||
|
||||
#include "dap/io.h"
|
||||
#include "dap/network.h"
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/session.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
int main(int, char*[]) {
|
||||
constexpr int kPort = 19021;
|
||||
|
||||
// Callback handler for a socket connection to the server
|
||||
auto onClientConnected =
|
||||
[&](const std::shared_ptr<dap::ReaderWriter>& socket) {
|
||||
auto session = dap::Session::create();
|
||||
|
||||
// Set the session to close on invalid data. This ensures that data received over the network
|
||||
// receives a baseline level of validation before being processed.
|
||||
session->setOnInvalidData(dap::kClose);
|
||||
|
||||
session->bind(socket);
|
||||
|
||||
// The Initialize request is the first message sent from the client and
|
||||
// the response reports debugger capabilities.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
|
||||
session->registerHandler([&](const dap::InitializeRequest&) {
|
||||
dap::InitializeResponse response;
|
||||
printf("Server received initialize request from client\n");
|
||||
return response;
|
||||
});
|
||||
|
||||
// Signal used to terminate the server session when a DisconnectRequest
|
||||
// is made by the client.
|
||||
bool terminate = false;
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex; // guards 'terminate'
|
||||
|
||||
// The Disconnect request is made by the client before it disconnects
|
||||
// from the server.
|
||||
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect
|
||||
session->registerHandler([&](const dap::DisconnectRequest&) {
|
||||
// Client wants to disconnect. Set terminate to true, and signal the
|
||||
// condition variable to unblock the server thread.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
terminate = true;
|
||||
cv.notify_one();
|
||||
return dap::DisconnectResponse{};
|
||||
});
|
||||
|
||||
// Wait for the client to disconnect (or reach a 5 second timeout)
|
||||
// before releasing the session and disconnecting the socket to the
|
||||
// client.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait_for(lock, std::chrono::seconds(5), [&] { return terminate; });
|
||||
printf("Server closing connection\n");
|
||||
};
|
||||
|
||||
// Error handler
|
||||
auto onError = [&](const char* msg) { printf("Server error: %s\n", msg); };
|
||||
|
||||
// Create the network server
|
||||
auto server = dap::net::Server::create();
|
||||
// Start listening on kPort.
|
||||
// onClientConnected will be called when a client wants to connect.
|
||||
// onError will be called on any connection errors.
|
||||
server->start(kPort, onClientConnected, onError);
|
||||
|
||||
// Create a socket to the server. This will be used for the client side of the
|
||||
// connection.
|
||||
auto client = dap::net::connect("localhost", kPort);
|
||||
if (!client) {
|
||||
printf("Couldn't connect to server\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Attach a session to the client socket.
|
||||
auto session = dap::Session::create();
|
||||
session->bind(client);
|
||||
|
||||
// Set an initialize request to the server.
|
||||
auto future = session->send(dap::InitializeRequest{});
|
||||
printf("Client sent initialize request to server\n");
|
||||
printf("Waiting for response from server...\n");
|
||||
// Wait on the response.
|
||||
auto response = future.get();
|
||||
printf("Response received from server\n");
|
||||
printf("Disconnecting...\n");
|
||||
// Disconnect.
|
||||
session->send(dap::DisconnectRequest{});
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
libraries/cppdap/examples/vscode/package.json
Normal file
28
libraries/cppdap/examples/vscode/package.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "cppdap-example-@target@",
|
||||
"displayName": "cppdap example: @target@",
|
||||
"description": "cppdap example: @target@",
|
||||
"version": "1.0.0",
|
||||
"preview": false,
|
||||
"publisher": "Google LLC",
|
||||
"author": {
|
||||
"name": "Google LLC"
|
||||
},
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"engines": {
|
||||
"vscode": "^1.32.0"
|
||||
},
|
||||
"categories": [
|
||||
"Debuggers"
|
||||
],
|
||||
"contributes": {
|
||||
"debuggers": [
|
||||
{
|
||||
"type": "@target@",
|
||||
"program": "@target@@CPPDAP_OS_EXE_EXT@",
|
||||
"label": "cppdap example: @target@",
|
||||
"configurationAttributes": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
372
libraries/cppdap/fuzz/dictionary.txt
Normal file
372
libraries/cppdap/fuzz/dictionary.txt
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
"MD5"
|
||||
"SHA1"
|
||||
"SHA256"
|
||||
"__restart"
|
||||
"accessType"
|
||||
"accessTypes"
|
||||
"adapterData"
|
||||
"adapterID"
|
||||
"additionalModuleColumns"
|
||||
"address"
|
||||
"addressRange"
|
||||
"algorithm"
|
||||
"all"
|
||||
"allThreadsContinued"
|
||||
"allThreadsStopped"
|
||||
"allowPartial"
|
||||
"always"
|
||||
"appliesTo"
|
||||
"areas"
|
||||
"args"
|
||||
"argsCanBeInterpretedByShell"
|
||||
"arguments"
|
||||
"asAddress"
|
||||
"attach"
|
||||
"attachForSuspendedLaunch"
|
||||
"attributeName"
|
||||
"attributes"
|
||||
"baseClass"
|
||||
"body"
|
||||
"boolean"
|
||||
"breakMode"
|
||||
"breakpoint"
|
||||
"breakpointLocations"
|
||||
"breakpointModes"
|
||||
"breakpoints"
|
||||
"bytes"
|
||||
"bytesWritten"
|
||||
"canPersist"
|
||||
"canRestart"
|
||||
"cancel"
|
||||
"cancellable"
|
||||
"cancelled"
|
||||
"capabilities"
|
||||
"category"
|
||||
"changed"
|
||||
"checksum"
|
||||
"checksums"
|
||||
"class"
|
||||
"clientID"
|
||||
"clientName"
|
||||
"clipboard"
|
||||
"color"
|
||||
"column"
|
||||
"columnsStartAt1"
|
||||
"command"
|
||||
"completionTriggerCharacters"
|
||||
"completions"
|
||||
"condition"
|
||||
"conditionDescription"
|
||||
"configuration"
|
||||
"configurationDone"
|
||||
"console"
|
||||
"constructor"
|
||||
"content"
|
||||
"context"
|
||||
"continue"
|
||||
"continued"
|
||||
"count"
|
||||
"customcolor"
|
||||
"cwd"
|
||||
"data"
|
||||
"data breakpoint"
|
||||
"dataBreakpoint"
|
||||
"dataBreakpointInfo"
|
||||
"dataId"
|
||||
"dateTimeStamp"
|
||||
"declarationLocationReference"
|
||||
"deemphasize"
|
||||
"default"
|
||||
"description"
|
||||
"detail"
|
||||
"details"
|
||||
"disassemble"
|
||||
"disconnect"
|
||||
"emphasize"
|
||||
"end"
|
||||
"endColumn"
|
||||
"endLine"
|
||||
"entry"
|
||||
"enum"
|
||||
"env"
|
||||
"error"
|
||||
"evaluate"
|
||||
"evaluateName"
|
||||
"event"
|
||||
"exception"
|
||||
"exceptionBreakpointFilters"
|
||||
"exceptionId"
|
||||
"exceptionInfo"
|
||||
"exceptionOptions"
|
||||
"exitCode"
|
||||
"exited"
|
||||
"expensive"
|
||||
"expression"
|
||||
"external"
|
||||
"failed"
|
||||
"field"
|
||||
"file"
|
||||
"filter"
|
||||
"filterId"
|
||||
"filterOptions"
|
||||
"filters"
|
||||
"final"
|
||||
"format"
|
||||
"frameId"
|
||||
"fullTypeName"
|
||||
"function"
|
||||
"function breakpoint"
|
||||
"goto"
|
||||
"gotoTargets"
|
||||
"granularity"
|
||||
"group"
|
||||
"hex"
|
||||
"hitBreakpointIds"
|
||||
"hitCondition"
|
||||
"hover"
|
||||
"id"
|
||||
"important"
|
||||
"includeAll"
|
||||
"indexed"
|
||||
"indexedVariables"
|
||||
"initialize"
|
||||
"initialized"
|
||||
"innerClass"
|
||||
"innerException"
|
||||
"instruction"
|
||||
"instruction breakpoint"
|
||||
"instructionBytes"
|
||||
"instructionCount"
|
||||
"instructionOffset"
|
||||
"instructionPointerReference"
|
||||
"instructionReference"
|
||||
"instructions"
|
||||
"integrated"
|
||||
"interface"
|
||||
"internal"
|
||||
"invalid"
|
||||
"invalidated"
|
||||
"isLocalProcess"
|
||||
"isOptimized"
|
||||
"isUserCode"
|
||||
"keyword"
|
||||
"kind"
|
||||
"label"
|
||||
"launch"
|
||||
"lazy"
|
||||
"length"
|
||||
"levels"
|
||||
"line"
|
||||
"lines"
|
||||
"linesStartAt1"
|
||||
"loadedSource"
|
||||
"loadedSources"
|
||||
"locale"
|
||||
"locals"
|
||||
"location"
|
||||
"locationReference"
|
||||
"locations"
|
||||
"logMessage"
|
||||
"memory"
|
||||
"memoryReference"
|
||||
"message"
|
||||
"method"
|
||||
"mimeType"
|
||||
"mode"
|
||||
"module"
|
||||
"moduleCount"
|
||||
"moduleId"
|
||||
"modules"
|
||||
"mostDerivedClass"
|
||||
"name"
|
||||
"named"
|
||||
"namedVariables"
|
||||
"names"
|
||||
"negate"
|
||||
"never"
|
||||
"new"
|
||||
"next"
|
||||
"noDebug"
|
||||
"normal"
|
||||
"notStopped"
|
||||
"number"
|
||||
"offset"
|
||||
"origin"
|
||||
"output"
|
||||
"parameterNames"
|
||||
"parameterTypes"
|
||||
"parameterValues"
|
||||
"parameters"
|
||||
"path"
|
||||
"pathFormat"
|
||||
"pause"
|
||||
"pending"
|
||||
"percentage"
|
||||
"pointerSize"
|
||||
"presentationHint"
|
||||
"preserveFocusHint"
|
||||
"private"
|
||||
"process"
|
||||
"processId"
|
||||
"progressEnd"
|
||||
"progressId"
|
||||
"progressStart"
|
||||
"progressUpdate"
|
||||
"property"
|
||||
"protected"
|
||||
"public"
|
||||
"read"
|
||||
"readMemory"
|
||||
"readWrite"
|
||||
"reason"
|
||||
"reference"
|
||||
"registers"
|
||||
"removed"
|
||||
"repl"
|
||||
"request"
|
||||
"requestId"
|
||||
"request_seq"
|
||||
"resolveSymbols"
|
||||
"response"
|
||||
"restart"
|
||||
"restartFrame"
|
||||
"result"
|
||||
"returnValue"
|
||||
"reverseContinue"
|
||||
"runInTerminal"
|
||||
"scopes"
|
||||
"selectionLength"
|
||||
"selectionStart"
|
||||
"sendTelemetry"
|
||||
"seq"
|
||||
"setBreakpoints"
|
||||
"setDataBreakpoints"
|
||||
"setExceptionBreakpoints"
|
||||
"setExpression"
|
||||
"setFunctionBreakpoints"
|
||||
"setInstructionBreakpoints"
|
||||
"setVariable"
|
||||
"shellProcessId"
|
||||
"showUser"
|
||||
"singleThread"
|
||||
"snippet"
|
||||
"sortText"
|
||||
"source"
|
||||
"sourceModified"
|
||||
"sourceReference"
|
||||
"sources"
|
||||
"stackFrameId"
|
||||
"stackFrames"
|
||||
"stackTrace"
|
||||
"stacks"
|
||||
"start"
|
||||
"startCollapsed"
|
||||
"startDebugging"
|
||||
"startFrame"
|
||||
"startMethod"
|
||||
"startModule"
|
||||
"started"
|
||||
"statement"
|
||||
"stderr"
|
||||
"stdout"
|
||||
"step"
|
||||
"stepBack"
|
||||
"stepIn"
|
||||
"stepInTargets"
|
||||
"stepOut"
|
||||
"stopped"
|
||||
"string"
|
||||
"subtle"
|
||||
"success"
|
||||
"supportSuspendDebuggee"
|
||||
"supportTerminateDebuggee"
|
||||
"supportedChecksumAlgorithms"
|
||||
"supportsANSIStyling"
|
||||
"supportsArgsCanBeInterpretedByShell"
|
||||
"supportsBreakpointLocationsRequest"
|
||||
"supportsCancelRequest"
|
||||
"supportsClipboardContext"
|
||||
"supportsCompletionsRequest"
|
||||
"supportsCondition"
|
||||
"supportsConditionalBreakpoints"
|
||||
"supportsConfigurationDoneRequest"
|
||||
"supportsDataBreakpointBytes"
|
||||
"supportsDataBreakpoints"
|
||||
"supportsDelayedStackTraceLoading"
|
||||
"supportsDisassembleRequest"
|
||||
"supportsEvaluateForHovers"
|
||||
"supportsExceptionFilterOptions"
|
||||
"supportsExceptionInfoRequest"
|
||||
"supportsExceptionOptions"
|
||||
"supportsFunctionBreakpoints"
|
||||
"supportsGotoTargetsRequest"
|
||||
"supportsHitConditionalBreakpoints"
|
||||
"supportsInstructionBreakpoints"
|
||||
"supportsInvalidatedEvent"
|
||||
"supportsLoadedSourcesRequest"
|
||||
"supportsLogPoints"
|
||||
"supportsMemoryEvent"
|
||||
"supportsMemoryReferences"
|
||||
"supportsModulesRequest"
|
||||
"supportsProgressReporting"
|
||||
"supportsReadMemoryRequest"
|
||||
"supportsRestartFrame"
|
||||
"supportsRestartRequest"
|
||||
"supportsRunInTerminalRequest"
|
||||
"supportsSetExpression"
|
||||
"supportsSetVariable"
|
||||
"supportsSingleThreadExecutionRequests"
|
||||
"supportsStartDebuggingRequest"
|
||||
"supportsStepBack"
|
||||
"supportsStepInTargetsRequest"
|
||||
"supportsSteppingGranularity"
|
||||
"supportsTerminateRequest"
|
||||
"supportsTerminateThreadsRequest"
|
||||
"supportsValueFormattingOptions"
|
||||
"supportsVariablePaging"
|
||||
"supportsVariableType"
|
||||
"supportsWriteMemoryRequest"
|
||||
"suspendDebuggee"
|
||||
"symbol"
|
||||
"symbolFilePath"
|
||||
"symbolStatus"
|
||||
"systemProcessId"
|
||||
"targetId"
|
||||
"targets"
|
||||
"telemetry"
|
||||
"terminate"
|
||||
"terminateDebuggee"
|
||||
"terminateThreads"
|
||||
"terminated"
|
||||
"text"
|
||||
"thread"
|
||||
"threadId"
|
||||
"threadIds"
|
||||
"threads"
|
||||
"timestamp"
|
||||
"title"
|
||||
"totalFrames"
|
||||
"totalModules"
|
||||
"type"
|
||||
"typeName"
|
||||
"unhandled"
|
||||
"unit"
|
||||
"unixTimestampUTC"
|
||||
"unreadableBytes"
|
||||
"uri"
|
||||
"url"
|
||||
"urlLabel"
|
||||
"userUnhandled"
|
||||
"value"
|
||||
"valueLocationReference"
|
||||
"variable"
|
||||
"variables"
|
||||
"variablesReference"
|
||||
"verified"
|
||||
"version"
|
||||
"virtual"
|
||||
"visibility"
|
||||
"watch"
|
||||
"width"
|
||||
"write"
|
||||
"writeMemory"
|
||||
128
libraries/cppdap/fuzz/fuzz.cpp
Normal file
128
libraries/cppdap/fuzz/fuzz.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// cppdap fuzzer program.
|
||||
// Run with: ${CPPDAP_PATH}/fuzz/run.sh
|
||||
// Requires modern clang toolchain.
|
||||
|
||||
#include "content_stream.h"
|
||||
#include "string_buffer.h"
|
||||
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/session.h"
|
||||
|
||||
#include "fuzz.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
|
||||
// Event provides a basic wait and signal synchronization primitive.
|
||||
class Event {
|
||||
public:
|
||||
// wait() blocks until the event is fired or the given timeout is reached.
|
||||
template <typename DURATION>
|
||||
inline void wait(const DURATION& duration) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait_for(lock, duration, [&] { return fired; });
|
||||
}
|
||||
|
||||
// fire() sets signals the event, and unblocks any calls to wait().
|
||||
inline void fire() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
fired = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool fired = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Fuzzing main function.
|
||||
// See http://llvm.org/docs/LibFuzzer.html for details.
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
// The first byte can optionally control fuzzing mode.
|
||||
enum class ControlMode {
|
||||
// Don't wrap the input data with a stream writer. Allows testing for stream
|
||||
// writing.
|
||||
TestStreamWriter,
|
||||
|
||||
// Don't append a 'done' request. This may cause the test to take longer to
|
||||
// complete (it may have to block on a timeout), but exercises the
|
||||
// unrecognised-message cases.
|
||||
DontAppendDoneRequest,
|
||||
|
||||
// Number of control modes in this enum.
|
||||
Count,
|
||||
};
|
||||
|
||||
// Scan first byte for control mode.
|
||||
bool useContentStreamWriter = true;
|
||||
bool appendDoneRequest = true;
|
||||
if (size > 0 && data[0] < static_cast<uint8_t>(ControlMode::Count)) {
|
||||
useContentStreamWriter =
|
||||
data[0] != static_cast<uint8_t>(ControlMode::TestStreamWriter);
|
||||
appendDoneRequest =
|
||||
data[0] != static_cast<uint8_t>(ControlMode::DontAppendDoneRequest);
|
||||
data++;
|
||||
size--;
|
||||
}
|
||||
|
||||
// in contains the input data
|
||||
auto in = std::make_shared<dap::StringBuffer>();
|
||||
|
||||
dap::ContentWriter writer(in);
|
||||
if (useContentStreamWriter) {
|
||||
writer.write(std::string(reinterpret_cast<const char*>(data), size));
|
||||
} else {
|
||||
in->write(data, size);
|
||||
}
|
||||
|
||||
if (appendDoneRequest) {
|
||||
writer.write(R"(
|
||||
{
|
||||
"seq": 10,
|
||||
"type": "request",
|
||||
"command": "done",
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
// Each test is done if we receive a request, or report an error.
|
||||
Event requestOrError;
|
||||
|
||||
#define DAP_REQUEST(REQUEST, RESPONSE) \
|
||||
session->registerHandler([&](const REQUEST&) { \
|
||||
requestOrError.fire(); \
|
||||
return RESPONSE{}; \
|
||||
});
|
||||
|
||||
auto session = dap::Session::create();
|
||||
DAP_REQUEST_LIST();
|
||||
|
||||
session->onError([&](const char*) { requestOrError.fire(); });
|
||||
|
||||
auto out = std::make_shared<dap::StringBuffer>();
|
||||
session->bind(dap::ReaderWriter::create(in, out));
|
||||
|
||||
// Give up after a second if we don't get a request or error reported.
|
||||
requestOrError.wait(std::chrono::seconds(1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
76
libraries/cppdap/fuzz/fuzz.h
Normal file
76
libraries/cppdap/fuzz/fuzz.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generated with protocol_gen.go -- do not edit this file.
|
||||
// go run scripts/protocol_gen/protocol_gen.go
|
||||
//
|
||||
// DAP version 1.68.0
|
||||
|
||||
#ifndef dap_fuzzer_h
|
||||
#define dap_fuzzer_h
|
||||
|
||||
#include "dap/protocol.h"
|
||||
|
||||
#define DAP_REQUEST_LIST() \
|
||||
DAP_REQUEST(dap::AttachRequest, dap::AttachResponse) \
|
||||
DAP_REQUEST(dap::BreakpointLocationsRequest, \
|
||||
dap::BreakpointLocationsResponse) \
|
||||
DAP_REQUEST(dap::CancelRequest, dap::CancelResponse) \
|
||||
DAP_REQUEST(dap::CompletionsRequest, dap::CompletionsResponse) \
|
||||
DAP_REQUEST(dap::ConfigurationDoneRequest, dap::ConfigurationDoneResponse) \
|
||||
DAP_REQUEST(dap::ContinueRequest, dap::ContinueResponse) \
|
||||
DAP_REQUEST(dap::DataBreakpointInfoRequest, dap::DataBreakpointInfoResponse) \
|
||||
DAP_REQUEST(dap::DisassembleRequest, dap::DisassembleResponse) \
|
||||
DAP_REQUEST(dap::DisconnectRequest, dap::DisconnectResponse) \
|
||||
DAP_REQUEST(dap::EvaluateRequest, dap::EvaluateResponse) \
|
||||
DAP_REQUEST(dap::ExceptionInfoRequest, dap::ExceptionInfoResponse) \
|
||||
DAP_REQUEST(dap::GotoRequest, dap::GotoResponse) \
|
||||
DAP_REQUEST(dap::GotoTargetsRequest, dap::GotoTargetsResponse) \
|
||||
DAP_REQUEST(dap::InitializeRequest, dap::InitializeResponse) \
|
||||
DAP_REQUEST(dap::LaunchRequest, dap::LaunchResponse) \
|
||||
DAP_REQUEST(dap::LoadedSourcesRequest, dap::LoadedSourcesResponse) \
|
||||
DAP_REQUEST(dap::LocationsRequest, dap::LocationsResponse) \
|
||||
DAP_REQUEST(dap::ModulesRequest, dap::ModulesResponse) \
|
||||
DAP_REQUEST(dap::NextRequest, dap::NextResponse) \
|
||||
DAP_REQUEST(dap::PauseRequest, dap::PauseResponse) \
|
||||
DAP_REQUEST(dap::ReadMemoryRequest, dap::ReadMemoryResponse) \
|
||||
DAP_REQUEST(dap::RestartFrameRequest, dap::RestartFrameResponse) \
|
||||
DAP_REQUEST(dap::RestartRequest, dap::RestartResponse) \
|
||||
DAP_REQUEST(dap::ReverseContinueRequest, dap::ReverseContinueResponse) \
|
||||
DAP_REQUEST(dap::RunInTerminalRequest, dap::RunInTerminalResponse) \
|
||||
DAP_REQUEST(dap::ScopesRequest, dap::ScopesResponse) \
|
||||
DAP_REQUEST(dap::SetBreakpointsRequest, dap::SetBreakpointsResponse) \
|
||||
DAP_REQUEST(dap::SetDataBreakpointsRequest, dap::SetDataBreakpointsResponse) \
|
||||
DAP_REQUEST(dap::SetExceptionBreakpointsRequest, \
|
||||
dap::SetExceptionBreakpointsResponse) \
|
||||
DAP_REQUEST(dap::SetExpressionRequest, dap::SetExpressionResponse) \
|
||||
DAP_REQUEST(dap::SetFunctionBreakpointsRequest, \
|
||||
dap::SetFunctionBreakpointsResponse) \
|
||||
DAP_REQUEST(dap::SetInstructionBreakpointsRequest, \
|
||||
dap::SetInstructionBreakpointsResponse) \
|
||||
DAP_REQUEST(dap::SetVariableRequest, dap::SetVariableResponse) \
|
||||
DAP_REQUEST(dap::SourceRequest, dap::SourceResponse) \
|
||||
DAP_REQUEST(dap::StackTraceRequest, dap::StackTraceResponse) \
|
||||
DAP_REQUEST(dap::StartDebuggingRequest, dap::StartDebuggingResponse) \
|
||||
DAP_REQUEST(dap::StepBackRequest, dap::StepBackResponse) \
|
||||
DAP_REQUEST(dap::StepInRequest, dap::StepInResponse) \
|
||||
DAP_REQUEST(dap::StepInTargetsRequest, dap::StepInTargetsResponse) \
|
||||
DAP_REQUEST(dap::StepOutRequest, dap::StepOutResponse) \
|
||||
DAP_REQUEST(dap::TerminateRequest, dap::TerminateResponse) \
|
||||
DAP_REQUEST(dap::TerminateThreadsRequest, dap::TerminateThreadsResponse) \
|
||||
DAP_REQUEST(dap::ThreadsRequest, dap::ThreadsResponse) \
|
||||
DAP_REQUEST(dap::VariablesRequest, dap::VariablesResponse) \
|
||||
DAP_REQUEST(dap::WriteMemoryRequest, dap::WriteMemoryResponse)
|
||||
|
||||
#endif // dap_fuzzer_h
|
||||
19
libraries/cppdap/fuzz/run.sh
Executable file
19
libraries/cppdap/fuzz/run.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e # Fail on any error.
|
||||
|
||||
FUZZ_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
|
||||
cd ${FUZZ_DIR}
|
||||
|
||||
# Ensure we're testing with latest build
|
||||
[ ! -d "build" ] && mkdir "build"
|
||||
cd "build"
|
||||
cmake ../.. -GNinja -DCPPDAP_BUILD_FUZZER=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
ninja
|
||||
|
||||
cd ${FUZZ_DIR}
|
||||
[ ! -d "corpus" ] && mkdir "corpus"
|
||||
[ ! -d "logs" ] && mkdir "logs"
|
||||
cd "logs"
|
||||
rm crash-* fuzz-* || true
|
||||
${FUZZ_DIR}/build/cppdap-fuzzer ${FUZZ_DIR}/corpus ${FUZZ_DIR}/seed -dict=${FUZZ_DIR}/dictionary.txt -jobs=128
|
||||
1
libraries/cppdap/fuzz/seed/empty_json
Normal file
1
libraries/cppdap/fuzz/seed/empty_json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
8
libraries/cppdap/fuzz/seed/request
Normal file
8
libraries/cppdap/fuzz/seed/request
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"seq": 153,
|
||||
"type": "request",
|
||||
"command": "next",
|
||||
"arguments": {
|
||||
"threadId": 3
|
||||
}
|
||||
}
|
||||
211
libraries/cppdap/include/dap/any.h
Normal file
211
libraries/cppdap/include/dap/any.h
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_any_h
|
||||
#define dap_any_h
|
||||
|
||||
#include "typeinfo.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dap {
|
||||
|
||||
template <typename T>
|
||||
struct TypeOf;
|
||||
class Deserializer;
|
||||
class Serializer;
|
||||
|
||||
// any provides a type-safe container for values of any of dap type (boolean,
|
||||
// integer, number, array, variant, any, null, dap-structs).
|
||||
class any {
|
||||
public:
|
||||
// constructors
|
||||
inline any() = default;
|
||||
inline any(const any& other) noexcept;
|
||||
inline any(any&& other) noexcept;
|
||||
|
||||
template <typename T>
|
||||
inline any(const T& val);
|
||||
|
||||
// destructors
|
||||
inline ~any();
|
||||
|
||||
// replaces the contained value with a null.
|
||||
inline void reset();
|
||||
|
||||
// assignment
|
||||
inline any& operator=(const any& rhs);
|
||||
inline any& operator=(any&& rhs) noexcept;
|
||||
template <typename T>
|
||||
inline any& operator=(const T& val);
|
||||
inline any& operator=(const std::nullptr_t& val);
|
||||
|
||||
// get() returns the contained value of the type T.
|
||||
// If the any does not contain a value of type T, then get() will assert.
|
||||
template <typename T>
|
||||
inline T& get() const;
|
||||
|
||||
// is() returns true iff the contained value is of type T.
|
||||
template <typename T>
|
||||
inline bool is() const;
|
||||
|
||||
private:
|
||||
friend class Deserializer;
|
||||
friend class Serializer;
|
||||
|
||||
static inline void* alignUp(void* val, size_t alignment);
|
||||
inline void alloc(size_t size, size_t align);
|
||||
inline void freeValue();
|
||||
inline bool isInBuffer(void* ptr) const;
|
||||
|
||||
void* value = nullptr;
|
||||
const TypeInfo* type = nullptr;
|
||||
void* heap = nullptr; // heap allocation
|
||||
uint8_t buffer[32]; // or internal allocation
|
||||
};
|
||||
|
||||
inline any::~any() {
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline any::any(const T& val) {
|
||||
*this = val;
|
||||
}
|
||||
|
||||
any::any(const any& other) noexcept : type(other.type) {
|
||||
if (other.value != nullptr) {
|
||||
alloc(type->size(), type->alignment());
|
||||
type->copyConstruct(value, other.value);
|
||||
}
|
||||
}
|
||||
|
||||
any::any(any&& other) noexcept : type(other.type) {
|
||||
if (other.isInBuffer(other.value)) {
|
||||
alloc(type->size(), type->alignment());
|
||||
type->copyConstruct(value, other.value);
|
||||
} else {
|
||||
value = other.value;
|
||||
}
|
||||
other.value = nullptr;
|
||||
other.type = nullptr;
|
||||
}
|
||||
|
||||
void any::reset() {
|
||||
if (value != nullptr) {
|
||||
type->destruct(value);
|
||||
freeValue();
|
||||
}
|
||||
value = nullptr;
|
||||
type = nullptr;
|
||||
}
|
||||
|
||||
any& any::operator=(const any& rhs) {
|
||||
reset();
|
||||
type = rhs.type;
|
||||
if (rhs.value != nullptr) {
|
||||
alloc(type->size(), type->alignment());
|
||||
type->copyConstruct(value, rhs.value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
any& any::operator=(any&& rhs) noexcept {
|
||||
reset();
|
||||
type = rhs.type;
|
||||
if (rhs.isInBuffer(rhs.value)) {
|
||||
alloc(type->size(), type->alignment());
|
||||
type->copyConstruct(value, rhs.value);
|
||||
} else {
|
||||
value = rhs.value;
|
||||
}
|
||||
rhs.value = nullptr;
|
||||
rhs.type = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
any& any::operator=(const T& val) {
|
||||
if (!is<T>()) {
|
||||
reset();
|
||||
type = TypeOf<T>::type();
|
||||
alloc(type->size(), type->alignment());
|
||||
type->copyConstruct(value, &val);
|
||||
} else {
|
||||
#ifdef __clang_analyzer__
|
||||
assert(value != nullptr);
|
||||
#endif
|
||||
*reinterpret_cast<T*>(value) = val;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
any& any::operator=(const std::nullptr_t&) {
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& any::get() const {
|
||||
static_assert(!std::is_same<T, std::nullptr_t>(),
|
||||
"Cannot get nullptr from 'any'.");
|
||||
assert(is<T>());
|
||||
return *reinterpret_cast<T*>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool any::is() const {
|
||||
return type == TypeOf<T>::type();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool any::is<std::nullptr_t>() const {
|
||||
return value == nullptr;
|
||||
}
|
||||
|
||||
void* any::alignUp(void* val, size_t alignment) {
|
||||
auto ptr = reinterpret_cast<uintptr_t>(val);
|
||||
return reinterpret_cast<void*>(alignment *
|
||||
((ptr + alignment - 1) / alignment));
|
||||
}
|
||||
|
||||
void any::alloc(size_t size, size_t align) {
|
||||
assert(value == nullptr);
|
||||
value = alignUp(buffer, align);
|
||||
if (isInBuffer(reinterpret_cast<uint8_t*>(value) + size - 1)) {
|
||||
return;
|
||||
}
|
||||
heap = new uint8_t[size + align];
|
||||
value = alignUp(heap, align);
|
||||
}
|
||||
|
||||
void any::freeValue() {
|
||||
assert(value != nullptr);
|
||||
if (heap != nullptr) {
|
||||
delete[] reinterpret_cast<uint8_t*>(heap);
|
||||
heap = nullptr;
|
||||
}
|
||||
value = nullptr;
|
||||
}
|
||||
|
||||
bool any::isInBuffer(void* ptr) const {
|
||||
auto addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
return addr >= reinterpret_cast<uintptr_t>(buffer) &&
|
||||
addr < reinterpret_cast<uintptr_t>(buffer + sizeof(buffer));
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_any_h
|
||||
35
libraries/cppdap/include/dap/dap.h
Normal file
35
libraries/cppdap/include/dap/dap.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_dap_h
|
||||
#define dap_dap_h
|
||||
|
||||
namespace dap {
|
||||
|
||||
// Explicit library initialization and termination functions.
|
||||
//
|
||||
// cppdap automatically initializes and terminates its internal state using lazy
|
||||
// static initialization, and so will usually work fine without explicit calls
|
||||
// to these functions.
|
||||
// However, if you use cppdap types in global state, you may need to call these
|
||||
// functions to ensure that cppdap is not uninitialized before the last usage.
|
||||
//
|
||||
// Each call to initialize() must have a corresponding call to terminate().
|
||||
// It is undefined behaviour to call initialize() after terminate().
|
||||
void initialize();
|
||||
void terminate();
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_dap_h
|
||||
179
libraries/cppdap/include/dap/future.h
Normal file
179
libraries/cppdap/include/dap/future.h
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_future_h
|
||||
#define dap_future_h
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace dap {
|
||||
|
||||
// internal functionality
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct promise_state {
|
||||
T val;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool hasVal = false;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// forward declaration
|
||||
template <typename T>
|
||||
class promise;
|
||||
|
||||
// future_status is the enumeration returned by future::wait_for and
|
||||
// future::wait_until.
|
||||
enum class future_status {
|
||||
ready,
|
||||
timeout,
|
||||
};
|
||||
|
||||
// future is a minimal reimplementation of std::future, that does not suffer
|
||||
// from TSAN false positives. See:
|
||||
// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
|
||||
template <typename T>
|
||||
class future {
|
||||
public:
|
||||
using State = detail::promise_state<T>;
|
||||
|
||||
// constructors
|
||||
inline future() = default;
|
||||
inline future(future&&) = default;
|
||||
|
||||
// valid() returns true if the future has an internal state.
|
||||
bool valid() const;
|
||||
|
||||
// get() blocks until the future has a valid result, and returns it.
|
||||
// The future must have a valid internal state to call this method.
|
||||
inline T get();
|
||||
|
||||
// wait() blocks until the future has a valid result.
|
||||
// The future must have a valid internal state to call this method.
|
||||
void wait() const;
|
||||
|
||||
// wait_for() blocks until the future has a valid result, or the timeout is
|
||||
// reached.
|
||||
// The future must have a valid internal state to call this method.
|
||||
template <class Rep, class Period>
|
||||
future_status wait_for(
|
||||
const std::chrono::duration<Rep, Period>& timeout) const;
|
||||
|
||||
// wait_until() blocks until the future has a valid result, or the timeout is
|
||||
// reached.
|
||||
// The future must have a valid internal state to call this method.
|
||||
template <class Clock, class Duration>
|
||||
future_status wait_until(
|
||||
const std::chrono::time_point<Clock, Duration>& timeout) const;
|
||||
|
||||
private:
|
||||
friend promise<T>;
|
||||
future(const future&) = delete;
|
||||
inline future(const std::shared_ptr<State>& state);
|
||||
|
||||
std::shared_ptr<State> state = std::make_shared<State>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
future<T>::future(const std::shared_ptr<State>& s) : state(s) {}
|
||||
|
||||
template <typename T>
|
||||
bool future<T>::valid() const {
|
||||
return static_cast<bool>(state);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T future<T>::get() {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
state->cv.wait(lock, [&] { return state->hasVal; });
|
||||
return state->val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void future<T>::wait() const {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
state->cv.wait(lock, [&] { return state->hasVal; });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class Rep, class Period>
|
||||
future_status future<T>::wait_for(
|
||||
const std::chrono::duration<Rep, Period>& timeout) const {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; })
|
||||
? future_status::ready
|
||||
: future_status::timeout;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class Clock, class Duration>
|
||||
future_status future<T>::wait_until(
|
||||
const std::chrono::time_point<Clock, Duration>& timeout) const {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; })
|
||||
? future_status::ready
|
||||
: future_status::timeout;
|
||||
}
|
||||
|
||||
// promise is a minimal reimplementation of std::promise, that does not suffer
|
||||
// from TSAN false positives. See:
|
||||
// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
|
||||
template <typename T>
|
||||
class promise {
|
||||
public:
|
||||
// constructors
|
||||
inline promise() = default;
|
||||
inline promise(promise&& other) = default;
|
||||
inline promise(const promise& other) = default;
|
||||
|
||||
// set_value() stores value to the shared state.
|
||||
// set_value() must only be called once.
|
||||
inline void set_value(const T& value) const;
|
||||
inline void set_value(T&& value) const;
|
||||
|
||||
// get_future() returns a future sharing this promise's state.
|
||||
future<T> get_future();
|
||||
|
||||
private:
|
||||
using State = detail::promise_state<T>;
|
||||
std::shared_ptr<State> state = std::make_shared<State>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
future<T> promise<T>::get_future() {
|
||||
return future<T>(state);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void promise<T>::set_value(const T& value) const {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
state->val = value;
|
||||
state->hasVal = true;
|
||||
state->cv.notify_all();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void promise<T>::set_value(T&& value) const {
|
||||
std::unique_lock<std::mutex> lock(state->mutex);
|
||||
state->val = std::move(value);
|
||||
state->hasVal = true;
|
||||
state->cv.notify_all();
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_future_h
|
||||
97
libraries/cppdap/include/dap/io.h
Normal file
97
libraries/cppdap/include/dap/io.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_io_h
|
||||
#define dap_io_h
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
#include <stdio.h> // FILE
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::pair
|
||||
|
||||
namespace dap {
|
||||
|
||||
class Closable {
|
||||
public:
|
||||
virtual ~Closable() = default;
|
||||
|
||||
// isOpen() returns true if the stream has not been closed.
|
||||
virtual bool isOpen() = 0;
|
||||
|
||||
// close() closes the stream.
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
// Reader is an interface for reading from a byte stream.
|
||||
class Reader : virtual public Closable {
|
||||
public:
|
||||
// read() attempts to read at most n bytes into buffer, returning the number
|
||||
// of bytes read.
|
||||
// read() will block until the stream is closed or at least one byte is read.
|
||||
virtual size_t read(void* buffer, size_t n) = 0;
|
||||
};
|
||||
|
||||
// Writer is an interface for writing to a byte stream.
|
||||
class Writer : virtual public Closable {
|
||||
public:
|
||||
// write() writes n bytes from buffer into the stream.
|
||||
// Returns true on success, or false if there was an error or the stream was
|
||||
// closed.
|
||||
virtual bool write(const void* buffer, size_t n) = 0;
|
||||
};
|
||||
|
||||
// ReaderWriter is an interface that combines the Reader and Writer interfaces.
|
||||
class ReaderWriter : public Reader, public Writer {
|
||||
public:
|
||||
// create() returns a ReaderWriter that delegates the interface methods on to
|
||||
// the provided Reader and Writer.
|
||||
// isOpen() returns true if the Reader and Writer both return true for
|
||||
// isOpen().
|
||||
// close() closes both the Reader and Writer.
|
||||
static std::shared_ptr<ReaderWriter> create(const std::shared_ptr<Reader>&,
|
||||
const std::shared_ptr<Writer>&);
|
||||
};
|
||||
|
||||
// pipe() returns a ReaderWriter where the Writer streams to the Reader.
|
||||
// Writes are internally buffered.
|
||||
// Calling close() on either the Reader or Writer will close both ends of the
|
||||
// stream.
|
||||
std::shared_ptr<ReaderWriter> pipe();
|
||||
|
||||
// file() wraps file with a ReaderWriter.
|
||||
// If closable is false, then a call to ReaderWriter::close() will not close the
|
||||
// underlying file.
|
||||
std::shared_ptr<ReaderWriter> file(FILE* file, bool closable = true);
|
||||
|
||||
// file() opens (or creates) the file with the given path.
|
||||
std::shared_ptr<ReaderWriter> file(const char* path);
|
||||
|
||||
// spy() returns a Reader that copies all reads from the Reader r to the Writer
|
||||
// s, using the given optional prefix.
|
||||
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
|
||||
const std::shared_ptr<Writer>& s,
|
||||
const char* prefix = "\n->");
|
||||
|
||||
// spy() returns a Writer that copies all writes to the Writer w to the Writer
|
||||
// s, using the given optional prefix.
|
||||
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
|
||||
const std::shared_ptr<Writer>& s,
|
||||
const char* prefix = "\n<-");
|
||||
|
||||
// writef writes the printf style string to the writer w.
|
||||
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...);
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_io_h
|
||||
71
libraries/cppdap/include/dap/network.h
Normal file
71
libraries/cppdap/include/dap/network.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_network_h
|
||||
#define dap_network_h
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dap {
|
||||
class ReaderWriter;
|
||||
|
||||
namespace net {
|
||||
|
||||
// connect() connects to the given TCP address and port.
|
||||
// If timeoutMillis is non-zero and no connection was made before timeoutMillis
|
||||
// milliseconds, then nullptr is returned.
|
||||
std::shared_ptr<ReaderWriter> connect(const char* addr,
|
||||
int port,
|
||||
uint32_t timeoutMillis = 0);
|
||||
|
||||
// Server implements a basic TCP server.
|
||||
class Server {
|
||||
// ignoreErrors() matches the OnError signature, and does nothing.
|
||||
static inline void ignoreErrors(const char*) {}
|
||||
|
||||
public:
|
||||
using OnError = std::function<void(const char*)>;
|
||||
using OnConnect = std::function<void(const std::shared_ptr<ReaderWriter>&)>;
|
||||
|
||||
virtual ~Server() = default;
|
||||
|
||||
// create() constructs and returns a new Server.
|
||||
static std::unique_ptr<Server> create();
|
||||
|
||||
// start() begins listening for connections on localhost and the given port.
|
||||
// callback will be called for each connection.
|
||||
// onError will be called for any connection errors.
|
||||
virtual bool start(int port,
|
||||
const OnConnect& callback,
|
||||
const OnError& onError = ignoreErrors) = 0;
|
||||
|
||||
// start() begins listening for connections on the given specific address and port.
|
||||
// callback will be called for each connection.
|
||||
// onError will be called for any connection errors.
|
||||
virtual bool start(const char* address,
|
||||
int port,
|
||||
const OnConnect& callback,
|
||||
const OnError& onError = ignoreErrors) = 0;
|
||||
|
||||
// stop() stops listening for connections.
|
||||
// stop() is implicitly called on destruction.
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_network_h
|
||||
263
libraries/cppdap/include/dap/optional.h
Normal file
263
libraries/cppdap/include/dap/optional.h
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_optional_h
|
||||
#define dap_optional_h
|
||||
|
||||
#include <assert.h>
|
||||
#include <type_traits>
|
||||
#include <utility> // std::move, std::forward
|
||||
|
||||
namespace dap {
|
||||
|
||||
// optional holds an 'optional' contained value.
|
||||
// This is similar to C++17's std::optional.
|
||||
template <typename T>
|
||||
class optional {
|
||||
template <typename U>
|
||||
using IsConvertibleToT =
|
||||
typename std::enable_if<std::is_convertible<U, T>::value>::type;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
// constructors
|
||||
inline optional() = default;
|
||||
inline optional(const optional& other);
|
||||
inline optional(optional&& other);
|
||||
template <typename U>
|
||||
inline optional(const optional<U>& other);
|
||||
template <typename U>
|
||||
inline optional(optional<U>&& other);
|
||||
template <typename U = value_type, typename = IsConvertibleToT<U>>
|
||||
inline optional(U&& value);
|
||||
|
||||
// value() returns the contained value.
|
||||
// If the optional does not contain a value, then value() will assert.
|
||||
inline T& value();
|
||||
inline const T& value() const;
|
||||
|
||||
// value() returns the contained value, or defaultValue if the optional does
|
||||
// not contain a value.
|
||||
inline const T& value(const T& defaultValue) const;
|
||||
|
||||
// operator bool() returns true if the optional contains a value.
|
||||
inline explicit operator bool() const noexcept;
|
||||
|
||||
// has_value() returns true if the optional contains a value.
|
||||
inline bool has_value() const;
|
||||
|
||||
// assignment
|
||||
inline optional& operator=(const optional& other);
|
||||
inline optional& operator=(optional&& other) noexcept;
|
||||
template <typename U = T, typename = IsConvertibleToT<U>>
|
||||
inline optional& operator=(U&& value);
|
||||
template <typename U>
|
||||
inline optional& operator=(const optional<U>& other);
|
||||
template <typename U>
|
||||
inline optional& operator=(optional<U>&& other);
|
||||
|
||||
// value access
|
||||
inline const T* operator->() const;
|
||||
inline T* operator->();
|
||||
inline const T& operator*() const;
|
||||
inline T& operator*();
|
||||
|
||||
private:
|
||||
T val{};
|
||||
bool set = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
optional<T>::optional(const optional& other) : val(other.val), set(other.set) {}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::optional(optional&& other)
|
||||
: val(std::move(other.val)), set(other.set) {}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
optional<T>::optional(const optional<U>& other) : set(other.has_value()) {
|
||||
if (set) {
|
||||
val = static_cast<T>(other.value());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
optional<T>::optional(optional<U>&& other) : set(other.has_value()) {
|
||||
if (set) {
|
||||
val = static_cast<T>(std::move(other.value()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U /*= T*/, typename>
|
||||
optional<T>::optional(U&& value) : val(std::forward<U>(value)), set(true) {}
|
||||
|
||||
template <typename T>
|
||||
T& optional<T>::value() {
|
||||
assert(set);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& optional<T>::value() const {
|
||||
assert(set);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& optional<T>::value(const T& defaultValue) const {
|
||||
if (!has_value()) {
|
||||
return defaultValue;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::operator bool() const noexcept {
|
||||
return set;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool optional<T>::has_value() const {
|
||||
return set;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>& optional<T>::operator=(const optional& other) {
|
||||
val = other.val;
|
||||
set = other.set;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>& optional<T>::operator=(optional&& other) noexcept {
|
||||
val = std::move(other.val);
|
||||
set = other.set;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U /* = T */, typename>
|
||||
optional<T>& optional<T>::operator=(U&& value) {
|
||||
val = std::forward<U>(value);
|
||||
set = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
optional<T>& optional<T>::operator=(const optional<U>& other) {
|
||||
val = other.val;
|
||||
set = other.set;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
optional<T>& optional<T>::operator=(optional<U>&& other) {
|
||||
val = std::move(other.val);
|
||||
set = other.set;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* optional<T>::operator->() const {
|
||||
assert(set);
|
||||
return &val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* optional<T>::operator->() {
|
||||
assert(set);
|
||||
return &val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& optional<T>::operator*() const {
|
||||
assert(set);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& optional<T>::operator*() {
|
||||
assert(set);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
if (!lhs.has_value() && !rhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
if (!lhs.has_value() || !rhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
return lhs.value() == rhs.value();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
if (!rhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (!lhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
return lhs.value() < rhs.value();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
if (!lhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
if (!rhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
return lhs.value() <= rhs.value();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
if (!lhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
if (!rhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
return lhs.value() > rhs.value();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
|
||||
if (!rhs.has_value()) {
|
||||
return true;
|
||||
}
|
||||
if (!lhs.has_value()) {
|
||||
return false;
|
||||
}
|
||||
return lhs.value() >= rhs.value();
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_optional_h
|
||||
2909
libraries/cppdap/include/dap/protocol.h
Normal file
2909
libraries/cppdap/include/dap/protocol.h
Normal file
File diff suppressed because it is too large
Load diff
253
libraries/cppdap/include/dap/serialization.h
Normal file
253
libraries/cppdap/include/dap/serialization.h
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_serialization_h
|
||||
#define dap_serialization_h
|
||||
|
||||
#include "typeof.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <cstddef> // ptrdiff_t
|
||||
#include <type_traits>
|
||||
|
||||
namespace dap {
|
||||
|
||||
// Field describes a single field of a struct.
|
||||
struct Field {
|
||||
std::string name; // name of the field
|
||||
ptrdiff_t offset; // offset of the field to the base of the struct
|
||||
const TypeInfo* type; // type of the field
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Deserializer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Deserializer is the interface used to decode data from structured storage.
|
||||
// Methods that return a bool use this to indicate success.
|
||||
class Deserializer {
|
||||
public:
|
||||
virtual ~Deserializer() = default;
|
||||
|
||||
// deserialization methods for simple data types.
|
||||
// If the stored object is not of the correct type, then these function will
|
||||
// return false.
|
||||
virtual bool deserialize(boolean*) const = 0;
|
||||
virtual bool deserialize(integer*) const = 0;
|
||||
virtual bool deserialize(number*) const = 0;
|
||||
virtual bool deserialize(string*) const = 0;
|
||||
virtual bool deserialize(object*) const = 0;
|
||||
virtual bool deserialize(any*) const = 0;
|
||||
|
||||
// count() returns the number of elements in the array object referenced by
|
||||
// this Deserializer.
|
||||
virtual size_t count() const = 0;
|
||||
|
||||
// array() calls the provided std::function for deserializing each array
|
||||
// element in the array object referenced by this Deserializer.
|
||||
virtual bool array(const std::function<bool(Deserializer*)>&) const = 0;
|
||||
|
||||
// field() calls the provided std::function for deserializing the field with
|
||||
// the given name from the struct object referenced by this Deserializer.
|
||||
virtual bool field(const std::string& name,
|
||||
const std::function<bool(Deserializer*)>&) const = 0;
|
||||
|
||||
// deserialize() delegates to TypeOf<T>::type()->deserialize().
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool deserialize(T*) const;
|
||||
|
||||
// deserialize() decodes an array.
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::array<T>*) const;
|
||||
|
||||
// deserialize() decodes an optional.
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::optional<T>*) const;
|
||||
|
||||
// deserialize() decodes an variant.
|
||||
template <typename T0, typename... Types>
|
||||
inline bool deserialize(dap::variant<T0, Types...>*) const;
|
||||
|
||||
// deserialize() decodes the struct field f with the given name.
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* f) const;
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
bool Deserializer::deserialize(T* ptr) const {
|
||||
return TypeOf<T>::type()->deserialize(this, ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Deserializer::deserialize(dap::array<T>* vec) const {
|
||||
auto n = count();
|
||||
vec->resize(n);
|
||||
size_t i = 0;
|
||||
if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Deserializer::deserialize(dap::optional<T>* opt) const {
|
||||
T v;
|
||||
if (deserialize(&v)) {
|
||||
*opt = v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
bool Deserializer::deserialize(dap::variant<T0, Types...>* var) const {
|
||||
return deserialize(&var->value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Deserializer::field(const std::string& name, T* v) const {
|
||||
return this->field(name,
|
||||
[&](const Deserializer* d) { return d->deserialize(v); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Serializer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class FieldSerializer;
|
||||
|
||||
// Serializer is the interface used to encode data to structured storage.
|
||||
// A Serializer is associated with a single storage object, whos type and value
|
||||
// is assigned by a call to serialize().
|
||||
// If serialize() is called multiple times on the same Serializer instance,
|
||||
// the last type and value is stored.
|
||||
// Methods that return a bool use this to indicate success.
|
||||
class Serializer {
|
||||
public:
|
||||
virtual ~Serializer() = default;
|
||||
|
||||
// serialization methods for simple data types.
|
||||
virtual bool serialize(boolean) = 0;
|
||||
virtual bool serialize(integer) = 0;
|
||||
virtual bool serialize(number) = 0;
|
||||
virtual bool serialize(const string&) = 0;
|
||||
virtual bool serialize(const dap::object&) = 0;
|
||||
virtual bool serialize(const any&) = 0;
|
||||
|
||||
// array() encodes count array elements to the array object referenced by this
|
||||
// Serializer. The std::function will be called count times, each time with a
|
||||
// Serializer that should be used to encode the n'th array element's data.
|
||||
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
|
||||
|
||||
// object() begins encoding the object referenced by this Serializer.
|
||||
// The std::function will be called with a FieldSerializer to serialize the
|
||||
// object's fields.
|
||||
virtual bool object(const std::function<bool(dap::FieldSerializer*)>&) = 0;
|
||||
|
||||
// remove() deletes the object referenced by this Serializer.
|
||||
// remove() can be used to serialize optionals with no value assigned.
|
||||
virtual void remove() = 0;
|
||||
|
||||
// serialize() delegates to TypeOf<T>::type()->serialize().
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool serialize(const T&);
|
||||
|
||||
// serialize() encodes the given array.
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::array<T>&);
|
||||
|
||||
// serialize() encodes the given optional.
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::optional<T>& v);
|
||||
|
||||
// serialize() encodes the given variant.
|
||||
template <typename T0, typename... Types>
|
||||
inline bool serialize(const dap::variant<T0, Types...>&);
|
||||
|
||||
// deserialize() encodes the given string.
|
||||
inline bool serialize(const char* v);
|
||||
protected:
|
||||
static inline const TypeInfo* get_any_type(const any&);
|
||||
static inline const void* get_any_val(const any&);
|
||||
};
|
||||
|
||||
inline const TypeInfo* Serializer::get_any_type(const any& a){
|
||||
return a.type;
|
||||
}
|
||||
const void* Serializer::get_any_val(const any& a) {
|
||||
return a.value;
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
bool Serializer::serialize(const T& object) {
|
||||
return TypeOf<T>::type()->serialize(this, &object);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Serializer::serialize(const dap::array<T>& vec) {
|
||||
auto it = vec.begin();
|
||||
return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Serializer::serialize(const dap::optional<T>& opt) {
|
||||
if (!opt.has_value()) {
|
||||
remove();
|
||||
return true;
|
||||
}
|
||||
return serialize(opt.value());
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
bool Serializer::serialize(const dap::variant<T0, Types...>& var) {
|
||||
return serialize(var.value);
|
||||
}
|
||||
|
||||
bool Serializer::serialize(const char* v) {
|
||||
return serialize(std::string(v));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FieldSerializer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FieldSerializer is the interface used to serialize fields of an object.
|
||||
class FieldSerializer {
|
||||
public:
|
||||
using SerializeFunc = std::function<bool(Serializer*)>;
|
||||
template <typename T>
|
||||
using IsSerializeFunc = std::is_convertible<T, SerializeFunc>;
|
||||
|
||||
virtual ~FieldSerializer() = default;
|
||||
|
||||
// field() encodes a field to the struct object referenced by this Serializer.
|
||||
// The SerializeFunc will be called with a Serializer used to encode the
|
||||
// field's data.
|
||||
virtual bool field(const std::string& name, const SerializeFunc&) = 0;
|
||||
|
||||
// field() encodes the field with the given name and value.
|
||||
template <
|
||||
typename T,
|
||||
typename = typename std::enable_if<!IsSerializeFunc<T>::value>::type>
|
||||
inline bool field(const std::string& name, const T& v);
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
bool FieldSerializer::field(const std::string& name, const T& v) {
|
||||
return this->field(name, [&](Serializer* s) { return s->serialize(v); });
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_serialization_h
|
||||
460
libraries/cppdap/include/dap/session.h
Normal file
460
libraries/cppdap/include/dap/session.h
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_session_h
|
||||
#define dap_session_h
|
||||
|
||||
#include "future.h"
|
||||
#include "io.h"
|
||||
#include "traits.h"
|
||||
#include "typeinfo.h"
|
||||
#include "typeof.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace dap {
|
||||
|
||||
// Forward declarations
|
||||
struct Request;
|
||||
struct Response;
|
||||
struct Event;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Error
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Error represents an error message in response to a DAP request.
|
||||
struct Error {
|
||||
Error() = default;
|
||||
Error(const std::string& error);
|
||||
Error(const char* msg, ...);
|
||||
|
||||
// operator bool() returns true if there is an error.
|
||||
inline operator bool() const { return message.size() > 0; }
|
||||
|
||||
std::string message; // empty represents success.
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ResponseOrError<T>
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ResponseOrError holds either the response to a DAP request or an error
|
||||
// message.
|
||||
template <typename T>
|
||||
struct ResponseOrError {
|
||||
using Request = T;
|
||||
|
||||
inline ResponseOrError() = default;
|
||||
inline ResponseOrError(const T& response);
|
||||
inline ResponseOrError(T&& response);
|
||||
inline ResponseOrError(const Error& error);
|
||||
inline ResponseOrError(Error&& error);
|
||||
inline ResponseOrError(const ResponseOrError& other);
|
||||
inline ResponseOrError(ResponseOrError&& other);
|
||||
|
||||
inline ResponseOrError& operator=(const ResponseOrError& other);
|
||||
inline ResponseOrError& operator=(ResponseOrError&& other);
|
||||
|
||||
T response;
|
||||
Error error; // empty represents success.
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(const T& resp) : response(resp) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(T&& resp) : response(std::move(resp)) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(const Error& err) : error(err) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(Error&& err) : error(std::move(err)) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(const ResponseOrError& other)
|
||||
: response(other.response), error(other.error) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>::ResponseOrError(ResponseOrError&& other)
|
||||
: response(std::move(other.response)), error(std::move(other.error)) {}
|
||||
template <typename T>
|
||||
ResponseOrError<T>& ResponseOrError<T>::operator=(
|
||||
const ResponseOrError& other) {
|
||||
response = other.response;
|
||||
error = other.error;
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
ResponseOrError<T>& ResponseOrError<T>::operator=(ResponseOrError&& other) {
|
||||
response = std::move(other.response);
|
||||
error = std::move(other.error);
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Session
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// An enum flag that controls how the Session handles invalid data.
|
||||
enum OnInvalidData {
|
||||
// Ignore invalid data.
|
||||
kIgnore,
|
||||
// Close the underlying reader when invalid data is received.
|
||||
kClose,
|
||||
};
|
||||
|
||||
// Session implements a DAP client or server endpoint.
|
||||
// The general usage is as follows:
|
||||
// (1) Create a session with Session::create().
|
||||
// (2) Register request and event handlers with registerHandler().
|
||||
// (3) Optionally register a protocol error handler with onError().
|
||||
// (3) Bind the session to the remote endpoint with bind().
|
||||
// (4) Send requests or events with send().
|
||||
class Session {
|
||||
template <typename F, int N>
|
||||
using ParamType = traits::ParameterType<F, N>;
|
||||
|
||||
template <typename T>
|
||||
using IsRequest = traits::EnableIfIsType<dap::Request, T>;
|
||||
|
||||
template <typename T>
|
||||
using IsEvent = traits::EnableIfIsType<dap::Event, T>;
|
||||
|
||||
template <typename F>
|
||||
using IsRequestHandlerWithoutCallback = traits::EnableIf<
|
||||
traits::CompatibleWith<F, std::function<void(dap::Request)>>::value>;
|
||||
|
||||
template <typename F, typename CallbackType>
|
||||
using IsRequestHandlerWithCallback = traits::EnableIf<traits::CompatibleWith<
|
||||
F,
|
||||
std::function<void(dap::Request, std::function<void(CallbackType)>)>>::
|
||||
value>;
|
||||
|
||||
public:
|
||||
virtual ~Session();
|
||||
|
||||
// ErrorHandler is the type of callback function used for reporting protocol
|
||||
// errors.
|
||||
using ErrorHandler = std::function<void(const char*)>;
|
||||
|
||||
// ClosedHandler is the type of callback function used to signal that a
|
||||
// connected endpoint has closed.
|
||||
using ClosedHandler = std::function<void()>;
|
||||
|
||||
// create() constructs and returns a new Session.
|
||||
static std::unique_ptr<Session> create();
|
||||
|
||||
// Sets how the Session handles invalid data.
|
||||
virtual void setOnInvalidData(OnInvalidData) = 0;
|
||||
|
||||
// onError() registers a error handler that will be called whenever a protocol
|
||||
// error is encountered.
|
||||
// Only one error handler can be bound at any given time, and later calls
|
||||
// will replace the existing error handler.
|
||||
virtual void onError(const ErrorHandler&) = 0;
|
||||
|
||||
// registerHandler() registers a request handler for a specific request type.
|
||||
// The function F must have one of the following signatures:
|
||||
// ResponseOrError<ResponseType>(const RequestType&)
|
||||
// ResponseType(const RequestType&)
|
||||
// Error(const RequestType&)
|
||||
template <typename F, typename RequestType = ParamType<F, 0>>
|
||||
inline IsRequestHandlerWithoutCallback<F> registerHandler(F&& handler);
|
||||
|
||||
// registerHandler() registers a request handler for a specific request type.
|
||||
// The handler has a response callback function for the second argument of the
|
||||
// handler function. This callback may be called after the handler has
|
||||
// returned.
|
||||
// The function F must have the following signature:
|
||||
// void(const RequestType& request,
|
||||
// std::function<void(ResponseType)> response)
|
||||
template <typename F,
|
||||
typename RequestType = ParamType<F, 0>,
|
||||
typename ResponseType = typename RequestType::Response>
|
||||
inline IsRequestHandlerWithCallback<F, ResponseType> registerHandler(
|
||||
F&& handler);
|
||||
|
||||
// registerHandler() registers a request handler for a specific request type.
|
||||
// The handler has a response callback function for the second argument of the
|
||||
// handler function. This callback may be called after the handler has
|
||||
// returned.
|
||||
// The function F must have the following signature:
|
||||
// void(const RequestType& request,
|
||||
// std::function<void(ResponseOrError<ResponseType>)> response)
|
||||
template <typename F,
|
||||
typename RequestType = ParamType<F, 0>,
|
||||
typename ResponseType = typename RequestType::Response>
|
||||
inline IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
|
||||
registerHandler(F&& handler);
|
||||
|
||||
// registerHandler() registers a event handler for a specific event type.
|
||||
// The function F must have the following signature:
|
||||
// void(const EventType&)
|
||||
template <typename F, typename EventType = ParamType<F, 0>>
|
||||
inline IsEvent<EventType> registerHandler(F&& handler);
|
||||
|
||||
// registerSentHandler() registers the function F to be called when a response
|
||||
// of the specific type has been sent.
|
||||
// The function F must have the following signature:
|
||||
// void(const ResponseOrError<ResponseType>&)
|
||||
template <typename F,
|
||||
typename ResponseType = typename ParamType<F, 0>::Request>
|
||||
inline void registerSentHandler(F&& handler);
|
||||
|
||||
// send() sends the request to the connected endpoint and returns a
|
||||
// future that is assigned the request response or error.
|
||||
template <typename T, typename = IsRequest<T>>
|
||||
future<ResponseOrError<typename T::Response>> send(const T& request);
|
||||
|
||||
// send() sends the event to the connected endpoint.
|
||||
template <typename T, typename = IsEvent<T>>
|
||||
void send(const T& event);
|
||||
|
||||
// bind() connects this Session to an endpoint using connect(), and then
|
||||
// starts processing incoming messages with startProcessingMessages().
|
||||
// onClose is the optional callback which will be called when the session
|
||||
// endpoint has been closed.
|
||||
inline void bind(const std::shared_ptr<Reader>& reader,
|
||||
const std::shared_ptr<Writer>& writer,
|
||||
const ClosedHandler& onClose);
|
||||
inline void bind(const std::shared_ptr<ReaderWriter>& readerWriter,
|
||||
const ClosedHandler& onClose);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Note:
|
||||
// Methods and members below this point are for advanced usage, and are more
|
||||
// likely to change signature than the methods above.
|
||||
// The methods above this point should be sufficient for most use cases.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// connect() connects this Session to an endpoint.
|
||||
// connect() can only be called once. Repeated calls will raise an error, but
|
||||
// otherwise will do nothing.
|
||||
// Note: This method is used for explicit control over message handling.
|
||||
// Most users will use bind() instead of calling this method directly.
|
||||
virtual void connect(const std::shared_ptr<Reader>&,
|
||||
const std::shared_ptr<Writer>&) = 0;
|
||||
inline void connect(const std::shared_ptr<ReaderWriter>&);
|
||||
|
||||
// startProcessingMessages() starts a new thread to receive and dispatch
|
||||
// incoming messages.
|
||||
// onClose is the optional callback which will be called when the session
|
||||
// endpoint has been closed.
|
||||
// Note: This method is used for explicit control over message handling.
|
||||
// Most users will use bind() instead of calling this method directly.
|
||||
virtual void startProcessingMessages(const ClosedHandler& onClose = {}) = 0;
|
||||
|
||||
// getPayload() blocks until the next incoming message is received, returning
|
||||
// the payload or an empty function if the connection was lost. The returned
|
||||
// payload is function that can be called on any thread to dispatch the
|
||||
// message to the Session handler.
|
||||
// Note: This method is used for explicit control over message handling.
|
||||
// Most users will use bind() instead of calling this method directly.
|
||||
virtual std::function<void()> getPayload() = 0;
|
||||
|
||||
// The callback function type called when a request handler is invoked, and
|
||||
// the request returns a successful result.
|
||||
// 'responseTypeInfo' is the type information of the response data structure.
|
||||
// 'responseData' is a pointer to response payload data.
|
||||
using RequestHandlerSuccessCallback =
|
||||
std::function<void(const TypeInfo* responseTypeInfo,
|
||||
const void* responseData)>;
|
||||
|
||||
// The callback function type used to notify when a DAP request fails.
|
||||
// 'responseTypeInfo' is the type information of the response data structure.
|
||||
// 'message' is the error message
|
||||
using RequestHandlerErrorCallback =
|
||||
std::function<void(const TypeInfo* responseTypeInfo,
|
||||
const Error& message)>;
|
||||
|
||||
// The callback function type used to invoke a request handler.
|
||||
// 'request' is a pointer to the request data structure
|
||||
// 'onSuccess' is the function to call if the request completed succesfully.
|
||||
// 'onError' is the function to call if the request failed.
|
||||
// For each call of the request handler, 'onSuccess' or 'onError' must be
|
||||
// called exactly once.
|
||||
using GenericRequestHandler =
|
||||
std::function<void(const void* request,
|
||||
const RequestHandlerSuccessCallback& onSuccess,
|
||||
const RequestHandlerErrorCallback& onError)>;
|
||||
|
||||
// The callback function type used to handle a response to a request.
|
||||
// 'response' is a pointer to the response data structure. May be nullptr.
|
||||
// 'error' is a pointer to the reponse error message. May be nullptr.
|
||||
// One of 'data' or 'error' will be nullptr.
|
||||
using GenericResponseHandler =
|
||||
std::function<void(const void* response, const Error* error)>;
|
||||
|
||||
// The callback function type used to handle an event.
|
||||
// 'event' is a pointer to the event data structure.
|
||||
using GenericEventHandler = std::function<void(const void* event)>;
|
||||
|
||||
// The callback function type used to notify when a response has been sent
|
||||
// from this session endpoint.
|
||||
// 'response' is a pointer to the response data structure.
|
||||
// 'error' is a pointer to the reponse error message. May be nullptr.
|
||||
using GenericResponseSentHandler =
|
||||
std::function<void(const void* response, const Error* error)>;
|
||||
|
||||
// registerHandler() registers 'handler' as the request handler callback for
|
||||
// requests of the type 'typeinfo'.
|
||||
virtual void registerHandler(const TypeInfo* typeinfo,
|
||||
const GenericRequestHandler& handler) = 0;
|
||||
|
||||
// registerHandler() registers 'handler' as the event handler callback for
|
||||
// events of the type 'typeinfo'.
|
||||
virtual void registerHandler(const TypeInfo* typeinfo,
|
||||
const GenericEventHandler& handler) = 0;
|
||||
|
||||
// registerHandler() registers 'handler' as the response-sent handler function
|
||||
// which is called whenever a response of the type 'typeinfo' is sent from
|
||||
// this session endpoint.
|
||||
virtual void registerHandler(const TypeInfo* typeinfo,
|
||||
const GenericResponseSentHandler& handler) = 0;
|
||||
|
||||
// send() sends a request to the remote endpoint.
|
||||
// 'requestTypeInfo' is the type info of the request data structure.
|
||||
// 'requestTypeInfo' is the type info of the response data structure.
|
||||
// 'request' is a pointer to the request data structure.
|
||||
// 'responseHandler' is the handler function for the response.
|
||||
virtual bool send(const dap::TypeInfo* requestTypeInfo,
|
||||
const dap::TypeInfo* responseTypeInfo,
|
||||
const void* request,
|
||||
const GenericResponseHandler& responseHandler) = 0;
|
||||
|
||||
// send() sends an event to the remote endpoint.
|
||||
// 'eventTypeInfo' is the type info for the event data structure.
|
||||
// 'event' is a pointer to the event data structure.
|
||||
virtual bool send(const TypeInfo* eventTypeInfo, const void* event) = 0;
|
||||
};
|
||||
|
||||
template <typename F, typename RequestType>
|
||||
Session::IsRequestHandlerWithoutCallback<F> Session::registerHandler(
|
||||
F&& handler) {
|
||||
using ResponseType = typename RequestType::Response;
|
||||
const TypeInfo* typeinfo = TypeOf<RequestType>::type();
|
||||
registerHandler(typeinfo,
|
||||
[handler](const void* args,
|
||||
const RequestHandlerSuccessCallback& onSuccess,
|
||||
const RequestHandlerErrorCallback& onError) {
|
||||
ResponseOrError<ResponseType> res =
|
||||
handler(*reinterpret_cast<const RequestType*>(args));
|
||||
if (res.error) {
|
||||
onError(TypeOf<ResponseType>::type(), res.error);
|
||||
} else {
|
||||
onSuccess(TypeOf<ResponseType>::type(), &res.response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename F, typename RequestType, typename ResponseType>
|
||||
Session::IsRequestHandlerWithCallback<F, ResponseType> Session::registerHandler(
|
||||
F&& handler) {
|
||||
using CallbackType = ParamType<F, 1>;
|
||||
registerHandler(
|
||||
TypeOf<RequestType>::type(),
|
||||
[handler](const void* args,
|
||||
const RequestHandlerSuccessCallback& onSuccess,
|
||||
const RequestHandlerErrorCallback&) {
|
||||
CallbackType responseCallback = [onSuccess](const ResponseType& res) {
|
||||
onSuccess(TypeOf<ResponseType>::type(), &res);
|
||||
};
|
||||
handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename F, typename RequestType, typename ResponseType>
|
||||
Session::IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
|
||||
Session::registerHandler(F&& handler) {
|
||||
using CallbackType = ParamType<F, 1>;
|
||||
registerHandler(
|
||||
TypeOf<RequestType>::type(),
|
||||
[handler](const void* args,
|
||||
const RequestHandlerSuccessCallback& onSuccess,
|
||||
const RequestHandlerErrorCallback& onError) {
|
||||
CallbackType responseCallback =
|
||||
[onError, onSuccess](const ResponseOrError<ResponseType>& res) {
|
||||
if (res.error) {
|
||||
onError(TypeOf<ResponseType>::type(), res.error);
|
||||
} else {
|
||||
onSuccess(TypeOf<ResponseType>::type(), &res.response);
|
||||
}
|
||||
};
|
||||
handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
Session::IsEvent<T> Session::registerHandler(F&& handler) {
|
||||
auto cb = [handler](const void* args) {
|
||||
handler(*reinterpret_cast<const T*>(args));
|
||||
};
|
||||
const TypeInfo* typeinfo = TypeOf<T>::type();
|
||||
registerHandler(typeinfo, cb);
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
void Session::registerSentHandler(F&& handler) {
|
||||
auto cb = [handler](const void* response, const Error* error) {
|
||||
if (error != nullptr) {
|
||||
handler(ResponseOrError<T>(*error));
|
||||
} else {
|
||||
handler(ResponseOrError<T>(*reinterpret_cast<const T*>(response)));
|
||||
}
|
||||
};
|
||||
const TypeInfo* typeinfo = TypeOf<T>::type();
|
||||
registerHandler(typeinfo, cb);
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
future<ResponseOrError<typename T::Response>> Session::send(const T& request) {
|
||||
using Response = typename T::Response;
|
||||
promise<ResponseOrError<Response>> promise;
|
||||
auto sent = send(TypeOf<T>::type(), TypeOf<Response>::type(), &request,
|
||||
[=](const void* result, const Error* error) {
|
||||
if (error != nullptr) {
|
||||
promise.set_value(ResponseOrError<Response>(*error));
|
||||
} else {
|
||||
promise.set_value(ResponseOrError<Response>(
|
||||
*reinterpret_cast<const Response*>(result)));
|
||||
}
|
||||
});
|
||||
if (!sent) {
|
||||
promise.set_value(Error("Failed to send request"));
|
||||
}
|
||||
return promise.get_future();
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
void Session::send(const T& event) {
|
||||
const TypeInfo* typeinfo = TypeOf<T>::type();
|
||||
send(typeinfo, &event);
|
||||
}
|
||||
|
||||
void Session::connect(const std::shared_ptr<ReaderWriter>& rw) {
|
||||
connect(rw, rw);
|
||||
}
|
||||
|
||||
void Session::bind(const std::shared_ptr<dap::Reader>& r,
|
||||
const std::shared_ptr<dap::Writer>& w,
|
||||
const ClosedHandler& onClose = {}) {
|
||||
connect(r, w);
|
||||
startProcessingMessages(onClose);
|
||||
}
|
||||
|
||||
void Session::bind(const std::shared_ptr<ReaderWriter>& rw,
|
||||
const ClosedHandler& onClose = {}) {
|
||||
bind(rw, rw, onClose);
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_session_h
|
||||
159
libraries/cppdap/include/dap/traits.h
Normal file
159
libraries/cppdap/include/dap/traits.h
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_traits_h
|
||||
#define dap_traits_h
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace dap {
|
||||
namespace traits {
|
||||
|
||||
// NthTypeOf returns the `N`th type in `Types`
|
||||
template <int N, typename... Types>
|
||||
using NthTypeOf = typename std::tuple_element<N, std::tuple<Types...>>::type;
|
||||
|
||||
// `IsTypeOrDerived<BASE, T>::value` is true iff `T` is of type `BASE`, or
|
||||
// derives from `BASE`.
|
||||
template <typename BASE, typename T>
|
||||
using IsTypeOrDerived = std::integral_constant<
|
||||
bool,
|
||||
std::is_base_of<BASE, typename std::decay<T>::type>::value ||
|
||||
std::is_same<BASE, typename std::decay<T>::type>::value>;
|
||||
|
||||
// `EachIsTypeOrDerived<N, BASES, TYPES>::value` is true iff all of the types in
|
||||
// the std::tuple `TYPES` is of, or derives from the corresponding indexed type
|
||||
// in the std::tuple `BASES`.
|
||||
// `N` must be equal to the number of types in both the std::tuple `BASES` and
|
||||
// `TYPES`.
|
||||
template <int N, typename BASES, typename TYPES>
|
||||
struct EachIsTypeOrDerived {
|
||||
using base = typename std::tuple_element<N - 1, BASES>::type;
|
||||
using type = typename std::tuple_element<N - 1, TYPES>::type;
|
||||
using last_matches = IsTypeOrDerived<base, type>;
|
||||
using others_match = EachIsTypeOrDerived<N - 1, BASES, TYPES>;
|
||||
static constexpr bool value = last_matches::value && others_match::value;
|
||||
};
|
||||
|
||||
// EachIsTypeOrDerived specialization for N = 1
|
||||
template <typename BASES, typename TYPES>
|
||||
struct EachIsTypeOrDerived<1, BASES, TYPES> {
|
||||
using base = typename std::tuple_element<0, BASES>::type;
|
||||
using type = typename std::tuple_element<0, TYPES>::type;
|
||||
static constexpr bool value = IsTypeOrDerived<base, type>::value;
|
||||
};
|
||||
|
||||
// EachIsTypeOrDerived specialization for N = 0
|
||||
template <typename BASES, typename TYPES>
|
||||
struct EachIsTypeOrDerived<0, BASES, TYPES> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
// Signature describes the signature of a function.
|
||||
template <typename RETURN, typename... PARAMETERS>
|
||||
struct Signature {
|
||||
// The return type of the function signature
|
||||
using ret = RETURN;
|
||||
// The parameters of the function signature held in a std::tuple
|
||||
using parameters = std::tuple<PARAMETERS...>;
|
||||
// The type of the Nth parameter of function signature
|
||||
template <std::size_t N>
|
||||
using parameter = NthTypeOf<N, PARAMETERS...>;
|
||||
// The total number of parameters
|
||||
static constexpr std::size_t parameter_count = sizeof...(PARAMETERS);
|
||||
};
|
||||
|
||||
// SignatureOf is a traits helper that infers the signature of the function,
|
||||
// method, static method, lambda, or function-like object `F`.
|
||||
template <typename F>
|
||||
struct SignatureOf {
|
||||
// The signature of the function-like object `F`
|
||||
using type = typename SignatureOf<decltype(&F::operator())>::type;
|
||||
};
|
||||
|
||||
// SignatureOf specialization for a regular function or static method.
|
||||
template <typename R, typename... ARGS>
|
||||
struct SignatureOf<R (*)(ARGS...)> {
|
||||
// The signature of the function-like object `F`
|
||||
using type = Signature<typename std::decay<R>::type,
|
||||
typename std::decay<ARGS>::type...>;
|
||||
};
|
||||
|
||||
// SignatureOf specialization for a non-static method.
|
||||
template <typename R, typename C, typename... ARGS>
|
||||
struct SignatureOf<R (C::*)(ARGS...)> {
|
||||
// The signature of the function-like object `F`
|
||||
using type = Signature<typename std::decay<R>::type,
|
||||
typename std::decay<ARGS>::type...>;
|
||||
};
|
||||
|
||||
// SignatureOf specialization for a non-static, const method.
|
||||
template <typename R, typename C, typename... ARGS>
|
||||
struct SignatureOf<R (C::*)(ARGS...) const> {
|
||||
// The signature of the function-like object `F`
|
||||
using type = Signature<typename std::decay<R>::type,
|
||||
typename std::decay<ARGS>::type...>;
|
||||
};
|
||||
|
||||
// SignatureOfT is an alias to `typename SignatureOf<F>::type`.
|
||||
template <typename F>
|
||||
using SignatureOfT = typename SignatureOf<F>::type;
|
||||
|
||||
// ParameterType is an alias to `typename SignatureOf<F>::type::parameter<N>`.
|
||||
template <typename F, std::size_t N>
|
||||
using ParameterType = typename SignatureOfT<F>::template parameter<N>;
|
||||
|
||||
// `HasSignature<F, S>::value` is true iff the function-like `F` has a matching
|
||||
// signature to the function-like `S`.
|
||||
template <typename F, typename S>
|
||||
using HasSignature = std::integral_constant<
|
||||
bool,
|
||||
std::is_same<SignatureOfT<F>, SignatureOfT<S>>::value>;
|
||||
|
||||
// `Min<A, B>::value` resolves to the smaller value of A and B.
|
||||
template <std::size_t A, std::size_t B>
|
||||
using Min = std::integral_constant<std::size_t, (A < B ? A : B)>;
|
||||
|
||||
// `CompatibleWith<F, S>::value` is true iff the function-like `F`
|
||||
// can be called with the argument types of the function-like `S`. Return type
|
||||
// of the two functions are not considered.
|
||||
template <typename F, typename S>
|
||||
using CompatibleWith = std::integral_constant<
|
||||
bool,
|
||||
(SignatureOfT<S>::parameter_count == SignatureOfT<F>::parameter_count) &&
|
||||
EachIsTypeOrDerived<Min<SignatureOfT<S>::parameter_count,
|
||||
SignatureOfT<F>::parameter_count>::value,
|
||||
typename SignatureOfT<S>::parameters,
|
||||
typename SignatureOfT<F>::parameters>::value>;
|
||||
|
||||
// If `CONDITION` is true then EnableIf resolves to type T, otherwise an
|
||||
// invalid type.
|
||||
template <bool CONDITION, typename T = void>
|
||||
using EnableIf = typename std::enable_if<CONDITION, T>::type;
|
||||
|
||||
// If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE_TY`,
|
||||
// otherwise an invalid type.
|
||||
template <typename BASE, typename T, typename TRUE_TY = void>
|
||||
using EnableIfIsType = EnableIf<IsTypeOrDerived<BASE, T>::value, TRUE_TY>;
|
||||
|
||||
// If the function-like `F` has a matching signature to the function-like `S`
|
||||
// then EnableIfHasSignature resolves to type `TRUE_TY`, otherwise an invalid type.
|
||||
template <typename F, typename S, typename TRUE_TY = void>
|
||||
using EnableIfHasSignature = EnableIf<HasSignature<F, S>::value, TRUE_TY>;
|
||||
|
||||
} // namespace traits
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_traits_h
|
||||
59
libraries/cppdap/include/dap/typeinfo.h
Normal file
59
libraries/cppdap/include/dap/typeinfo.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_typeinfo_h
|
||||
#define dap_typeinfo_h
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace dap {
|
||||
|
||||
class any;
|
||||
class Deserializer;
|
||||
class Serializer;
|
||||
|
||||
// The TypeInfo interface provides basic runtime type information about DAP
|
||||
// types. TypeInfo is used by the serialization system to encode and decode DAP
|
||||
// requests, responses, events and structs.
|
||||
struct TypeInfo {
|
||||
virtual ~TypeInfo();
|
||||
virtual std::string name() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
virtual size_t alignment() const = 0;
|
||||
virtual void construct(void*) const = 0;
|
||||
virtual void copyConstruct(void* dst, const void* src) const = 0;
|
||||
virtual void destruct(void*) const = 0;
|
||||
virtual bool deserialize(const Deserializer*, void*) const = 0;
|
||||
virtual bool serialize(Serializer*, const void*) const = 0;
|
||||
|
||||
// create() allocates and constructs the TypeInfo of type T, registers the
|
||||
// pointer for deletion on cppdap library termination, and returns the pointer
|
||||
// to T.
|
||||
template <typename T, typename... ARGS>
|
||||
static T* create(ARGS&&... args) {
|
||||
auto typeinfo = new T(std::forward<ARGS>(args)...);
|
||||
deleteOnExit(typeinfo);
|
||||
return typeinfo;
|
||||
}
|
||||
|
||||
private:
|
||||
// deleteOnExit() ensures that the TypeInfo is destructed and deleted on
|
||||
// library termination.
|
||||
static void deleteOnExit(TypeInfo*);
|
||||
};
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_typeinfo_h
|
||||
266
libraries/cppdap/include/dap/typeof.h
Normal file
266
libraries/cppdap/include/dap/typeof.h
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_typeof_h
|
||||
#define dap_typeof_h
|
||||
|
||||
#include "typeinfo.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "serialization.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
// BasicTypeInfo is an implementation of the TypeInfo interface for the simple
|
||||
// template type T.
|
||||
template <typename T>
|
||||
struct BasicTypeInfo : public TypeInfo {
|
||||
constexpr BasicTypeInfo(std::string&& name) : name_(std::move(name)) {}
|
||||
|
||||
// TypeInfo compliance
|
||||
inline std::string name() const override { return name_; }
|
||||
inline size_t size() const override { return sizeof(T); }
|
||||
inline size_t alignment() const override { return alignof(T); }
|
||||
inline void construct(void* ptr) const override { new (ptr) T(); }
|
||||
inline void copyConstruct(void* dst, const void* src) const override {
|
||||
new (dst) T(*reinterpret_cast<const T*>(src));
|
||||
}
|
||||
inline void destruct(void* ptr) const override {
|
||||
reinterpret_cast<T*>(ptr)->~T();
|
||||
}
|
||||
inline bool deserialize(const Deserializer* d, void* ptr) const override {
|
||||
return d->deserialize(reinterpret_cast<T*>(ptr));
|
||||
}
|
||||
inline bool serialize(Serializer* s, const void* ptr) const override {
|
||||
return s->serialize(*reinterpret_cast<const T*>(ptr));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
// TypeOf has a template specialization for each DAP type, each declaring a
|
||||
// const TypeInfo* type() static member function that describes type T.
|
||||
template <typename T>
|
||||
struct TypeOf {};
|
||||
|
||||
template <>
|
||||
struct TypeOf<boolean> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<string> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<integer> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<number> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<object> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<any> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <>
|
||||
struct TypeOf<null> {
|
||||
static const TypeInfo* type();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TypeOf<array<T>> {
|
||||
static inline const TypeInfo* type() {
|
||||
static auto typeinfo = TypeInfo::create<BasicTypeInfo<array<T>>>(
|
||||
"array<" + TypeOf<T>::type()->name() + ">");
|
||||
return typeinfo;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
struct TypeOf<variant<T0, Types...>> {
|
||||
static inline const TypeInfo* type() {
|
||||
static auto typeinfo =
|
||||
TypeInfo::create<BasicTypeInfo<variant<T0, Types...>>>("variant");
|
||||
return typeinfo;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TypeOf<optional<T>> {
|
||||
static inline const TypeInfo* type() {
|
||||
static auto typeinfo = TypeInfo::create<BasicTypeInfo<optional<T>>>(
|
||||
"optional<" + TypeOf<T>::type()->name() + ">");
|
||||
return typeinfo;
|
||||
}
|
||||
};
|
||||
|
||||
// DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in
|
||||
// <cstddef>. It evaluates to the offset of the given field, with fewer
|
||||
// restrictions than offsetof(). We cast the address '32' and subtract it again,
|
||||
// because null-dereference is undefined behavior.
|
||||
#define DAP_OFFSETOF(s, m) \
|
||||
((int)(size_t) & reinterpret_cast<const volatile char&>((((s*)32)->m)) - 32)
|
||||
|
||||
// internal functionality
|
||||
namespace detail {
|
||||
template <class T, class M>
|
||||
M member_type(M T::*);
|
||||
} // namespace detail
|
||||
|
||||
// DAP_TYPEOF() returns the type of the struct (s) member (m).
|
||||
#define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m))
|
||||
|
||||
// DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO
|
||||
// macro.
|
||||
// FIELD is the name of the struct field.
|
||||
// NAME is the serialized name of the field, as described by the DAP
|
||||
// specification.
|
||||
#define DAP_FIELD(FIELD, NAME) \
|
||||
::dap::Field { \
|
||||
NAME, DAP_OFFSETOF(StructTy, FIELD), \
|
||||
TypeOf<DAP_TYPEOF(StructTy, FIELD)>::type(), \
|
||||
}
|
||||
|
||||
// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \
|
||||
template <> \
|
||||
struct TypeOf<STRUCT> { \
|
||||
static constexpr bool has_custom_serialization = true; \
|
||||
static const TypeInfo* type(); \
|
||||
static bool deserializeFields(const Deserializer*, void* obj); \
|
||||
static bool serializeFields(FieldSerializer*, const void* obj); \
|
||||
}
|
||||
|
||||
// DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() implements the deserializeFields()
|
||||
// and serializeFields() static methods of a TypeOf<> specialization. Used
|
||||
// internally by DAP_IMPLEMENT_STRUCT_TYPEINFO() and
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT().
|
||||
// You probably do not want to use this directly.
|
||||
#define DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, ...) \
|
||||
bool TypeOf<STRUCT>::deserializeFields(const Deserializer* fd, void* obj) { \
|
||||
using StructTy = STRUCT; \
|
||||
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
|
||||
for (auto& field : std::initializer_list<Field>{__VA_ARGS__}) { \
|
||||
if (!fd->field(field.name, [&](Deserializer* d) { \
|
||||
auto ptr = reinterpret_cast<uint8_t*>(obj) + field.offset; \
|
||||
return field.type->deserialize(d, ptr); \
|
||||
})) { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
bool TypeOf<STRUCT>::serializeFields(FieldSerializer* fs, const void* obj) {\
|
||||
using StructTy = STRUCT; \
|
||||
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
|
||||
for (auto& field : std::initializer_list<Field>{__VA_ARGS__}) { \
|
||||
if (!fs->field(field.name, [&](Serializer* s) { \
|
||||
auto ptr = reinterpret_cast<const uint8_t*>(obj) + field.offset; \
|
||||
return field.type->serialize(s, ptr); \
|
||||
})) { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
return true; \
|
||||
}
|
||||
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO() implements the type() member function for the
|
||||
// TypeOf<> specialization for STRUCT.
|
||||
// STRUCT is the structure typename.
|
||||
// NAME is the serialized name of the structure, as described by the DAP
|
||||
// specification. The variadic (...) parameters should be a repeated list of
|
||||
// DAP_FIELD()s, one for each field of the struct.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
|
||||
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
|
||||
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
|
||||
struct TI : BasicTypeInfo<STRUCT> { \
|
||||
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
|
||||
bool deserialize(const Deserializer* d, void* obj) const override { \
|
||||
return deserializeFields(d, obj); \
|
||||
} \
|
||||
bool serialize(Serializer* s, const void* obj) const override { \
|
||||
return s->object( \
|
||||
[&](FieldSerializer* fs) { return serializeFields(fs, obj); }); \
|
||||
} \
|
||||
}; \
|
||||
static TI typeinfo; \
|
||||
return &typeinfo; \
|
||||
}
|
||||
|
||||
// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<>
|
||||
// specialization for STRUCT in a single statement.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
|
||||
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__)
|
||||
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT() implements the type() member function for
|
||||
// the TypeOf<> specialization for STRUCT that derives from BASE.
|
||||
// STRUCT is the structure typename.
|
||||
// BASE is the base structure typename.
|
||||
// NAME is the serialized name of the structure, as described by the DAP
|
||||
// specification. The variadic (...) parameters should be a repeated list of
|
||||
// DAP_FIELD()s, one for each field of the struct.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
|
||||
static_assert(std::is_base_of<BASE, STRUCT>::value, \
|
||||
#STRUCT " does not derive from " #BASE); \
|
||||
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
|
||||
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
|
||||
struct TI : BasicTypeInfo<STRUCT> { \
|
||||
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
|
||||
bool deserialize(const Deserializer* d, void* obj) const override { \
|
||||
auto derived = static_cast<STRUCT*>(obj); \
|
||||
auto base = static_cast<BASE*>(obj); \
|
||||
return TypeOf<BASE>::deserializeFields(d, base) && \
|
||||
deserializeFields(d, derived); \
|
||||
} \
|
||||
bool serialize(Serializer* s, const void* obj) const override { \
|
||||
return s->object([&](FieldSerializer* fs) { \
|
||||
auto derived = static_cast<const STRUCT*>(obj); \
|
||||
auto base = static_cast<const BASE*>(obj); \
|
||||
return TypeOf<BASE>::serializeFields(fs, base) && \
|
||||
serializeFields(fs, derived); \
|
||||
}); \
|
||||
} \
|
||||
}; \
|
||||
static TI typeinfo; \
|
||||
return &typeinfo; \
|
||||
}
|
||||
|
||||
// DAP_STRUCT_TYPEINFO_EXT() is a helper for declaring and implementing a
|
||||
// TypeOf<> specialization for STRUCT that derives from BASE in a single
|
||||
// statement.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
|
||||
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, __VA_ARGS__)
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_typeof_h
|
||||
104
libraries/cppdap/include/dap/types.h
Normal file
104
libraries/cppdap/include/dap/types.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file holds the basic serializable types used by the debug adapter
|
||||
// protocol.
|
||||
|
||||
#ifndef dap_types_h
|
||||
#define dap_types_h
|
||||
|
||||
#include "any.h"
|
||||
#include "optional.h"
|
||||
#include "variant.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dap {
|
||||
|
||||
// string is a sequence of characters.
|
||||
// string defaults to an empty string.
|
||||
using string = std::string;
|
||||
|
||||
// boolean holds a true or false value.
|
||||
// boolean defaults to false.
|
||||
class boolean {
|
||||
public:
|
||||
inline boolean() : val(false) {}
|
||||
inline boolean(bool i) : val(i) {}
|
||||
inline operator bool() const { return val; }
|
||||
inline boolean& operator=(bool i) {
|
||||
val = i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool val;
|
||||
};
|
||||
|
||||
// integer holds a whole signed number.
|
||||
// integer defaults to 0.
|
||||
class integer {
|
||||
public:
|
||||
inline integer() : val(0) {}
|
||||
inline integer(int64_t i) : val(i) {}
|
||||
inline operator int64_t() const { return val; }
|
||||
inline integer& operator=(int64_t i) {
|
||||
val = i;
|
||||
return *this;
|
||||
}
|
||||
inline integer operator++(int) {
|
||||
auto copy = *this;
|
||||
val++;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t val;
|
||||
};
|
||||
|
||||
// number holds a 64-bit floating point number.
|
||||
// number defaults to 0.
|
||||
class number {
|
||||
public:
|
||||
inline number() : val(0.0) {}
|
||||
inline number(double i) : val(i) {}
|
||||
inline operator double() const { return val; }
|
||||
inline number& operator=(double i) {
|
||||
val = i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
double val;
|
||||
};
|
||||
|
||||
// array is a list of items of type T.
|
||||
// array defaults to an empty list.
|
||||
template <typename T>
|
||||
using array = std::vector<T>;
|
||||
|
||||
// object is a map of string to any.
|
||||
// object defaults to an empty map.
|
||||
using object = std::unordered_map<string, any>;
|
||||
|
||||
// null represents no value.
|
||||
// null is used by any to check for no-value.
|
||||
using null = std::nullptr_t;
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_types_h
|
||||
108
libraries/cppdap/include/dap/variant.h
Normal file
108
libraries/cppdap/include/dap/variant.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_variant_h
|
||||
#define dap_variant_h
|
||||
|
||||
#include "any.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
// internal functionality
|
||||
namespace detail {
|
||||
template <typename T, typename...>
|
||||
struct TypeIsIn {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename T, typename List0, typename... ListN>
|
||||
struct TypeIsIn<T, List0, ListN...> {
|
||||
static constexpr bool value =
|
||||
std::is_same<T, List0>::value || TypeIsIn<T, ListN...>::value;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// variant represents a type-safe union of DAP types.
|
||||
// variant can hold a value of any of the template argument types.
|
||||
// variant defaults to a default-constructed T0.
|
||||
template <typename T0, typename... Types>
|
||||
class variant {
|
||||
public:
|
||||
// constructors
|
||||
inline variant();
|
||||
template <typename T>
|
||||
inline variant(const T& val);
|
||||
|
||||
// assignment
|
||||
template <typename T>
|
||||
inline variant& operator=(const T& val);
|
||||
|
||||
// get() returns the contained value of the type T.
|
||||
// If the any does not contain a value of type T, then get() will assert.
|
||||
template <typename T>
|
||||
inline T& get() const;
|
||||
|
||||
// is() returns true iff the contained value is of type T.
|
||||
template <typename T>
|
||||
inline bool is() const;
|
||||
|
||||
// accepts() returns true iff the variant accepts values of type T.
|
||||
template <typename T>
|
||||
static constexpr bool accepts();
|
||||
|
||||
private:
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
any value;
|
||||
};
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
variant<T0, Types...>::variant() : value(T0()) {}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
template <typename T>
|
||||
variant<T0, Types...>::variant(const T& v) : value(v) {
|
||||
static_assert(accepts<T>(), "variant does not accept template type T");
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
template <typename T>
|
||||
variant<T0, Types...>& variant<T0, Types...>::operator=(const T& v) {
|
||||
static_assert(accepts<T>(), "variant does not accept template type T");
|
||||
value = v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
template <typename T>
|
||||
T& variant<T0, Types...>::get() const {
|
||||
static_assert(accepts<T>(), "variant does not accept template type T");
|
||||
return value.get<T>();
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
template <typename T>
|
||||
bool variant<T0, Types...>::is() const {
|
||||
return value.is<T>();
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
template <typename T>
|
||||
constexpr bool variant<T0, Types...>::accepts() {
|
||||
return detail::TypeIsIn<T, T0, Types...>::value;
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_variant_h
|
||||
25
libraries/cppdap/kokoro/license-check/build.sh
Executable file
25
libraries/cppdap/kokoro/license-check/build.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e # Fail on any error.
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
|
||||
ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
|
||||
|
||||
docker run --rm -i \
|
||||
--volume "${ROOT_DIR}:${ROOT_DIR}:ro" \
|
||||
--workdir "${ROOT_DIR}" \
|
||||
us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/license-checker
|
||||
4
libraries/cppdap/kokoro/license-check/presubmit.cfg
Normal file
4
libraries/cppdap/kokoro/license-check/presubmit.cfg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
build_file: "cppdap/kokoro/license-check/build.sh"
|
||||
|
||||
14
libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg
Normal file
14
libraries/cppdap/kokoro/macos/clang-x64/cmake/presubmit.cfg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/macos/presubmit.sh"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "x64"
|
||||
}
|
||||
37
libraries/cppdap/kokoro/macos/presubmit.sh
Executable file
37
libraries/cppdap/kokoro/macos/presubmit.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e # Fail on any error.
|
||||
set -x # Display commands being run.
|
||||
|
||||
BUILD_ROOT=$PWD
|
||||
|
||||
cd github/cppdap
|
||||
|
||||
git submodule update --init
|
||||
|
||||
if [ "$BUILD_SYSTEM" == "cmake" ]; then
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1
|
||||
make -j$(sysctl -n hw.logicalcpu)
|
||||
|
||||
./cppdap-unittests
|
||||
else
|
||||
echo "Unknown build system: $BUILD_SYSTEM"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/ubuntu/presubmit.sh"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "x64"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SANITIZER"
|
||||
value: "asan"
|
||||
}
|
||||
14
libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg
Normal file
14
libraries/cppdap/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/ubuntu/presubmit.sh"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "x64"
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/ubuntu/presubmit.sh"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "x64"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SANITIZER"
|
||||
value: "tsan"
|
||||
}
|
||||
56
libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh
Executable file
56
libraries/cppdap/kokoro/ubuntu/presubmit-docker.sh
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e # Fail on any error.
|
||||
|
||||
. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
|
||||
|
||||
set -x # Display commands being run.
|
||||
|
||||
cd github/cppdap
|
||||
|
||||
# Silence "fatal: unsafe repository" errors
|
||||
git config --global --add safe.directory '*'
|
||||
|
||||
git submodule update --init
|
||||
|
||||
if [ "$BUILD_SYSTEM" == "cmake" ]; then
|
||||
using cmake-3.31.2
|
||||
using gcc-13
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
build_and_run() {
|
||||
cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1 $1
|
||||
make --jobs=$(nproc)
|
||||
|
||||
./cppdap-unittests
|
||||
}
|
||||
|
||||
if [ "$BUILD_SANITIZER" == "asan" ]; then
|
||||
build_and_run "-DCPPDAP_ASAN=1"
|
||||
elif [ "$BUILD_SANITIZER" == "msan" ]; then
|
||||
build_and_run "-DCPPDAP_MSAN=1"
|
||||
elif [ "$BUILD_SANITIZER" == "tsan" ]; then
|
||||
build_and_run "-DCPPDAP_TSAN=1"
|
||||
else
|
||||
build_and_run
|
||||
fi
|
||||
else
|
||||
echo "Unknown build system: $BUILD_SYSTEM"
|
||||
exit 1
|
||||
fi
|
||||
32
libraries/cppdap/kokoro/ubuntu/presubmit.sh
Executable file
32
libraries/cppdap/kokoro/ubuntu/presubmit.sh
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e # Fail on any error.
|
||||
|
||||
ROOT_DIR=`pwd`
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
# --privileged is required for some sanitizer builds, as they seem to require PTRACE privileges
|
||||
docker run --rm -i \
|
||||
--privileged \
|
||||
--volume "${ROOT_DIR}:${ROOT_DIR}" \
|
||||
--volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \
|
||||
--workdir "${ROOT_DIR}" \
|
||||
--env BUILD_SYSTEM=$BUILD_SYSTEM \
|
||||
--env BUILD_TARGET_ARCH=$BUILD_TARGET_ARCH \
|
||||
--env BUILD_SANITIZER=$BUILD_SANITIZER \
|
||||
--entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \
|
||||
us-east4-docker.pkg.dev/shaderc-build/radial-docker/ubuntu-24.04-amd64/cpp-builder
|
||||
45
libraries/cppdap/kokoro/windows/presubmit.bat
Normal file
45
libraries/cppdap/kokoro/windows/presubmit.bat
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
REM Copyright 2020 Google LLC
|
||||
REM
|
||||
REM Licensed under the Apache License, Version 2.0 (the "License");
|
||||
REM you may not use this file except in compliance with the License.
|
||||
REM You may obtain a copy of the License at
|
||||
REM
|
||||
REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
REM
|
||||
REM Unless required by applicable law or agreed to in writing, software
|
||||
REM distributed under the License is distributed on an "AS IS" BASIS,
|
||||
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
REM See the License for the specific language governing permissions and
|
||||
REM limitations under the License.
|
||||
|
||||
@echo on
|
||||
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
SET BUILD_ROOT=%cd%
|
||||
SET PATH=C:\python312;C:\cmake-3.31.2\bin;%PATH%
|
||||
SET SRC=%cd%\github\cppdap
|
||||
|
||||
cd %SRC%
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
|
||||
git submodule update --init
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
|
||||
SET CONFIG=Release
|
||||
|
||||
mkdir %SRC%\build
|
||||
cd %SRC%\build
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
|
||||
IF /I "%BUILD_SYSTEM%"=="cmake" (
|
||||
cmake .. -G "%BUILD_GENERATOR%" -A %BUILD_TARGET_ARCH% "-DCPPDAP_BUILD_TESTS=1" "-DCPPDAP_BUILD_EXAMPLES=1" "-DCPPDAP_WARNINGS_AS_ERRORS=1"
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
cmake --build . --config %CONFIG%
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
%CONFIG%\cppdap-unittests.exe
|
||||
if !ERRORLEVEL! neq 0 exit !ERRORLEVEL!
|
||||
) ELSE (
|
||||
echo "Unknown build system: %BUILD_SYSTEM%"
|
||||
exit /b 1
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/windows/presubmit.bat"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_GENERATOR"
|
||||
value: "Visual Studio 17 2022"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "x64"
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Format: //devtools/kokoro/config/proto/build.proto
|
||||
|
||||
# Location of the continuous bash script in Git.
|
||||
build_file: "cppdap/kokoro/windows/presubmit.bat"
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_SYSTEM"
|
||||
value: "cmake"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_GENERATOR"
|
||||
value: "Visual Studio 17 2022"
|
||||
}
|
||||
|
||||
env_vars {
|
||||
key: "BUILD_TARGET_ARCH"
|
||||
value: "Win32"
|
||||
}
|
||||
28
libraries/cppdap/license-checker.cfg
Normal file
28
libraries/cppdap/license-checker.cfg
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"licenses": [
|
||||
"Apache-2.0",
|
||||
"Apache-2.0-Header"
|
||||
],
|
||||
"paths": [
|
||||
{
|
||||
"exclude": [
|
||||
".clang-format",
|
||||
".gitattributes",
|
||||
".github/workflows/main.yml",
|
||||
".gitignore",
|
||||
".gitmodules",
|
||||
".vscode/*.json",
|
||||
"**.md",
|
||||
"CONTRIBUTING",
|
||||
"LICENSE",
|
||||
"build/**",
|
||||
"examples/vscode/package.json",
|
||||
"fuzz/**",
|
||||
"kokoro/**.cfg",
|
||||
"third_party/googletest/**",
|
||||
"third_party/json/**",
|
||||
"third_party/rapidjson/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
262
libraries/cppdap/src/any_test.cpp
Normal file
262
libraries/cppdap/src/any_test.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/any.h"
|
||||
#include "dap/typeof.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct AnyTestObject {
|
||||
dap::integer i;
|
||||
dap::number n;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(AnyTestObject,
|
||||
"AnyTestObject",
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"));
|
||||
|
||||
inline bool operator==(const AnyTestObject& a, const AnyTestObject& b) {
|
||||
return a.i == b.i && a.n == b.n;
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
struct TestValue {};
|
||||
|
||||
template <>
|
||||
struct TestValue<dap::null> {
|
||||
static const dap::null value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::integer> {
|
||||
static const dap::integer value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::boolean> {
|
||||
static const dap::boolean value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::number> {
|
||||
static const dap::number value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::string> {
|
||||
static const dap::string value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::array<dap::string>> {
|
||||
static const dap::array<dap::string> value;
|
||||
};
|
||||
template <>
|
||||
struct TestValue<dap::AnyTestObject> {
|
||||
static const dap::AnyTestObject value;
|
||||
};
|
||||
|
||||
const dap::null TestValue<dap::null>::value = nullptr;
|
||||
const dap::integer TestValue<dap::integer>::value = 20;
|
||||
const dap::boolean TestValue<dap::boolean>::value = true;
|
||||
const dap::number TestValue<dap::number>::value = 123.45;
|
||||
const dap::string TestValue<dap::string>::value = "hello world";
|
||||
const dap::array<dap::string> TestValue<dap::array<dap::string>>::value = {
|
||||
"one", "two", "three"};
|
||||
const dap::AnyTestObject TestValue<dap::AnyTestObject>::value = {10, 20.30};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(Any, EmptyConstruct) {
|
||||
dap::any any;
|
||||
ASSERT_TRUE(any.is<dap::null>());
|
||||
ASSERT_FALSE(any.is<dap::boolean>());
|
||||
ASSERT_FALSE(any.is<dap::integer>());
|
||||
ASSERT_FALSE(any.is<dap::number>());
|
||||
ASSERT_FALSE(any.is<dap::object>());
|
||||
ASSERT_FALSE(any.is<dap::string>());
|
||||
ASSERT_FALSE(any.is<dap::array<dap::integer>>());
|
||||
ASSERT_FALSE(any.is<dap::AnyTestObject>());
|
||||
}
|
||||
|
||||
TEST(Any, Boolean) {
|
||||
dap::any any(dap::boolean(true));
|
||||
ASSERT_TRUE(any.is<dap::boolean>());
|
||||
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
|
||||
}
|
||||
|
||||
TEST(Any, Integer) {
|
||||
dap::any any(dap::integer(10));
|
||||
ASSERT_TRUE(any.is<dap::integer>());
|
||||
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
|
||||
}
|
||||
|
||||
TEST(Any, Number) {
|
||||
dap::any any(dap::number(123.0f));
|
||||
ASSERT_TRUE(any.is<dap::number>());
|
||||
ASSERT_EQ(any.get<dap::number>(), dap::number(123.0f));
|
||||
}
|
||||
|
||||
TEST(Any, String) {
|
||||
dap::any any(dap::string("hello world"));
|
||||
ASSERT_TRUE(any.is<dap::string>());
|
||||
ASSERT_EQ(any.get<dap::string>(), dap::string("hello world"));
|
||||
}
|
||||
|
||||
TEST(Any, Array) {
|
||||
using array = dap::array<dap::integer>;
|
||||
dap::any any(array({10, 20, 30}));
|
||||
ASSERT_TRUE(any.is<array>());
|
||||
ASSERT_EQ(any.get<array>(), array({10, 20, 30}));
|
||||
}
|
||||
|
||||
TEST(Any, Object) {
|
||||
dap::object o;
|
||||
o["one"] = dap::integer(1);
|
||||
o["two"] = dap::integer(2);
|
||||
o["three"] = dap::integer(3);
|
||||
dap::any any(o);
|
||||
ASSERT_TRUE(any.is<dap::object>());
|
||||
if (any.is<dap::object>()) {
|
||||
auto got = any.get<dap::object>();
|
||||
ASSERT_EQ(got.size(), 3U);
|
||||
ASSERT_EQ(got.count("one"), 1U);
|
||||
ASSERT_EQ(got.count("two"), 1U);
|
||||
ASSERT_EQ(got.count("three"), 1U);
|
||||
ASSERT_TRUE(got["one"].is<dap::integer>());
|
||||
ASSERT_TRUE(got["two"].is<dap::integer>());
|
||||
ASSERT_TRUE(got["three"].is<dap::integer>());
|
||||
ASSERT_EQ(got["one"].get<dap::integer>(), dap::integer(1));
|
||||
ASSERT_EQ(got["two"].get<dap::integer>(), dap::integer(2));
|
||||
ASSERT_EQ(got["three"].get<dap::integer>(), dap::integer(3));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Any, TestObject) {
|
||||
dap::any any(dap::AnyTestObject{5, 3.0});
|
||||
ASSERT_TRUE(any.is<dap::AnyTestObject>());
|
||||
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
|
||||
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class AnyT : public ::testing::Test {
|
||||
protected:
|
||||
template <typename T0,
|
||||
typename = std::enable_if<std::is_same<T, T0>::value &&
|
||||
!std::is_same<T0, dap::null>::value>>
|
||||
void check_val(const dap::any& any, const T0& expect) {
|
||||
ASSERT_EQ(any.is<T>(), any.is<T0>());
|
||||
ASSERT_EQ(any.get<T>(), expect);
|
||||
}
|
||||
|
||||
// Special case for Null assignment, as we can assign nullptr_t to any but
|
||||
// can't `get()` it
|
||||
template <typename = dap::null>
|
||||
void check_val(const dap::any& any, const dap::null& expect) {
|
||||
ASSERT_EQ(nullptr, expect);
|
||||
ASSERT_TRUE(any.is<dap::null>());
|
||||
}
|
||||
|
||||
void check_type(const dap::any& any) {
|
||||
ASSERT_EQ(any.is<dap::null>(), (std::is_same<T, dap::null>::value));
|
||||
ASSERT_EQ(any.is<dap::integer>(), (std::is_same<T, dap::integer>::value));
|
||||
ASSERT_EQ(any.is<dap::boolean>(), (std::is_same<T, dap::boolean>::value));
|
||||
ASSERT_EQ(any.is<dap::number>(), (std::is_same<T, dap::number>::value));
|
||||
ASSERT_EQ(any.is<dap::string>(), (std::is_same<T, dap::string>::value));
|
||||
ASSERT_EQ(any.is<dap::array<dap::string>>(),
|
||||
(std::is_same<T, dap::array<dap::string>>::value));
|
||||
ASSERT_EQ(any.is<dap::AnyTestObject>(),
|
||||
(std::is_same<T, dap::AnyTestObject>::value));
|
||||
}
|
||||
};
|
||||
TYPED_TEST_SUITE_P(AnyT);
|
||||
|
||||
TYPED_TEST_P(AnyT, CopyConstruct) {
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any(val);
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(AnyT, MoveConstruct) {
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any(std::move(val));
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(AnyT, Assign) {
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any;
|
||||
any = val;
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(AnyT, MoveAssign) {
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any;
|
||||
any = std::move(val);
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(AnyT, RepeatedAssign) {
|
||||
dap::string str = "hello world";
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any;
|
||||
any = str;
|
||||
any = val;
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(AnyT, RepeatedMoveAssign) {
|
||||
dap::string str = "hello world";
|
||||
auto val = TestValue<TypeParam>::value;
|
||||
dap::any any;
|
||||
any = std::move(str);
|
||||
any = std::move(val);
|
||||
this->check_type(any);
|
||||
this->check_val(any, val);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(AnyT,
|
||||
CopyConstruct,
|
||||
MoveConstruct,
|
||||
Assign,
|
||||
MoveAssign,
|
||||
RepeatedAssign,
|
||||
RepeatedMoveAssign);
|
||||
|
||||
using AnyTypes = ::testing::Types<dap::null,
|
||||
dap::integer,
|
||||
dap::boolean,
|
||||
dap::number,
|
||||
dap::string,
|
||||
dap::array<dap::string>,
|
||||
dap::AnyTestObject>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(T, AnyT, AnyTypes);
|
||||
|
||||
TEST(Any, Reset) {
|
||||
dap::any any(dap::integer(10));
|
||||
ASSERT_TRUE(any.is<dap::integer>());
|
||||
any.reset();
|
||||
ASSERT_FALSE(any.is<dap::integer>());
|
||||
}
|
||||
90
libraries/cppdap/src/chan.h
Normal file
90
libraries/cppdap/src/chan.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_chan_h
|
||||
#define dap_chan_h
|
||||
|
||||
#include "dap/optional.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace dap {
|
||||
|
||||
template <typename T>
|
||||
struct Chan {
|
||||
public:
|
||||
void reset();
|
||||
void close();
|
||||
optional<T> take();
|
||||
void put(T&& in);
|
||||
void put(const T& in);
|
||||
|
||||
private:
|
||||
bool closed = false;
|
||||
std::queue<T> queue;
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::reset() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
queue = {};
|
||||
closed = false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::close() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
closed = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T> Chan<T>::take() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return queue.size() > 0 || closed; });
|
||||
if (queue.size() == 0) {
|
||||
return optional<T>();
|
||||
}
|
||||
auto out = std::move(queue.front());
|
||||
queue.pop();
|
||||
return optional<T>(std::move(out));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::put(T&& in) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
auto notify = queue.size() == 0 && !closed;
|
||||
queue.push(std::move(in));
|
||||
if (notify) {
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::put(const T& in) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
auto notify = queue.size() == 0 && !closed;
|
||||
queue.push(in);
|
||||
if (notify) {
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_chan_h
|
||||
35
libraries/cppdap/src/chan_test.cpp
Normal file
35
libraries/cppdap/src/chan_test.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "chan.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
TEST(ChanTest, PutTakeClose) {
|
||||
dap::Chan<int> chan;
|
||||
auto thread = std::thread([&] {
|
||||
chan.put(10);
|
||||
chan.put(20);
|
||||
chan.put(30);
|
||||
chan.close();
|
||||
});
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(10));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(20));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(30));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>());
|
||||
thread.join();
|
||||
}
|
||||
198
libraries/cppdap/src/content_stream.cpp
Normal file
198
libraries/cppdap/src/content_stream.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "content_stream.h"
|
||||
|
||||
#include "dap/io.h"
|
||||
|
||||
#include <string.h> // strlen
|
||||
#include <algorithm> // std::min
|
||||
|
||||
namespace dap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ContentReader
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
ContentReader::ContentReader(
|
||||
const std::shared_ptr<Reader>& reader,
|
||||
OnInvalidData on_invalid_data /* = OnInvalidData::kIgnore */)
|
||||
: reader(reader), on_invalid_data(on_invalid_data) {}
|
||||
|
||||
ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept {
|
||||
buf = std::move(rhs.buf);
|
||||
reader = std::move(rhs.reader);
|
||||
on_invalid_data = std::move(rhs.on_invalid_data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ContentReader::isOpen() {
|
||||
return reader ? reader->isOpen() : false;
|
||||
}
|
||||
|
||||
void ContentReader::close() {
|
||||
if (reader) {
|
||||
reader->close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string ContentReader::read() {
|
||||
// Find Content-Length header prefix
|
||||
if (on_invalid_data == kClose) {
|
||||
if (!match("Content-Length:")) {
|
||||
return badHeader();
|
||||
}
|
||||
} else {
|
||||
if (!scan("Content-Length:")) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
// Skip whitespace and tabs
|
||||
while (matchAny(" \t")) {
|
||||
}
|
||||
// Parse length
|
||||
size_t len = 0;
|
||||
while (true) {
|
||||
auto c = matchAny("0123456789");
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
len *= 10;
|
||||
len += size_t(c) - size_t('0');
|
||||
}
|
||||
if (len == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Expect \r\n\r\n
|
||||
if (!match("\r\n\r\n")) {
|
||||
return badHeader();
|
||||
}
|
||||
|
||||
// Read message
|
||||
if (!buffer(len)) {
|
||||
return "";
|
||||
}
|
||||
std::string out;
|
||||
out.reserve(len);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
out.push_back(static_cast<char>(buf.front()));
|
||||
buf.pop_front();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ContentReader::scan(const uint8_t* seq, size_t len) {
|
||||
while (buffer(len)) {
|
||||
if (match(seq, len)) {
|
||||
return true;
|
||||
}
|
||||
buf.pop_front();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContentReader::scan(const char* str) {
|
||||
auto len = strlen(str);
|
||||
return scan(reinterpret_cast<const uint8_t*>(str), len);
|
||||
}
|
||||
|
||||
bool ContentReader::match(const uint8_t* seq, size_t len) {
|
||||
if (!buffer(len)) {
|
||||
return false;
|
||||
}
|
||||
auto it = buf.begin();
|
||||
for (size_t i = 0; i < len; i++, it++) {
|
||||
if (*it != seq[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
buf.pop_front();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContentReader::match(const char* str) {
|
||||
auto len = strlen(str);
|
||||
return match(reinterpret_cast<const uint8_t*>(str), len);
|
||||
}
|
||||
|
||||
char ContentReader::matchAny(const char* chars) {
|
||||
if (!buffer(1)) {
|
||||
return false;
|
||||
}
|
||||
int c = buf.front();
|
||||
if (auto p = strchr(chars, c)) {
|
||||
buf.pop_front();
|
||||
return *p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ContentReader::buffer(size_t bytes) {
|
||||
if (bytes < buf.size()) {
|
||||
return true;
|
||||
}
|
||||
bytes -= buf.size();
|
||||
while (bytes > 0) {
|
||||
uint8_t chunk[256];
|
||||
auto numWant = std::min(sizeof(chunk), bytes);
|
||||
auto numGot = reader->read(chunk, numWant);
|
||||
if (numGot == 0) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < numGot; i++) {
|
||||
buf.push_back(chunk[i]);
|
||||
}
|
||||
bytes -= numGot;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ContentReader::badHeader() {
|
||||
if (on_invalid_data == kClose) {
|
||||
close();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ContentWriter
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs)
|
||||
: writer(rhs) {}
|
||||
|
||||
ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept {
|
||||
writer = std::move(rhs.writer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ContentWriter::isOpen() {
|
||||
return writer ? writer->isOpen() : false;
|
||||
}
|
||||
|
||||
void ContentWriter::close() {
|
||||
if (writer) {
|
||||
writer->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool ContentWriter::write(const std::string& msg) const {
|
||||
auto header =
|
||||
std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n";
|
||||
return writer->write(header.data(), header.size()) &&
|
||||
writer->write(msg.data(), msg.size());
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
73
libraries/cppdap/src/content_stream.h
Normal file
73
libraries/cppdap/src/content_stream.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_content_stream_h
|
||||
#define dap_content_stream_h
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dap/session.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
// Forward declarations
|
||||
class Reader;
|
||||
class Writer;
|
||||
|
||||
class ContentReader {
|
||||
public:
|
||||
ContentReader() = default;
|
||||
ContentReader(const std::shared_ptr<Reader>&,
|
||||
const OnInvalidData on_invalid_data = kIgnore);
|
||||
ContentReader& operator=(ContentReader&&) noexcept;
|
||||
|
||||
bool isOpen();
|
||||
void close();
|
||||
std::string read();
|
||||
|
||||
private:
|
||||
bool scan(const uint8_t* seq, size_t len);
|
||||
bool scan(const char* str);
|
||||
bool match(const uint8_t* seq, size_t len);
|
||||
bool match(const char* str);
|
||||
char matchAny(const char* chars);
|
||||
bool buffer(size_t bytes);
|
||||
std::string badHeader();
|
||||
|
||||
std::shared_ptr<Reader> reader;
|
||||
std::deque<uint8_t> buf;
|
||||
OnInvalidData on_invalid_data;
|
||||
};
|
||||
|
||||
class ContentWriter {
|
||||
public:
|
||||
ContentWriter() = default;
|
||||
ContentWriter(const std::shared_ptr<Writer>&);
|
||||
ContentWriter& operator=(ContentWriter&&) noexcept;
|
||||
|
||||
bool isOpen();
|
||||
void close();
|
||||
bool write(const std::string&) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Writer> writer;
|
||||
};
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_content_stream_h
|
||||
126
libraries/cppdap/src/content_stream_test.cpp
Normal file
126
libraries/cppdap/src/content_stream_test.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "content_stream.h"
|
||||
|
||||
#include "string_buffer.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
|
||||
// SingleByteReader wraps a dap::Reader to only provide a single byte for each
|
||||
// read() call, regardless of the number of bytes actually requested.
|
||||
class SingleByteReader : public dap::Reader {
|
||||
public:
|
||||
SingleByteReader(std::unique_ptr<dap::Reader>&& inner)
|
||||
: inner(std::move(inner)) {}
|
||||
|
||||
bool isOpen() override { return inner->isOpen(); }
|
||||
void close() override { return inner->close(); }
|
||||
size_t read(void* buffer, size_t) override { return inner->read(buffer, 1); };
|
||||
|
||||
private:
|
||||
std::unique_ptr<dap::Reader> inner;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ContentStreamTest, Write) {
|
||||
auto sb = dap::StringBuffer::create();
|
||||
auto ptr = sb.get();
|
||||
dap::ContentWriter cw(std::move(sb));
|
||||
cw.write("Content payload number one");
|
||||
cw.write("Content payload number two");
|
||||
cw.write("Content payload number three");
|
||||
ASSERT_EQ(ptr->string(),
|
||||
"Content-Length: 26\r\n\r\nContent payload number one"
|
||||
"Content-Length: 26\r\n\r\nContent payload number two"
|
||||
"Content-Length: 28\r\n\r\nContent payload number three");
|
||||
}
|
||||
|
||||
TEST(ContentStreamTest, Read) {
|
||||
auto sb = dap::StringBuffer::create();
|
||||
sb->write("Content-Length: 26\r\n\r\nContent payload number one");
|
||||
sb->write("some unrecognised garbage");
|
||||
sb->write("Content-Length: 26\r\n\r\nContent payload number two");
|
||||
sb->write("some more unrecognised garbage");
|
||||
sb->write("Content-Length: 28\r\n\r\nContent payload number three");
|
||||
dap::ContentReader cs(std::move(sb));
|
||||
ASSERT_EQ(cs.read(), "Content payload number one");
|
||||
ASSERT_EQ(cs.read(), "Content payload number two");
|
||||
ASSERT_EQ(cs.read(), "Content payload number three");
|
||||
ASSERT_EQ(cs.read(), "");
|
||||
}
|
||||
|
||||
TEST(ContentStreamTest, ShortRead) {
|
||||
auto sb = dap::StringBuffer::create();
|
||||
sb->write("Content-Length: 26\r\n\r\nContent payload number one");
|
||||
sb->write("some unrecognised garbage");
|
||||
sb->write("Content-Length: 26\r\n\r\nContent payload number two");
|
||||
sb->write("some more unrecognised garbage");
|
||||
sb->write("Content-Length: 28\r\n\r\nContent payload number three");
|
||||
dap::ContentReader cs(
|
||||
std::unique_ptr<SingleByteReader>(new SingleByteReader(std::move(sb))));
|
||||
ASSERT_EQ(cs.read(), "Content payload number one");
|
||||
ASSERT_EQ(cs.read(), "Content payload number two");
|
||||
ASSERT_EQ(cs.read(), "Content payload number three");
|
||||
ASSERT_EQ(cs.read(), "");
|
||||
}
|
||||
|
||||
TEST(ContentStreamTest, PartialReadAndParse) {
|
||||
auto sb = std::make_shared<dap::StringBuffer>();
|
||||
sb->write("Content");
|
||||
sb->write("-Length: ");
|
||||
sb->write("26");
|
||||
sb->write("\r\n\r\n");
|
||||
sb->write("Content payload number one");
|
||||
|
||||
dap::ContentReader cs(sb);
|
||||
ASSERT_EQ(cs.read(), "Content payload number one");
|
||||
ASSERT_EQ(cs.read(), "");
|
||||
}
|
||||
|
||||
TEST(ContentStreamTest, HttpRequest) {
|
||||
const char* const part1 =
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Host: localhost:8001\r\n"
|
||||
"Connection: keep-alive\r\n"
|
||||
"Content-Length: 99\r\n";
|
||||
const char* const part2 =
|
||||
"Pragma: no-cache\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Content-Type: text/plain;charset=UTF-8\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"Origin: null\r\n"
|
||||
"Sec-Fetch-Site: cross-site\r\n"
|
||||
"Sec-Fetch-Mode: cors\r\n"
|
||||
"Sec-Fetch-Dest: empty\r\n"
|
||||
"Accept-Encoding: gzip, deflate, br\r\n"
|
||||
"Accept-Language: en-US,en;q=0.9\r\n"
|
||||
"\r\n"
|
||||
"{\"type\":\"request\",\"command\":\"launch\",\"arguments\":{\"cmd\":\"/"
|
||||
"bin/sh -c 'echo remote code execution'\"}}";
|
||||
|
||||
auto sb = dap::StringBuffer::create();
|
||||
sb->write(part1);
|
||||
sb->write(part2);
|
||||
|
||||
dap::ContentReader cr(std::move(sb), dap::kClose);
|
||||
ASSERT_EQ(cr.read(), "");
|
||||
ASSERT_FALSE(cr.isOpen());
|
||||
}
|
||||
72
libraries/cppdap/src/dap_test.cpp
Normal file
72
libraries/cppdap/src/dap_test.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/dap.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
TEST(DAP, PairedInitializeTerminate) {
|
||||
dap::initialize();
|
||||
dap::terminate();
|
||||
}
|
||||
|
||||
TEST(DAP, NestedInitializeTerminate) {
|
||||
dap::initialize();
|
||||
dap::initialize();
|
||||
dap::initialize();
|
||||
dap::terminate();
|
||||
dap::terminate();
|
||||
dap::terminate();
|
||||
}
|
||||
|
||||
TEST(DAP, MultiThreadedInitializeTerminate) {
|
||||
const size_t numThreads = 64;
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
size_t numInits = 0;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(numThreads);
|
||||
for (size_t i = 0; i < numThreads; i++) {
|
||||
threads.emplace_back([&] {
|
||||
dap::initialize();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
numInits++;
|
||||
if (numInits == numThreads) {
|
||||
cv.notify_all();
|
||||
} else {
|
||||
cv.wait(lock, [&] { return numInits == numThreads; });
|
||||
}
|
||||
}
|
||||
dap::terminate();
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
258
libraries/cppdap/src/io.cpp
Normal file
258
libraries/cppdap/src/io.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/io.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring> // strlen
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
class Pipe : public dap::ReaderWriter {
|
||||
public:
|
||||
// dap::ReaderWriter compliance
|
||||
bool isOpen() override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
return !closed;
|
||||
}
|
||||
|
||||
void close() override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
closed = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
size_t read(void* buffer, size_t bytes) override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
auto out = reinterpret_cast<uint8_t*>(buffer);
|
||||
size_t n = 0;
|
||||
while (true) {
|
||||
cv.wait(lock, [&] { return closed || data.size() > 0; });
|
||||
if (closed) {
|
||||
return n;
|
||||
}
|
||||
for (; n < bytes && data.size() > 0; n++) {
|
||||
out[n] = data.front();
|
||||
data.pop_front();
|
||||
}
|
||||
if (n == bytes) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool write(const void* buffer, size_t bytes) override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (closed) {
|
||||
return false;
|
||||
}
|
||||
if (bytes == 0) {
|
||||
return true;
|
||||
}
|
||||
auto notify = data.size() == 0;
|
||||
auto src = reinterpret_cast<const uint8_t*>(buffer);
|
||||
for (size_t i = 0; i < bytes; i++) {
|
||||
data.emplace_back(src[i]);
|
||||
}
|
||||
if (notify) {
|
||||
cv.notify_all();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::deque<uint8_t> data;
|
||||
bool closed = false;
|
||||
};
|
||||
|
||||
class RW : public dap::ReaderWriter {
|
||||
public:
|
||||
RW(const std::shared_ptr<Reader>& r, const std::shared_ptr<Writer>& w)
|
||||
: r(r), w(w) {}
|
||||
|
||||
// dap::ReaderWriter compliance
|
||||
bool isOpen() override { return r->isOpen() && w->isOpen(); }
|
||||
void close() override {
|
||||
r->close();
|
||||
w->close();
|
||||
}
|
||||
size_t read(void* buffer, size_t n) override { return r->read(buffer, n); }
|
||||
bool write(const void* buffer, size_t n) override {
|
||||
return w->write(buffer, n);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<dap::Reader> r;
|
||||
const std::shared_ptr<dap::Writer> w;
|
||||
};
|
||||
|
||||
class File : public dap::ReaderWriter {
|
||||
public:
|
||||
File(FILE* f, bool closable) : f(f), closable(closable) {}
|
||||
|
||||
~File() { close(); }
|
||||
|
||||
// dap::ReaderWriter compliance
|
||||
bool isOpen() override { return !closed; }
|
||||
void close() override {
|
||||
if (closable) {
|
||||
if (!closed.exchange(true)) {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t read(void* buffer, size_t n) override {
|
||||
std::unique_lock<std::mutex> lock(readMutex);
|
||||
auto out = reinterpret_cast<char*>(buffer);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
int c = fgetc(f);
|
||||
if (c == EOF) {
|
||||
return i;
|
||||
}
|
||||
out[i] = char(c);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
bool write(const void* buffer, size_t n) override {
|
||||
std::unique_lock<std::mutex> lock(writeMutex);
|
||||
if (fwrite(buffer, 1, n, f) == n) {
|
||||
fflush(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* const f;
|
||||
const bool closable;
|
||||
std::mutex readMutex;
|
||||
std::mutex writeMutex;
|
||||
std::atomic<bool> closed = {false};
|
||||
};
|
||||
|
||||
class ReaderSpy : public dap::Reader {
|
||||
public:
|
||||
ReaderSpy(const std::shared_ptr<dap::Reader>& r,
|
||||
const std::shared_ptr<dap::Writer>& s,
|
||||
const std::string& prefix)
|
||||
: r(r), s(s), prefix(prefix) {}
|
||||
|
||||
// dap::Reader compliance
|
||||
bool isOpen() override { return r->isOpen(); }
|
||||
void close() override { r->close(); }
|
||||
size_t read(void* buffer, size_t n) override {
|
||||
auto c = r->read(buffer, n);
|
||||
if (c > 0) {
|
||||
auto chars = reinterpret_cast<const char*>(buffer);
|
||||
std::string buf = prefix;
|
||||
buf.append(chars, chars + c);
|
||||
s->write(buf.data(), buf.size());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<dap::Reader> r;
|
||||
const std::shared_ptr<dap::Writer> s;
|
||||
const std::string prefix;
|
||||
};
|
||||
|
||||
class WriterSpy : public dap::Writer {
|
||||
public:
|
||||
WriterSpy(const std::shared_ptr<dap::Writer>& w,
|
||||
const std::shared_ptr<dap::Writer>& s,
|
||||
const std::string& prefix)
|
||||
: w(w), s(s), prefix(prefix) {}
|
||||
|
||||
// dap::Writer compliance
|
||||
bool isOpen() override { return w->isOpen(); }
|
||||
void close() override { w->close(); }
|
||||
bool write(const void* buffer, size_t n) override {
|
||||
if (!w->write(buffer, n)) {
|
||||
return false;
|
||||
}
|
||||
auto chars = reinterpret_cast<const char*>(buffer);
|
||||
std::string buf = prefix;
|
||||
buf.append(chars, chars + n);
|
||||
s->write(buf.data(), buf.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<dap::Writer> w;
|
||||
const std::shared_ptr<dap::Writer> s;
|
||||
const std::string prefix;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace dap {
|
||||
|
||||
std::shared_ptr<ReaderWriter> ReaderWriter::create(
|
||||
const std::shared_ptr<Reader>& r,
|
||||
const std::shared_ptr<Writer>& w) {
|
||||
return std::make_shared<RW>(r, w);
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> pipe() {
|
||||
return std::make_shared<Pipe>();
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> file(FILE* f, bool closable /* = true */) {
|
||||
return std::make_shared<File>(f, closable);
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> file(const char* path) {
|
||||
if (auto f = fopen(path, "wb")) {
|
||||
return std::make_shared<File>(f, true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// spy() returns a Reader that copies all reads from the Reader r to the Writer
|
||||
// s, using the given optional prefix.
|
||||
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
|
||||
const std::shared_ptr<Writer>& s,
|
||||
const char* prefix /* = "\n<-" */) {
|
||||
return std::make_shared<ReaderSpy>(r, s, prefix);
|
||||
}
|
||||
|
||||
// spy() returns a Writer that copies all writes to the Writer w to the Writer
|
||||
// s, using the given optional prefix.
|
||||
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
|
||||
const std::shared_ptr<Writer>& s,
|
||||
const char* prefix /* = "\n->" */) {
|
||||
return std::make_shared<WriterSpy>(w, s, prefix);
|
||||
}
|
||||
|
||||
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...) {
|
||||
char buf[2048];
|
||||
|
||||
va_list vararg;
|
||||
va_start(vararg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, vararg);
|
||||
va_end(vararg);
|
||||
|
||||
return w->write(buf, strlen(buf));
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
47
libraries/cppdap/src/json_serializer.h
Normal file
47
libraries/cppdap/src/json_serializer.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_json_serializer_h
|
||||
#define dap_json_serializer_h
|
||||
|
||||
#if defined(CPPDAP_JSON_NLOHMANN)
|
||||
#include "nlohmann_json_serializer.h"
|
||||
#elif defined(CPPDAP_JSON_RAPID)
|
||||
#include "rapid_json_serializer.h"
|
||||
#elif defined(CPPDAP_JSON_JSONCPP)
|
||||
#include "jsoncpp_json_serializer.h"
|
||||
#else
|
||||
#error "Unrecognised cppdap JSON library"
|
||||
#endif
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
#if defined(CPPDAP_JSON_NLOHMANN)
|
||||
using Deserializer = NlohmannDeserializer;
|
||||
using Serializer = NlohmannSerializer;
|
||||
#elif defined(CPPDAP_JSON_RAPID)
|
||||
using Deserializer = RapidDeserializer;
|
||||
using Serializer = RapidSerializer;
|
||||
#elif defined(CPPDAP_JSON_JSONCPP)
|
||||
using Deserializer = JsonCppDeserializer;
|
||||
using Serializer = JsonCppSerializer;
|
||||
#else
|
||||
#error "Unrecognised cppdap JSON library"
|
||||
#endif
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_json_serializer_h
|
||||
266
libraries/cppdap/src/json_serializer_test.cpp
Normal file
266
libraries/cppdap/src/json_serializer_test.cpp
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "json_serializer.h"
|
||||
|
||||
#include "dap/typeinfo.h"
|
||||
#include "dap/typeof.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct JSONInnerTestObject {
|
||||
integer i;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(JSONInnerTestObject,
|
||||
"json-inner-test-object",
|
||||
DAP_FIELD(i, "i"));
|
||||
|
||||
struct JSONTestObject {
|
||||
boolean b;
|
||||
integer i;
|
||||
number n;
|
||||
array<integer> a;
|
||||
object o;
|
||||
string s;
|
||||
optional<integer> o1;
|
||||
optional<integer> o2;
|
||||
JSONInnerTestObject inner;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(JSONTestObject,
|
||||
"json-test-object",
|
||||
DAP_FIELD(b, "b"),
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"),
|
||||
DAP_FIELD(a, "a"),
|
||||
DAP_FIELD(o, "o"),
|
||||
DAP_FIELD(s, "s"),
|
||||
DAP_FIELD(o1, "o1"),
|
||||
DAP_FIELD(o2, "o2"),
|
||||
DAP_FIELD(inner, "inner"));
|
||||
|
||||
struct JSONObjectNoFields {};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields");
|
||||
|
||||
struct SimpleJSONTestObject {
|
||||
boolean b;
|
||||
integer i;
|
||||
};
|
||||
DAP_STRUCT_TYPEINFO(SimpleJSONTestObject,
|
||||
"simple-json-test-object",
|
||||
DAP_FIELD(b, "b"),
|
||||
DAP_FIELD(i, "i"));
|
||||
|
||||
} // namespace dap
|
||||
|
||||
class JSONSerializer : public testing::Test {
|
||||
protected:
|
||||
static dap::object GetSimpleObject() {
|
||||
return dap::object({{"one", dap::integer(1)},
|
||||
{"two", dap::number(2)},
|
||||
{"three", dap::string("three")},
|
||||
{"four", dap::boolean(true)}});
|
||||
}
|
||||
void TEST_SIMPLE_OBJECT(const dap::object& obj) {
|
||||
NESTED_TEST_FAILED = true;
|
||||
auto ref_obj = GetSimpleObject();
|
||||
ASSERT_EQ(obj.size(), ref_obj.size());
|
||||
ASSERT_TRUE(obj.at("one").is<dap::integer>());
|
||||
ASSERT_TRUE(obj.at("two").is<dap::number>());
|
||||
ASSERT_TRUE(obj.at("three").is<dap::string>());
|
||||
ASSERT_TRUE(obj.at("four").is<dap::boolean>());
|
||||
|
||||
ASSERT_EQ(ref_obj.at("one").get<dap::integer>(),
|
||||
obj.at("one").get<dap::integer>());
|
||||
ASSERT_EQ(ref_obj.at("two").get<dap::number>(),
|
||||
obj.at("two").get<dap::number>());
|
||||
ASSERT_EQ(ref_obj.at("three").get<dap::string>(),
|
||||
obj.at("three").get<dap::string>());
|
||||
ASSERT_EQ(ref_obj.at("four").get<dap::boolean>(),
|
||||
obj.at("four").get<dap::boolean>());
|
||||
NESTED_TEST_FAILED = false;
|
||||
}
|
||||
template <typename T>
|
||||
void TEST_SERIALIZING_DESERIALIZING(const T& encoded, T& decoded) {
|
||||
NESTED_TEST_FAILED = true;
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(encoded));
|
||||
dap::json::Deserializer d(s.dump());
|
||||
ASSERT_TRUE(d.deserialize(&decoded));
|
||||
NESTED_TEST_FAILED = false;
|
||||
}
|
||||
bool NESTED_TEST_FAILED = false;
|
||||
#define _ASSERT_PASS(NESTED_TEST) \
|
||||
NESTED_TEST; \
|
||||
ASSERT_FALSE(NESTED_TEST_FAILED);
|
||||
};
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserialize) {
|
||||
dap::JSONTestObject encoded;
|
||||
encoded.b = true;
|
||||
encoded.i = 32;
|
||||
encoded.n = 123.456;
|
||||
encoded.a = {2, 4, 6, 8, 0x100000000, -2, -4, -6, -8, -0x100000000};
|
||||
encoded.o["one"] = dap::integer(1);
|
||||
encoded.o["two"] = dap::number(2);
|
||||
encoded.s = "hello world";
|
||||
encoded.o2 = 42;
|
||||
encoded.inner.i = 70;
|
||||
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(encoded));
|
||||
|
||||
dap::JSONTestObject decoded;
|
||||
dap::json::Deserializer d(s.dump());
|
||||
ASSERT_TRUE(d.deserialize(&decoded));
|
||||
|
||||
ASSERT_EQ(encoded.b, decoded.b);
|
||||
ASSERT_EQ(encoded.i, decoded.i);
|
||||
ASSERT_EQ(encoded.n, decoded.n);
|
||||
ASSERT_EQ(encoded.a, decoded.a);
|
||||
ASSERT_EQ(encoded.o["one"].get<dap::integer>(),
|
||||
decoded.o["one"].get<dap::integer>());
|
||||
ASSERT_EQ(encoded.o["two"].get<dap::number>(),
|
||||
decoded.o["two"].get<dap::number>());
|
||||
ASSERT_EQ(encoded.s, decoded.s);
|
||||
ASSERT_EQ(encoded.o2, decoded.o2);
|
||||
ASSERT_EQ(encoded.inner.i, decoded.inner.i);
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeObjectNoFields) {
|
||||
dap::JSONObjectNoFields obj;
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(obj));
|
||||
ASSERT_EQ(s.dump(), "{}");
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeObject) {
|
||||
dap::object encoded = GetSimpleObject();
|
||||
dap::object decoded;
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded));
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObject) {
|
||||
dap::object encoded;
|
||||
dap::object decoded;
|
||||
// object nested inside object
|
||||
dap::object encoded_embed_obj = GetSimpleObject();
|
||||
dap::object decoded_embed_obj;
|
||||
encoded["embed_obj"] = encoded_embed_obj;
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
ASSERT_TRUE(decoded["embed_obj"].is<dap::object>());
|
||||
decoded_embed_obj = decoded["embed_obj"].get<dap::object>();
|
||||
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_obj));
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedStruct) {
|
||||
dap::object encoded;
|
||||
dap::object decoded;
|
||||
// object nested inside object
|
||||
dap::SimpleJSONTestObject encoded_embed_struct;
|
||||
encoded_embed_struct.b = true;
|
||||
encoded_embed_struct.i = 50;
|
||||
encoded["embed_struct"] = encoded_embed_struct;
|
||||
|
||||
dap::object decoded_embed_obj;
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
ASSERT_TRUE(decoded["embed_struct"].is<dap::object>());
|
||||
decoded_embed_obj = decoded["embed_struct"].get<dap::object>();
|
||||
ASSERT_TRUE(decoded_embed_obj.at("b").is<dap::boolean>());
|
||||
ASSERT_TRUE(decoded_embed_obj.at("i").is<dap::integer>());
|
||||
|
||||
ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get<dap::boolean>());
|
||||
ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get<dap::integer>());
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) {
|
||||
dap::object encoded;
|
||||
dap::object decoded;
|
||||
// array nested inside object
|
||||
dap::array<dap::integer> encoded_embed_arr = {1, 2, 3, 4};
|
||||
dap::array<dap::any> decoded_embed_arr;
|
||||
|
||||
encoded["embed_arr"] = encoded_embed_arr;
|
||||
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
// TODO: Deserializing array should infer basic member types
|
||||
ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>());
|
||||
decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>();
|
||||
ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size());
|
||||
for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) {
|
||||
ASSERT_TRUE(decoded_embed_arr[i].is<dap::integer>());
|
||||
ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get<dap::integer>());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) {
|
||||
dap::object encoded;
|
||||
dap::object decoded;
|
||||
|
||||
dap::array<dap::object> encoded_embed_arr = {GetSimpleObject(),
|
||||
GetSimpleObject()};
|
||||
dap::array<dap::any> decoded_embed_arr;
|
||||
|
||||
encoded["embed_arr"] = encoded_embed_arr;
|
||||
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
// TODO: Deserializing array should infer basic member types
|
||||
ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>());
|
||||
decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>();
|
||||
ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size());
|
||||
for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) {
|
||||
ASSERT_TRUE(decoded_embed_arr[i].is<dap::object>());
|
||||
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get<dap::object>()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, DeserializeSerializeEmptyObject) {
|
||||
auto empty_obj = "{}";
|
||||
dap::object decoded;
|
||||
dap::json::Deserializer d(empty_obj);
|
||||
ASSERT_TRUE(d.deserialize(&decoded));
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(decoded));
|
||||
ASSERT_EQ(s.dump(), empty_obj);
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedEmptyObject) {
|
||||
dap::object encoded_empty_obj;
|
||||
dap::object encoded = {{"empty_obj", encoded_empty_obj}};
|
||||
dap::object decoded;
|
||||
|
||||
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
|
||||
ASSERT_TRUE(decoded["empty_obj"].is<dap::object>());
|
||||
dap::object decoded_empty_obj = decoded["empty_obj"].get<dap::object>();
|
||||
ASSERT_EQ(encoded_empty_obj.size(), decoded_empty_obj.size());
|
||||
}
|
||||
|
||||
TEST_F(JSONSerializer, SerializeDeserializeObjectWithNulledField) {
|
||||
auto thing = dap::any(dap::null());
|
||||
dap::object encoded;
|
||||
encoded["nulled_field"] = dap::null();
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(encoded));
|
||||
dap::object decoded;
|
||||
auto dump = s.dump();
|
||||
dap::json::Deserializer d(dump);
|
||||
ASSERT_TRUE(d.deserialize(&decoded));
|
||||
ASSERT_TRUE(encoded["nulled_field"].is<dap::null>());
|
||||
}
|
||||
272
libraries/cppdap/src/jsoncpp_json_serializer.cpp
Normal file
272
libraries/cppdap/src/jsoncpp_json_serializer.cpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "jsoncpp_json_serializer.h"
|
||||
|
||||
#include "null_json_serializer.h"
|
||||
|
||||
#include <json/json.h>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
JsonCppDeserializer::JsonCppDeserializer(const std::string& str)
|
||||
: json(new Json::Value(JsonCppDeserializer::parse(str))), ownsJson(true) {}
|
||||
|
||||
JsonCppDeserializer::JsonCppDeserializer(const Json::Value* json)
|
||||
: json(json), ownsJson(false) {}
|
||||
|
||||
JsonCppDeserializer::~JsonCppDeserializer() {
|
||||
if (ownsJson) {
|
||||
delete json;
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::boolean* v) const {
|
||||
if (!json->isBool()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->asBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::integer* v) const {
|
||||
if (!json->isInt64()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->asInt64();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::number* v) const {
|
||||
if (!json->isNumeric()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->asDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::string* v) const {
|
||||
if (!json->isString()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->asString();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::object* v) const {
|
||||
v->reserve(json->size());
|
||||
for (auto i = json->begin(); i != json->end(); i++) {
|
||||
JsonCppDeserializer d(&*i);
|
||||
dap::any val;
|
||||
if (!d.deserialize(&val)) {
|
||||
return false;
|
||||
}
|
||||
(*v)[i.name()] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::deserialize(dap::any* v) const {
|
||||
if (json->isBool()) {
|
||||
*v = dap::boolean(json->asBool());
|
||||
} else if (json->type() == Json::ValueType::realValue) {
|
||||
// json->isDouble() returns true for integers as well, so we need to
|
||||
// explicitly look for the realValue type.
|
||||
*v = dap::number(json->asDouble());
|
||||
} else if (json->isInt64()) {
|
||||
*v = dap::integer(json->asInt64());
|
||||
} else if (json->isString()) {
|
||||
*v = json->asString();
|
||||
} else if (json->isObject()) {
|
||||
dap::object obj;
|
||||
if (!deserialize(&obj)) {
|
||||
return false;
|
||||
}
|
||||
*v = obj;
|
||||
} else if (json->isArray()) {
|
||||
dap::array<any> arr;
|
||||
if (!deserialize(&arr)) {
|
||||
return false;
|
||||
}
|
||||
*v = arr;
|
||||
} else if (json->isNull()) {
|
||||
*v = null();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t JsonCppDeserializer::count() const {
|
||||
return json->size();
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::array(
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json->isArray()) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& value : *json) {
|
||||
JsonCppDeserializer d(&value);
|
||||
if (!cb(&d)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppDeserializer::field(
|
||||
const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json->isObject()) {
|
||||
return false;
|
||||
}
|
||||
auto value = json->find(name.data(), name.data() + name.size());
|
||||
if (value == nullptr) {
|
||||
return cb(&NullDeserializer::instance);
|
||||
}
|
||||
JsonCppDeserializer d(value);
|
||||
return cb(&d);
|
||||
}
|
||||
|
||||
Json::Value JsonCppDeserializer::parse(const std::string& text) {
|
||||
Json::CharReaderBuilder builder;
|
||||
auto jsonReader = std::unique_ptr<Json::CharReader>(builder.newCharReader());
|
||||
Json::Value json;
|
||||
std::string error;
|
||||
if (!jsonReader->parse(text.data(), text.data() + text.size(), &json,
|
||||
&error)) {
|
||||
// cppdap expects that the JSON layer does not throw exceptions.
|
||||
std::abort();
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
JsonCppSerializer::JsonCppSerializer()
|
||||
: json(new Json::Value()), ownsJson(true) {}
|
||||
|
||||
JsonCppSerializer::JsonCppSerializer(Json::Value* json)
|
||||
: json(json), ownsJson(false) {}
|
||||
|
||||
JsonCppSerializer::~JsonCppSerializer() {
|
||||
if (ownsJson) {
|
||||
delete json;
|
||||
}
|
||||
}
|
||||
|
||||
std::string JsonCppSerializer::dump() const {
|
||||
Json::StreamWriterBuilder writer;
|
||||
return Json::writeString(writer, *json);
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(dap::boolean v) {
|
||||
*json = (bool)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(dap::integer v) {
|
||||
*json = (Json::LargestInt)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(dap::number v) {
|
||||
*json = (double)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(const dap::string& v) {
|
||||
*json = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(const dap::object& v) {
|
||||
if (!json->isObject()) {
|
||||
*json = Json::Value(Json::objectValue);
|
||||
}
|
||||
for (auto& it : v) {
|
||||
JsonCppSerializer s(&(*json)[it.first]);
|
||||
if (!s.serialize(it.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::serialize(const dap::any& v) {
|
||||
if (v.is<dap::boolean>()) {
|
||||
*json = (bool)v.get<dap::boolean>();
|
||||
} else if (v.is<dap::integer>()) {
|
||||
*json = (Json::LargestInt)v.get<dap::integer>();
|
||||
} else if (v.is<dap::number>()) {
|
||||
*json = (double)v.get<dap::number>();
|
||||
} else if (v.is<dap::string>()) {
|
||||
*json = v.get<dap::string>();
|
||||
} else if (v.is<dap::object>()) {
|
||||
// reachable if dap::object nested is inside other dap::object
|
||||
return serialize(v.get<dap::object>());
|
||||
} else if (v.is<dap::null>()) {
|
||||
} else {
|
||||
// reachable if array or custom serialized type is nested inside other
|
||||
auto type = get_any_type(v);
|
||||
auto value = get_any_val(v);
|
||||
if (type && value) {
|
||||
return type->serialize(this, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>& cb) {
|
||||
*json = Json::Value(Json::arrayValue);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
JsonCppSerializer s(&(*json)[Json::Value::ArrayIndex(i)]);
|
||||
if (!cb(&s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonCppSerializer::object(
|
||||
const std::function<bool(dap::FieldSerializer*)>& cb) {
|
||||
struct FS : public FieldSerializer {
|
||||
Json::Value* const json;
|
||||
|
||||
FS(Json::Value* json) : json(json) {}
|
||||
bool field(const std::string& name, const SerializeFunc& cb) override {
|
||||
JsonCppSerializer s(&(*json)[name]);
|
||||
auto res = cb(&s);
|
||||
if (s.removed) {
|
||||
json->removeMember(name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
*json = Json::Value(Json::objectValue);
|
||||
FS fs{json};
|
||||
return cb(&fs);
|
||||
}
|
||||
|
||||
void JsonCppSerializer::remove() {
|
||||
removed = true;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
134
libraries/cppdap/src/jsoncpp_json_serializer.h
Normal file
134
libraries/cppdap/src/jsoncpp_json_serializer.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_jsoncpp_json_serializer_h
|
||||
#define dap_jsoncpp_json_serializer_h
|
||||
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/serialization.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include <json/forwards.h>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
struct JsonCppDeserializer : public dap::Deserializer {
|
||||
explicit JsonCppDeserializer(const std::string&);
|
||||
~JsonCppDeserializer();
|
||||
|
||||
// dap::Deserializer compliance
|
||||
bool deserialize(boolean* v) const override;
|
||||
bool deserialize(integer* v) const override;
|
||||
bool deserialize(number* v) const override;
|
||||
bool deserialize(string* v) const override;
|
||||
bool deserialize(object* v) const override;
|
||||
bool deserialize(any* v) const override;
|
||||
size_t count() const override;
|
||||
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
bool field(const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) {
|
||||
return dap::Deserializer::field(name, v);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool deserialize(T* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::array<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::optional<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool deserialize(dap::variant<T0, Types...>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) const {
|
||||
return dap::Deserializer::deserialize(name, v);
|
||||
}
|
||||
|
||||
private:
|
||||
JsonCppDeserializer(const Json::Value*);
|
||||
static Json::Value parse(const std::string& text);
|
||||
const Json::Value* const json;
|
||||
const bool ownsJson;
|
||||
};
|
||||
|
||||
struct JsonCppSerializer : public dap::Serializer {
|
||||
JsonCppSerializer();
|
||||
~JsonCppSerializer();
|
||||
|
||||
std::string dump() const;
|
||||
|
||||
// dap::Serializer compliance
|
||||
bool serialize(boolean v) override;
|
||||
bool serialize(integer v) override;
|
||||
bool serialize(number v) override;
|
||||
bool serialize(const string& v) override;
|
||||
bool serialize(const dap::object& v) override;
|
||||
bool serialize(const any& v) override;
|
||||
bool array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>&) override;
|
||||
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
|
||||
void remove() override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool serialize(const T& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::array<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::optional<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool serialize(const dap::variant<T0, Types...>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
|
||||
|
||||
private:
|
||||
JsonCppSerializer(Json::Value*);
|
||||
Json::Value* const json;
|
||||
const bool ownsJson;
|
||||
bool removed = false;
|
||||
};
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_jsoncpp_json_serializer_h
|
||||
107
libraries/cppdap/src/network.cpp
Normal file
107
libraries/cppdap/src/network.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/network.h"
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
class Impl : public dap::net::Server {
|
||||
public:
|
||||
Impl() : stopped{true} {}
|
||||
|
||||
~Impl() { stop(); }
|
||||
|
||||
bool start(int port,
|
||||
const OnConnect& onConnect,
|
||||
const OnError& onError) override {
|
||||
return start("localhost", port, onConnect, onError);
|
||||
}
|
||||
|
||||
bool start(const char* address,
|
||||
int port,
|
||||
const OnConnect& onConnect,
|
||||
const OnError& onError) override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
stopWithLock();
|
||||
socket = std::unique_ptr<dap::Socket>(
|
||||
new dap::Socket(address, std::to_string(port).c_str()));
|
||||
|
||||
if (!socket->isOpen()) {
|
||||
onError("Failed to open socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
stopped = false;
|
||||
thread = std::thread([=] {
|
||||
while (true) {
|
||||
if (auto rw = socket->accept()) {
|
||||
onConnect(rw);
|
||||
continue;
|
||||
}
|
||||
if (!stopped) {
|
||||
onError("Failed to accept connection");
|
||||
}
|
||||
break;
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
stopWithLock();
|
||||
}
|
||||
|
||||
private:
|
||||
bool isRunning() { return !stopped; }
|
||||
|
||||
void stopWithLock() {
|
||||
if (!stopped.exchange(true)) {
|
||||
socket->close();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex mutex;
|
||||
std::thread thread;
|
||||
std::unique_ptr<dap::Socket> socket;
|
||||
std::atomic<bool> stopped;
|
||||
OnError errorHandler;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace dap {
|
||||
namespace net {
|
||||
|
||||
std::unique_ptr<Server> Server::create() {
|
||||
return std::unique_ptr<Server>(new Impl());
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> connect(const char* addr,
|
||||
int port,
|
||||
uint32_t timeoutMillis) {
|
||||
return Socket::connect(addr, std::to_string(port).c_str(), timeoutMillis);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace dap
|
||||
110
libraries/cppdap/src/network_test.cpp
Normal file
110
libraries/cppdap/src/network_test.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/network.h"
|
||||
#include "dap/io.h"
|
||||
|
||||
#include "chan.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int port = 19021;
|
||||
|
||||
bool write(const std::shared_ptr<dap::Writer>& w, const std::string& s) {
|
||||
return w->write(s.data(), s.size()) && w->write("\0", 1);
|
||||
}
|
||||
|
||||
std::string read(const std::shared_ptr<dap::Reader>& r) {
|
||||
char c;
|
||||
std::string s;
|
||||
while (r->read(&c, sizeof(c)) > 0) {
|
||||
if (c == '\0') {
|
||||
return s;
|
||||
}
|
||||
s += c;
|
||||
}
|
||||
return r->isOpen() ? "<read failed>" : "<stream closed>";
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST(Network, ClientServer) {
|
||||
dap::Chan<bool> done;
|
||||
auto server = dap::net::Server::create();
|
||||
if (!server->start(
|
||||
port,
|
||||
[&](const std::shared_ptr<dap::ReaderWriter>& rw) {
|
||||
ASSERT_EQ(read(rw), "client to server");
|
||||
ASSERT_TRUE(write(rw, "server to client"));
|
||||
done.put(true);
|
||||
},
|
||||
[&](const char* err) { FAIL() << "Server error: " << err; })) {
|
||||
FAIL() << "Couldn't start server";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
auto client = dap::net::connect("localhost", port);
|
||||
ASSERT_NE(client, nullptr) << "Failed to connect client " << i;
|
||||
ASSERT_TRUE(write(client, "client to server"));
|
||||
ASSERT_EQ(read(client), "server to client");
|
||||
done.take();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
server.reset();
|
||||
}
|
||||
|
||||
TEST(Network, ServerRepeatStopAndRestart) {
|
||||
dap::Chan<bool> done;
|
||||
auto onConnect = [&](const std::shared_ptr<dap::ReaderWriter>& rw) {
|
||||
ASSERT_EQ(read(rw), "client to server");
|
||||
ASSERT_TRUE(write(rw, "server to client"));
|
||||
done.put(true);
|
||||
};
|
||||
auto onError = [&](const char* err) { FAIL() << "Server error: " << err; };
|
||||
|
||||
auto server = dap::net::Server::create();
|
||||
if (!server->start(port, onConnect, onError)) {
|
||||
FAIL() << "Couldn't start server";
|
||||
return;
|
||||
}
|
||||
|
||||
server->stop();
|
||||
server->stop();
|
||||
server->stop();
|
||||
|
||||
if (!server->start(port, onConnect, onError)) {
|
||||
FAIL() << "Couldn't restart server";
|
||||
return;
|
||||
}
|
||||
|
||||
auto client = dap::net::connect("localhost", port);
|
||||
ASSERT_NE(client, nullptr) << "Failed to connect";
|
||||
ASSERT_TRUE(write(client, "client to server"));
|
||||
ASSERT_EQ(read(client), "server to client");
|
||||
done.take();
|
||||
|
||||
server->stop();
|
||||
server->stop();
|
||||
server->stop();
|
||||
|
||||
server.reset();
|
||||
}
|
||||
260
libraries/cppdap/src/nlohmann_json_serializer.cpp
Normal file
260
libraries/cppdap/src/nlohmann_json_serializer.cpp
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "nlohmann_json_serializer.h"
|
||||
|
||||
#include "null_json_serializer.h"
|
||||
|
||||
// Disable JSON exceptions. We should be guarding against any exceptions being
|
||||
// fired in this file.
|
||||
#define JSON_NOEXCEPTION 1
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
NlohmannDeserializer::NlohmannDeserializer(const std::string& str)
|
||||
: json(new nlohmann::json(nlohmann::json::parse(str, nullptr, false))),
|
||||
ownsJson(true) {}
|
||||
|
||||
NlohmannDeserializer::NlohmannDeserializer(const nlohmann::json* json)
|
||||
: json(json), ownsJson(false) {}
|
||||
|
||||
NlohmannDeserializer::~NlohmannDeserializer() {
|
||||
if (ownsJson) {
|
||||
delete json;
|
||||
}
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::boolean* v) const {
|
||||
if (!json->is_boolean()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::integer* v) const {
|
||||
if (!json->is_number_integer()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->get<int64_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::number* v) const {
|
||||
if (!json->is_number()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->get<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::string* v) const {
|
||||
if (!json->is_string()) {
|
||||
return false;
|
||||
}
|
||||
*v = json->get<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::object* v) const {
|
||||
v->reserve(json->size());
|
||||
for (auto& el : json->items()) {
|
||||
NlohmannDeserializer d(&el.value());
|
||||
dap::any val;
|
||||
if (!d.deserialize(&val)) {
|
||||
return false;
|
||||
}
|
||||
(*v)[el.key()] = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::deserialize(dap::any* v) const {
|
||||
if (json->is_boolean()) {
|
||||
*v = dap::boolean(json->get<bool>());
|
||||
} else if (json->is_number_float()) {
|
||||
*v = dap::number(json->get<double>());
|
||||
} else if (json->is_number_integer()) {
|
||||
*v = dap::integer(json->get<int64_t>());
|
||||
} else if (json->is_string()) {
|
||||
*v = json->get<std::string>();
|
||||
} else if (json->is_object()) {
|
||||
dap::object obj;
|
||||
if (!deserialize(&obj)) {
|
||||
return false;
|
||||
}
|
||||
*v = obj;
|
||||
} else if (json->is_array()) {
|
||||
dap::array<any> arr;
|
||||
if (!deserialize(&arr)) {
|
||||
return false;
|
||||
}
|
||||
*v = arr;
|
||||
} else if (json->is_null()) {
|
||||
*v = null();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t NlohmannDeserializer::count() const {
|
||||
return json->size();
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::array(
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json->is_array()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < json->size(); i++) {
|
||||
NlohmannDeserializer d(&(*json)[i]);
|
||||
if (!cb(&d)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannDeserializer::field(
|
||||
const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json->is_structured()) {
|
||||
return false;
|
||||
}
|
||||
auto it = json->find(name);
|
||||
if (it == json->end()) {
|
||||
return cb(&NullDeserializer::instance);
|
||||
}
|
||||
auto obj = *it;
|
||||
NlohmannDeserializer d(&obj);
|
||||
return cb(&d);
|
||||
}
|
||||
|
||||
NlohmannSerializer::NlohmannSerializer()
|
||||
: json(new nlohmann::json()), ownsJson(true) {}
|
||||
|
||||
NlohmannSerializer::NlohmannSerializer(nlohmann::json* json)
|
||||
: json(json), ownsJson(false) {}
|
||||
|
||||
NlohmannSerializer::~NlohmannSerializer() {
|
||||
if (ownsJson) {
|
||||
delete json;
|
||||
}
|
||||
}
|
||||
|
||||
std::string NlohmannSerializer::dump() const {
|
||||
return json->dump();
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(dap::boolean v) {
|
||||
*json = (bool)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(dap::integer v) {
|
||||
*json = (int64_t)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(dap::number v) {
|
||||
*json = (double)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(const dap::string& v) {
|
||||
*json = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(const dap::object& v) {
|
||||
if (!json->is_object()) {
|
||||
*json = nlohmann::json::object();
|
||||
}
|
||||
for (auto& it : v) {
|
||||
NlohmannSerializer s(&(*json)[it.first]);
|
||||
if (!s.serialize(it.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::serialize(const dap::any& v) {
|
||||
if (v.is<dap::boolean>()) {
|
||||
*json = (bool)v.get<dap::boolean>();
|
||||
} else if (v.is<dap::integer>()) {
|
||||
*json = (int64_t)v.get<dap::integer>();
|
||||
} else if (v.is<dap::number>()) {
|
||||
*json = (double)v.get<dap::number>();
|
||||
} else if (v.is<dap::string>()) {
|
||||
*json = v.get<dap::string>();
|
||||
} else if (v.is<dap::object>()) {
|
||||
// reachable if dap::object nested is inside other dap::object
|
||||
return serialize(v.get<dap::object>());
|
||||
} else if (v.is<dap::null>()) {
|
||||
} else {
|
||||
// reachable if array or custom serialized type is nested inside other
|
||||
auto type = get_any_type(v);
|
||||
auto value = get_any_val(v);
|
||||
if (type && value) {
|
||||
return type->serialize(this, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::array(
|
||||
size_t count,
|
||||
const std::function<bool(dap::Serializer*)>& cb) {
|
||||
*json = std::vector<int>();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
NlohmannSerializer s(&(*json)[i]);
|
||||
if (!cb(&s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NlohmannSerializer::object(
|
||||
const std::function<bool(dap::FieldSerializer*)>& cb) {
|
||||
struct FS : public FieldSerializer {
|
||||
nlohmann::json* const json;
|
||||
|
||||
FS(nlohmann::json* json) : json(json) {}
|
||||
bool field(const std::string& name, const SerializeFunc& cb) override {
|
||||
NlohmannSerializer s(&(*json)[name]);
|
||||
auto res = cb(&s);
|
||||
if (s.removed) {
|
||||
json->erase(name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
*json = nlohmann::json({}, false, nlohmann::json::value_t::object);
|
||||
FS fs{json};
|
||||
return cb(&fs);
|
||||
}
|
||||
|
||||
void NlohmannSerializer::remove() {
|
||||
removed = true;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
133
libraries/cppdap/src/nlohmann_json_serializer.h
Normal file
133
libraries/cppdap/src/nlohmann_json_serializer.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_nlohmann_json_serializer_h
|
||||
#define dap_nlohmann_json_serializer_h
|
||||
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/serialization.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
struct NlohmannDeserializer : public dap::Deserializer {
|
||||
explicit NlohmannDeserializer(const std::string&);
|
||||
~NlohmannDeserializer();
|
||||
|
||||
// dap::Deserializer compliance
|
||||
bool deserialize(boolean* v) const override;
|
||||
bool deserialize(integer* v) const override;
|
||||
bool deserialize(number* v) const override;
|
||||
bool deserialize(string* v) const override;
|
||||
bool deserialize(object* v) const override;
|
||||
bool deserialize(any* v) const override;
|
||||
size_t count() const override;
|
||||
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
bool field(const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) {
|
||||
return dap::Deserializer::field(name, v);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool deserialize(T* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::array<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::optional<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool deserialize(dap::variant<T0, Types...>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) const {
|
||||
return dap::Deserializer::deserialize(name, v);
|
||||
}
|
||||
|
||||
private:
|
||||
NlohmannDeserializer(const nlohmann::json*);
|
||||
const nlohmann::json* const json;
|
||||
const bool ownsJson;
|
||||
};
|
||||
|
||||
struct NlohmannSerializer : public dap::Serializer {
|
||||
NlohmannSerializer();
|
||||
~NlohmannSerializer();
|
||||
|
||||
std::string dump() const;
|
||||
|
||||
// dap::Serializer compliance
|
||||
bool serialize(boolean v) override;
|
||||
bool serialize(integer v) override;
|
||||
bool serialize(number v) override;
|
||||
bool serialize(const string& v) override;
|
||||
bool serialize(const dap::object& v) override;
|
||||
bool serialize(const any& v) override;
|
||||
bool array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>&) override;
|
||||
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
|
||||
void remove() override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool serialize(const T& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::array<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::optional<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool serialize(const dap::variant<T0, Types...>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
|
||||
|
||||
private:
|
||||
NlohmannSerializer(nlohmann::json*);
|
||||
nlohmann::json* const json;
|
||||
const bool ownsJson;
|
||||
bool removed = false;
|
||||
};
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_nlohmann_json_serializer_h
|
||||
23
libraries/cppdap/src/null_json_serializer.cpp
Normal file
23
libraries/cppdap/src/null_json_serializer.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "null_json_serializer.h"
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
NullDeserializer NullDeserializer::instance;
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
47
libraries/cppdap/src/null_json_serializer.h
Normal file
47
libraries/cppdap/src/null_json_serializer.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_null_json_serializer_h
|
||||
#define dap_null_json_serializer_h
|
||||
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/serialization.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
struct NullDeserializer : public dap::Deserializer {
|
||||
static NullDeserializer instance;
|
||||
|
||||
bool deserialize(dap::boolean*) const override { return false; }
|
||||
bool deserialize(dap::integer*) const override { return false; }
|
||||
bool deserialize(dap::number*) const override { return false; }
|
||||
bool deserialize(dap::string*) const override { return false; }
|
||||
bool deserialize(dap::object*) const override { return false; }
|
||||
bool deserialize(dap::any*) const override { return false; }
|
||||
size_t count() const override { return 0; }
|
||||
bool array(const std::function<bool(dap::Deserializer*)>&) const override {
|
||||
return false;
|
||||
}
|
||||
bool field(const std::string&,
|
||||
const std::function<bool(dap::Deserializer*)>&) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_null_json_serializer_h
|
||||
169
libraries/cppdap/src/optional_test.cpp
Normal file
169
libraries/cppdap/src/optional_test.cpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/optional.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
TEST(Optional, EmptyConstruct) {
|
||||
dap::optional<std::string> opt;
|
||||
ASSERT_FALSE(opt);
|
||||
ASSERT_FALSE(opt.has_value());
|
||||
}
|
||||
|
||||
TEST(Optional, ValueConstruct) {
|
||||
dap::optional<int> opt(10);
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_TRUE(opt.has_value());
|
||||
ASSERT_EQ(opt.value(), 10);
|
||||
}
|
||||
|
||||
TEST(Optional, CopyConstruct) {
|
||||
dap::optional<std::string> a("meow");
|
||||
dap::optional<std::string> b(a);
|
||||
ASSERT_EQ(a, b);
|
||||
ASSERT_EQ(a.value(), "meow");
|
||||
ASSERT_EQ(b.value(), "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, CopyCastConstruct) {
|
||||
dap::optional<int> a(10);
|
||||
dap::optional<uint16_t> b(a);
|
||||
ASSERT_EQ(a, b);
|
||||
ASSERT_EQ(b.value(), (uint16_t)10);
|
||||
}
|
||||
|
||||
TEST(Optional, MoveConstruct) {
|
||||
dap::optional<std::string> a("meow");
|
||||
dap::optional<std::string> b(std::move(a));
|
||||
ASSERT_EQ(b.value(), "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, MoveCastConstruct) {
|
||||
dap::optional<int> a(10);
|
||||
dap::optional<uint16_t> b(std::move(a));
|
||||
ASSERT_EQ(b.value(), (uint16_t)10);
|
||||
}
|
||||
|
||||
TEST(Optional, AssignValue) {
|
||||
dap::optional<std::string> a;
|
||||
std::string b = "meow";
|
||||
a = b;
|
||||
ASSERT_EQ(a.value(), "meow");
|
||||
ASSERT_EQ(b, "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, AssignOptional) {
|
||||
dap::optional<std::string> a;
|
||||
dap::optional<std::string> b("meow");
|
||||
a = b;
|
||||
ASSERT_EQ(a.value(), "meow");
|
||||
ASSERT_EQ(b.value(), "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, MoveAssignOptional) {
|
||||
dap::optional<std::string> a;
|
||||
dap::optional<std::string> b("meow");
|
||||
a = std::move(b);
|
||||
ASSERT_EQ(a.value(), "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, StarDeref) {
|
||||
dap::optional<std::string> a("meow");
|
||||
ASSERT_EQ(*a, "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, StarDerefConst) {
|
||||
const dap::optional<std::string> a("meow");
|
||||
ASSERT_EQ(*a, "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, ArrowDeref) {
|
||||
struct S {
|
||||
int i;
|
||||
};
|
||||
dap::optional<S> a(S{10});
|
||||
ASSERT_EQ(a->i, 10);
|
||||
}
|
||||
|
||||
TEST(Optional, ArrowDerefConst) {
|
||||
struct S {
|
||||
int i;
|
||||
};
|
||||
const dap::optional<S> a(S{10});
|
||||
ASSERT_EQ(a->i, 10);
|
||||
}
|
||||
|
||||
TEST(Optional, Value) {
|
||||
const dap::optional<std::string> a("meow");
|
||||
ASSERT_EQ(a.value(), "meow");
|
||||
}
|
||||
|
||||
TEST(Optional, ValueDefault) {
|
||||
const dap::optional<std::string> a;
|
||||
const dap::optional<std::string> b("woof");
|
||||
ASSERT_EQ(a.value("meow"), "meow");
|
||||
ASSERT_EQ(b.value("meow"), "woof");
|
||||
}
|
||||
|
||||
TEST(Optional, CompareLT) {
|
||||
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(3));
|
||||
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(5));
|
||||
ASSERT_TRUE(dap::optional<int>(5) < dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() < dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() < dap::optional<int>());
|
||||
}
|
||||
|
||||
TEST(Optional, CompareLE) {
|
||||
ASSERT_FALSE(dap::optional<int>(5) <= dap::optional<int>(3));
|
||||
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(5));
|
||||
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>());
|
||||
}
|
||||
|
||||
TEST(Optional, CompareGT) {
|
||||
ASSERT_TRUE(dap::optional<int>(5) > dap::optional<int>(3));
|
||||
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(5));
|
||||
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>());
|
||||
}
|
||||
|
||||
TEST(Optional, CompareGE) {
|
||||
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(3));
|
||||
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(5));
|
||||
ASSERT_FALSE(dap::optional<int>(5) >= dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() >= dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() >= dap::optional<int>());
|
||||
}
|
||||
|
||||
TEST(Optional, CompareEQ) {
|
||||
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(3));
|
||||
ASSERT_TRUE(dap::optional<int>(5) == dap::optional<int>(5));
|
||||
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() == dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() == dap::optional<int>());
|
||||
}
|
||||
|
||||
TEST(Optional, CompareNEQ) {
|
||||
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(3));
|
||||
ASSERT_FALSE(dap::optional<int>(5) != dap::optional<int>(5));
|
||||
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(10));
|
||||
ASSERT_TRUE(dap::optional<int>() != dap::optional<int>(10));
|
||||
ASSERT_FALSE(dap::optional<int>() != dap::optional<int>());
|
||||
}
|
||||
127
libraries/cppdap/src/protocol_events.cpp
Normal file
127
libraries/cppdap/src/protocol_events.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generated with protocol_gen.go -- do not edit this file.
|
||||
// go run scripts/protocol_gen/protocol_gen.go
|
||||
//
|
||||
// DAP version 1.68.0
|
||||
|
||||
#include "dap/protocol.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent,
|
||||
"breakpoint",
|
||||
DAP_FIELD(breakpoint, "breakpoint"),
|
||||
DAP_FIELD(reason, "reason"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent,
|
||||
"capabilities",
|
||||
DAP_FIELD(capabilities, "capabilities"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent,
|
||||
"continued",
|
||||
DAP_FIELD(allThreadsContinued,
|
||||
"allThreadsContinued"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent,
|
||||
"exited",
|
||||
DAP_FIELD(exitCode, "exitCode"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(InvalidatedEvent,
|
||||
"invalidated",
|
||||
DAP_FIELD(areas, "areas"),
|
||||
DAP_FIELD(stackFrameId, "stackFrameId"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent,
|
||||
"loadedSource",
|
||||
DAP_FIELD(reason, "reason"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(MemoryEvent,
|
||||
"memory",
|
||||
DAP_FIELD(count, "count"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(offset, "offset"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent,
|
||||
"module",
|
||||
DAP_FIELD(module, "module"),
|
||||
DAP_FIELD(reason, "reason"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent,
|
||||
"output",
|
||||
DAP_FIELD(category, "category"),
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(data, "data"),
|
||||
DAP_FIELD(group, "group"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(locationReference, "locationReference"),
|
||||
DAP_FIELD(output, "output"),
|
||||
DAP_FIELD(source, "source"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProcessEvent,
|
||||
"process",
|
||||
DAP_FIELD(isLocalProcess, "isLocalProcess"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(pointerSize, "pointerSize"),
|
||||
DAP_FIELD(startMethod, "startMethod"),
|
||||
DAP_FIELD(systemProcessId, "systemProcessId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressEndEvent,
|
||||
"progressEnd",
|
||||
DAP_FIELD(message, "message"),
|
||||
DAP_FIELD(progressId, "progressId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressStartEvent,
|
||||
"progressStart",
|
||||
DAP_FIELD(cancellable, "cancellable"),
|
||||
DAP_FIELD(message, "message"),
|
||||
DAP_FIELD(percentage, "percentage"),
|
||||
DAP_FIELD(progressId, "progressId"),
|
||||
DAP_FIELD(requestId, "requestId"),
|
||||
DAP_FIELD(title, "title"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressUpdateEvent,
|
||||
"progressUpdate",
|
||||
DAP_FIELD(message, "message"),
|
||||
DAP_FIELD(percentage, "percentage"),
|
||||
DAP_FIELD(progressId, "progressId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent,
|
||||
"stopped",
|
||||
DAP_FIELD(allThreadsStopped, "allThreadsStopped"),
|
||||
DAP_FIELD(description, "description"),
|
||||
DAP_FIELD(hitBreakpointIds, "hitBreakpointIds"),
|
||||
DAP_FIELD(preserveFocusHint, "preserveFocusHint"),
|
||||
DAP_FIELD(reason, "reason"),
|
||||
DAP_FIELD(text, "text"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent,
|
||||
"terminated",
|
||||
DAP_FIELD(restart, "restart"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent,
|
||||
"thread",
|
||||
DAP_FIELD(reason, "reason"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
} // namespace dap
|
||||
293
libraries/cppdap/src/protocol_requests.cpp
Normal file
293
libraries/cppdap/src/protocol_requests.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generated with protocol_gen.go -- do not edit this file.
|
||||
// go run scripts/protocol_gen/protocol_gen.go
|
||||
//
|
||||
// DAP version 1.68.0
|
||||
|
||||
#include "dap/protocol.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachRequest,
|
||||
"attach",
|
||||
DAP_FIELD(restart, "__restart"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsRequest,
|
||||
"breakpointLocations",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelRequest,
|
||||
"cancel",
|
||||
DAP_FIELD(progressId, "progressId"),
|
||||
DAP_FIELD(requestId, "requestId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsRequest,
|
||||
"completions",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(frameId, "frameId"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(text, "text"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneRequest, "configurationDone");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest,
|
||||
"continue",
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest,
|
||||
"dataBreakpointInfo",
|
||||
DAP_FIELD(asAddress, "asAddress"),
|
||||
DAP_FIELD(bytes, "bytes"),
|
||||
DAP_FIELD(frameId, "frameId"),
|
||||
DAP_FIELD(mode, "mode"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleRequest,
|
||||
"disassemble",
|
||||
DAP_FIELD(instructionCount, "instructionCount"),
|
||||
DAP_FIELD(instructionOffset, "instructionOffset"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(offset, "offset"),
|
||||
DAP_FIELD(resolveSymbols, "resolveSymbols"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectRequest,
|
||||
"disconnect",
|
||||
DAP_FIELD(restart, "restart"),
|
||||
DAP_FIELD(suspendDebuggee, "suspendDebuggee"),
|
||||
DAP_FIELD(terminateDebuggee,
|
||||
"terminateDebuggee"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateRequest,
|
||||
"evaluate",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(context, "context"),
|
||||
DAP_FIELD(expression, "expression"),
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(frameId, "frameId"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoRequest,
|
||||
"exceptionInfo",
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoRequest,
|
||||
"goto",
|
||||
DAP_FIELD(targetId, "targetId"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsRequest,
|
||||
"gotoTargets",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(
|
||||
InitializeRequest,
|
||||
"initialize",
|
||||
DAP_FIELD(adapterID, "adapterID"),
|
||||
DAP_FIELD(clientID, "clientID"),
|
||||
DAP_FIELD(clientName, "clientName"),
|
||||
DAP_FIELD(columnsStartAt1, "columnsStartAt1"),
|
||||
DAP_FIELD(linesStartAt1, "linesStartAt1"),
|
||||
DAP_FIELD(locale, "locale"),
|
||||
DAP_FIELD(pathFormat, "pathFormat"),
|
||||
DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"),
|
||||
DAP_FIELD(supportsArgsCanBeInterpretedByShell,
|
||||
"supportsArgsCanBeInterpretedByShell"),
|
||||
DAP_FIELD(supportsInvalidatedEvent, "supportsInvalidatedEvent"),
|
||||
DAP_FIELD(supportsMemoryEvent, "supportsMemoryEvent"),
|
||||
DAP_FIELD(supportsMemoryReferences, "supportsMemoryReferences"),
|
||||
DAP_FIELD(supportsProgressReporting, "supportsProgressReporting"),
|
||||
DAP_FIELD(supportsRunInTerminalRequest, "supportsRunInTerminalRequest"),
|
||||
DAP_FIELD(supportsStartDebuggingRequest, "supportsStartDebuggingRequest"),
|
||||
DAP_FIELD(supportsVariablePaging, "supportsVariablePaging"),
|
||||
DAP_FIELD(supportsVariableType, "supportsVariableType"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchRequest,
|
||||
"launch",
|
||||
DAP_FIELD(restart, "__restart"),
|
||||
DAP_FIELD(noDebug, "noDebug"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesRequest, "loadedSources");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LocationsRequest,
|
||||
"locations",
|
||||
DAP_FIELD(locationReference,
|
||||
"locationReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesRequest,
|
||||
"modules",
|
||||
DAP_FIELD(moduleCount, "moduleCount"),
|
||||
DAP_FIELD(startModule, "startModule"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextRequest,
|
||||
"next",
|
||||
DAP_FIELD(granularity, "granularity"),
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseRequest,
|
||||
"pause",
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryRequest,
|
||||
"readMemory",
|
||||
DAP_FIELD(count, "count"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(offset, "offset"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameRequest,
|
||||
"restartFrame",
|
||||
DAP_FIELD(frameId, "frameId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartRequest,
|
||||
"restart",
|
||||
DAP_FIELD(arguments, "arguments"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueRequest,
|
||||
"reverseContinue",
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalRequest,
|
||||
"runInTerminal",
|
||||
DAP_FIELD(args, "args"),
|
||||
DAP_FIELD(argsCanBeInterpretedByShell,
|
||||
"argsCanBeInterpretedByShell"),
|
||||
DAP_FIELD(cwd, "cwd"),
|
||||
DAP_FIELD(env, "env"),
|
||||
DAP_FIELD(kind, "kind"),
|
||||
DAP_FIELD(title, "title"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesRequest,
|
||||
"scopes",
|
||||
DAP_FIELD(frameId, "frameId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsRequest,
|
||||
"setBreakpoints",
|
||||
DAP_FIELD(breakpoints, "breakpoints"),
|
||||
DAP_FIELD(lines, "lines"),
|
||||
DAP_FIELD(source, "source"),
|
||||
DAP_FIELD(sourceModified, "sourceModified"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsRequest,
|
||||
"setDataBreakpoints",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest,
|
||||
"setExceptionBreakpoints",
|
||||
DAP_FIELD(exceptionOptions, "exceptionOptions"),
|
||||
DAP_FIELD(filterOptions, "filterOptions"),
|
||||
DAP_FIELD(filters, "filters"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionRequest,
|
||||
"setExpression",
|
||||
DAP_FIELD(expression, "expression"),
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(frameId, "frameId"),
|
||||
DAP_FIELD(value, "value"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest,
|
||||
"setFunctionBreakpoints",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest,
|
||||
"setInstructionBreakpoints",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableRequest,
|
||||
"setVariable",
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(value, "value"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceRequest,
|
||||
"source",
|
||||
DAP_FIELD(source, "source"),
|
||||
DAP_FIELD(sourceReference, "sourceReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceRequest,
|
||||
"stackTrace",
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(levels, "levels"),
|
||||
DAP_FIELD(startFrame, "startFrame"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingRequest,
|
||||
"startDebugging",
|
||||
DAP_FIELD(configuration, "configuration"),
|
||||
DAP_FIELD(request, "request"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackRequest,
|
||||
"stepBack",
|
||||
DAP_FIELD(granularity, "granularity"),
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInRequest,
|
||||
"stepIn",
|
||||
DAP_FIELD(granularity, "granularity"),
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(targetId, "targetId"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsRequest,
|
||||
"stepInTargets",
|
||||
DAP_FIELD(frameId, "frameId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutRequest,
|
||||
"stepOut",
|
||||
DAP_FIELD(granularity, "granularity"),
|
||||
DAP_FIELD(singleThread, "singleThread"),
|
||||
DAP_FIELD(threadId, "threadId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateRequest,
|
||||
"terminate",
|
||||
DAP_FIELD(restart, "restart"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsRequest,
|
||||
"terminateThreads",
|
||||
DAP_FIELD(threadIds, "threadIds"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsRequest, "threads");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesRequest,
|
||||
"variables",
|
||||
DAP_FIELD(count, "count"),
|
||||
DAP_FIELD(filter, "filter"),
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(start, "start"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryRequest,
|
||||
"writeMemory",
|
||||
DAP_FIELD(allowPartial, "allowPartial"),
|
||||
DAP_FIELD(data, "data"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(offset, "offset"));
|
||||
|
||||
} // namespace dap
|
||||
262
libraries/cppdap/src/protocol_response.cpp
Normal file
262
libraries/cppdap/src/protocol_response.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generated with protocol_gen.go -- do not edit this file.
|
||||
// go run scripts/protocol_gen/protocol_gen.go
|
||||
//
|
||||
// DAP version 1.68.0
|
||||
|
||||
#include "dap/protocol.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsResponse,
|
||||
"",
|
||||
DAP_FIELD(targets, "targets"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueResponse,
|
||||
"",
|
||||
DAP_FIELD(allThreadsContinued,
|
||||
"allThreadsContinued"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoResponse,
|
||||
"",
|
||||
DAP_FIELD(accessTypes, "accessTypes"),
|
||||
DAP_FIELD(canPersist, "canPersist"),
|
||||
DAP_FIELD(dataId, "dataId"),
|
||||
DAP_FIELD(description, "description"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleResponse,
|
||||
"",
|
||||
DAP_FIELD(instructions, "instructions"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ErrorResponse, "", DAP_FIELD(error, "error"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateResponse,
|
||||
"",
|
||||
DAP_FIELD(indexedVariables, "indexedVariables"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(namedVariables, "namedVariables"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(result, "result"),
|
||||
DAP_FIELD(type, "type"),
|
||||
DAP_FIELD(valueLocationReference,
|
||||
"valueLocationReference"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoResponse,
|
||||
"",
|
||||
DAP_FIELD(breakMode, "breakMode"),
|
||||
DAP_FIELD(description, "description"),
|
||||
DAP_FIELD(details, "details"),
|
||||
DAP_FIELD(exceptionId, "exceptionId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsResponse,
|
||||
"",
|
||||
DAP_FIELD(targets, "targets"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(
|
||||
InitializeResponse,
|
||||
"",
|
||||
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
|
||||
DAP_FIELD(breakpointModes, "breakpointModes"),
|
||||
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
|
||||
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
|
||||
DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"),
|
||||
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
|
||||
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
|
||||
DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"),
|
||||
DAP_FIELD(supportsBreakpointLocationsRequest,
|
||||
"supportsBreakpointLocationsRequest"),
|
||||
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
|
||||
DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"),
|
||||
DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"),
|
||||
DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"),
|
||||
DAP_FIELD(supportsConfigurationDoneRequest,
|
||||
"supportsConfigurationDoneRequest"),
|
||||
DAP_FIELD(supportsDataBreakpointBytes, "supportsDataBreakpointBytes"),
|
||||
DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"),
|
||||
DAP_FIELD(supportsDelayedStackTraceLoading,
|
||||
"supportsDelayedStackTraceLoading"),
|
||||
DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"),
|
||||
DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"),
|
||||
DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"),
|
||||
DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
|
||||
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
|
||||
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
|
||||
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
|
||||
DAP_FIELD(supportsHitConditionalBreakpoints,
|
||||
"supportsHitConditionalBreakpoints"),
|
||||
DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"),
|
||||
DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"),
|
||||
DAP_FIELD(supportsLogPoints, "supportsLogPoints"),
|
||||
DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"),
|
||||
DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"),
|
||||
DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"),
|
||||
DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"),
|
||||
DAP_FIELD(supportsSetExpression, "supportsSetExpression"),
|
||||
DAP_FIELD(supportsSetVariable, "supportsSetVariable"),
|
||||
DAP_FIELD(supportsSingleThreadExecutionRequests,
|
||||
"supportsSingleThreadExecutionRequests"),
|
||||
DAP_FIELD(supportsStepBack, "supportsStepBack"),
|
||||
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
|
||||
DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"),
|
||||
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
|
||||
DAP_FIELD(supportsTerminateThreadsRequest,
|
||||
"supportsTerminateThreadsRequest"),
|
||||
DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"),
|
||||
DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesResponse,
|
||||
"",
|
||||
DAP_FIELD(sources, "sources"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(LocationsResponse,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesResponse,
|
||||
"",
|
||||
DAP_FIELD(modules, "modules"),
|
||||
DAP_FIELD(totalModules, "totalModules"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryResponse,
|
||||
"",
|
||||
DAP_FIELD(address, "address"),
|
||||
DAP_FIELD(data, "data"),
|
||||
DAP_FIELD(unreadableBytes, "unreadableBytes"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalResponse,
|
||||
"",
|
||||
DAP_FIELD(processId, "processId"),
|
||||
DAP_FIELD(shellProcessId, "shellProcessId"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesResponse, "", DAP_FIELD(scopes, "scopes"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionResponse,
|
||||
"",
|
||||
DAP_FIELD(indexedVariables, "indexedVariables"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(namedVariables, "namedVariables"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(type, "type"),
|
||||
DAP_FIELD(value, "value"),
|
||||
DAP_FIELD(valueLocationReference,
|
||||
"valueLocationReference"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse,
|
||||
"",
|
||||
DAP_FIELD(breakpoints, "breakpoints"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableResponse,
|
||||
"",
|
||||
DAP_FIELD(indexedVariables, "indexedVariables"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(namedVariables, "namedVariables"),
|
||||
DAP_FIELD(type, "type"),
|
||||
DAP_FIELD(value, "value"),
|
||||
DAP_FIELD(valueLocationReference,
|
||||
"valueLocationReference"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceResponse,
|
||||
"",
|
||||
DAP_FIELD(content, "content"),
|
||||
DAP_FIELD(mimeType, "mimeType"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceResponse,
|
||||
"",
|
||||
DAP_FIELD(stackFrames, "stackFrames"),
|
||||
DAP_FIELD(totalFrames, "totalFrames"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsResponse,
|
||||
"",
|
||||
DAP_FIELD(targets, "targets"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsResponse, "");
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsResponse,
|
||||
"",
|
||||
DAP_FIELD(threads, "threads"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesResponse,
|
||||
"",
|
||||
DAP_FIELD(variables, "variables"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryResponse,
|
||||
"",
|
||||
DAP_FIELD(bytesWritten, "bytesWritten"),
|
||||
DAP_FIELD(offset, "offset"));
|
||||
|
||||
} // namespace dap
|
||||
333
libraries/cppdap/src/protocol_types.cpp
Normal file
333
libraries/cppdap/src/protocol_types.cpp
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Generated with protocol_gen.go -- do not edit this file.
|
||||
// go run scripts/protocol_gen/protocol_gen.go
|
||||
//
|
||||
// DAP version 1.68.0
|
||||
|
||||
#include "dap/protocol.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Checksum,
|
||||
"",
|
||||
DAP_FIELD(algorithm, "algorithm"),
|
||||
DAP_FIELD(checksum, "checksum"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Source,
|
||||
"",
|
||||
DAP_FIELD(adapterData, "adapterData"),
|
||||
DAP_FIELD(checksums, "checksums"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(origin, "origin"),
|
||||
DAP_FIELD(path, "path"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(sourceReference, "sourceReference"),
|
||||
DAP_FIELD(sources, "sources"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(instructionReference,
|
||||
"instructionReference"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(message, "message"),
|
||||
DAP_FIELD(offset, "offset"),
|
||||
DAP_FIELD(reason, "reason"),
|
||||
DAP_FIELD(source, "source"),
|
||||
DAP_FIELD(verified, "verified"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocation,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(line, "line"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ColumnDescriptor,
|
||||
"",
|
||||
DAP_FIELD(attributeName, "attributeName"),
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(type, "type"),
|
||||
DAP_FIELD(width, "width"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointMode,
|
||||
"",
|
||||
DAP_FIELD(appliesTo, "appliesTo"),
|
||||
DAP_FIELD(description, "description"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(mode, "mode"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter,
|
||||
"",
|
||||
DAP_FIELD(conditionDescription,
|
||||
"conditionDescription"),
|
||||
DAP_FIELD(def, "default"),
|
||||
DAP_FIELD(description, "description"),
|
||||
DAP_FIELD(filter, "filter"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(supportsCondition,
|
||||
"supportsCondition"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(
|
||||
Capabilities,
|
||||
"",
|
||||
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
|
||||
DAP_FIELD(breakpointModes, "breakpointModes"),
|
||||
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
|
||||
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
|
||||
DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"),
|
||||
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
|
||||
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
|
||||
DAP_FIELD(supportsANSIStyling, "supportsANSIStyling"),
|
||||
DAP_FIELD(supportsBreakpointLocationsRequest,
|
||||
"supportsBreakpointLocationsRequest"),
|
||||
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
|
||||
DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"),
|
||||
DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"),
|
||||
DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"),
|
||||
DAP_FIELD(supportsConfigurationDoneRequest,
|
||||
"supportsConfigurationDoneRequest"),
|
||||
DAP_FIELD(supportsDataBreakpointBytes, "supportsDataBreakpointBytes"),
|
||||
DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"),
|
||||
DAP_FIELD(supportsDelayedStackTraceLoading,
|
||||
"supportsDelayedStackTraceLoading"),
|
||||
DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"),
|
||||
DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"),
|
||||
DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"),
|
||||
DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
|
||||
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
|
||||
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
|
||||
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
|
||||
DAP_FIELD(supportsHitConditionalBreakpoints,
|
||||
"supportsHitConditionalBreakpoints"),
|
||||
DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"),
|
||||
DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"),
|
||||
DAP_FIELD(supportsLogPoints, "supportsLogPoints"),
|
||||
DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"),
|
||||
DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"),
|
||||
DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"),
|
||||
DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"),
|
||||
DAP_FIELD(supportsSetExpression, "supportsSetExpression"),
|
||||
DAP_FIELD(supportsSetVariable, "supportsSetVariable"),
|
||||
DAP_FIELD(supportsSingleThreadExecutionRequests,
|
||||
"supportsSingleThreadExecutionRequests"),
|
||||
DAP_FIELD(supportsStepBack, "supportsStepBack"),
|
||||
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
|
||||
DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"),
|
||||
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
|
||||
DAP_FIELD(supportsTerminateThreadsRequest,
|
||||
"supportsTerminateThreadsRequest"),
|
||||
DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"),
|
||||
DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItem,
|
||||
"",
|
||||
DAP_FIELD(detail, "detail"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(length, "length"),
|
||||
DAP_FIELD(selectionLength, "selectionLength"),
|
||||
DAP_FIELD(selectionStart, "selectionStart"),
|
||||
DAP_FIELD(sortText, "sortText"),
|
||||
DAP_FIELD(start, "start"),
|
||||
DAP_FIELD(text, "text"),
|
||||
DAP_FIELD(type, "type"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembledInstruction,
|
||||
"",
|
||||
DAP_FIELD(address, "address"),
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(instruction, "instruction"),
|
||||
DAP_FIELD(instructionBytes, "instructionBytes"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(location, "location"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(symbol, "symbol"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Message,
|
||||
"",
|
||||
DAP_FIELD(format, "format"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(sendTelemetry, "sendTelemetry"),
|
||||
DAP_FIELD(showUser, "showUser"),
|
||||
DAP_FIELD(url, "url"),
|
||||
DAP_FIELD(urlLabel, "urlLabel"),
|
||||
DAP_FIELD(variables, "variables"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablePresentationHint,
|
||||
"",
|
||||
DAP_FIELD(attributes, "attributes"),
|
||||
DAP_FIELD(kind, "kind"),
|
||||
DAP_FIELD(lazy, "lazy"),
|
||||
DAP_FIELD(visibility, "visibility"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ValueFormat, "", DAP_FIELD(hex, "hex"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionDetails,
|
||||
"",
|
||||
DAP_FIELD(evaluateName, "evaluateName"),
|
||||
DAP_FIELD(fullTypeName, "fullTypeName"),
|
||||
DAP_FIELD(innerException, "innerException"),
|
||||
DAP_FIELD(message, "message"),
|
||||
DAP_FIELD(stackTrace, "stackTrace"),
|
||||
DAP_FIELD(typeName, "typeName"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTarget,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(instructionPointerReference,
|
||||
"instructionPointerReference"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(line, "line"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Module,
|
||||
"",
|
||||
DAP_FIELD(addressRange, "addressRange"),
|
||||
DAP_FIELD(dateTimeStamp, "dateTimeStamp"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(isOptimized, "isOptimized"),
|
||||
DAP_FIELD(isUserCode, "isUserCode"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(path, "path"),
|
||||
DAP_FIELD(symbolFilePath, "symbolFilePath"),
|
||||
DAP_FIELD(symbolStatus, "symbolStatus"),
|
||||
DAP_FIELD(version, "version"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Scope,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(expensive, "expensive"),
|
||||
DAP_FIELD(indexedVariables, "indexedVariables"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(namedVariables, "namedVariables"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(source, "source"),
|
||||
DAP_FIELD(variablesReference,
|
||||
"variablesReference"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceBreakpoint,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(condition, "condition"),
|
||||
DAP_FIELD(hitCondition, "hitCondition"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(logMessage, "logMessage"),
|
||||
DAP_FIELD(mode, "mode"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint,
|
||||
"",
|
||||
DAP_FIELD(accessType, "accessType"),
|
||||
DAP_FIELD(condition, "condition"),
|
||||
DAP_FIELD(dataId, "dataId"),
|
||||
DAP_FIELD(hitCondition, "hitCondition"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionPathSegment,
|
||||
"",
|
||||
DAP_FIELD(names, "names"),
|
||||
DAP_FIELD(negate, "negate"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions,
|
||||
"",
|
||||
DAP_FIELD(breakMode, "breakMode"),
|
||||
DAP_FIELD(path, "path"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionFilterOptions,
|
||||
"",
|
||||
DAP_FIELD(condition, "condition"),
|
||||
DAP_FIELD(filterId, "filterId"),
|
||||
DAP_FIELD(mode, "mode"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint,
|
||||
"",
|
||||
DAP_FIELD(condition, "condition"),
|
||||
DAP_FIELD(hitCondition, "hitCondition"),
|
||||
DAP_FIELD(name, "name"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(InstructionBreakpoint,
|
||||
"",
|
||||
DAP_FIELD(condition, "condition"),
|
||||
DAP_FIELD(hitCondition, "hitCondition"),
|
||||
DAP_FIELD(instructionReference,
|
||||
"instructionReference"),
|
||||
DAP_FIELD(mode, "mode"),
|
||||
DAP_FIELD(offset, "offset"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame,
|
||||
"",
|
||||
DAP_FIELD(canRestart, "canRestart"),
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(instructionPointerReference,
|
||||
"instructionPointerReference"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(moduleId, "moduleId"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(source, "source"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrameFormat,
|
||||
"",
|
||||
DAP_FIELD(includeAll, "includeAll"),
|
||||
DAP_FIELD(line, "line"),
|
||||
DAP_FIELD(module, "module"),
|
||||
DAP_FIELD(parameterNames, "parameterNames"),
|
||||
DAP_FIELD(parameterTypes, "parameterTypes"),
|
||||
DAP_FIELD(parameterValues, "parameterValues"),
|
||||
DAP_FIELD(parameters, "parameters"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTarget,
|
||||
"",
|
||||
DAP_FIELD(column, "column"),
|
||||
DAP_FIELD(endColumn, "endColumn"),
|
||||
DAP_FIELD(endLine, "endLine"),
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(label, "label"),
|
||||
DAP_FIELD(line, "line"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(Thread,
|
||||
"",
|
||||
DAP_FIELD(id, "id"),
|
||||
DAP_FIELD(name, "name"));
|
||||
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(
|
||||
Variable,
|
||||
"",
|
||||
DAP_FIELD(declarationLocationReference, "declarationLocationReference"),
|
||||
DAP_FIELD(evaluateName, "evaluateName"),
|
||||
DAP_FIELD(indexedVariables, "indexedVariables"),
|
||||
DAP_FIELD(memoryReference, "memoryReference"),
|
||||
DAP_FIELD(name, "name"),
|
||||
DAP_FIELD(namedVariables, "namedVariables"),
|
||||
DAP_FIELD(presentationHint, "presentationHint"),
|
||||
DAP_FIELD(type, "type"),
|
||||
DAP_FIELD(value, "value"),
|
||||
DAP_FIELD(valueLocationReference, "valueLocationReference"),
|
||||
DAP_FIELD(variablesReference, "variablesReference"));
|
||||
|
||||
} // namespace dap
|
||||
290
libraries/cppdap/src/rapid_json_serializer.cpp
Normal file
290
libraries/cppdap/src/rapid_json_serializer.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rapid_json_serializer.h"
|
||||
|
||||
#include "null_json_serializer.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
RapidDeserializer::RapidDeserializer(const std::string& str)
|
||||
: doc(new rapidjson::Document()) {
|
||||
doc->Parse(str.c_str());
|
||||
}
|
||||
|
||||
RapidDeserializer::RapidDeserializer(rapidjson::Value* json) : val(json) {}
|
||||
|
||||
RapidDeserializer::~RapidDeserializer() {
|
||||
delete doc;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::boolean* v) const {
|
||||
if (!json()->IsBool()) {
|
||||
return false;
|
||||
}
|
||||
*v = json()->GetBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::integer* v) const {
|
||||
if (json()->IsInt()) {
|
||||
*v = json()->GetInt();
|
||||
return true;
|
||||
} else if (json()->IsUint()) {
|
||||
*v = static_cast<int64_t>(json()->GetUint());
|
||||
return true;
|
||||
} else if (json()->IsInt64()) {
|
||||
*v = json()->GetInt64();
|
||||
return true;
|
||||
} else if (json()->IsUint64()) {
|
||||
*v = static_cast<int64_t>(json()->GetUint64());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::number* v) const {
|
||||
if (!json()->IsNumber()) {
|
||||
return false;
|
||||
}
|
||||
*v = json()->GetDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::string* v) const {
|
||||
if (!json()->IsString()) {
|
||||
return false;
|
||||
}
|
||||
*v = json()->GetString();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::object* v) const {
|
||||
v->reserve(json()->MemberCount());
|
||||
for (auto el = json()->MemberBegin(); el != json()->MemberEnd(); el++) {
|
||||
dap::any el_val;
|
||||
RapidDeserializer d(&(el->value));
|
||||
if (!d.deserialize(&el_val)) {
|
||||
return false;
|
||||
}
|
||||
(*v)[el->name.GetString()] = el_val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::deserialize(dap::any* v) const {
|
||||
if (json()->IsBool()) {
|
||||
*v = dap::boolean(json()->GetBool());
|
||||
} else if (json()->IsDouble()) {
|
||||
*v = dap::number(json()->GetDouble());
|
||||
} else if (json()->IsInt()) {
|
||||
*v = dap::integer(json()->GetInt());
|
||||
} else if (json()->IsString()) {
|
||||
*v = dap::string(json()->GetString());
|
||||
} else if (json()->IsNull()) {
|
||||
*v = null();
|
||||
} else if (json()->IsObject()) {
|
||||
dap::object obj;
|
||||
if (!deserialize(&obj)) {
|
||||
return false;
|
||||
}
|
||||
*v = obj;
|
||||
} else if (json()->IsArray()) {
|
||||
dap::array<any> arr;
|
||||
if (!deserialize(&arr)) {
|
||||
return false;
|
||||
}
|
||||
*v = arr;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RapidDeserializer::count() const {
|
||||
return json()->Size();
|
||||
}
|
||||
|
||||
bool RapidDeserializer::array(
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json()->IsArray()) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < json()->Size(); i++) {
|
||||
RapidDeserializer d(&(*json())[i]);
|
||||
if (!cb(&d)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidDeserializer::field(
|
||||
const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>& cb) const {
|
||||
if (!json()->IsObject()) {
|
||||
return false;
|
||||
}
|
||||
auto it = json()->FindMember(name.c_str());
|
||||
if (it == json()->MemberEnd()) {
|
||||
return cb(&NullDeserializer::instance);
|
||||
}
|
||||
RapidDeserializer d(&(it->value));
|
||||
return cb(&d);
|
||||
}
|
||||
|
||||
RapidSerializer::RapidSerializer()
|
||||
: doc(new rapidjson::Document(rapidjson::kObjectType)),
|
||||
allocator(doc->GetAllocator()) {}
|
||||
|
||||
RapidSerializer::RapidSerializer(rapidjson::Value* json,
|
||||
rapidjson::Document::AllocatorType& allocator)
|
||||
: val(json), allocator(allocator) {}
|
||||
|
||||
RapidSerializer::~RapidSerializer() {
|
||||
delete doc;
|
||||
}
|
||||
|
||||
std::string RapidSerializer::dump() const {
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
|
||||
json()->Accept(writer);
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(dap::boolean v) {
|
||||
json()->SetBool(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(dap::integer v) {
|
||||
json()->SetInt64(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(dap::number v) {
|
||||
json()->SetDouble(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(const dap::string& v) {
|
||||
json()->SetString(v.data(), static_cast<uint32_t>(v.length()), allocator);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(const dap::object& v) {
|
||||
if (!json()->IsObject()) {
|
||||
json()->SetObject();
|
||||
}
|
||||
for (auto& it : v) {
|
||||
if (!json()->HasMember(it.first.c_str())) {
|
||||
rapidjson::Value name_value{it.first.c_str(), allocator};
|
||||
json()->AddMember(name_value, rapidjson::Value(), allocator);
|
||||
}
|
||||
rapidjson::Value& member = (*json())[it.first.c_str()];
|
||||
RapidSerializer s(&member, allocator);
|
||||
if (!s.serialize(it.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::serialize(const dap::any& v) {
|
||||
if (v.is<dap::boolean>()) {
|
||||
json()->SetBool((bool)v.get<dap::boolean>());
|
||||
} else if (v.is<dap::integer>()) {
|
||||
json()->SetInt64(v.get<dap::integer>());
|
||||
} else if (v.is<dap::number>()) {
|
||||
json()->SetDouble((double)v.get<dap::number>());
|
||||
} else if (v.is<dap::string>()) {
|
||||
auto s = v.get<dap::string>();
|
||||
json()->SetString(s.data(), static_cast<uint32_t>(s.length()), allocator);
|
||||
} else if (v.is<dap::object>()) {
|
||||
// reachable if dap::object nested is inside other dap::object
|
||||
return serialize(v.get<dap::object>());
|
||||
} else if (v.is<dap::null>()) {
|
||||
} else {
|
||||
// reachable if array or custom serialized type is nested inside other
|
||||
// dap::object
|
||||
auto type = get_any_type(v);
|
||||
auto value = get_any_val(v);
|
||||
if (type && value) {
|
||||
return type->serialize(this, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>& cb) {
|
||||
if (!json()->IsArray()) {
|
||||
json()->SetArray();
|
||||
}
|
||||
|
||||
while (count > json()->Size()) {
|
||||
json()->PushBack(rapidjson::Value(), allocator);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
RapidSerializer s(&(*json())[i], allocator);
|
||||
if (!cb(&s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RapidSerializer::object(
|
||||
const std::function<bool(dap::FieldSerializer*)>& cb) {
|
||||
struct FS : public FieldSerializer {
|
||||
rapidjson::Value* const json;
|
||||
rapidjson::Document::AllocatorType& allocator;
|
||||
|
||||
FS(rapidjson::Value* json, rapidjson::Document::AllocatorType& allocator)
|
||||
: json(json), allocator(allocator) {}
|
||||
bool field(const std::string& name, const SerializeFunc& cb) override {
|
||||
if (!json->HasMember(name.c_str())) {
|
||||
rapidjson::Value name_value{name.c_str(), allocator};
|
||||
json->AddMember(name_value, rapidjson::Value(), allocator);
|
||||
}
|
||||
rapidjson::Value& member = (*json)[name.c_str()];
|
||||
RapidSerializer s(&member, allocator);
|
||||
auto res = cb(&s);
|
||||
if (s.removed) {
|
||||
json->RemoveMember(name.c_str());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
if (!json()->IsObject()) {
|
||||
json()->SetObject();
|
||||
}
|
||||
FS fs{json(), allocator};
|
||||
return cb(&fs);
|
||||
}
|
||||
|
||||
void RapidSerializer::remove() {
|
||||
removed = true;
|
||||
}
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
138
libraries/cppdap/src/rapid_json_serializer.h
Normal file
138
libraries/cppdap/src/rapid_json_serializer.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_rapid_json_serializer_h
|
||||
#define dap_rapid_json_serializer_h
|
||||
|
||||
#include "dap/protocol.h"
|
||||
#include "dap/serialization.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
namespace dap {
|
||||
namespace json {
|
||||
|
||||
struct RapidDeserializer : public dap::Deserializer {
|
||||
explicit RapidDeserializer(const std::string&);
|
||||
~RapidDeserializer();
|
||||
|
||||
// dap::Deserializer compliance
|
||||
bool deserialize(boolean* v) const override;
|
||||
bool deserialize(integer* v) const override;
|
||||
bool deserialize(number* v) const override;
|
||||
bool deserialize(string* v) const override;
|
||||
bool deserialize(object* v) const override;
|
||||
bool deserialize(any* v) const override;
|
||||
size_t count() const override;
|
||||
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
bool field(const std::string& name,
|
||||
const std::function<bool(dap::Deserializer*)>&) const override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) {
|
||||
return dap::Deserializer::field(name, v);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool deserialize(T* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::array<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool deserialize(dap::optional<T>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool deserialize(dap::variant<T0, Types...>* v) const {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) const {
|
||||
return dap::Deserializer::deserialize(name, v);
|
||||
}
|
||||
|
||||
inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; }
|
||||
|
||||
private:
|
||||
RapidDeserializer(rapidjson::Value*);
|
||||
rapidjson::Document* const doc = nullptr;
|
||||
rapidjson::Value* const val = nullptr;
|
||||
};
|
||||
|
||||
struct RapidSerializer : public dap::Serializer {
|
||||
RapidSerializer();
|
||||
~RapidSerializer();
|
||||
|
||||
std::string dump() const;
|
||||
|
||||
// dap::Serializer compliance
|
||||
bool serialize(boolean v) override;
|
||||
bool serialize(integer v) override;
|
||||
bool serialize(number v) override;
|
||||
bool serialize(const string& v) override;
|
||||
bool serialize(const dap::object& v) override;
|
||||
bool serialize(const any& v) override;
|
||||
bool array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>&) override;
|
||||
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
|
||||
void remove() override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool serialize(const T& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::array<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool serialize(const dap::optional<T>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
template <typename T0, typename... Types>
|
||||
inline bool serialize(const dap::variant<T0, Types...>& v) {
|
||||
return dap::Serializer::serialize(v);
|
||||
}
|
||||
|
||||
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
|
||||
|
||||
inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; }
|
||||
|
||||
private:
|
||||
RapidSerializer(rapidjson::Value*, rapidjson::Document::AllocatorType&);
|
||||
rapidjson::Document* const doc = nullptr;
|
||||
rapidjson::Value* const val = nullptr;
|
||||
rapidjson::Document::AllocatorType& allocator;
|
||||
bool removed = false;
|
||||
};
|
||||
|
||||
} // namespace json
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_rapid_json_serializer_h
|
||||
172
libraries/cppdap/src/rwmutex.h
Normal file
172
libraries/cppdap/src/rwmutex.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_rwmutex_h
|
||||
#define dap_rwmutex_h
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace dap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RWMutex
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A RWMutex is a reader/writer mutual exclusion lock.
|
||||
// The lock can be held by an arbitrary number of readers or a single writer.
|
||||
// Also known as a shared mutex.
|
||||
class RWMutex {
|
||||
public:
|
||||
inline RWMutex() = default;
|
||||
|
||||
// lockReader() locks the mutex for reading.
|
||||
// Multiple read locks can be held while there are no writer locks.
|
||||
inline void lockReader();
|
||||
|
||||
// unlockReader() unlocks the mutex for reading.
|
||||
inline void unlockReader();
|
||||
|
||||
// lockWriter() locks the mutex for writing.
|
||||
// If the lock is already locked for reading or writing, lockWriter blocks
|
||||
// until the lock is available.
|
||||
inline void lockWriter();
|
||||
|
||||
// unlockWriter() unlocks the mutex for writing.
|
||||
inline void unlockWriter();
|
||||
|
||||
private:
|
||||
RWMutex(const RWMutex&) = delete;
|
||||
RWMutex& operator=(const RWMutex&) = delete;
|
||||
|
||||
int readLocks = 0;
|
||||
int pendingWriteLocks = 0;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
void RWMutex::lockReader() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
readLocks++;
|
||||
}
|
||||
|
||||
void RWMutex::unlockReader() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
readLocks--;
|
||||
if (readLocks == 0 && pendingWriteLocks > 0) {
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void RWMutex::lockWriter() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (readLocks > 0) {
|
||||
pendingWriteLocks++;
|
||||
cv.wait(lock, [&] { return readLocks == 0; });
|
||||
pendingWriteLocks--;
|
||||
}
|
||||
lock.release(); // Keep lock held
|
||||
}
|
||||
|
||||
void RWMutex::unlockWriter() {
|
||||
if (pendingWriteLocks > 0) {
|
||||
cv.notify_one();
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RLock
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// RLock is a RAII read lock helper for a RWMutex.
|
||||
class RLock {
|
||||
public:
|
||||
inline RLock(RWMutex& mutex);
|
||||
inline ~RLock();
|
||||
|
||||
inline RLock(RLock&&);
|
||||
inline RLock& operator=(RLock&&);
|
||||
|
||||
private:
|
||||
RLock(const RLock&) = delete;
|
||||
RLock& operator=(const RLock&) = delete;
|
||||
|
||||
RWMutex* m;
|
||||
};
|
||||
|
||||
RLock::RLock(RWMutex& mutex) : m(&mutex) {
|
||||
m->lockReader();
|
||||
}
|
||||
|
||||
RLock::~RLock() {
|
||||
if (m != nullptr) {
|
||||
m->unlockReader();
|
||||
}
|
||||
}
|
||||
|
||||
RLock::RLock(RLock&& other) {
|
||||
m = other.m;
|
||||
other.m = nullptr;
|
||||
}
|
||||
|
||||
RLock& RLock::operator=(RLock&& other) {
|
||||
m = other.m;
|
||||
other.m = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// WLock
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// WLock is a RAII write lock helper for a RWMutex.
|
||||
class WLock {
|
||||
public:
|
||||
inline WLock(RWMutex& mutex);
|
||||
inline ~WLock();
|
||||
|
||||
inline WLock(WLock&&);
|
||||
inline WLock& operator=(WLock&&);
|
||||
|
||||
private:
|
||||
WLock(const WLock&) = delete;
|
||||
WLock& operator=(const WLock&) = delete;
|
||||
|
||||
RWMutex* m;
|
||||
};
|
||||
|
||||
WLock::WLock(RWMutex& mutex) : m(&mutex) {
|
||||
m->lockWriter();
|
||||
}
|
||||
|
||||
WLock::~WLock() {
|
||||
if (m != nullptr) {
|
||||
m->unlockWriter();
|
||||
}
|
||||
}
|
||||
|
||||
WLock::WLock(WLock&& other) {
|
||||
m = other.m;
|
||||
other.m = nullptr;
|
||||
}
|
||||
|
||||
WLock& WLock::operator=(WLock&& other) {
|
||||
m = other.m;
|
||||
other.m = nullptr;
|
||||
return *this;
|
||||
}
|
||||
} // namespace dap
|
||||
|
||||
#endif
|
||||
113
libraries/cppdap/src/rwmutex_test.cpp
Normal file
113
libraries/cppdap/src/rwmutex_test.cpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rwmutex.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr const size_t NumThreads = 8;
|
||||
}
|
||||
|
||||
// Check that WLock behaves like regular mutex.
|
||||
TEST(RWMutex, WLock) {
|
||||
dap::RWMutex rwmutex;
|
||||
int counter = 0;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (size_t i = 0; i < NumThreads; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
dap::WLock lock(rwmutex);
|
||||
counter++;
|
||||
EXPECT_EQ(counter, 1);
|
||||
counter--;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(counter, 0);
|
||||
}
|
||||
|
||||
TEST(RWMutex, NoRLockWithWLock) {
|
||||
dap::RWMutex rwmutex;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::array<int, NumThreads> counters = {};
|
||||
|
||||
{ // With WLock held...
|
||||
dap::WLock wlock(rwmutex);
|
||||
|
||||
for (size_t i = 0; i < counters.size(); i++) {
|
||||
int* counter = &counters[i];
|
||||
threads.emplace_back([&rwmutex, counter] {
|
||||
dap::RLock lock(rwmutex);
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
(*counter)++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// RLocks should block
|
||||
for (int counter : counters) {
|
||||
EXPECT_EQ(counter, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
for (int counter : counters) {
|
||||
EXPECT_EQ(counter, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RWMutex, NoWLockWithRLock) {
|
||||
dap::RWMutex rwmutex;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
size_t counter = 0;
|
||||
|
||||
{ // With RLocks held...
|
||||
dap::RLock rlockA(rwmutex);
|
||||
dap::RLock rlockB(rwmutex);
|
||||
dap::RLock rlockC(rwmutex);
|
||||
|
||||
for (size_t i = 0; i < NumThreads; i++) {
|
||||
threads.emplace_back(std::thread([&] {
|
||||
dap::WLock lock(rwmutex);
|
||||
counter++;
|
||||
}));
|
||||
}
|
||||
|
||||
// ... WLocks should block
|
||||
EXPECT_EQ(counter, 0U);
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(counter, NumThreads);
|
||||
}
|
||||
521
libraries/cppdap/src/session.cpp
Normal file
521
libraries/cppdap/src/session.cpp
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "content_stream.h"
|
||||
|
||||
#include "dap/any.h"
|
||||
#include "dap/session.h"
|
||||
|
||||
#include "chan.h"
|
||||
#include "json_serializer.h"
|
||||
#include "socket.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
class Impl : public dap::Session {
|
||||
public:
|
||||
void setOnInvalidData(dap::OnInvalidData onInvalidData_) override {
|
||||
this->onInvalidData = onInvalidData_;
|
||||
}
|
||||
|
||||
void onError(const ErrorHandler& handler) override { handlers.put(handler); }
|
||||
|
||||
void registerHandler(const dap::TypeInfo* typeinfo,
|
||||
const GenericRequestHandler& handler) override {
|
||||
handlers.put(typeinfo, handler);
|
||||
}
|
||||
|
||||
void registerHandler(const dap::TypeInfo* typeinfo,
|
||||
const GenericEventHandler& handler) override {
|
||||
handlers.put(typeinfo, handler);
|
||||
}
|
||||
|
||||
void registerHandler(const dap::TypeInfo* typeinfo,
|
||||
const GenericResponseSentHandler& handler) override {
|
||||
handlers.put(typeinfo, handler);
|
||||
}
|
||||
|
||||
std::function<void()> getPayload() override {
|
||||
auto request = reader.read();
|
||||
if (request.size() > 0) {
|
||||
if (auto payload = processMessage(request)) {
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void connect(const std::shared_ptr<dap::Reader>& r,
|
||||
const std::shared_ptr<dap::Writer>& w) override {
|
||||
if (isBound.exchange(true)) {
|
||||
handlers.error("Session::connect called twice");
|
||||
return;
|
||||
}
|
||||
|
||||
reader = dap::ContentReader(r, this->onInvalidData);
|
||||
writer = dap::ContentWriter(w);
|
||||
}
|
||||
|
||||
void startProcessingMessages(
|
||||
const ClosedHandler& onClose /* = {} */) override {
|
||||
if (isProcessingMessages.exchange(true)) {
|
||||
handlers.error("Session::startProcessingMessages() called twice");
|
||||
return;
|
||||
}
|
||||
recvThread = std::thread([this, onClose] {
|
||||
while (reader.isOpen()) {
|
||||
if (auto payload = getPayload()) {
|
||||
inbox.put(std::move(payload));
|
||||
}
|
||||
}
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
});
|
||||
|
||||
dispatchThread = std::thread([this] {
|
||||
while (auto payload = inbox.take()) {
|
||||
payload.value()();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool send(const dap::TypeInfo* requestTypeInfo,
|
||||
const dap::TypeInfo* responseTypeInfo,
|
||||
const void* request,
|
||||
const GenericResponseHandler& responseHandler) override {
|
||||
int seq = nextSeq++;
|
||||
|
||||
handlers.put(seq, responseTypeInfo, responseHandler);
|
||||
|
||||
dap::json::Serializer s;
|
||||
if (!s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(seq)) &&
|
||||
fs->field("type", "request") &&
|
||||
fs->field("command", requestTypeInfo->name()) &&
|
||||
fs->field("arguments", [&](dap::Serializer* s) {
|
||||
return requestTypeInfo->serialize(s, request);
|
||||
});
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
return send(s.dump());
|
||||
}
|
||||
|
||||
bool send(const dap::TypeInfo* typeinfo, const void* event) override {
|
||||
dap::json::Serializer s;
|
||||
if (!s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "event") &&
|
||||
fs->field("event", typeinfo->name()) &&
|
||||
fs->field("body", [&](dap::Serializer* s) {
|
||||
return typeinfo->serialize(s, event);
|
||||
});
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
return send(s.dump());
|
||||
}
|
||||
|
||||
~Impl() override {
|
||||
inbox.close();
|
||||
reader.close();
|
||||
writer.close();
|
||||
if (recvThread.joinable()) {
|
||||
recvThread.join();
|
||||
}
|
||||
if (dispatchThread.joinable()) {
|
||||
dispatchThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
using Payload = std::function<void()>;
|
||||
|
||||
class EventHandlers {
|
||||
public:
|
||||
void put(const ErrorHandler& handler) {
|
||||
std::unique_lock<std::mutex> lock(errorMutex);
|
||||
errorHandler = handler;
|
||||
}
|
||||
|
||||
void error(const char* format, ...) {
|
||||
va_list vararg;
|
||||
va_start(vararg, format);
|
||||
std::unique_lock<std::mutex> lock(errorMutex);
|
||||
errorLocked(format, vararg);
|
||||
va_end(vararg);
|
||||
}
|
||||
|
||||
std::pair<const dap::TypeInfo*, GenericRequestHandler> request(
|
||||
const std::string& name) {
|
||||
std::unique_lock<std::mutex> lock(requestMutex);
|
||||
auto it = requestMap.find(name);
|
||||
return (it != requestMap.end()) ? it->second : decltype(it->second){};
|
||||
}
|
||||
|
||||
void put(const dap::TypeInfo* typeinfo,
|
||||
const GenericRequestHandler& handler) {
|
||||
std::unique_lock<std::mutex> lock(requestMutex);
|
||||
auto added =
|
||||
requestMap
|
||||
.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
|
||||
.second;
|
||||
if (!added) {
|
||||
errorfLocked("Request handler for '%s' already registered",
|
||||
typeinfo->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<const dap::TypeInfo*, GenericResponseHandler> response(
|
||||
int64_t seq) {
|
||||
std::unique_lock<std::mutex> lock(responseMutex);
|
||||
auto responseIt = responseMap.find(seq);
|
||||
if (responseIt == responseMap.end()) {
|
||||
errorfLocked("Unknown response with sequence %d", seq);
|
||||
return {};
|
||||
}
|
||||
auto out = std::move(responseIt->second);
|
||||
responseMap.erase(seq);
|
||||
return out;
|
||||
}
|
||||
|
||||
void put(int seq,
|
||||
const dap::TypeInfo* typeinfo,
|
||||
const GenericResponseHandler& handler) {
|
||||
std::unique_lock<std::mutex> lock(responseMutex);
|
||||
auto added =
|
||||
responseMap.emplace(seq, std::make_pair(typeinfo, handler)).second;
|
||||
if (!added) {
|
||||
errorfLocked("Response handler for sequence %d already registered",
|
||||
seq);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<const dap::TypeInfo*, GenericEventHandler> event(
|
||||
const std::string& name) {
|
||||
std::unique_lock<std::mutex> lock(eventMutex);
|
||||
auto it = eventMap.find(name);
|
||||
return (it != eventMap.end()) ? it->second : decltype(it->second){};
|
||||
}
|
||||
|
||||
void put(const dap::TypeInfo* typeinfo,
|
||||
const GenericEventHandler& handler) {
|
||||
std::unique_lock<std::mutex> lock(eventMutex);
|
||||
auto added =
|
||||
eventMap.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
|
||||
.second;
|
||||
if (!added) {
|
||||
errorfLocked("Event handler for '%s' already registered",
|
||||
typeinfo->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
GenericResponseSentHandler responseSent(const dap::TypeInfo* typeinfo) {
|
||||
std::unique_lock<std::mutex> lock(responseSentMutex);
|
||||
auto it = responseSentMap.find(typeinfo);
|
||||
return (it != responseSentMap.end()) ? it->second
|
||||
: decltype(it->second){};
|
||||
}
|
||||
|
||||
void put(const dap::TypeInfo* typeinfo,
|
||||
const GenericResponseSentHandler& handler) {
|
||||
std::unique_lock<std::mutex> lock(responseSentMutex);
|
||||
auto added = responseSentMap.emplace(typeinfo, handler).second;
|
||||
if (!added) {
|
||||
errorfLocked("Response sent handler for '%s' already registered",
|
||||
typeinfo->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void errorfLocked(const char* format, ...) {
|
||||
va_list vararg;
|
||||
va_start(vararg, format);
|
||||
errorLocked(format, vararg);
|
||||
va_end(vararg);
|
||||
}
|
||||
|
||||
void errorLocked(const char* format, va_list args) {
|
||||
char buf[2048];
|
||||
vsnprintf(buf, sizeof(buf), format, args);
|
||||
if (errorHandler) {
|
||||
errorHandler(buf);
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex errorMutex;
|
||||
ErrorHandler errorHandler;
|
||||
|
||||
std::mutex requestMutex;
|
||||
std::unordered_map<std::string,
|
||||
std::pair<const dap::TypeInfo*, GenericRequestHandler>>
|
||||
requestMap;
|
||||
|
||||
std::mutex responseMutex;
|
||||
std::unordered_map<int64_t,
|
||||
std::pair<const dap::TypeInfo*, GenericResponseHandler>>
|
||||
responseMap;
|
||||
|
||||
std::mutex eventMutex;
|
||||
std::unordered_map<std::string,
|
||||
std::pair<const dap::TypeInfo*, GenericEventHandler>>
|
||||
eventMap;
|
||||
|
||||
std::mutex responseSentMutex;
|
||||
std::unordered_map<const dap::TypeInfo*, GenericResponseSentHandler>
|
||||
responseSentMap;
|
||||
}; // EventHandlers
|
||||
|
||||
Payload processMessage(const std::string& str) {
|
||||
auto d = dap::json::Deserializer(str);
|
||||
dap::string type;
|
||||
if (!d.field("type", &type)) {
|
||||
handlers.error("Message missing string 'type' field");
|
||||
return {};
|
||||
}
|
||||
|
||||
dap::integer sequence = 0;
|
||||
if (!d.field("seq", &sequence)) {
|
||||
handlers.error("Message missing number 'seq' field");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (type == "request") {
|
||||
return processRequest(&d, sequence);
|
||||
} else if (type == "event") {
|
||||
return processEvent(&d);
|
||||
} else if (type == "response") {
|
||||
processResponse(&d);
|
||||
return {};
|
||||
} else {
|
||||
handlers.error("Unknown message type '%s'", type.c_str());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Payload processRequest(dap::json::Deserializer* d, dap::integer sequence) {
|
||||
dap::string command;
|
||||
if (!d->field("command", &command)) {
|
||||
handlers.error("Request missing string 'command' field");
|
||||
return {};
|
||||
}
|
||||
|
||||
const dap::TypeInfo* typeinfo;
|
||||
GenericRequestHandler handler;
|
||||
std::tie(typeinfo, handler) = handlers.request(command);
|
||||
if (!typeinfo) {
|
||||
handlers.error("No request handler registered for command '%s'",
|
||||
command.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto data = new uint8_t[typeinfo->size()];
|
||||
typeinfo->construct(data);
|
||||
|
||||
if (!d->field("arguments", [&](dap::Deserializer* d) {
|
||||
return typeinfo->deserialize(d, data);
|
||||
})) {
|
||||
handlers.error("Failed to deserialize request");
|
||||
typeinfo->destruct(data);
|
||||
delete[] data;
|
||||
return {};
|
||||
}
|
||||
|
||||
return [=] {
|
||||
handler(
|
||||
data,
|
||||
[=](const dap::TypeInfo* typeinfo, const void* data) {
|
||||
// onSuccess
|
||||
dap::json::Serializer s;
|
||||
s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "response") &&
|
||||
fs->field("request_seq", sequence) &&
|
||||
fs->field("success", dap::boolean(true)) &&
|
||||
fs->field("command", command) &&
|
||||
fs->field("body", [&](dap::Serializer* s) {
|
||||
return typeinfo->serialize(s, data);
|
||||
});
|
||||
});
|
||||
send(s.dump());
|
||||
|
||||
if (auto handler = handlers.responseSent(typeinfo)) {
|
||||
handler(data, nullptr);
|
||||
}
|
||||
},
|
||||
[=](const dap::TypeInfo* typeinfo, const dap::Error& error) {
|
||||
// onError
|
||||
dap::json::Serializer s;
|
||||
s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "response") &&
|
||||
fs->field("request_seq", sequence) &&
|
||||
fs->field("success", dap::boolean(false)) &&
|
||||
fs->field("command", command) &&
|
||||
fs->field("message", error.message);
|
||||
});
|
||||
send(s.dump());
|
||||
|
||||
if (auto handler = handlers.responseSent(typeinfo)) {
|
||||
handler(nullptr, &error);
|
||||
}
|
||||
});
|
||||
typeinfo->destruct(data);
|
||||
delete[] data;
|
||||
};
|
||||
}
|
||||
|
||||
Payload processEvent(dap::json::Deserializer* d) {
|
||||
dap::string event;
|
||||
if (!d->field("event", &event)) {
|
||||
handlers.error("Event missing string 'event' field");
|
||||
return {};
|
||||
}
|
||||
|
||||
const dap::TypeInfo* typeinfo;
|
||||
GenericEventHandler handler;
|
||||
std::tie(typeinfo, handler) = handlers.event(event);
|
||||
if (!typeinfo) {
|
||||
handlers.error("No event handler registered for event '%s'",
|
||||
event.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
auto data = new uint8_t[typeinfo->size()];
|
||||
typeinfo->construct(data);
|
||||
|
||||
// "body" is an optional field for some events, such as "Terminated Event".
|
||||
bool body_ok = true;
|
||||
d->field("body", [&](dap::Deserializer* d) {
|
||||
if (!typeinfo->deserialize(d, data)) {
|
||||
body_ok = false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!body_ok) {
|
||||
handlers.error("Failed to deserialize event '%s' body", event.c_str());
|
||||
typeinfo->destruct(data);
|
||||
delete[] data;
|
||||
return {};
|
||||
}
|
||||
|
||||
return [=] {
|
||||
handler(data);
|
||||
typeinfo->destruct(data);
|
||||
delete[] data;
|
||||
};
|
||||
}
|
||||
|
||||
void processResponse(const dap::Deserializer* d) {
|
||||
dap::integer requestSeq = 0;
|
||||
if (!d->field("request_seq", &requestSeq)) {
|
||||
handlers.error("Response missing int 'request_seq' field");
|
||||
return;
|
||||
}
|
||||
|
||||
const dap::TypeInfo* typeinfo;
|
||||
GenericResponseHandler handler;
|
||||
std::tie(typeinfo, handler) = handlers.response(requestSeq);
|
||||
if (!typeinfo) {
|
||||
handlers.error("Unknown response with sequence %d", requestSeq);
|
||||
return;
|
||||
}
|
||||
|
||||
dap::boolean success = false;
|
||||
if (!d->field("success", &success)) {
|
||||
handlers.error("Response missing int 'success' field");
|
||||
return;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
auto data = std::unique_ptr<uint8_t[]>(new uint8_t[typeinfo->size()]);
|
||||
typeinfo->construct(data.get());
|
||||
|
||||
// "body" field in Response is an optional field.
|
||||
d->field("body", [&](const dap::Deserializer* d) {
|
||||
return typeinfo->deserialize(d, data.get());
|
||||
});
|
||||
|
||||
handler(data.get(), nullptr);
|
||||
typeinfo->destruct(data.get());
|
||||
} else {
|
||||
std::string message;
|
||||
if (!d->field("message", &message)) {
|
||||
handlers.error("Failed to deserialize message");
|
||||
return;
|
||||
}
|
||||
auto error = dap::Error("%s", message.c_str());
|
||||
handler(nullptr, &error);
|
||||
}
|
||||
}
|
||||
|
||||
bool send(const std::string& s) {
|
||||
std::unique_lock<std::mutex> lock(sendMutex);
|
||||
if (!writer.isOpen()) {
|
||||
handlers.error("Send failed as the writer is closed");
|
||||
return false;
|
||||
}
|
||||
return writer.write(s);
|
||||
}
|
||||
|
||||
std::atomic<bool> isBound = {false};
|
||||
std::atomic<bool> isProcessingMessages = {false};
|
||||
dap::ContentReader reader;
|
||||
dap::ContentWriter writer;
|
||||
|
||||
std::atomic<bool> shutdown = {false};
|
||||
EventHandlers handlers;
|
||||
std::thread recvThread;
|
||||
std::thread dispatchThread;
|
||||
dap::Chan<Payload> inbox;
|
||||
std::atomic<uint32_t> nextSeq = {1};
|
||||
std::mutex sendMutex;
|
||||
dap::OnInvalidData onInvalidData = dap::kIgnore;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace dap {
|
||||
|
||||
Error::Error(const std::string& message) : message(message) {}
|
||||
|
||||
Error::Error(const char* msg, ...) {
|
||||
char buf[2048];
|
||||
va_list vararg;
|
||||
va_start(vararg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, vararg);
|
||||
va_end(vararg);
|
||||
message = buf;
|
||||
}
|
||||
|
||||
Session::~Session() = default;
|
||||
|
||||
std::unique_ptr<Session> Session::create() {
|
||||
return std::unique_ptr<Session>(new Impl());
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
625
libraries/cppdap/src/session_test.cpp
Normal file
625
libraries/cppdap/src/session_test.cpp
Normal file
|
|
@ -0,0 +1,625 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/session.h"
|
||||
#include "dap/io.h"
|
||||
#include "dap/protocol.h"
|
||||
|
||||
#include "chan.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct TestResponse : public Response {
|
||||
boolean b;
|
||||
integer i;
|
||||
number n;
|
||||
array<integer> a;
|
||||
object o;
|
||||
string s;
|
||||
optional<integer> o1;
|
||||
optional<integer> o2;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(TestResponse,
|
||||
"test-response",
|
||||
DAP_FIELD(b, "res_b"),
|
||||
DAP_FIELD(i, "res_i"),
|
||||
DAP_FIELD(n, "res_n"),
|
||||
DAP_FIELD(a, "res_a"),
|
||||
DAP_FIELD(o, "res_o"),
|
||||
DAP_FIELD(s, "res_s"),
|
||||
DAP_FIELD(o1, "res_o1"),
|
||||
DAP_FIELD(o2, "res_o2"));
|
||||
|
||||
struct TestRequest : public Request {
|
||||
using Response = TestResponse;
|
||||
|
||||
boolean b;
|
||||
integer i;
|
||||
number n;
|
||||
array<integer> a;
|
||||
object o;
|
||||
string s;
|
||||
optional<integer> o1;
|
||||
optional<integer> o2;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(TestRequest,
|
||||
"test-request",
|
||||
DAP_FIELD(b, "req_b"),
|
||||
DAP_FIELD(i, "req_i"),
|
||||
DAP_FIELD(n, "req_n"),
|
||||
DAP_FIELD(a, "req_a"),
|
||||
DAP_FIELD(o, "req_o"),
|
||||
DAP_FIELD(s, "req_s"),
|
||||
DAP_FIELD(o1, "req_o1"),
|
||||
DAP_FIELD(o2, "req_o2"));
|
||||
|
||||
struct TestEvent : public Event {
|
||||
boolean b;
|
||||
integer i;
|
||||
number n;
|
||||
array<integer> a;
|
||||
object o;
|
||||
string s;
|
||||
optional<integer> o1;
|
||||
optional<integer> o2;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(TestEvent,
|
||||
"test-event",
|
||||
DAP_FIELD(b, "evt_b"),
|
||||
DAP_FIELD(i, "evt_i"),
|
||||
DAP_FIELD(n, "evt_n"),
|
||||
DAP_FIELD(a, "evt_a"),
|
||||
DAP_FIELD(o, "evt_o"),
|
||||
DAP_FIELD(s, "evt_s"),
|
||||
DAP_FIELD(o1, "evt_o1"),
|
||||
DAP_FIELD(o2, "evt_o2"));
|
||||
|
||||
}; // namespace dap
|
||||
|
||||
namespace {
|
||||
|
||||
dap::TestRequest createRequest() {
|
||||
dap::TestRequest request;
|
||||
request.b = false;
|
||||
request.i = 72;
|
||||
request.n = 9.87;
|
||||
request.a = {2, 5, 7, 8};
|
||||
request.o = {
|
||||
std::make_pair("a", dap::integer(1)),
|
||||
std::make_pair("b", dap::number(2)),
|
||||
std::make_pair("c", dap::string("3")),
|
||||
};
|
||||
request.s = "request";
|
||||
request.o2 = 42;
|
||||
return request;
|
||||
}
|
||||
|
||||
dap::TestResponse createResponse() {
|
||||
dap::TestResponse response;
|
||||
response.b = true;
|
||||
response.i = 99;
|
||||
response.n = 123.456;
|
||||
response.a = {5, 4, 3, 2, 1};
|
||||
response.o = {
|
||||
std::make_pair("one", dap::integer(1)),
|
||||
std::make_pair("two", dap::number(2)),
|
||||
std::make_pair("three", dap::string("3")),
|
||||
};
|
||||
response.s = "ROGER";
|
||||
response.o1 = 50;
|
||||
return response;
|
||||
}
|
||||
|
||||
dap::TestEvent createEvent() {
|
||||
dap::TestEvent event;
|
||||
event.b = false;
|
||||
event.i = 72;
|
||||
event.n = 9.87;
|
||||
event.a = {2, 5, 7, 8};
|
||||
event.o = {
|
||||
std::make_pair("a", dap::integer(1)),
|
||||
std::make_pair("b", dap::number(2)),
|
||||
std::make_pair("c", dap::string("3")),
|
||||
};
|
||||
event.s = "event";
|
||||
event.o2 = 42;
|
||||
return event;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class SessionTest : public testing::Test {
|
||||
public:
|
||||
void bind() {
|
||||
auto client2server = dap::pipe();
|
||||
auto server2client = dap::pipe();
|
||||
client->bind(server2client, client2server);
|
||||
server->bind(client2server, server2client);
|
||||
}
|
||||
|
||||
std::unique_ptr<dap::Session> client = dap::Session::create();
|
||||
std::unique_ptr<dap::Session> server = dap::Session::create();
|
||||
};
|
||||
|
||||
TEST_F(SessionTest, Request) {
|
||||
dap::TestRequest received;
|
||||
server->registerHandler([&](const dap::TestRequest& req) {
|
||||
received = req;
|
||||
return createResponse();
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto request = createRequest();
|
||||
client->send(request).get();
|
||||
|
||||
// Check request was received correctly.
|
||||
ASSERT_EQ(received.b, request.b);
|
||||
ASSERT_EQ(received.i, request.i);
|
||||
ASSERT_EQ(received.n, request.n);
|
||||
ASSERT_EQ(received.a, request.a);
|
||||
ASSERT_EQ(received.o.size(), 3U);
|
||||
ASSERT_EQ(received.o["a"].get<dap::integer>(),
|
||||
request.o["a"].get<dap::integer>());
|
||||
ASSERT_EQ(received.o["b"].get<dap::number>(),
|
||||
request.o["b"].get<dap::number>());
|
||||
ASSERT_EQ(received.o["c"].get<dap::string>(),
|
||||
request.o["c"].get<dap::string>());
|
||||
ASSERT_EQ(received.s, request.s);
|
||||
ASSERT_EQ(received.o1, request.o1);
|
||||
ASSERT_EQ(received.o2, request.o2);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestResponseSuccess) {
|
||||
server->registerHandler(
|
||||
[&](const dap::TestRequest&) { return createResponse(); });
|
||||
|
||||
bind();
|
||||
|
||||
auto request = createRequest();
|
||||
auto response = client->send(request);
|
||||
|
||||
auto got = response.get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.b, dap::boolean(true));
|
||||
ASSERT_EQ(got.response.i, dap::integer(99));
|
||||
ASSERT_EQ(got.response.n, dap::number(123.456));
|
||||
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
|
||||
ASSERT_EQ(got.response.o.size(), 3U);
|
||||
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
|
||||
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
|
||||
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
|
||||
ASSERT_EQ(got.response.s, "ROGER");
|
||||
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
|
||||
ASSERT_FALSE(got.response.o2.has_value());
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, BreakPointRequestResponseSuccess) {
|
||||
server->registerHandler([&](const dap::SetBreakpointsRequest&) {
|
||||
dap::SetBreakpointsResponse response;
|
||||
dap::Breakpoint bp;
|
||||
bp.line = 2;
|
||||
response.breakpoints.emplace_back(std::move(bp));
|
||||
return response;
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto got = client->send(dap::SetBreakpointsRequest{}).get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.breakpoints.size(), 1U);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestResponseOrError) {
|
||||
server->registerHandler(
|
||||
[&](const dap::TestRequest&) -> dap::ResponseOrError<dap::TestResponse> {
|
||||
return dap::Error("Oh noes!");
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto response = client->send(createRequest());
|
||||
|
||||
auto got = response.get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, true);
|
||||
ASSERT_EQ(got.error.message, "Oh noes!");
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestResponseError) {
|
||||
server->registerHandler(
|
||||
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
|
||||
|
||||
bind();
|
||||
|
||||
auto response = client->send(createRequest());
|
||||
|
||||
auto got = response.get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, true);
|
||||
ASSERT_EQ(got.error.message, "Oh noes!");
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestCallbackResponse) {
|
||||
using ResponseCallback = std::function<void(dap::SetBreakpointsResponse)>;
|
||||
|
||||
server->registerHandler(
|
||||
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
|
||||
dap::SetBreakpointsResponse response;
|
||||
dap::Breakpoint bp;
|
||||
bp.line = 2;
|
||||
response.breakpoints.emplace_back(std::move(bp));
|
||||
callback(response);
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto got = client->send(dap::SetBreakpointsRequest{}).get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.breakpoints.size(), 1U);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestCallbackResponseOrError) {
|
||||
using ResponseCallback =
|
||||
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
|
||||
|
||||
server->registerHandler(
|
||||
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
|
||||
dap::SetBreakpointsResponse response;
|
||||
dap::Breakpoint bp;
|
||||
bp.line = 2;
|
||||
response.breakpoints.emplace_back(std::move(bp));
|
||||
callback(response);
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto got = client->send(dap::SetBreakpointsRequest{}).get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.breakpoints.size(), 1U);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestCallbackError) {
|
||||
using ResponseCallback =
|
||||
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
|
||||
|
||||
server->registerHandler(
|
||||
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
|
||||
callback(dap::Error("Oh noes!"));
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto got = client->send(dap::SetBreakpointsRequest{}).get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, true);
|
||||
ASSERT_EQ(got.error.message, "Oh noes!");
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestCallbackSuccessAfterReturn) {
|
||||
using ResponseCallback =
|
||||
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
|
||||
|
||||
ResponseCallback callback;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
server->registerHandler(
|
||||
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
callback = cb;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto future = client->send(dap::SetBreakpointsRequest{});
|
||||
|
||||
{
|
||||
dap::SetBreakpointsResponse response;
|
||||
dap::Breakpoint bp;
|
||||
bp.line = 2;
|
||||
response.breakpoints.emplace_back(std::move(bp));
|
||||
|
||||
// Wait for the handler to be called.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return static_cast<bool>(callback); });
|
||||
|
||||
// Issue the callback
|
||||
callback(response);
|
||||
}
|
||||
|
||||
auto got = future.get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.breakpoints.size(), 1U);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RequestCallbackErrorAfterReturn) {
|
||||
using ResponseCallback =
|
||||
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
|
||||
|
||||
ResponseCallback callback;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
server->registerHandler(
|
||||
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
callback = cb;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
auto future = client->send(dap::SetBreakpointsRequest{});
|
||||
|
||||
{
|
||||
// Wait for the handler to be called.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return static_cast<bool>(callback); });
|
||||
|
||||
// Issue the callback
|
||||
callback(dap::Error("Oh noes!"));
|
||||
}
|
||||
|
||||
auto got = future.get();
|
||||
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, true);
|
||||
ASSERT_EQ(got.error.message, "Oh noes!");
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, ResponseSentHandlerSuccess) {
|
||||
const auto response = createResponse();
|
||||
|
||||
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
|
||||
server->registerHandler([&](const dap::TestRequest&) { return response; });
|
||||
server->registerSentHandler(
|
||||
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
|
||||
|
||||
bind();
|
||||
|
||||
client->send(createRequest());
|
||||
|
||||
auto got = chan.take().value();
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.b, dap::boolean(true));
|
||||
ASSERT_EQ(got.response.i, dap::integer(99));
|
||||
ASSERT_EQ(got.response.n, dap::number(123.456));
|
||||
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
|
||||
ASSERT_EQ(got.response.o.size(), 3U);
|
||||
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
|
||||
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
|
||||
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
|
||||
ASSERT_EQ(got.response.s, "ROGER");
|
||||
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
|
||||
ASSERT_FALSE(got.response.o2.has_value());
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, ResponseSentHandlerError) {
|
||||
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
|
||||
server->registerHandler(
|
||||
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
|
||||
server->registerSentHandler(
|
||||
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
|
||||
|
||||
bind();
|
||||
|
||||
client->send(createRequest());
|
||||
|
||||
auto got = chan.take().value();
|
||||
ASSERT_EQ(got.error, true);
|
||||
ASSERT_EQ(got.error.message, "Oh noes!");
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, Event) {
|
||||
dap::Chan<dap::TestEvent> received;
|
||||
server->registerHandler([&](const dap::TestEvent& e) { received.put(e); });
|
||||
|
||||
bind();
|
||||
|
||||
auto event = createEvent();
|
||||
client->send(event);
|
||||
|
||||
// Check event was received correctly.
|
||||
auto got = received.take().value();
|
||||
|
||||
ASSERT_EQ(got.b, event.b);
|
||||
ASSERT_EQ(got.i, event.i);
|
||||
ASSERT_EQ(got.n, event.n);
|
||||
ASSERT_EQ(got.a, event.a);
|
||||
ASSERT_EQ(got.o.size(), 3U);
|
||||
ASSERT_EQ(got.o["a"].get<dap::integer>(), event.o["a"].get<dap::integer>());
|
||||
ASSERT_EQ(got.o["b"].get<dap::number>(), event.o["b"].get<dap::number>());
|
||||
ASSERT_EQ(got.o["c"].get<dap::string>(), event.o["c"].get<dap::string>());
|
||||
ASSERT_EQ(got.s, event.s);
|
||||
ASSERT_EQ(got.o1, event.o1);
|
||||
ASSERT_EQ(got.o2, event.o2);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, RegisterHandlerFunction) {
|
||||
struct S {
|
||||
static dap::TestResponse requestA(const dap::TestRequest&) { return {}; }
|
||||
static dap::Error requestB(const dap::TestRequest&) { return {}; }
|
||||
static dap::ResponseOrError<dap::TestResponse> requestC(
|
||||
const dap::TestRequest&) {
|
||||
return dap::Error();
|
||||
}
|
||||
static void event(const dap::TestEvent&) {}
|
||||
static void sent(const dap::ResponseOrError<dap::TestResponse>&) {}
|
||||
};
|
||||
client->registerHandler(&S::requestA);
|
||||
client->registerHandler(&S::requestB);
|
||||
client->registerHandler(&S::requestC);
|
||||
client->registerHandler(&S::event);
|
||||
client->registerSentHandler(&S::sent);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, SendRequestNoBind) {
|
||||
bool errored = false;
|
||||
client->onError([&](const std::string&) { errored = true; });
|
||||
auto res = client->send(createRequest()).get();
|
||||
ASSERT_TRUE(errored);
|
||||
ASSERT_TRUE(res.error);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, SendEventNoBind) {
|
||||
bool errored = false;
|
||||
client->onError([&](const std::string&) { errored = true; });
|
||||
client->send(createEvent());
|
||||
ASSERT_TRUE(errored);
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, SingleThread) {
|
||||
server->registerHandler(
|
||||
[&](const dap::TestRequest&) { return createResponse(); });
|
||||
|
||||
// Explicitly connect and process request on this test thread instead of
|
||||
// calling bind() which inturn starts processing messages immediately on a new
|
||||
// thread.
|
||||
auto client2server = dap::pipe();
|
||||
auto server2client = dap::pipe();
|
||||
client->connect(server2client, client2server);
|
||||
server->connect(client2server, server2client);
|
||||
|
||||
auto request = createRequest();
|
||||
auto response = client->send(request);
|
||||
|
||||
// Process request and response on this thread
|
||||
if (auto payload = server->getPayload()) {
|
||||
payload();
|
||||
}
|
||||
if (auto payload = client->getPayload()) {
|
||||
payload();
|
||||
}
|
||||
|
||||
auto got = response.get();
|
||||
// Check response was received correctly.
|
||||
ASSERT_EQ(got.error, false);
|
||||
ASSERT_EQ(got.response.b, dap::boolean(true));
|
||||
ASSERT_EQ(got.response.i, dap::integer(99));
|
||||
ASSERT_EQ(got.response.n, dap::number(123.456));
|
||||
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
|
||||
ASSERT_EQ(got.response.o.size(), 3U);
|
||||
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
|
||||
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
|
||||
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
|
||||
ASSERT_EQ(got.response.s, "ROGER");
|
||||
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
|
||||
ASSERT_FALSE(got.response.o2.has_value());
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, Concurrency) {
|
||||
std::atomic<int> numEventsHandled = {0};
|
||||
std::atomic<bool> done = {false};
|
||||
|
||||
server->registerHandler(
|
||||
[](const dap::TestRequest&) { return dap::TestResponse(); });
|
||||
|
||||
server->registerHandler([&](const dap::TestEvent&) {
|
||||
if (numEventsHandled++ > 10000) {
|
||||
done = true;
|
||||
}
|
||||
});
|
||||
|
||||
bind();
|
||||
|
||||
constexpr int numThreads = 32;
|
||||
std::array<std::thread, numThreads> threads;
|
||||
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
threads[i] = std::thread([&] {
|
||||
while (!done) {
|
||||
client->send(createEvent());
|
||||
client->send(createRequest());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
|
||||
client.reset();
|
||||
server.reset();
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, OnClientClosed) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool clientClosed = false;
|
||||
|
||||
auto client2server = dap::pipe();
|
||||
auto server2client = dap::pipe();
|
||||
|
||||
client->bind(server2client, client2server);
|
||||
server->bind(client2server, server2client, [&] {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
clientClosed = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
client.reset();
|
||||
|
||||
// Wait for the client closed handler to be called.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return static_cast<bool>(clientClosed); });
|
||||
}
|
||||
|
||||
TEST_F(SessionTest, OnServerClosed) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool serverClosed = false;
|
||||
|
||||
auto client2server = dap::pipe();
|
||||
auto server2client = dap::pipe();
|
||||
|
||||
client->bind(server2client, client2server, [&] {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
serverClosed = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
server->bind(client2server, server2client);
|
||||
|
||||
server.reset();
|
||||
|
||||
// Wait for the client closed handler to be called.
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return static_cast<bool>(serverClosed); });
|
||||
}
|
||||
337
libraries/cppdap/src/socket.cpp
Normal file
337
libraries/cppdap/src/socket.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include "rwmutex.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <atomic>
|
||||
namespace {
|
||||
std::atomic<int> wsaInitCount = {0};
|
||||
} // anonymous namespace
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
namespace {
|
||||
using SOCKET = int;
|
||||
} // anonymous namespace
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
constexpr SOCKET InvalidSocket = static_cast<SOCKET>(-1);
|
||||
void init() {
|
||||
#if defined(_WIN32)
|
||||
if (wsaInitCount++ == 0) {
|
||||
WSADATA winsockData;
|
||||
(void)WSAStartup(MAKEWORD(2, 2), &winsockData);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void term() {
|
||||
#if defined(_WIN32)
|
||||
if (--wsaInitCount == 0) {
|
||||
WSACleanup();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool setBlocking(SOCKET s, bool blocking) {
|
||||
#if defined(_WIN32)
|
||||
u_long mode = blocking ? 0 : 1;
|
||||
return ioctlsocket(s, FIONBIO, &mode) == NO_ERROR;
|
||||
#else
|
||||
auto arg = fcntl(s, F_GETFL, nullptr);
|
||||
if (arg < 0) {
|
||||
return false;
|
||||
}
|
||||
arg = blocking ? (arg & ~O_NONBLOCK) : (arg | O_NONBLOCK);
|
||||
return fcntl(s, F_SETFL, arg) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool errored(SOCKET s) {
|
||||
if (s == InvalidSocket) {
|
||||
return true;
|
||||
}
|
||||
char error = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
return error != 0;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class dap::Socket::Shared : public dap::ReaderWriter {
|
||||
public:
|
||||
static std::shared_ptr<Shared> create(const char* address, const char* port) {
|
||||
init();
|
||||
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
addrinfo* info = nullptr;
|
||||
getaddrinfo(address, port, &hints, &info);
|
||||
|
||||
if (info) {
|
||||
auto socket =
|
||||
::socket(info->ai_family, info->ai_socktype, info->ai_protocol);
|
||||
auto out = std::make_shared<Shared>(info, socket);
|
||||
out->setOptions();
|
||||
return out;
|
||||
}
|
||||
|
||||
term();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Shared(SOCKET socket) : info(nullptr), s(socket) {}
|
||||
Shared(addrinfo* info, SOCKET socket) : info(info), s(socket) {}
|
||||
|
||||
~Shared() {
|
||||
if (info) {
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
close();
|
||||
term();
|
||||
}
|
||||
|
||||
template <typename FUNCTION>
|
||||
void lock(FUNCTION&& f) {
|
||||
RLock l(mutex);
|
||||
f(s, info);
|
||||
}
|
||||
|
||||
void setOptions() {
|
||||
RLock l(mutex);
|
||||
if (s == InvalidSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
int enable = 1;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
// Prevent sockets lingering after process termination, causing
|
||||
// reconnection issues on the same port.
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable));
|
||||
|
||||
struct {
|
||||
int l_onoff; /* linger active */
|
||||
int l_linger; /* how many seconds to linger for */
|
||||
} linger = {false, 0};
|
||||
setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
|
||||
#endif // !defined(_WIN32)
|
||||
|
||||
// Enable TCP_NODELAY.
|
||||
// DAP usually consists of small packet requests, with small packet
|
||||
// responses. When there are many frequent, blocking requests made,
|
||||
// Nagle's algorithm can dramatically limit the request->response rates.
|
||||
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable));
|
||||
}
|
||||
|
||||
// dap::ReaderWriter compliance
|
||||
bool isOpen() {
|
||||
{
|
||||
RLock l(mutex);
|
||||
if ((s != InvalidSocket) && !errored(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
WLock lock(mutex);
|
||||
s = InvalidSocket;
|
||||
return false;
|
||||
}
|
||||
|
||||
void close() {
|
||||
{
|
||||
RLock l(mutex);
|
||||
if (s != InvalidSocket) {
|
||||
#if defined(_WIN32)
|
||||
closesocket(s);
|
||||
#elif __APPLE__
|
||||
// ::shutdown() *should* be sufficient to unblock ::accept(), but
|
||||
// apparently on macos it can return ENOTCONN and ::accept() continues
|
||||
// to block indefinitely.
|
||||
// Note: There is a race here. Calling ::close() frees the socket ID,
|
||||
// which may be reused before `s` is assigned InvalidSocket.
|
||||
::shutdown(s, SHUT_RDWR);
|
||||
::close(s);
|
||||
#else
|
||||
// ::shutdown() to unblock ::accept(). We'll actually close the socket
|
||||
// under lock below.
|
||||
::shutdown(s, SHUT_RDWR);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
WLock l(mutex);
|
||||
if (s != InvalidSocket) {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
::close(s);
|
||||
#endif
|
||||
s = InvalidSocket;
|
||||
}
|
||||
}
|
||||
|
||||
size_t read(void* buffer, size_t bytes) {
|
||||
RLock lock(mutex);
|
||||
if (s == InvalidSocket) {
|
||||
return 0;
|
||||
}
|
||||
auto len =
|
||||
recv(s, reinterpret_cast<char*>(buffer), static_cast<int>(bytes), 0);
|
||||
return (len < 0) ? 0 : len;
|
||||
}
|
||||
|
||||
bool write(const void* buffer, size_t bytes) {
|
||||
RLock lock(mutex);
|
||||
if (s == InvalidSocket) {
|
||||
return false;
|
||||
}
|
||||
if (bytes == 0) {
|
||||
return true;
|
||||
}
|
||||
return ::send(s, reinterpret_cast<const char*>(buffer),
|
||||
static_cast<int>(bytes), 0) > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
addrinfo* const info;
|
||||
SOCKET s = InvalidSocket;
|
||||
RWMutex mutex;
|
||||
};
|
||||
|
||||
namespace dap {
|
||||
|
||||
Socket::Socket(const char* address, const char* port)
|
||||
: shared(Shared::create(address, port)) {
|
||||
if (shared) {
|
||||
bool reset = false;
|
||||
shared->lock([&](SOCKET socket, const addrinfo* info) {
|
||||
if (bind(socket, info->ai_addr, (int)info->ai_addrlen) != 0) {
|
||||
reset = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(socket, 0) != 0) {
|
||||
reset = true;
|
||||
}
|
||||
});
|
||||
if (reset) {
|
||||
shared.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> Socket::accept() const {
|
||||
std::shared_ptr<Shared> out;
|
||||
if (shared) {
|
||||
shared->lock([&](SOCKET socket, const addrinfo*) {
|
||||
if (socket != InvalidSocket && !errored(socket)) {
|
||||
init();
|
||||
auto accepted = ::accept(socket, 0, 0);
|
||||
if (accepted != InvalidSocket) {
|
||||
out = std::make_shared<Shared>(accepted);
|
||||
out->setOptions();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Socket::isOpen() const {
|
||||
if (shared) {
|
||||
return shared->isOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Socket::close() const {
|
||||
if (shared) {
|
||||
shared->close();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
|
||||
const char* port,
|
||||
uint32_t timeoutMillis) {
|
||||
auto shared = Shared::create(address, port);
|
||||
if (!shared) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<ReaderWriter> out;
|
||||
shared->lock([&](SOCKET socket, const addrinfo* info) {
|
||||
if (socket == InvalidSocket) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeoutMillis == 0) {
|
||||
if (::connect(socket, info->ai_addr, (int)info->ai_addrlen) == 0) {
|
||||
out = shared;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!setBlocking(socket, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = ::connect(socket, info->ai_addr, (int)info->ai_addrlen);
|
||||
if (res == 0) {
|
||||
if (setBlocking(socket, true)) {
|
||||
out = shared;
|
||||
}
|
||||
} else {
|
||||
const auto microseconds = timeoutMillis * 1000;
|
||||
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(socket, &fdset);
|
||||
|
||||
timeval tv;
|
||||
tv.tv_sec = microseconds / 1000000;
|
||||
tv.tv_usec = microseconds - static_cast<uint32_t>(tv.tv_sec * 1000000);
|
||||
res = select(static_cast<int>(socket + 1), nullptr, &fdset, nullptr, &tv);
|
||||
if (res > 0 && !errored(socket) && setBlocking(socket, true)) {
|
||||
out = shared;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!out) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return out->isOpen() ? out : nullptr;
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
47
libraries/cppdap/src/socket.h
Normal file
47
libraries/cppdap/src/socket.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_socket_h
|
||||
#define dap_socket_h
|
||||
|
||||
#include "dap/io.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
namespace dap {
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
class Shared;
|
||||
|
||||
// connect() connects to the given TCP address and port.
|
||||
// If timeoutMillis is non-zero and no connection was made before
|
||||
// timeoutMillis milliseconds, then nullptr is returned.
|
||||
static std::shared_ptr<ReaderWriter> connect(const char* address,
|
||||
const char* port,
|
||||
uint32_t timeoutMillis);
|
||||
|
||||
Socket(const char* address, const char* port);
|
||||
bool isOpen() const;
|
||||
std::shared_ptr<ReaderWriter> accept() const;
|
||||
void close() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Shared> shared;
|
||||
};
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_socket_h
|
||||
104
libraries/cppdap/src/socket_test.cpp
Normal file
104
libraries/cppdap/src/socket_test.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
// Basic socket send & receive test
|
||||
TEST(Socket, SendRecv) {
|
||||
const char* port = "19021";
|
||||
|
||||
auto server = dap::Socket("localhost", port);
|
||||
|
||||
auto client = dap::Socket::connect("localhost", port, 0);
|
||||
ASSERT_TRUE(client != nullptr);
|
||||
|
||||
const std::string expect = "Hello World!";
|
||||
std::string read;
|
||||
|
||||
auto thread = std::thread([&] {
|
||||
auto conn = server.accept();
|
||||
ASSERT_TRUE(conn != nullptr);
|
||||
char c;
|
||||
while (conn->read(&c, 1) != 0) {
|
||||
read += c;
|
||||
}
|
||||
});
|
||||
|
||||
ASSERT_TRUE(client->write(expect.data(), expect.size()));
|
||||
|
||||
client->close();
|
||||
thread.join();
|
||||
|
||||
ASSERT_EQ(read, expect);
|
||||
}
|
||||
|
||||
// See https://github.com/google/cppdap/issues/37
|
||||
TEST(Socket, CloseOnDifferentThread) {
|
||||
const char* port = "19021";
|
||||
|
||||
auto server = dap::Socket("localhost", port);
|
||||
|
||||
auto client = dap::Socket::connect("localhost", port, 0);
|
||||
ASSERT_TRUE(client != nullptr);
|
||||
|
||||
auto conn = server.accept();
|
||||
|
||||
auto thread = std::thread([&] {
|
||||
// Closing client on different thread should unblock client->read().
|
||||
client->close();
|
||||
});
|
||||
|
||||
char c;
|
||||
while (client->read(&c, 1) != 0) {
|
||||
}
|
||||
|
||||
thread.join();
|
||||
}
|
||||
|
||||
TEST(Socket, ConnectTimeout) {
|
||||
const char* port = "19021";
|
||||
const int timeoutMillis = 200;
|
||||
const int maxAttempts = 1024;
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
auto server = dap::Socket("localhost", port);
|
||||
|
||||
std::vector<std::shared_ptr<dap::ReaderWriter>> connections;
|
||||
|
||||
for (int i = 0; i < maxAttempts; i++) {
|
||||
auto start = system_clock::now();
|
||||
auto connection = dap::Socket::connect("localhost", port, timeoutMillis);
|
||||
auto end = system_clock::now();
|
||||
|
||||
if (!connection) {
|
||||
auto timeTakenMillis = duration_cast<milliseconds>(end - start).count();
|
||||
ASSERT_GE(timeTakenMillis + 20, // +20ms for a bit of timing wiggle room
|
||||
timeoutMillis);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep hold of the connections to saturate any incoming socket buffers.
|
||||
connections.emplace_back(std::move(connection));
|
||||
}
|
||||
|
||||
FAIL() << "Failed to test timeout after " << maxAttempts << " attempts";
|
||||
}
|
||||
95
libraries/cppdap/src/string_buffer.h
Normal file
95
libraries/cppdap/src/string_buffer.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_string_buffer_h
|
||||
#define dap_string_buffer_h
|
||||
|
||||
#include "dap/io.h"
|
||||
|
||||
#include <algorithm> // std::min
|
||||
#include <cstring> // memcpy
|
||||
#include <deque>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <string>
|
||||
|
||||
namespace dap {
|
||||
|
||||
class StringBuffer : public virtual Reader, public virtual Writer {
|
||||
public:
|
||||
static inline std::unique_ptr<StringBuffer> create();
|
||||
|
||||
inline bool write(const std::string& s);
|
||||
inline std::string string() const;
|
||||
|
||||
// Reader / Writer compilance
|
||||
inline bool isOpen() override;
|
||||
inline void close() override;
|
||||
inline size_t read(void* buffer, size_t bytes) override;
|
||||
inline bool write(const void* buffer, size_t bytes) override;
|
||||
|
||||
private:
|
||||
std::string str;
|
||||
std::deque<size_t> chunk_lengths;
|
||||
bool closed = false;
|
||||
};
|
||||
|
||||
bool StringBuffer::isOpen() {
|
||||
return !closed;
|
||||
}
|
||||
void StringBuffer::close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<StringBuffer> StringBuffer::create() {
|
||||
return std::unique_ptr<StringBuffer>(new StringBuffer());
|
||||
}
|
||||
|
||||
bool StringBuffer::write(const std::string& s) {
|
||||
return write(s.data(), s.size());
|
||||
}
|
||||
|
||||
std::string StringBuffer::string() const {
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t StringBuffer::read(void* buffer, size_t bytes) {
|
||||
if (closed || bytes == 0 || str.size() == 0 || chunk_lengths.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t& chunk_length = chunk_lengths.front();
|
||||
|
||||
auto len = std::min(bytes, chunk_length);
|
||||
memcpy(buffer, str.data(), len);
|
||||
str = std::string(str.begin() + len, str.end());
|
||||
if (bytes < chunk_length) {
|
||||
chunk_length -= bytes;
|
||||
} else {
|
||||
chunk_lengths.pop_front();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
bool StringBuffer::write(const void* buffer, size_t bytes) {
|
||||
if (closed) {
|
||||
return false;
|
||||
}
|
||||
auto chars = reinterpret_cast<const char*>(buffer);
|
||||
str.append(chars, chars + bytes);
|
||||
chunk_lengths.push_back(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_string_buffer_h
|
||||
387
libraries/cppdap/src/traits_test.cpp
Normal file
387
libraries/cppdap/src/traits_test.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/traits.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
namespace traits {
|
||||
|
||||
namespace {
|
||||
struct S {};
|
||||
struct E : S {};
|
||||
void F1(S) {}
|
||||
void F3(int, S, float) {}
|
||||
void E1(E) {}
|
||||
void E3(int, E, float) {}
|
||||
} // namespace
|
||||
|
||||
TEST(ParameterType, Function) {
|
||||
F1({}); // Avoid unused method warning
|
||||
F3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(std::is_same<ParameterType<decltype(&F1), 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&F3), 0>, int>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&F3), 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&F3), 2>, float>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(ParameterType, Method) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) {}
|
||||
void F3(int, S, float) {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
|
||||
"");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(ParameterType, ConstMethod) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) const {}
|
||||
void F3(int, S, float) const {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
|
||||
"");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(ParameterType, StaticMethod) {
|
||||
class C {
|
||||
public:
|
||||
static void F1(S) {}
|
||||
static void F3(int, S, float) {}
|
||||
};
|
||||
C::F1({}); // Avoid unused method warning
|
||||
C::F3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
|
||||
"");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(ParameterType, FunctionLike) {
|
||||
using F1 = std::function<void(S)>;
|
||||
using F3 = std::function<void(int, S, float)>;
|
||||
static_assert(std::is_same<ParameterType<F1, 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<F3, 0>, int>::value, "");
|
||||
static_assert(std::is_same<ParameterType<F3, 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<F3, 2>, float>::value, "");
|
||||
}
|
||||
|
||||
TEST(ParameterType, Lambda) {
|
||||
auto l1 = [](S) {};
|
||||
auto l3 = [](int, S, float) {};
|
||||
static_assert(std::is_same<ParameterType<decltype(l1), 0>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(l3), 0>, int>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(l3), 1>, S>::value, "");
|
||||
static_assert(std::is_same<ParameterType<decltype(l3), 2>, float>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, Function) {
|
||||
F1({}); // Avoid unused method warning
|
||||
F3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(HasSignature<decltype(&F1), decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&F1), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, Method) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) {}
|
||||
void F3(int, S, float) {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, ConstMethod) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) const {}
|
||||
void F3(int, S, float) const {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, StaticMethod) {
|
||||
class C {
|
||||
public:
|
||||
static void F1(S) {}
|
||||
static void F3(int, S, float) {}
|
||||
};
|
||||
C::F1({}); // Avoid unused method warning
|
||||
C::F3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, FunctionLike) {
|
||||
using f1 = std::function<void(S)>;
|
||||
using f3 = std::function<void(int, S, float)>;
|
||||
static_assert(HasSignature<f1, decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<f1, decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
TEST(HasSignature, Lambda) {
|
||||
auto l1 = [](S) {};
|
||||
auto l3 = [](int, S, float) {};
|
||||
static_assert(HasSignature<decltype(l1), decltype(&F1)>::value, "");
|
||||
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
|
||||
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(l1), decltype(&F3)>::value, "");
|
||||
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
|
||||
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
TEST(CompatibleWith, Function) {
|
||||
F1({}); // Avoid unused method warning
|
||||
F3(0, {}, 0); // Avoid unused method warning
|
||||
E1({}); // Avoid unused method warning
|
||||
E3(0, {}, 0); // Avoid unused method warning
|
||||
static_assert(CompatibleWith<decltype(&F1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&F1), decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<decltype(&E1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&F1), decltype(&E1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
|
||||
}
|
||||
|
||||
TEST(CompatibleWith, Method) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) {}
|
||||
void F3(int, S, float) {}
|
||||
void E1(E) {}
|
||||
void E3(int, E, float) {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
C().E1({}); // Avoid unused method warning
|
||||
C().E3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
}
|
||||
|
||||
TEST(CompatibleWith, ConstMethod) {
|
||||
class C {
|
||||
public:
|
||||
void F1(S) const {}
|
||||
void F3(int, S, float) const {}
|
||||
void E1(E) const {}
|
||||
void E3(int, E, float) const {}
|
||||
};
|
||||
C().F1({}); // Avoid unused method warning
|
||||
C().F3(0, {}, 0); // Avoid unused method warning
|
||||
C().E1({}); // Avoid unused method warning
|
||||
C().E3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
}
|
||||
|
||||
TEST(CompatibleWith, StaticMethod) {
|
||||
class C {
|
||||
public:
|
||||
static void F1(S) {}
|
||||
static void F3(int, S, float) {}
|
||||
static void E1(E) {}
|
||||
static void E3(int, E, float) {}
|
||||
};
|
||||
C::F1({}); // Avoid unused method warning
|
||||
C::F3(0, {}, 0); // Avoid unused method warning
|
||||
C::E1({}); // Avoid unused method warning
|
||||
C::E3(0, {}, 0); // Avoid unused method warning
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
|
||||
}
|
||||
|
||||
TEST(CompatibleWith, FunctionLike) {
|
||||
using f1 = std::function<void(S)>;
|
||||
using f3 = std::function<void(int, S, float)>;
|
||||
using e1 = std::function<void(E)>;
|
||||
using e3 = std::function<void(int, E, float)>;
|
||||
static_assert(CompatibleWith<f1, decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<f1, decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<e1, f1>::value, "");
|
||||
static_assert(CompatibleWith<e3, f3>::value, "");
|
||||
static_assert(CompatibleWith<e3, f3>::value, "");
|
||||
static_assert(CompatibleWith<e3, f3>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<f1, e1>::value, "");
|
||||
static_assert(!CompatibleWith<f3, e3>::value, "");
|
||||
static_assert(!CompatibleWith<f3, e3>::value, "");
|
||||
static_assert(!CompatibleWith<f3, e3>::value, "");
|
||||
}
|
||||
|
||||
TEST(CompatibleWith, Lambda) {
|
||||
auto f1 = [](S) {};
|
||||
auto f3 = [](int, S, float) {};
|
||||
auto e1 = [](E) {};
|
||||
auto e3 = [](int, E, float) {};
|
||||
static_assert(CompatibleWith<decltype(f1), decltype(&F1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(f1), decltype(&F3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
|
||||
|
||||
static_assert(CompatibleWith<decltype(e1), decltype(f1)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
|
||||
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
|
||||
|
||||
static_assert(!CompatibleWith<decltype(f1), decltype(e1)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
|
||||
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
|
||||
}
|
||||
|
||||
} // namespace traits
|
||||
} // namespace dap
|
||||
21
libraries/cppdap/src/typeinfo.cpp
Normal file
21
libraries/cppdap/src/typeinfo.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/typeinfo.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
TypeInfo::~TypeInfo() = default;
|
||||
|
||||
} // namespace dap
|
||||
65
libraries/cppdap/src/typeinfo_test.cpp
Normal file
65
libraries/cppdap/src/typeinfo_test.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/typeof.h"
|
||||
#include "dap/types.h"
|
||||
#include "json_serializer.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct BaseStruct {
|
||||
dap::integer i;
|
||||
dap::number n;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(BaseStruct,
|
||||
"BaseStruct",
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"));
|
||||
|
||||
struct DerivedStruct : public BaseStruct {
|
||||
dap::string s;
|
||||
dap::boolean b;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO_EXT(DerivedStruct,
|
||||
BaseStruct,
|
||||
"DerivedStruct",
|
||||
DAP_FIELD(s, "s"),
|
||||
DAP_FIELD(b, "b"));
|
||||
|
||||
} // namespace dap
|
||||
|
||||
TEST(TypeInfo, Derived) {
|
||||
dap::DerivedStruct in;
|
||||
in.s = "hello world";
|
||||
in.b = true;
|
||||
in.i = 42;
|
||||
in.n = 3.14;
|
||||
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(in));
|
||||
|
||||
dap::DerivedStruct out;
|
||||
dap::json::Deserializer d(s.dump());
|
||||
ASSERT_TRUE(d.deserialize(&out)) << "Failed to deserialize\n" << s.dump();
|
||||
|
||||
ASSERT_EQ(out.s, "hello world");
|
||||
ASSERT_EQ(out.b, true);
|
||||
ASSERT_EQ(out.i, 42);
|
||||
ASSERT_EQ(out.n, 3.14);
|
||||
}
|
||||
144
libraries/cppdap/src/typeof.cpp
Normal file
144
libraries/cppdap/src/typeof.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/typeof.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
// TypeInfos owns all the dap::TypeInfo instances.
|
||||
struct TypeInfos {
|
||||
// get() returns the TypeInfos singleton pointer.
|
||||
// TypeInfos is constructed with an internal reference count of 1.
|
||||
static TypeInfos* get();
|
||||
|
||||
// reference() increments the TypeInfos reference count.
|
||||
inline void reference() {
|
||||
assert(refcount.load() > 0);
|
||||
refcount++;
|
||||
}
|
||||
|
||||
// release() decrements the TypeInfos reference count.
|
||||
// If the reference count becomes 0, then the TypeInfos is destructed.
|
||||
inline void release() {
|
||||
if (--refcount == 0) {
|
||||
this->~TypeInfos();
|
||||
}
|
||||
}
|
||||
|
||||
struct NullTI : public dap::TypeInfo {
|
||||
using null = dap::null;
|
||||
inline std::string name() const override { return "null"; }
|
||||
inline size_t size() const override { return sizeof(null); }
|
||||
inline size_t alignment() const override { return alignof(null); }
|
||||
inline void construct(void* ptr) const override { new (ptr) null(); }
|
||||
inline void copyConstruct(void* dst, const void* src) const override {
|
||||
new (dst) null(*reinterpret_cast<const null*>(src));
|
||||
}
|
||||
inline void destruct(void* ptr) const override {
|
||||
reinterpret_cast<null*>(ptr)->~null();
|
||||
}
|
||||
inline bool deserialize(const dap::Deserializer*, void*) const override {
|
||||
return true;
|
||||
}
|
||||
inline bool serialize(dap::Serializer*, const void*) const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
dap::BasicTypeInfo<dap::boolean> boolean = {"boolean"};
|
||||
dap::BasicTypeInfo<dap::string> string = {"string"};
|
||||
dap::BasicTypeInfo<dap::integer> integer = {"integer"};
|
||||
dap::BasicTypeInfo<dap::number> number = {"number"};
|
||||
dap::BasicTypeInfo<dap::object> object = {"object"};
|
||||
dap::BasicTypeInfo<dap::any> any = {"any"};
|
||||
NullTI null;
|
||||
std::vector<std::unique_ptr<dap::TypeInfo>> types;
|
||||
|
||||
private:
|
||||
TypeInfos() = default;
|
||||
~TypeInfos() = default;
|
||||
std::atomic<uint64_t> refcount = {1};
|
||||
};
|
||||
|
||||
// aligned_storage() is a replacement for std::aligned_storage that isn't busted
|
||||
// on older versions of MSVC.
|
||||
template <size_t SIZE, size_t ALIGNMENT>
|
||||
struct aligned_storage {
|
||||
struct alignas(ALIGNMENT) type {
|
||||
unsigned char data[SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
TypeInfos* TypeInfos::get() {
|
||||
static aligned_storage<sizeof(TypeInfos), alignof(TypeInfos)>::type memory;
|
||||
|
||||
struct Instance {
|
||||
TypeInfos* ptr() { return reinterpret_cast<TypeInfos*>(memory.data); }
|
||||
Instance() { new (ptr()) TypeInfos(); }
|
||||
~Instance() { ptr()->release(); }
|
||||
};
|
||||
|
||||
static Instance instance;
|
||||
return instance.ptr();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace dap {
|
||||
|
||||
const TypeInfo* TypeOf<boolean>::type() {
|
||||
return &TypeInfos::get()->boolean;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<string>::type() {
|
||||
return &TypeInfos::get()->string;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<integer>::type() {
|
||||
return &TypeInfos::get()->integer;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<number>::type() {
|
||||
return &TypeInfos::get()->number;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<object>::type() {
|
||||
return &TypeInfos::get()->object;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<any>::type() {
|
||||
return &TypeInfos::get()->any;
|
||||
}
|
||||
|
||||
const TypeInfo* TypeOf<null>::type() {
|
||||
return &TypeInfos::get()->null;
|
||||
}
|
||||
|
||||
void TypeInfo::deleteOnExit(TypeInfo* ti) {
|
||||
TypeInfos::get()->types.emplace_back(std::unique_ptr<TypeInfo>(ti));
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
TypeInfos::get()->reference();
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
TypeInfos::get()->release();
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
94
libraries/cppdap/src/variant_test.cpp
Normal file
94
libraries/cppdap/src/variant_test.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/variant.h"
|
||||
#include "dap/typeof.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct VariantTestObject {
|
||||
dap::integer i;
|
||||
dap::number n;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(VariantTestObject,
|
||||
"VariantTestObject",
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"));
|
||||
|
||||
} // namespace dap
|
||||
|
||||
TEST(Variant, EmptyConstruct) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant;
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
}
|
||||
|
||||
TEST(Variant, Boolean) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::boolean(true));
|
||||
ASSERT_TRUE(variant.is<dap::boolean>());
|
||||
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
|
||||
}
|
||||
|
||||
TEST(Variant, Integer) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::integer(10));
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
|
||||
}
|
||||
|
||||
TEST(Variant, TestObject) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::VariantTestObject{5, 3.0});
|
||||
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
|
||||
}
|
||||
|
||||
TEST(Variant, Assign) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::integer(10));
|
||||
variant = dap::integer(10);
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
|
||||
variant = dap::boolean(true);
|
||||
ASSERT_FALSE(variant.is<dap::integer>());
|
||||
ASSERT_TRUE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
|
||||
variant = dap::VariantTestObject{5, 3.0};
|
||||
ASSERT_FALSE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
|
||||
}
|
||||
|
||||
TEST(Variant, Accepts) {
|
||||
using variant =
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject>;
|
||||
ASSERT_TRUE(variant::accepts<dap::integer>());
|
||||
ASSERT_TRUE(variant::accepts<dap::boolean>());
|
||||
ASSERT_TRUE(variant::accepts<dap::VariantTestObject>());
|
||||
ASSERT_FALSE(variant::accepts<dap::number>());
|
||||
ASSERT_FALSE(variant::accepts<dap::string>());
|
||||
}
|
||||
27
libraries/cppdap/third_party/json/.circleci/config.yml
vendored
Normal file
27
libraries/cppdap/third_party/json/.circleci/config.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: debian:stretch
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install sudo
|
||||
command: 'apt-get update && apt-get install -y sudo && rm -rf /var/lib/apt/lists/*'
|
||||
- run:
|
||||
name: Install GCC
|
||||
command: 'apt-get update && apt-get install -y gcc g++'
|
||||
- run:
|
||||
name: Install CMake
|
||||
command: 'apt-get update && sudo apt-get install -y cmake'
|
||||
- run:
|
||||
name: Create build files
|
||||
command: 'mkdir build ; cd build ; cmake ..'
|
||||
- run:
|
||||
name: Compile
|
||||
command: 'cmake --build build'
|
||||
- run:
|
||||
name: Execute test suite
|
||||
command: 'cd build ; ctest -j 2'
|
||||
26
libraries/cppdap/third_party/json/.clang-tidy
vendored
Normal file
26
libraries/cppdap/third_party/json/.clang-tidy
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Checks: '-*,
|
||||
bugprone-*,
|
||||
cert-*,
|
||||
clang-analyzer-*,
|
||||
google-*,
|
||||
-google-runtime-references,
|
||||
-google-explicit-constructor,
|
||||
hicpp-*,
|
||||
-hicpp-no-array-decay,
|
||||
-hicpp-uppercase-literal-suffix,
|
||||
-hicpp-explicit-conversions,
|
||||
misc-*,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
llvm-*,
|
||||
-llvm-header-guard,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-readability-magic-numbers,
|
||||
-readability-uppercase-literal-suffix'
|
||||
|
||||
CheckOptions:
|
||||
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
|
||||
value: 1
|
||||
82
libraries/cppdap/third_party/json/.doozer.json
vendored
Normal file
82
libraries/cppdap/third_party/json/.doozer.json
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"targets": {
|
||||
"raspbian-jessie": {
|
||||
"buildenv": "raspbian-jessie",
|
||||
"builddeps": ["build-essential", "wget"],
|
||||
"buildcmd": [
|
||||
"uname -a",
|
||||
"cat /etc/os-release",
|
||||
"g++ --version",
|
||||
"cd",
|
||||
"wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz",
|
||||
"tar xfz cmake-3.14.0.tar.gz",
|
||||
"cd cmake-3.14.0",
|
||||
"./bootstrap",
|
||||
"make -j8",
|
||||
"cd",
|
||||
"mkdir build",
|
||||
"cd build",
|
||||
"../cmake-3.14.0/bin/cmake /project/repo/checkout",
|
||||
"make -j8",
|
||||
"../cmake-3.14.0/bin/ctest -VV -j4 --timeout 10000"
|
||||
]
|
||||
},
|
||||
"xenial-armhf": {
|
||||
"buildenv": "xenial-armhf",
|
||||
"builddeps": ["build-essential", "wget"],
|
||||
"buildcmd": [
|
||||
"uname -a",
|
||||
"lsb_release -a",
|
||||
"g++ --version",
|
||||
"cd",
|
||||
"wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz",
|
||||
"tar xfz cmake-3.14.0.tar.gz",
|
||||
"cd cmake-3.14.0",
|
||||
"./bootstrap",
|
||||
"make -j8",
|
||||
"cd",
|
||||
"mkdir build",
|
||||
"cd build",
|
||||
"../cmake-3.14.0/bin/cmake /project/repo/checkout",
|
||||
"make -j8",
|
||||
"../cmake-3.14.0/bin/ctest -VV -j4 --timeout 10000"
|
||||
]
|
||||
},
|
||||
"fedora24-x86_64": {
|
||||
"buildenv": "fedora24-x86_64",
|
||||
"builddeps": ["cmake", "make", "gcc gcc-c++"],
|
||||
"buildcmd": [
|
||||
"uname -a",
|
||||
"cat /etc/fedora-release",
|
||||
"g++ --version",
|
||||
"cd",
|
||||
"mkdir build",
|
||||
"cd build",
|
||||
"cmake /project/repo/checkout",
|
||||
"make -j8",
|
||||
"ctest -VV -j8"
|
||||
]
|
||||
},
|
||||
"centos7-x86_64": {
|
||||
"buildenv": "centos7-x86_64",
|
||||
"builddeps": ["make", "wget", "gcc-c++"],
|
||||
"buildcmd": [
|
||||
"uname -a",
|
||||
"rpm -q centos-release",
|
||||
"g++ --version",
|
||||
"cd",
|
||||
"wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0.tar.gz",
|
||||
"tar xfz cmake-3.14.0.tar.gz",
|
||||
"cd cmake-3.14.0",
|
||||
"./bootstrap",
|
||||
"make -j8",
|
||||
"cd",
|
||||
"mkdir build",
|
||||
"cd build",
|
||||
"../cmake-3.14.0/bin/cmake /project/repo/checkout",
|
||||
"make -j8",
|
||||
"../cmake-3.14.0/bin/ctest -VV -j8"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
6
libraries/cppdap/third_party/json/.github/CODEOWNERS
vendored
Normal file
6
libraries/cppdap/third_party/json/.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# JSON for Modern C++ has been originally written by Niels Lohmann.
|
||||
# Since 2013 over 140 contributors have helped to improve the library.
|
||||
# This CODEOWNERS file is only to make sure that @nlohmann is requsted
|
||||
# for a code review in case of a pull request.
|
||||
|
||||
* @nlohmann
|
||||
71
libraries/cppdap/third_party/json/.github/CONTRIBUTING.md
vendored
Normal file
71
libraries/cppdap/third_party/json/.github/CONTRIBUTING.md
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
[](http://issuestats.com/github/nlohmann/json) [](http://issuestats.com/github/nlohmann/json)
|
||||
|
||||
# How to contribute
|
||||
|
||||
This project started as a little excuse to exercise some of the cool new C++11 features. Over time, people actually started to use the JSON library (yey!) and started to help improve it by proposing features, finding bugs, or even fixing my mistakes. I am really [thankful](https://github.com/nlohmann/json/blob/master/README.md#thanks) for this and try to keep track of all the helpers.
|
||||
|
||||
To make it as easy as possible for you to contribute and for me to keep an overview, here are a few guidelines which should help us avoid all kinds of unnecessary work or disappointment. And of course, this document is subject to discussion, so please [create an issue](https://github.com/nlohmann/json/issues/new/choose) or a pull request if you find a way to improve it!
|
||||
|
||||
## Private reports
|
||||
|
||||
Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to <mail@nlohmann.me>.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Please [create an issue](https://github.com/nlohmann/json/issues/new/choose), assuming one does not already exist, and describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this.
|
||||
|
||||
## Describe your issue
|
||||
|
||||
Clearly describe the issue:
|
||||
|
||||
- If it is a bug, please describe how to **reproduce** it. If possible, attach a complete example which demonstrates the error. Please also state what you **expected** to happen instead of the error.
|
||||
- If you propose a change or addition, try to give an **example** how the improved code could look like or how to use it.
|
||||
- If you found a compilation error, please tell us which **compiler** (version and operating system) you used and paste the (relevant part of) the error messages to the ticket.
|
||||
|
||||
Please stick to the provided issue templates ([bug report](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/Bug_report.md), [feature request](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/Feature_request.md), or [question](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE/question.md)) if possible.
|
||||
|
||||
## Files to change
|
||||
|
||||
:exclamation: Before you make any changes, note the single-header file [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is **generated** from the source files in the [`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). Please **do not** edit file `single_include/nlohmann/json.hpp` directly, but change the `include/nlohmann` sources and regenerate file `single_include/nlohmann/json.hpp` by executing `make amalgamate`.
|
||||
|
||||
To make changes, you need to edit the following files:
|
||||
|
||||
1. [`include/nlohmann/*`](https://github.com/nlohmann/json/tree/develop/include/nlohmann) - These files are the sources of the library. Before testing or creating a pull request, execute `make amalgamate` to regenerate `single_include/nlohmann/json.hpp`.
|
||||
|
||||
2. [`test/src/unit-*.cpp`](https://github.com/nlohmann/json/tree/develop/test/src) - These files contain the [doctest](https://github.com/onqtam/doctest) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
|
||||
|
||||
If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with
|
||||
|
||||
```sh
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ cmake --build .
|
||||
$ ctest
|
||||
```
|
||||
|
||||
The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request.
|
||||
|
||||
|
||||
## Note
|
||||
|
||||
- If you open a pull request, the code will be automatically tested with [Valgrind](http://valgrind.org)'s Memcheck tool to detect memory leaks. Please be aware that the execution with Valgrind _may_ in rare cases yield different behavior than running the code directly. This can result in failing unit tests which run successfully without Valgrind.
|
||||
- There is a Makefile target `make pretty` which runs [Artistic Style](http://astyle.sourceforge.net) to fix indentation. If possible, run it before opening the pull request. Otherwise, we shall run it afterward.
|
||||
|
||||
## Please don't
|
||||
|
||||
- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means.
|
||||
- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
|
||||
- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
|
||||
- We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments.
|
||||
- We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
|
||||
|
||||
- Please do not open pull requests that address **multiple issues**.
|
||||
|
||||
## Wanted
|
||||
|
||||
The following areas really need contribution:
|
||||
|
||||
- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the bleeding-edge versions of GCC or Clang.
|
||||
- Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser with hand coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for parsing.
|
||||
- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency is not everything, speed and memory consumption are very important characteristics for C++ developers, so having proper comparisons would be interesting.
|
||||
1
libraries/cppdap/third_party/json/.github/FUNDING.yml
vendored
Normal file
1
libraries/cppdap/third_party/json/.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
custom: http://paypal.me/nlohmann
|
||||
22
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
22
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'kind: bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- What is the issue you have?
|
||||
|
||||
- Please describe the steps to reproduce the issue. Can you provide a small but working code example?
|
||||
|
||||
- What is the expected behavior?
|
||||
|
||||
- And what is the actual behavior instead?
|
||||
|
||||
- Which compiler and operating system are you using? Is it a [supported compiler](https://github.com/nlohmann/json#supported-compilers)?
|
||||
|
||||
- Did you use a released version of the library or the version from the `develop` branch?
|
||||
|
||||
- If you experience a compilation error: can you [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests)?
|
||||
12
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
12
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'kind: enhancement/improvement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- Describe the feature in as much detail as possible.
|
||||
|
||||
- Include sample usage where appropriate.
|
||||
16
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
16
libraries/cppdap/third_party/json/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
name: Question
|
||||
about: Ask a question regarding the library.
|
||||
title: ''
|
||||
labels: 'kind: question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
- Describe what you want to achieve.
|
||||
|
||||
- Describe what you tried.
|
||||
|
||||
- Describe which system (OS, compiler) you are using.
|
||||
|
||||
- Describe which version of the library you are using (release version, develop branch).
|
||||
19
libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
19
libraries/cppdap/third_party/json/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[Describe your pull request here. Please read the text below the line, and make sure you follow the checklist.]
|
||||
|
||||
* * *
|
||||
|
||||
## Pull request checklist
|
||||
|
||||
Read the [Contribution Guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md) for detailed information.
|
||||
|
||||
- [ ] Changes are described in the pull request, or an [existing issue is referenced](https://github.com/nlohmann/json/issues).
|
||||
- [ ] The test suite [compiles and runs](https://github.com/nlohmann/json/blob/develop/README.md#execute-unit-tests) without error.
|
||||
- [ ] [Code coverage](https://coveralls.io/github/nlohmann/json) is 100%. Test cases can be added by editing the [test suite](https://github.com/nlohmann/json/tree/develop/test/src).
|
||||
- [ ] The source code is amalgamated; that is, after making changes to the sources in the `include/nlohmann` directory, run `make amalgamate` to create the single-header file `single_include/nlohmann/json.hpp`. The whole process is described [here](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#files-to-change).
|
||||
|
||||
## Please don't
|
||||
|
||||
- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means.
|
||||
- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
|
||||
- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
|
||||
- Please do not open pull requests that address **multiple issues**.
|
||||
5
libraries/cppdap/third_party/json/.github/SECURITY.md
vendored
Normal file
5
libraries/cppdap/third_party/json/.github/SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Usually, all issues are tracked publicly on [GitHub](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be published), please send an email to <mail@nlohmann.me>. You can use [this key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69) for encryption.
|
||||
19
libraries/cppdap/third_party/json/.github/config.yml
vendored
Normal file
19
libraries/cppdap/third_party/json/.github/config.yml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Configuration for sentiment-bot - https://github.com/behaviorbot/sentiment-bot
|
||||
|
||||
# *Required* toxicity threshold between 0 and .99 with the higher numbers being the most toxic
|
||||
# Anything higher than this threshold will be marked as toxic and commented on
|
||||
sentimentBotToxicityThreshold: .7
|
||||
|
||||
# *Required* Comment to reply with
|
||||
sentimentBotReplyComment: >
|
||||
Please be sure to review the [code of conduct](https://github.com/nlohmann/json/blob/develop/CODE_OF_CONDUCT.md) and be respectful of other users. cc/ @nlohmann
|
||||
|
||||
|
||||
# Configuration for request-info - https://github.com/behaviorbot/request-info
|
||||
|
||||
# *Required* Comment to reply with
|
||||
requestInfoReplyComment: >
|
||||
We would appreciate it if you could provide us with more info about this issue or pull request! Please check the [issue template](https://github.com/nlohmann/json/blob/develop/.github/ISSUE_TEMPLATE.md) and the [pull request template](https://github.com/nlohmann/json/blob/develop/.github/PULL_REQUEST_TEMPLATE.md).
|
||||
|
||||
# *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given
|
||||
requestInfoLabelToAdd: "state: needs more info"
|
||||
17
libraries/cppdap/third_party/json/.github/stale.yml
vendored
Normal file
17
libraries/cppdap/third_party/json/.github/stale.yml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: "state: stale"
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
19
libraries/cppdap/third_party/json/.github/workflows/ccpp.yml
vendored
Normal file
19
libraries/cppdap/third_party/json/.github/workflows/ccpp.yml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: mkdir build
|
||||
- name: cmake
|
||||
run: cd build ; cmake ..
|
||||
- name: build
|
||||
run: make -C build
|
||||
- name: test
|
||||
run: cd build ; ctest -j 10
|
||||
25
libraries/cppdap/third_party/json/.gitignore
vendored
Normal file
25
libraries/cppdap/third_party/json/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
json_unit
|
||||
json_benchmarks
|
||||
json_benchmarks_simple
|
||||
fuzz-testing
|
||||
|
||||
*.dSYM
|
||||
*.o
|
||||
*.gcno
|
||||
*.gcda
|
||||
|
||||
build
|
||||
build_coverage
|
||||
clang_analyze_build
|
||||
|
||||
doc/xml
|
||||
doc/html
|
||||
me.nlohmann.json.docset
|
||||
|
||||
benchmarks/files/numbers/*.json
|
||||
|
||||
.idea
|
||||
cmake-build-debug
|
||||
|
||||
test/test-*
|
||||
/.vs
|
||||
345
libraries/cppdap/third_party/json/.travis.yml
vendored
Normal file
345
libraries/cppdap/third_party/json/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
#########################
|
||||
# project configuration #
|
||||
#########################
|
||||
|
||||
# C++ project
|
||||
language: cpp
|
||||
|
||||
dist: trusty
|
||||
sudo: required
|
||||
group: edge
|
||||
|
||||
|
||||
###################
|
||||
# global settings #
|
||||
###################
|
||||
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA="
|
||||
|
||||
|
||||
################
|
||||
# build matrix #
|
||||
################
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Valgrind
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
- CMAKE_OPTIONS=-DJSON_Valgrind=ON
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'valgrind', 'ninja-build']
|
||||
|
||||
# clang sanitizer
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env:
|
||||
- COMPILER=clang++-7
|
||||
- CMAKE_OPTIONS=-DJSON_Sanitizer=ON
|
||||
- UBSAN_OPTIONS=print_stacktrace=1,suppressions=$(pwd)/test/src/UBSAN.supp
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7']
|
||||
packages: ['g++-6', 'clang-7', 'ninja-build']
|
||||
before_script:
|
||||
- export PATH=$PATH:/usr/lib/llvm-7/bin
|
||||
|
||||
# cppcheck
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
- SPECIAL=cppcheck
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'cppcheck', 'ninja-build']
|
||||
after_success:
|
||||
- make cppcheck
|
||||
|
||||
# no exceptions
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
- CMAKE_OPTIONS=-DJSON_NoExceptions=ON
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'ninja-build']
|
||||
|
||||
# check amalgamation
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
- SPECIAL=amalgamation
|
||||
- MULTIPLE_HEADERS=ON
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'astyle', 'ninja-build']
|
||||
after_success:
|
||||
- make check-amalgamation
|
||||
|
||||
# Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/)
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'ninja-build']
|
||||
before_script:
|
||||
- pip install --user cpp-coveralls
|
||||
after_success:
|
||||
- coveralls --build-root test --include include/nlohmann --gcov 'gcov-4.9' --gcov-options '\-lp'
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
- CMAKE_OPTIONS=-DJSON_Coverage=ON
|
||||
- MULTIPLE_HEADERS=ON
|
||||
|
||||
# Coverity (only for branch coverity_scan)
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: ['g++-6', 'clang-3.6', 'ninja-build']
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "nlohmann/json"
|
||||
description: "Build submitted via Travis CI"
|
||||
notification_email: niels.lohmann@gmail.com
|
||||
build_command_prepend: "mkdir coverity_build ; cd coverity_build ; cmake .. ; cd .."
|
||||
build_command: "make -C coverity_build"
|
||||
branch_pattern: coverity_scan
|
||||
env:
|
||||
- SPECIAL=coverity
|
||||
- COMPILER=clang++-3.6
|
||||
|
||||
# OSX / Clang
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.1
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.2
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.3
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.4
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode10.1
|
||||
|
||||
# Linux / GCC
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: compiler=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.8', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: compiler=g++-4.9
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-5
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-5', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-6
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-7
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-7', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-8
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-8', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-9
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-9', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++-9
|
||||
- CXXFLAGS=-std=c++2a
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-9', 'ninja-build']
|
||||
|
||||
# Linux / Clang
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-3.5
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5']
|
||||
packages: ['g++-6', 'clang-3.5', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-3.6
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: ['g++-6', 'clang-3.6', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-3.7
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
|
||||
packages: ['g++-6', 'clang-3.7', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-3.8
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6', 'clang-3.8', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-3.9
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6', 'clang-3.9', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-4.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0']
|
||||
packages: ['g++-6', 'clang-4.0', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-5.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0']
|
||||
packages: ['g++-6', 'clang-5.0', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-6.0
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0']
|
||||
packages: ['g++-6', 'clang-6.0', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: COMPILER=clang++-7
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7']
|
||||
packages: ['g++-6', 'clang-7', 'ninja-build']
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env:
|
||||
- COMPILER=clang++-7
|
||||
- CXXFLAGS=-std=c++1z
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7']
|
||||
packages: ['g++-6', 'clang-7', 'ninja-build']
|
||||
|
||||
################
|
||||
# build script #
|
||||
################
|
||||
|
||||
script:
|
||||
# get CMake and Ninja (only for systems with brew - macOS)
|
||||
- |
|
||||
if [[ (-x $(which brew)) ]]; then
|
||||
brew update
|
||||
brew install cmake ninja
|
||||
brew upgrade cmake
|
||||
cmake --version
|
||||
fi
|
||||
|
||||
# make sure CXX is correctly set
|
||||
- if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi
|
||||
# by default, use the single-header version
|
||||
- if [[ "${MULTIPLE_HEADERS}" == "" ]]; then export MULTIPLE_HEADERS=OFF; fi
|
||||
|
||||
# show OS/compiler version
|
||||
- uname -a
|
||||
- $CXX --version
|
||||
|
||||
# compile and execute unit tests
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. ${CMAKE_OPTIONS} -DJSON_MultipleHeaders=${MULTIPLE_HEADERS} -GNinja && cmake --build . --config Release
|
||||
- ctest -C Release --timeout 2700 -V -j
|
||||
- cd ..
|
||||
|
||||
# check if homebrew works (only checks develop branch)
|
||||
- if [ `which brew` ]; then
|
||||
brew update ;
|
||||
brew tap nlohmann/json ;
|
||||
brew install nlohmann_json --HEAD ;
|
||||
brew test nlohmann_json ;
|
||||
fi
|
||||
131
libraries/cppdap/third_party/json/CMakeLists.txt
vendored
Normal file
131
libraries/cppdap/third_party/json/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
##
|
||||
## PROJECT
|
||||
## name and version
|
||||
##
|
||||
project(nlohmann_json VERSION 3.7.0 LANGUAGES CXX)
|
||||
|
||||
##
|
||||
## INCLUDE
|
||||
##
|
||||
##
|
||||
include(ExternalProject)
|
||||
|
||||
##
|
||||
## OPTIONS
|
||||
##
|
||||
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON)
|
||||
option(JSON_Install "Install CMake targets during install step." ON)
|
||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
|
||||
|
||||
##
|
||||
## CONFIGURATION
|
||||
##
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME})
|
||||
set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE INTERNAL "")
|
||||
set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(NLOHMANN_JSON_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
|
||||
set(NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE "cmake/config.cmake.in")
|
||||
set(NLOHMANN_JSON_CMAKE_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Config.cmake")
|
||||
set(NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE "${NLOHMANN_JSON_CMAKE_CONFIG_DIR}/${PROJECT_NAME}Targets.cmake")
|
||||
|
||||
if (JSON_MultipleHeaders)
|
||||
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/")
|
||||
message(STATUS "Using the multi-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
|
||||
else()
|
||||
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/single_include/")
|
||||
message(STATUS "Using the single-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
|
||||
endif()
|
||||
|
||||
##
|
||||
## TARGET
|
||||
## create target and add include path
|
||||
##
|
||||
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
|
||||
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.8.0")
|
||||
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_range_for)
|
||||
else()
|
||||
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
${NLOHMANN_JSON_TARGET_NAME}
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${NLOHMANN_JSON_INCLUDE_BUILD_DIR}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
## add debug view definition file for msvc (natvis)
|
||||
if (MSVC)
|
||||
set(NLOHMANN_ADD_NATVIS TRUE)
|
||||
set(NLOHMANN_NATVIS_FILE "nlohmann_json.natvis")
|
||||
target_sources(
|
||||
${NLOHMANN_JSON_TARGET_NAME}
|
||||
INTERFACE
|
||||
$<INSTALL_INTERFACE:${NLOHMANN_NATVIS_FILE}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${NLOHMANN_NATVIS_FILE}>
|
||||
)
|
||||
endif()
|
||||
|
||||
##
|
||||
## TESTS
|
||||
## create and configure the unit test target
|
||||
##
|
||||
include(CTest) #adds option BUILD_TESTING (default ON)
|
||||
|
||||
if(BUILD_TESTING AND JSON_BuildTests)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
##
|
||||
## INSTALL
|
||||
## install header files, generate and install cmake config files for find_package()
|
||||
##
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE} COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
configure_file(
|
||||
${NLOHMANN_JSON_CMAKE_CONFIG_TEMPLATE}
|
||||
${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE}
|
||||
@ONLY
|
||||
)
|
||||
|
||||
if(JSON_Install)
|
||||
install(
|
||||
DIRECTORY ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}
|
||||
DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}
|
||||
)
|
||||
install(
|
||||
FILES ${NLOHMANN_JSON_CMAKE_PROJECT_CONFIG_FILE} ${NLOHMANN_JSON_CMAKE_VERSION_CONFIG_FILE}
|
||||
DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR}
|
||||
)
|
||||
if (NLOHMANN_ADD_NATVIS)
|
||||
install(
|
||||
FILES ${NLOHMANN_NATVIS_FILE}
|
||||
DESTINATION .
|
||||
)
|
||||
endif()
|
||||
export(
|
||||
TARGETS ${NLOHMANN_JSON_TARGET_NAME}
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
FILE ${NLOHMANN_JSON_CMAKE_PROJECT_TARGETS_FILE}
|
||||
)
|
||||
install(
|
||||
TARGETS ${NLOHMANN_JSON_TARGET_NAME}
|
||||
EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME}
|
||||
INCLUDES DESTINATION ${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}
|
||||
)
|
||||
install(
|
||||
EXPORT ${NLOHMANN_JSON_TARGETS_EXPORT_NAME}
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${NLOHMANN_JSON_CONFIG_INSTALL_DIR}
|
||||
)
|
||||
endif()
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue