diff --git a/.gitignore b/.gitignore
index 3a3b7cc3d..d8d361a3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ fmodapi*linux/
/src/gl/unused
/mapfiles_release/*.map
/AppDir
+.cache/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1088d2483..ba2d6f9ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -216,6 +216,9 @@ option( NO_OPENAL "Disable OpenAL sound support" OFF )
find_package( BZip2 )
find_package( VPX )
+if (NOT FORCE_INTERNAL_CPPDAP)
+ find_package( cppdap CONFIG )
+endif()
include( TargetArch )
@@ -334,6 +337,9 @@ set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" )
option(FORCE_INTERNAL_BZIP2 "Use internal bzip2")
option(FORCE_INTERNAL_ASMJIT "Use internal asmjit" ON)
+option(FORCE_INTERNAL_CPPDAP "Use internal cppdap" ON)
+
+
mark_as_advanced( FORCE_INTERNAL_ASMJIT )
if (HAVE_VULKAN)
@@ -385,6 +391,17 @@ else()
set( BZIP2_LIBRARY bz2 )
endif()
+if ( CPPDAP_FOUND AND NOT FORCE_INTERNAL_CPPDAP )
+ message( STATUS "Using system cppdap library, includes found at ${CPPDAP_INCLUDE_DIR}" )
+else()
+ message( STATUS "Using internal cppdap library" )
+ add_subdirectory( libraries/cppdap )
+ set( CPPDAP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/cppdap" )
+ set( CPPDAP_LIBRARIES cppdap )
+ set( CPPDAP_LIBRARY cppdap )
+ set( CPPDAP_FOUND TRUE )
+endif()
+
set( LZMA_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/lzma/C" )
if( NOT CMAKE_CROSSCOMPILING )
diff --git a/docs/console.html b/docs/console.html
index 7fb5d4c8c..4dd6a0fe5 100644
--- a/docs/console.html
+++ b/docs/console.html
@@ -1814,6 +1814,14 @@ default: 1.0
Sends you to the specified coordinates immediately. This can be used
with
idmypos to debug problems in a specific area of a
map.
+ vm_debug
+ boolean: false
+ When set to true, the game will enable the ZScript debug
+ server on the port specified in vm_debug_port. Note that enabling this
+ will disable JIT and requires restarting the game.
+ vm_debug_port
+ integer: 19021
+ This is the port that the ZScript debug server will listen on.
Effects
diff --git a/libraries/cppdap/.gitattributes b/libraries/cppdap/.gitattributes
new file mode 100644
index 000000000..175297ae8
--- /dev/null
+++ b/libraries/cppdap/.gitattributes
@@ -0,0 +1,4 @@
+* text=auto
+
+*.sh eol=lf
+*.bat eol=crlf
diff --git a/libraries/cppdap/.github/workflows/main.yml b/libraries/cppdap/.github/workflows/main.yml
new file mode 100644
index 000000000..72d2934e9
--- /dev/null
+++ b/libraries/cppdap/.github/workflows/main.yml
@@ -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/
diff --git a/libraries/cppdap/.gitignore b/libraries/cppdap/.gitignore
new file mode 100644
index 000000000..af8d40aae
--- /dev/null
+++ b/libraries/cppdap/.gitignore
@@ -0,0 +1,6 @@
+build/
+fuzz/corpus
+fuzz/logs
+.vs/
+.vscode/settings.json
+CMakeSettings.json
diff --git a/libraries/cppdap/CMakeLists.txt b/libraries/cppdap/CMakeLists.txt
new file mode 100644
index 000000000..a20152d77
--- /dev/null
+++ b/libraries/cppdap/CMakeLists.txt
@@ -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 "$
")
+ 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
+ "$"
+ "$"
+ )
+ 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 $ ${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)
diff --git a/libraries/cppdap/CONTRIBUTING b/libraries/cppdap/CONTRIBUTING
new file mode 100644
index 000000000..9a86ba025
--- /dev/null
+++ b/libraries/cppdap/CONTRIBUTING
@@ -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 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/).
diff --git a/libraries/cppdap/LICENSE b/libraries/cppdap/LICENSE
new file mode 100644
index 000000000..7a4a3ea24
--- /dev/null
+++ b/libraries/cppdap/LICENSE
@@ -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.
\ No newline at end of file
diff --git a/libraries/cppdap/README.md b/libraries/cppdap/README.md
new file mode 100644
index 000000000..cd45bed78
--- /dev/null
+++ b/libraries/cppdap/README.md
@@ -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
+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
+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 ) # example : "${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( cppdap) # replace 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 ) # defaults to ${CPPDAP_DIR}/third_party
+set(CPPDAP_JSON_DIR ) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/json
+set(CPPDAP_GOOGLETEST_DIR ) # defaults to ${CPPDAP_THIRD_PARTY_DIR}/googletest
+add_subdirectory(${CPPDAP_DIR})
+```
+
+---
+
+Note: This is not an officially supported Google product
diff --git a/libraries/cppdap/clang-format-all.sh b/libraries/cppdap/clang-format-all.sh
new file mode 100755
index 000000000..8bfb593b9
--- /dev/null
+++ b/libraries/cppdap/clang-format-all.sh
@@ -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
diff --git a/libraries/cppdap/cmake/Config.cmake.in b/libraries/cppdap/cmake/Config.cmake.in
new file mode 100644
index 000000000..384aa3f50
--- /dev/null
+++ b/libraries/cppdap/cmake/Config.cmake.in
@@ -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()
\ No newline at end of file
diff --git a/libraries/cppdap/examples/hello_debugger.cpp b/libraries/cppdap/examples/hello_debugger.cpp
new file mode 100644
index 000000000..62b619f9e
--- /dev/null
+++ b/libraries/cppdap/examples/hello_debugger.cpp
@@ -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
+#include
+#include
+#include
+
+#ifdef _MSC_VER
+#define OS_WINDOWS 1
+#endif
+
+// Uncomment the line below and change to a file path to
+// write all DAP communications to the given path.
+//
+// #define LOG_TO_FILE ""
+
+#ifdef OS_WINDOWS
+#include // _O_BINARY
+#include // _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;
+
+ 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 breakpoints;
+};
+
+Debugger::Debugger(const EventHandler& onEvent) : onEvent(onEvent) {}
+
+void Debugger::run() {
+ std::unique_lock 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 lock(mutex);
+ return line;
+}
+
+void Debugger::stepForward() {
+ std::unique_lock lock(mutex);
+ line = (line % numSourceLines) + 1;
+ lock.unlock();
+ onEvent(Event::Stepped);
+}
+
+void Debugger::clearBreakpoints() {
+ std::unique_lock lock(mutex);
+ this->breakpoints.clear();
+}
+
+void Debugger::addBreakpoint(int64_t l) {
+ std::unique_lock 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 lock(mutex);
+ cv.wait(lock, [&] { return fired; });
+}
+
+void Event::fire() {
+ std::unique_lock 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 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&) {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 in = dap::file(stdin, false);
+ std::shared_ptr 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;
+}
diff --git a/libraries/cppdap/examples/simple_net_client_server.cpp b/libraries/cppdap/examples/simple_net_client_server.cpp
new file mode 100644
index 000000000..c93cc9c1d
--- /dev/null
+++ b/libraries/cppdap/examples/simple_net_client_server.cpp
@@ -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
+#include
+
+int main(int, char*[]) {
+ constexpr int kPort = 19021;
+
+ // Callback handler for a socket connection to the server
+ auto onClientConnected =
+ [&](const std::shared_ptr& 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 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 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;
+}
diff --git a/libraries/cppdap/examples/vscode/package.json b/libraries/cppdap/examples/vscode/package.json
new file mode 100644
index 000000000..9a524131d
--- /dev/null
+++ b/libraries/cppdap/examples/vscode/package.json
@@ -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": {}
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/libraries/cppdap/fuzz/dictionary.txt b/libraries/cppdap/fuzz/dictionary.txt
new file mode 100644
index 000000000..639a8b0e8
--- /dev/null
+++ b/libraries/cppdap/fuzz/dictionary.txt
@@ -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"
\ No newline at end of file
diff --git a/libraries/cppdap/fuzz/fuzz.cpp b/libraries/cppdap/fuzz/fuzz.cpp
new file mode 100644
index 000000000..02a21cc6f
--- /dev/null
+++ b/libraries/cppdap/fuzz/fuzz.cpp
@@ -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
+#include
+
+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
+ inline void wait(const DURATION& duration) {
+ std::unique_lock 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 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(ControlMode::Count)) {
+ useContentStreamWriter =
+ data[0] != static_cast(ControlMode::TestStreamWriter);
+ appendDoneRequest =
+ data[0] != static_cast(ControlMode::DontAppendDoneRequest);
+ data++;
+ size--;
+ }
+
+ // in contains the input data
+ auto in = std::make_shared();
+
+ dap::ContentWriter writer(in);
+ if (useContentStreamWriter) {
+ writer.write(std::string(reinterpret_cast(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();
+ 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;
+}
\ No newline at end of file
diff --git a/libraries/cppdap/fuzz/fuzz.h b/libraries/cppdap/fuzz/fuzz.h
new file mode 100644
index 000000000..995f54025
--- /dev/null
+++ b/libraries/cppdap/fuzz/fuzz.h
@@ -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
diff --git a/libraries/cppdap/fuzz/run.sh b/libraries/cppdap/fuzz/run.sh
new file mode 100755
index 000000000..2d5403909
--- /dev/null
+++ b/libraries/cppdap/fuzz/run.sh
@@ -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
diff --git a/libraries/cppdap/fuzz/seed/empty_json b/libraries/cppdap/fuzz/seed/empty_json
new file mode 100644
index 000000000..9e26dfeeb
--- /dev/null
+++ b/libraries/cppdap/fuzz/seed/empty_json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/libraries/cppdap/fuzz/seed/request b/libraries/cppdap/fuzz/seed/request
new file mode 100644
index 000000000..7a183aded
--- /dev/null
+++ b/libraries/cppdap/fuzz/seed/request
@@ -0,0 +1,8 @@
+{
+ "seq": 153,
+ "type": "request",
+ "command": "next",
+ "arguments": {
+ "threadId": 3
+ }
+}
\ No newline at end of file
diff --git a/libraries/cppdap/include/dap/any.h b/libraries/cppdap/include/dap/any.h
new file mode 100644
index 000000000..e799c44cc
--- /dev/null
+++ b/libraries/cppdap/include/dap/any.h
@@ -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
+#include
+
+namespace dap {
+
+template
+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
+ 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
+ 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
+ inline T& get() const;
+
+ // is() returns true iff the contained value is of type T.
+ template
+ 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
+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
+any& any::operator=(const T& val) {
+ if (!is()) {
+ reset();
+ type = TypeOf::type();
+ alloc(type->size(), type->alignment());
+ type->copyConstruct(value, &val);
+ } else {
+#ifdef __clang_analyzer__
+ assert(value != nullptr);
+#endif
+ *reinterpret_cast(value) = val;
+ }
+ return *this;
+}
+
+any& any::operator=(const std::nullptr_t&) {
+ reset();
+ return *this;
+}
+
+template
+T& any::get() const {
+ static_assert(!std::is_same(),
+ "Cannot get nullptr from 'any'.");
+ assert(is());
+ return *reinterpret_cast(value);
+}
+
+template
+bool any::is() const {
+ return type == TypeOf::type();
+}
+
+template <>
+inline bool any::is() const {
+ return value == nullptr;
+}
+
+void* any::alignUp(void* val, size_t alignment) {
+ auto ptr = reinterpret_cast(val);
+ return reinterpret_cast(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(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(heap);
+ heap = nullptr;
+ }
+ value = nullptr;
+}
+
+bool any::isInBuffer(void* ptr) const {
+ auto addr = reinterpret_cast(ptr);
+ return addr >= reinterpret_cast(buffer) &&
+ addr < reinterpret_cast(buffer + sizeof(buffer));
+}
+
+} // namespace dap
+
+#endif // dap_any_h
diff --git a/libraries/cppdap/include/dap/dap.h b/libraries/cppdap/include/dap/dap.h
new file mode 100644
index 000000000..587e80c35
--- /dev/null
+++ b/libraries/cppdap/include/dap/dap.h
@@ -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
diff --git a/libraries/cppdap/include/dap/future.h b/libraries/cppdap/include/dap/future.h
new file mode 100644
index 000000000..af103c33b
--- /dev/null
+++ b/libraries/cppdap/include/dap/future.h
@@ -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
+#include
+#include
+
+namespace dap {
+
+// internal functionality
+namespace detail {
+template
+struct promise_state {
+ T val;
+ std::mutex mutex;
+ std::condition_variable cv;
+ bool hasVal = false;
+};
+} // namespace detail
+
+// forward declaration
+template
+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
+class future {
+ public:
+ using State = detail::promise_state;
+
+ // 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
+ future_status wait_for(
+ const std::chrono::duration& 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
+ future_status wait_until(
+ const std::chrono::time_point& timeout) const;
+
+ private:
+ friend promise;
+ future(const future&) = delete;
+ inline future(const std::shared_ptr& state);
+
+ std::shared_ptr state = std::make_shared();
+};
+
+template
+future::future(const std::shared_ptr& s) : state(s) {}
+
+template
+bool future::valid() const {
+ return static_cast(state);
+}
+
+template
+T future::get() {
+ std::unique_lock lock(state->mutex);
+ state->cv.wait(lock, [&] { return state->hasVal; });
+ return state->val;
+}
+
+template
+void future::wait() const {
+ std::unique_lock lock(state->mutex);
+ state->cv.wait(lock, [&] { return state->hasVal; });
+}
+
+template
+template
+future_status future::wait_for(
+ const std::chrono::duration& timeout) const {
+ std::unique_lock lock(state->mutex);
+ return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; })
+ ? future_status::ready
+ : future_status::timeout;
+}
+
+template
+template
+future_status future::wait_until(
+ const std::chrono::time_point& timeout) const {
+ std::unique_lock 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
+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 get_future();
+
+ private:
+ using State = detail::promise_state;
+ std::shared_ptr state = std::make_shared();
+};
+
+template
+future promise::get_future() {
+ return future(state);
+}
+
+template
+void promise::set_value(const T& value) const {
+ std::unique_lock lock(state->mutex);
+ state->val = value;
+ state->hasVal = true;
+ state->cv.notify_all();
+}
+
+template
+void promise::set_value(T&& value) const {
+ std::unique_lock lock(state->mutex);
+ state->val = std::move(value);
+ state->hasVal = true;
+ state->cv.notify_all();
+}
+
+} // namespace dap
+
+#endif // dap_future_h
diff --git a/libraries/cppdap/include/dap/io.h b/libraries/cppdap/include/dap/io.h
new file mode 100644
index 000000000..61681cc4a
--- /dev/null
+++ b/libraries/cppdap/include/dap/io.h
@@ -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 // size_t
+#include // FILE
+#include // std::unique_ptr
+#include // 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 create(const std::shared_ptr&,
+ const std::shared_ptr&);
+};
+
+// 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 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 file(FILE* file, bool closable = true);
+
+// file() opens (or creates) the file with the given path.
+std::shared_ptr 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 spy(const std::shared_ptr& r,
+ const std::shared_ptr& 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 spy(const std::shared_ptr& w,
+ const std::shared_ptr& s,
+ const char* prefix = "\n<-");
+
+// writef writes the printf style string to the writer w.
+bool writef(const std::shared_ptr& w, const char* msg, ...);
+
+} // namespace dap
+
+#endif // dap_io_h
diff --git a/libraries/cppdap/include/dap/network.h b/libraries/cppdap/include/dap/network.h
new file mode 100644
index 000000000..03b940925
--- /dev/null
+++ b/libraries/cppdap/include/dap/network.h
@@ -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
+#include
+#include
+
+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 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;
+ using OnConnect = std::function&)>;
+
+ virtual ~Server() = default;
+
+ // create() constructs and returns a new Server.
+ static std::unique_ptr 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
diff --git a/libraries/cppdap/include/dap/optional.h b/libraries/cppdap/include/dap/optional.h
new file mode 100644
index 000000000..9a3d21667
--- /dev/null
+++ b/libraries/cppdap/include/dap/optional.h
@@ -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
+#include
+#include // std::move, std::forward
+
+namespace dap {
+
+// optional holds an 'optional' contained value.
+// This is similar to C++17's std::optional.
+template
+class optional {
+ template
+ using IsConvertibleToT =
+ typename std::enable_if::value>::type;
+
+ public:
+ using value_type = T;
+
+ // constructors
+ inline optional() = default;
+ inline optional(const optional& other);
+ inline optional(optional&& other);
+ template
+ inline optional(const optional& other);
+ template
+ inline optional(optional&& other);
+ template >
+ 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 >
+ inline optional& operator=(U&& value);
+ template
+ inline optional& operator=(const optional& other);
+ template
+ inline optional& operator=(optional&& 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
+optional::optional(const optional& other) : val(other.val), set(other.set) {}
+
+template
+optional::optional(optional&& other)
+ : val(std::move(other.val)), set(other.set) {}
+
+template
+template
+optional::optional(const optional& other) : set(other.has_value()) {
+ if (set) {
+ val = static_cast(other.value());
+ }
+}
+
+template
+template
+optional::optional(optional&& other) : set(other.has_value()) {
+ if (set) {
+ val = static_cast(std::move(other.value()));
+ }
+}
+
+template
+template
+optional::optional(U&& value) : val(std::forward(value)), set(true) {}
+
+template
+T& optional::value() {
+ assert(set);
+ return val;
+}
+
+template
+const T& optional::value() const {
+ assert(set);
+ return val;
+}
+
+template
+const T& optional::value(const T& defaultValue) const {
+ if (!has_value()) {
+ return defaultValue;
+ }
+ return val;
+}
+
+template
+optional::operator bool() const noexcept {
+ return set;
+}
+
+template
+bool optional::has_value() const {
+ return set;
+}
+
+template
+optional& optional::operator=(const optional& other) {
+ val = other.val;
+ set = other.set;
+ return *this;
+}
+
+template
+optional& optional::operator=(optional&& other) noexcept {
+ val = std::move(other.val);
+ set = other.set;
+ return *this;
+}
+
+template
+template
+optional& optional::operator=(U&& value) {
+ val = std::forward(value);
+ set = true;
+ return *this;
+}
+
+template
+template
+optional& optional::operator=(const optional& other) {
+ val = other.val;
+ set = other.set;
+ return *this;
+}
+
+template
+template
+optional& optional::operator=(optional&& other) {
+ val = std::move(other.val);
+ set = other.set;
+ return *this;
+}
+
+template
+const T* optional::operator->() const {
+ assert(set);
+ return &val;
+}
+
+template
+T* optional::operator->() {
+ assert(set);
+ return &val;
+}
+
+template
+const T& optional::operator*() const {
+ assert(set);
+ return val;
+}
+
+template
+T& optional::operator*() {
+ assert(set);
+ return val;
+}
+
+template
+inline bool operator==(const optional& lhs, const optional& 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
+inline bool operator!=(const optional& lhs, const optional& rhs) {
+ return !(lhs == rhs);
+}
+
+template
+inline bool operator<(const optional& lhs, const optional& rhs) {
+ if (!rhs.has_value()) {
+ return false;
+ }
+ if (!lhs.has_value()) {
+ return true;
+ }
+ return lhs.value() < rhs.value();
+}
+
+template
+inline bool operator<=(const optional& lhs, const optional& rhs) {
+ if (!lhs.has_value()) {
+ return true;
+ }
+ if (!rhs.has_value()) {
+ return false;
+ }
+ return lhs.value() <= rhs.value();
+}
+
+template
+inline bool operator>(const optional& lhs, const optional& rhs) {
+ if (!lhs.has_value()) {
+ return false;
+ }
+ if (!rhs.has_value()) {
+ return true;
+ }
+ return lhs.value() > rhs.value();
+}
+
+template
+inline bool operator>=(const optional& lhs, const optional& rhs) {
+ if (!rhs.has_value()) {
+ return true;
+ }
+ if (!lhs.has_value()) {
+ return false;
+ }
+ return lhs.value() >= rhs.value();
+}
+
+} // namespace dap
+
+#endif // dap_optional_h
diff --git a/libraries/cppdap/include/dap/protocol.h b/libraries/cppdap/include/dap/protocol.h
new file mode 100644
index 000000000..db5a0ef9a
--- /dev/null
+++ b/libraries/cppdap/include/dap/protocol.h
@@ -0,0 +1,2909 @@
+// 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_protocol_h
+#define dap_protocol_h
+
+#include "optional.h"
+#include "typeinfo.h"
+#include "typeof.h"
+#include "variant.h"
+
+#include
+#include
+#include
+
+namespace dap {
+
+struct Request {};
+struct Response {};
+struct Event {};
+
+// Response to `attach` request. This is just an acknowledgement, so no body
+// field is required.
+struct AttachResponse : public Response {};
+
+DAP_DECLARE_STRUCT_TYPEINFO(AttachResponse);
+
+// The `attach` request is sent from the client to the debug adapter to attach
+// to a debuggee that is already running. Since attaching is debugger/runtime
+// specific, the arguments for this request are not part of this specification.
+struct AttachRequest : public Request {
+ using Response = AttachResponse;
+ // Arbitrary data from the previous, restarted session.
+ // The data is sent as the `restart` attribute of the `terminated` event.
+ // The client should leave the data intact.
+ optional, boolean, integer, null, number, object, string>>
+ restart;
+};
+
+DAP_DECLARE_STRUCT_TYPEINFO(AttachRequest);
+
+// Names of checksum algorithms that may be supported by a debug adapter.
+//
+// Must be one of the following enumeration values:
+// 'MD5', 'SHA1', 'SHA256', 'timestamp'
+using ChecksumAlgorithm = string;
+
+// The checksum of an item calculated by the specified algorithm.
+struct Checksum {
+ // The algorithm used to calculate this checksum.
+ ChecksumAlgorithm algorithm = "MD5";
+ // Value of the checksum, encoded as a hexadecimal value.
+ string checksum;
+};
+
+DAP_DECLARE_STRUCT_TYPEINFO(Checksum);
+
+// A `Source` is a descriptor for source code.
+// It is returned from the debug adapter as part of a `StackFrame` and it is
+// used by clients when specifying breakpoints.
+struct Source {
+ // Additional data that a debug adapter might want to loop through the client.
+ // The client should leave the data intact and persist it across sessions. The
+ // client should not interpret the data.
+ optional, boolean, integer, null, number, object, string>>
+ adapterData;
+ // The checksums associated with this file.
+ optional