pax_global_header00006660000000000000000000000064144373212210014511gustar00rootroot0000000000000052 comment=252b56807b532533ea7362a4d949758dcb481d2b google-cppdap-252b568/000077500000000000000000000000001443732122100145115ustar00rootroot00000000000000google-cppdap-252b568/.clang-format000066400000000000000000000001201443732122100170550ustar00rootroot00000000000000# http://clang.llvm.org/docs/ClangFormatStyleOptions.html BasedOnStyle: Chromiumgoogle-cppdap-252b568/.gitattributes000066400000000000000000000000501443732122100173770ustar00rootroot00000000000000* text=auto *.sh eol=lf *.bat eol=crlf google-cppdap-252b568/.gitignore000066400000000000000000000001131443732122100164740ustar00rootroot00000000000000build/ fuzz/corpus fuzz/logs .vs/ .vscode/settings.json CMakeSettings.json google-cppdap-252b568/.gitmodules000066400000000000000000000003301443732122100166620ustar00rootroot00000000000000[submodule "third_party/json"] path = third_party/json url = https://github.com/nlohmann/json.git [submodule "third_party/googletest"] path = third_party/googletest url = https://github.com/google/googletest.git google-cppdap-252b568/.vscode/000077500000000000000000000000001443732122100160525ustar00rootroot00000000000000google-cppdap-252b568/.vscode/c_cpp_properties.json000066400000000000000000000007761443732122100223170ustar00rootroot00000000000000{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "${workspaceFolder}/third_party/json/include", "${workspaceFolder}/third_party/googletest/googlemock/include" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c11", "cppStandard": "c++11", "intelliSenseMode": "clang-x64" } ], "version": 4 }google-cppdap-252b568/.vscode/launch.json000066400000000000000000000027771443732122100202340ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "cppdap: hello_debugger", "type": "hello_debugger", "request": "launch" }, { "type": "lldb", "request": "launch", "name": "unittests (lldb)", "program": "${workspaceFolder}/build/cppdap-unittests", "cwd": "${workspaceRoot}", "args": [] }, { "type": "cppdbg", "request": "launch", "name": "fuzzer (lldb)", "program": "${workspaceFolder}/fuzz/build/cppdap-fuzzer", "cwd": "${workspaceRoot}", "args": [] }, { "name": "unittests (gdb)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/cppdap-unittests", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }google-cppdap-252b568/.vscode/tasks.json000066400000000000000000000046221443732122100200760ustar00rootroot00000000000000{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "make", "group": { "kind": "build", "isDefault": true }, "type": "shell", "command": "sh", "osx": { "args": [ "-c", "cmake --build . && echo Done" ] }, "linux": { "args": [ "-c", "cmake --build . && echo Done" ] }, "windows": { "args": [ "-c", "cmake --build . && echo Done" ] }, "options": { "cwd": "${workspaceRoot}/build", }, "presentation": { "echo": false, "reveal": "always", "focus": false, "panel": "shared", "showReuseMessage": false, "clear": true, }, "problemMatcher": { "owner": "cpp", "fileLocation": "absolute", "pattern": { "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } } }, { "label": "cmake", "type": "shell", "command": "sh", "args": [ "-c", "mkdir build; cd build; cmake .. -GNinja -DCMAKE_BUILD_TYPE=${input:buildType} -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_INSTALL_VSCODE_EXAMPLES=1 -DCPPDAP_WARNINGS_AS_ERRORS=1", ], "options": { "cwd": "${workspaceRoot}" }, "problemMatcher": [], }, ], "inputs": [ { "id": "buildType", "type": "pickString", "options": [ "Debug", "Release", "MinSizeRel", "RelWithDebInfo", ], "default": "Debug", "description": "The type of build", }, ] }google-cppdap-252b568/CMakeLists.txt000066400000000000000000000347201443732122100172570ustar00rootroot00000000000000# 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.13) project(cppdap VERSION 1.59.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) ########################################################### # 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) 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(CPPDAP_BUILD_TESTS) ########################################################### # 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 STATIC ${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}" 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) 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 ${CPPDAP_GOOGLETEST_DIR}/googletest/src/gtest-all.cc ) set(DAP_TEST_INCLUDE_DIR ${CPPDAP_GOOGLETEST_DIR}/googlemock/include/ ${CPPDAP_GOOGLETEST_DIR}/googletest/ ${CPPDAP_GOOGLETEST_DIR}/googletest/include/ ) add_executable(cppdap-unittests ${DAP_TEST_LIST}) target_include_directories(cppdap-unittests PUBLIC ${DAP_TEST_INCLUDE_DIR} ) 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) target_link_libraries(cppdap-unittests PRIVATE cppdap) 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) google-cppdap-252b568/CONTRIBUTING000066400000000000000000000021111443732122100163360ustar00rootroot00000000000000# 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/). google-cppdap-252b568/LICENSE000066400000000000000000000261351443732122100155250ustar00rootroot00000000000000 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.google-cppdap-252b568/README.md000066400000000000000000000061031443732122100157700ustar00rootroot00000000000000# 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 google-cppdap-252b568/clang-format-all.sh000077500000000000000000000017111443732122100201700ustar00rootroot00000000000000# Copyright 2020 The Marl Authors. # # 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 google-cppdap-252b568/cmake/000077500000000000000000000000001443732122100155715ustar00rootroot00000000000000google-cppdap-252b568/cmake/Config.cmake.in000066400000000000000000000016511443732122100204100ustar00rootroot00000000000000# 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()google-cppdap-252b568/examples/000077500000000000000000000000001443732122100163275ustar00rootroot00000000000000google-cppdap-252b568/examples/hello_debugger.cpp000066400000000000000000000367151443732122100220160ustar00rootroot00000000000000// 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; } google-cppdap-252b568/examples/simple_net_client_server.cpp000066400000000000000000000074641443732122100241310ustar00rootroot00000000000000// 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(); 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; } google-cppdap-252b568/examples/vscode/000077500000000000000000000000001443732122100176125ustar00rootroot00000000000000google-cppdap-252b568/examples/vscode/package.json000066400000000000000000000012621443732122100221010ustar00rootroot00000000000000{ "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": {} } ] } }google-cppdap-252b568/fuzz/000077500000000000000000000000001443732122100155075ustar00rootroot00000000000000google-cppdap-252b568/fuzz/dictionary.txt000066400000000000000000000121711443732122100204170ustar00rootroot00000000000000"MD5" "SHA1" "SHA256" "__restart" "accessType" "accessTypes" "adapterData" "adapterID" "additionalModuleColumns" "address" "addressRange" "algorithm" "all" "allThreadsContinued" "allThreadsStopped" "allowPartial" "always" "areas" "args" "argsCanBeInterpretedByShell" "arguments" "attach" "attachForSuspendedLaunch" "attributeName" "attributes" "baseClass" "body" "boolean" "breakMode" "breakpoint" "breakpointLocations" "breakpoints" "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" "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" "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" "invalidated" "isLocalProcess" "isOptimized" "isUserCode" "keyword" "kind" "label" "launch" "lazy" "length" "levels" "line" "lines" "linesStartAt1" "loadedSource" "loadedSources" "locale" "locals" "location" "logMessage" "memory" "memoryReference" "message" "method" "mimeType" "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" "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" "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" "supportsArgsCanBeInterpretedByShell" "supportsBreakpointLocationsRequest" "supportsCancelRequest" "supportsClipboardContext" "supportsCompletionsRequest" "supportsCondition" "supportsConditionalBreakpoints" "supportsConfigurationDoneRequest" "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" "variable" "variables" "variablesReference" "verified" "version" "virtual" "visibility" "watch" "width" "write" "writeMemory"google-cppdap-252b568/fuzz/fuzz.cpp000066400000000000000000000070771443732122100172240ustar00rootroot00000000000000// 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; }google-cppdap-252b568/fuzz/fuzz.h000066400000000000000000000112441443732122100166600ustar00rootroot00000000000000// 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.59.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::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 google-cppdap-252b568/fuzz/run.sh000077500000000000000000000010271443732122100166520ustar00rootroot00000000000000#!/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 google-cppdap-252b568/fuzz/seed/000077500000000000000000000000001443732122100164275ustar00rootroot00000000000000google-cppdap-252b568/fuzz/seed/empty_json000066400000000000000000000000021443732122100205310ustar00rootroot00000000000000{}google-cppdap-252b568/fuzz/seed/request000066400000000000000000000001601443732122100200370ustar00rootroot00000000000000{ "seq": 153, "type": "request", "command": "next", "arguments": { "threadId": 3 } }google-cppdap-252b568/include/000077500000000000000000000000001443732122100161345ustar00rootroot00000000000000google-cppdap-252b568/include/dap/000077500000000000000000000000001443732122100167005ustar00rootroot00000000000000google-cppdap-252b568/include/dap/any.h000066400000000000000000000116371443732122100176500ustar00rootroot00000000000000// 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 free(); 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); free(); } 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::free() { 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 google-cppdap-252b568/include/dap/dap.h000066400000000000000000000023761443732122100176250ustar00rootroot00000000000000// 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 google-cppdap-252b568/include/dap/future.h000066400000000000000000000120561443732122100203670ustar00rootroot00000000000000// 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 google-cppdap-252b568/include/dap/io.h000066400000000000000000000067241443732122100174710ustar00rootroot00000000000000// 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 google-cppdap-252b568/include/dap/network.h000066400000000000000000000037571443732122100205560ustar00rootroot00000000000000// 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 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 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; // stop() stops listening for connections. // stop() is implicitly called on destruction. virtual void stop() = 0; }; } // namespace net } // namespace dap #endif // dap_network_h google-cppdap-252b568/include/dap/optional.h000066400000000000000000000142161443732122100207020ustar00rootroot00000000000000// 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 google-cppdap-252b568/include/dap/protocol.h000066400000000000000000003514141443732122100207220ustar00rootroot00000000000000// 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.59.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> checksums; // The short name of the source. Every source returned from the debug adapter // has a name. When sending a source to the debug adapter this name is // optional. optional name; // The origin of this source. For example, 'internal module', 'inlined content // from source map', etc. optional origin; // The path of the source to be shown in the UI. // It is only used to locate and load the content of the source if no // `sourceReference` is specified (or its value is 0). optional path; // A hint for how to present the source in the UI. // A value of `deemphasize` can be used to indicate that the source is not // available or that it is skipped on stepping. // // Must be one of the following enumeration values: // 'normal', 'emphasize', 'deemphasize' optional presentationHint; // If the value > 0 the contents of the source must be retrieved through the // `source` request (even if a path is specified). Since a `sourceReference` // is only valid for a session, it can not be used to persist a source. The // value should be less than or equal to 2147483647 (2^31-1). optional sourceReference; // A list of sources that are related to this source. These may be the source // that generated this source. optional> sources; }; DAP_DECLARE_STRUCT_TYPEINFO(Source); // Information about a breakpoint created in `setBreakpoints`, // `setFunctionBreakpoints`, `setInstructionBreakpoints`, or // `setDataBreakpoints` requests. struct Breakpoint { // Start position of the source range covered by the breakpoint. It is // measured in UTF-16 code units and the client capability `columnsStartAt1` // determines whether it is 0- or 1-based. optional column; // End position of the source range covered by the breakpoint. It is measured // in UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. If no end line is given, then the end column // is assumed to be in the start line. optional endColumn; // The end line of the actual range covered by the breakpoint. optional endLine; // The identifier for the breakpoint. It is needed if breakpoint events are // used to update or remove breakpoints. optional id; // A memory reference to where the breakpoint is set. optional instructionReference; // The start line of the actual range covered by the breakpoint. optional line; // A message about the state of the breakpoint. // This is shown to the user and can be used to explain why a breakpoint could // not be verified. optional message; // The offset from the instruction reference. // This can be negative. optional offset; // The source where the breakpoint is located. optional source; // If true, the breakpoint could be set (but not necessarily at the desired // location). boolean verified; }; DAP_DECLARE_STRUCT_TYPEINFO(Breakpoint); // The event indicates that some information about a breakpoint has changed. struct BreakpointEvent : public Event { // The `id` attribute is used to find the target breakpoint, the other // attributes are used as the new values. Breakpoint breakpoint; // The reason for the event. // // May be one of the following enumeration values: // 'changed', 'new', 'removed' string reason; }; DAP_DECLARE_STRUCT_TYPEINFO(BreakpointEvent); // Properties of a breakpoint location returned from the `breakpointLocations` // request. struct BreakpointLocation { // The start position of a breakpoint location. Position is measured in UTF-16 // code units and the client capability `columnsStartAt1` determines whether // it is 0- or 1-based. optional column; // The end position of a breakpoint location (if the location covers a range). // Position is measured in UTF-16 code units and the client capability // `columnsStartAt1` determines whether it is 0- or 1-based. optional endColumn; // The end line of breakpoint location if the location covers a range. optional endLine; // Start line of breakpoint location. integer line; }; DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocation); // Response to `breakpointLocations` request. // Contains possible locations for source breakpoints. struct BreakpointLocationsResponse : public Response { // Sorted set of possible breakpoint locations. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocationsResponse); // The `breakpointLocations` request returns all possible locations for source // breakpoints in a given range. Clients should only call this request if the // corresponding capability `supportsBreakpointLocationsRequest` is true. struct BreakpointLocationsRequest : public Request { using Response = BreakpointLocationsResponse; // Start position within `line` to search possible breakpoint locations in. It // is measured in UTF-16 code units and the client capability // `columnsStartAt1` determines whether it is 0- or 1-based. If no column is // given, the first position in the start line is assumed. optional column; // End position within `endLine` to search possible breakpoint locations in. // It is measured in UTF-16 code units and the client capability // `columnsStartAt1` determines whether it is 0- or 1-based. If no end column // is given, the last position in the end line is assumed. optional endColumn; // End line of range to search possible breakpoint locations in. If no end // line is given, then the end line is assumed to be the start line. optional endLine; // Start line of range to search possible breakpoint locations in. If only the // line is specified, the request returns all possible locations in that line. integer line; // The source location of the breakpoints; either `source.path` or // `source.reference` must be specified. Source source; }; DAP_DECLARE_STRUCT_TYPEINFO(BreakpointLocationsRequest); // Response to `cancel` request. This is just an acknowledgement, so no body // field is required. struct CancelResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(CancelResponse); // The `cancel` request is used by the client in two situations: // - to indicate that it is no longer interested in the result produced by a // specific request issued earlier // - to cancel a progress sequence. Clients should only call this request if the // corresponding capability `supportsCancelRequest` is true. This request has a // hint characteristic: a debug adapter can only be expected to make a 'best // effort' in honoring this request but there are no guarantees. The `cancel` // request may return an error if it could not cancel an operation but a client // should refrain from presenting this error to end users. The request that got // cancelled still needs to send a response back. This can either be a normal // result (`success` attribute true) or an error response (`success` attribute // false and the `message` set to `cancelled`). Returning partial results from a // cancelled request is possible but please note that a client has no generic // way for detecting that a response is partial or not. The progress that got // cancelled still needs to send a `progressEnd` event back. // A client should not assume that progress just got cancelled after sending // the `cancel` request. struct CancelRequest : public Request { using Response = CancelResponse; // The ID (attribute `progressId`) of the progress to cancel. If missing no // progress is cancelled. Both a `requestId` and a `progressId` can be // specified in one request. optional progressId; // The ID (attribute `seq`) of the request to cancel. If missing no request is // cancelled. Both a `requestId` and a `progressId` can be specified in one // request. optional requestId; }; DAP_DECLARE_STRUCT_TYPEINFO(CancelRequest); // A `ColumnDescriptor` specifies what module attribute to show in a column of // the modules view, how to format it, and what the column's label should be. It // is only used if the underlying UI actually supports this level of // customization. struct ColumnDescriptor { // Name of the attribute rendered in this column. string attributeName; // Format to use for the rendered values in this column. TBD how the format // strings looks like. optional format; // Header UI label of column. string label; // Datatype of values in this column. Defaults to `string` if not specified. // // Must be one of the following enumeration values: // 'string', 'number', 'boolean', 'unixTimestampUTC' optional type; // Width of this column in characters (hint only). optional width; }; DAP_DECLARE_STRUCT_TYPEINFO(ColumnDescriptor); // An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for // configuring how exceptions are dealt with. struct ExceptionBreakpointsFilter { // A help text providing information about the condition. This string is shown // as the placeholder text for a text box and can be translated. optional conditionDescription; // Initial value of the filter option. If not specified a value false is // assumed. optional def; // A help text providing additional information about the exception filter. // This string is typically shown as a hover and can be translated. optional description; // The internal ID of the filter option. This value is passed to the // `setExceptionBreakpoints` request. string filter; // The name of the filter option. This is shown in the UI. string label; // Controls whether a condition can be specified for this filter option. If // false or missing, a condition can not be set. optional supportsCondition; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionBreakpointsFilter); // Information about the capabilities of a debug adapter. struct Capabilities { // The set of additional module information exposed by the debug adapter. optional> additionalModuleColumns; // The set of characters that should trigger completion in a REPL. If not // specified, the UI should assume the `.` character. optional> completionTriggerCharacters; // Available exception filter options for the `setExceptionBreakpoints` // request. optional> exceptionBreakpointFilters; // The debug adapter supports the `suspendDebuggee` attribute on the // `disconnect` request. optional supportSuspendDebuggee; // The debug adapter supports the `terminateDebuggee` attribute on the // `disconnect` request. optional supportTerminateDebuggee; // Checksum algorithms supported by the debug adapter. optional> supportedChecksumAlgorithms; // The debug adapter supports the `breakpointLocations` request. optional supportsBreakpointLocationsRequest; // The debug adapter supports the `cancel` request. optional supportsCancelRequest; // The debug adapter supports the `clipboard` context value in the `evaluate` // request. optional supportsClipboardContext; // The debug adapter supports the `completions` request. optional supportsCompletionsRequest; // The debug adapter supports conditional breakpoints. optional supportsConditionalBreakpoints; // The debug adapter supports the `configurationDone` request. optional supportsConfigurationDoneRequest; // The debug adapter supports data breakpoints. optional supportsDataBreakpoints; // The debug adapter supports the delayed loading of parts of the stack, which // requires that both the `startFrame` and `levels` arguments and the // `totalFrames` result of the `stackTrace` request are supported. optional supportsDelayedStackTraceLoading; // The debug adapter supports the `disassemble` request. optional supportsDisassembleRequest; // The debug adapter supports a (side effect free) `evaluate` request for data // hovers. optional supportsEvaluateForHovers; // The debug adapter supports `filterOptions` as an argument on the // `setExceptionBreakpoints` request. optional supportsExceptionFilterOptions; // The debug adapter supports the `exceptionInfo` request. optional supportsExceptionInfoRequest; // The debug adapter supports `exceptionOptions` on the // `setExceptionBreakpoints` request. optional supportsExceptionOptions; // The debug adapter supports function breakpoints. optional supportsFunctionBreakpoints; // The debug adapter supports the `gotoTargets` request. optional supportsGotoTargetsRequest; // The debug adapter supports breakpoints that break execution after a // specified number of hits. optional supportsHitConditionalBreakpoints; // The debug adapter supports adding breakpoints based on instruction // references. optional supportsInstructionBreakpoints; // The debug adapter supports the `loadedSources` request. optional supportsLoadedSourcesRequest; // The debug adapter supports log points by interpreting the `logMessage` // attribute of the `SourceBreakpoint`. optional supportsLogPoints; // The debug adapter supports the `modules` request. optional supportsModulesRequest; // The debug adapter supports the `readMemory` request. optional supportsReadMemoryRequest; // The debug adapter supports restarting a frame. optional supportsRestartFrame; // The debug adapter supports the `restart` request. In this case a client // should not implement `restart` by terminating and relaunching the adapter // but by calling the `restart` request. optional supportsRestartRequest; // The debug adapter supports the `setExpression` request. optional supportsSetExpression; // The debug adapter supports setting a variable to a value. optional supportsSetVariable; // The debug adapter supports the `singleThread` property on the execution // requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, // `stepBack`). optional supportsSingleThreadExecutionRequests; // The debug adapter supports stepping back via the `stepBack` and // `reverseContinue` requests. optional supportsStepBack; // The debug adapter supports the `stepInTargets` request. optional supportsStepInTargetsRequest; // The debug adapter supports stepping granularities (argument `granularity`) // for the stepping requests. optional supportsSteppingGranularity; // The debug adapter supports the `terminate` request. optional supportsTerminateRequest; // The debug adapter supports the `terminateThreads` request. optional supportsTerminateThreadsRequest; // The debug adapter supports a `format` attribute on the `stackTrace`, // `variables`, and `evaluate` requests. optional supportsValueFormattingOptions; // The debug adapter supports the `writeMemory` request. optional supportsWriteMemoryRequest; }; DAP_DECLARE_STRUCT_TYPEINFO(Capabilities); // The event indicates that one or more capabilities have changed. // Since the capabilities are dependent on the client and its UI, it might not // be possible to change that at random times (or too late). Consequently this // event has a hint characteristic: a client can only be expected to make a // 'best effort' in honoring individual capabilities but there are no // guarantees. Only changed capabilities need to be included, all other // capabilities keep their values. struct CapabilitiesEvent : public Event { // The set of updated capabilities. Capabilities capabilities; }; DAP_DECLARE_STRUCT_TYPEINFO(CapabilitiesEvent); // Some predefined types for the CompletionItem. Please note that not all // clients have specific icons for all of them. // // Must be one of the following enumeration values: // 'method', 'function', 'constructor', 'field', 'variable', 'class', // 'interface', 'module', 'property', 'unit', 'value', 'enum', 'keyword', // 'snippet', 'text', 'color', 'file', 'reference', 'customcolor' using CompletionItemType = string; // `CompletionItems` are the suggestions returned from the `completions` // request. struct CompletionItem { // A human-readable string with additional information about this item, like // type or symbol information. optional detail; // The label of this completion item. By default this is also the text that is // inserted when selecting this completion. string label; // Length determines how many characters are overwritten by the completion // text and it is measured in UTF-16 code units. If missing the value 0 is // assumed which results in the completion text being inserted. optional length; // Determines the length of the new selection after the text has been inserted // (or replaced) and it is measured in UTF-16 code units. The selection can // not extend beyond the bounds of the completion text. If omitted the length // is assumed to be 0. optional selectionLength; // Determines the start of the new selection after the text has been inserted // (or replaced). `selectionStart` is measured in UTF-16 code units and must // be in the range 0 and length of the completion text. If omitted the // selection starts at the end of the completion text. optional selectionStart; // A string that should be used when comparing this item with other items. If // not returned or an empty string, the `label` is used instead. optional sortText; // Start position (within the `text` attribute of the `completions` request) // where the completion text is added. The position is measured in UTF-16 code // units and the client capability `columnsStartAt1` determines whether it is // 0- or 1-based. If the start position is omitted the text is added at the // location specified by the `column` attribute of the `completions` request. optional start; // If text is returned and not an empty string, then it is inserted instead of // the label. optional text; // The item's type. Typically the client uses this information to render the // item in the UI with an icon. optional type; }; DAP_DECLARE_STRUCT_TYPEINFO(CompletionItem); // Response to `completions` request. struct CompletionsResponse : public Response { // The possible completions for . array targets; }; DAP_DECLARE_STRUCT_TYPEINFO(CompletionsResponse); // Returns a list of possible completions for a given caret position and text. // Clients should only call this request if the corresponding capability // `supportsCompletionsRequest` is true. struct CompletionsRequest : public Request { using Response = CompletionsResponse; // The position within `text` for which to determine the completion proposals. // It is measured in UTF-16 code units and the client capability // `columnsStartAt1` determines whether it is 0- or 1-based. integer column; // Returns completions in the scope of this stack frame. If not specified, the // completions are returned for the global scope. optional frameId; // A line for which to determine the completion proposals. If missing the // first line of the text is assumed. optional line; // One or more source lines. Typically this is the text users have typed into // the debug console before they asked for completion. string text; }; DAP_DECLARE_STRUCT_TYPEINFO(CompletionsRequest); // Response to `configurationDone` request. This is just an acknowledgement, so // no body field is required. struct ConfigurationDoneResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(ConfigurationDoneResponse); // This request indicates that the client has finished initialization of the // debug adapter. So it is the last request in the sequence of configuration // requests (which was started by the `initialized` event). Clients should only // call this request if the corresponding capability // `supportsConfigurationDoneRequest` is true. struct ConfigurationDoneRequest : public Request { using Response = ConfigurationDoneResponse; }; DAP_DECLARE_STRUCT_TYPEINFO(ConfigurationDoneRequest); // Response to `continue` request. struct ContinueResponse : public Response { // The value true (or a missing property) signals to the client that all // threads have been resumed. The value false indicates that not all threads // were resumed. optional allThreadsContinued; }; DAP_DECLARE_STRUCT_TYPEINFO(ContinueResponse); // The request resumes execution of all threads. If the debug adapter supports // single thread execution (see capability // `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument // to true resumes only the specified thread. If not all threads were resumed, // the `allThreadsContinued` attribute of the response should be set to false. struct ContinueRequest : public Request { using Response = ContinueResponse; // If this flag is true, execution is resumed only for the thread with given // `threadId`. optional singleThread; // Specifies the active thread. If the debug adapter supports single thread // execution (see `supportsSingleThreadExecutionRequests`) and the argument // `singleThread` is true, only the thread with this ID is resumed. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(ContinueRequest); // The event indicates that the execution of the debuggee has continued. // Please note: a debug adapter is not expected to send this event in response // to a request that implies that execution continues, e.g. `launch` or // `continue`. It is only necessary to send a `continued` event if there was no // previous request that implied this. struct ContinuedEvent : public Event { // If `allThreadsContinued` is true, a debug adapter can announce that all // threads have continued. optional allThreadsContinued; // The thread which was continued. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(ContinuedEvent); // This enumeration defines all possible access types for data breakpoints. // // Must be one of the following enumeration values: // 'read', 'write', 'readWrite' using DataBreakpointAccessType = string; // Response to `dataBreakpointInfo` request. struct DataBreakpointInfoResponse : public Response { // Attribute lists the available access types for a potential data breakpoint. // A UI client could surface this information. optional> accessTypes; // Attribute indicates that a potential data breakpoint could be persisted // across sessions. optional canPersist; // An identifier for the data on which a data breakpoint can be registered // with the `setDataBreakpoints` request or null if no data breakpoint is // available. variant dataId; // UI string that describes on what data the breakpoint is set on or why a // data breakpoint is not available. string description; }; DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpointInfoResponse); // Obtains information on a possible data breakpoint that could be set on an // expression or variable. Clients should only call this request if the // corresponding capability `supportsDataBreakpoints` is true. struct DataBreakpointInfoRequest : public Request { using Response = DataBreakpointInfoResponse; // When `name` is an expression, evaluate it in the scope of this stack frame. // If not specified, the expression is evaluated in the global scope. When // `variablesReference` is specified, this property has no effect. optional frameId; // The name of the variable's child to obtain data breakpoint information for. // If `variablesReference` isn't specified, this can be an expression. string name; // Reference to the variable container if the data breakpoint is requested for // a child of the container. The `variablesReference` must have been obtained // in the current suspended state. See 'Lifetime of Object References' in the // Overview section for details. optional variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpointInfoRequest); // Represents a single disassembled instruction. struct DisassembledInstruction { // The address of the instruction. Treated as a hex value if prefixed with // `0x`, or as a decimal value otherwise. string address; // The column within the line that corresponds to this instruction, if any. optional column; // The end column of the range that corresponds to this instruction, if any. optional endColumn; // The end line of the range that corresponds to this instruction, if any. optional endLine; // Text representing the instruction and its operands, in an // implementation-defined format. string instruction; // Raw bytes representing the instruction and its operands, in an // implementation-defined format. optional instructionBytes; // The line within the source location that corresponds to this instruction, // if any. optional line; // Source location that corresponds to this instruction, if any. // Should always be set (if available) on the first instruction returned, // but can be omitted afterwards if this instruction maps to the same source // file as the previous instruction. optional location; // Name of the symbol that corresponds with the location of this instruction, // if any. optional symbol; }; DAP_DECLARE_STRUCT_TYPEINFO(DisassembledInstruction); // Response to `disassemble` request. struct DisassembleResponse : public Response { // The list of disassembled instructions. array instructions; }; DAP_DECLARE_STRUCT_TYPEINFO(DisassembleResponse); // Disassembles code stored at the provided location. // Clients should only call this request if the corresponding capability // `supportsDisassembleRequest` is true. struct DisassembleRequest : public Request { using Response = DisassembleResponse; // Number of instructions to disassemble starting at the specified location // and offset. An adapter must return exactly this number of instructions - // any unavailable instructions should be replaced with an // implementation-defined 'invalid instruction' value. integer instructionCount; // Offset (in instructions) to be applied after the byte offset (if any) // before disassembling. Can be negative. optional instructionOffset; // Memory reference to the base location containing the instructions to // disassemble. string memoryReference; // Offset (in bytes) to be applied to the reference location before // disassembling. Can be negative. optional offset; // If true, the adapter should attempt to resolve memory addresses and other // values to symbolic names. optional resolveSymbols; }; DAP_DECLARE_STRUCT_TYPEINFO(DisassembleRequest); // Response to `disconnect` request. This is just an acknowledgement, so no body // field is required. struct DisconnectResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(DisconnectResponse); // The `disconnect` request asks the debug adapter to disconnect from the // debuggee (thus ending the debug session) and then to shut down itself (the // debug adapter). In addition, the debug adapter must terminate the debuggee if // it was started with the `launch` request. If an `attach` request was used to // connect to the debuggee, then the debug adapter must not terminate the // debuggee. This implicit behavior of when to terminate the debuggee can be // overridden with the `terminateDebuggee` argument (which is only supported by // a debug adapter if the corresponding capability `supportTerminateDebuggee` is // true). struct DisconnectRequest : public Request { using Response = DisconnectResponse; // A value of true indicates that this `disconnect` request is part of a // restart sequence. optional restart; // Indicates whether the debuggee should stay suspended when the debugger is // disconnected. If unspecified, the debuggee should resume execution. The // attribute is only honored by a debug adapter if the corresponding // capability `supportSuspendDebuggee` is true. optional suspendDebuggee; // Indicates whether the debuggee should be terminated when the debugger is // disconnected. If unspecified, the debug adapter is free to do whatever it // thinks is best. The attribute is only honored by a debug adapter if the // corresponding capability `supportTerminateDebuggee` is true. optional terminateDebuggee; }; DAP_DECLARE_STRUCT_TYPEINFO(DisconnectRequest); // A structured message object. Used to return errors from requests. struct Message { // A format string for the message. Embedded variables have the form `{name}`. // If variable name starts with an underscore character, the variable does not // contain user data (PII) and can be safely used for telemetry purposes. string format; // Unique (within a debug adapter implementation) identifier for the message. // The purpose of these error IDs is to help extension authors that have the // requirement that every user visible error message needs a corresponding // error number, so that users or customer support can find information about // the specific error more easily. integer id; // If true send to telemetry. optional sendTelemetry; // If true show user. optional showUser; // A url where additional information about this message can be found. optional url; // A label that is presented to the user as the UI for opening the url. optional urlLabel; // An object used as a dictionary for looking up the variables in the format // string. optional variables; }; DAP_DECLARE_STRUCT_TYPEINFO(Message); // On error (whenever `success` is false), the body can provide more details. struct ErrorResponse : public Response { // A structured error message. optional error; }; DAP_DECLARE_STRUCT_TYPEINFO(ErrorResponse); // Properties of a variable that can be used to determine how to render the // variable in the UI. struct VariablePresentationHint { // Set of attributes represented as an array of strings. Before introducing // additional values, try to use the listed values. optional> attributes; // The kind of variable. Before introducing additional values, try to use the // listed values. // // May be one of the following enumeration values: // 'property', 'method', 'class', 'data', 'event', 'baseClass', 'innerClass', // 'interface', 'mostDerivedClass', 'virtual', 'dataBreakpoint' optional kind; // If true, clients can present the variable with a UI that supports a // specific gesture to trigger its evaluation. This mechanism can be used for // properties that require executing code when retrieving their value and // where the code execution can be expensive and/or produce side-effects. A // typical example are properties based on a getter function. Please note that // in addition to the `lazy` flag, the variable's `variablesReference` is // expected to refer to a variable that will provide the value through another // `variable` request. optional lazy; // Visibility of variable. Before introducing additional values, try to use // the listed values. // // May be one of the following enumeration values: // 'public', 'private', 'protected', 'internal', 'final' optional visibility; }; DAP_DECLARE_STRUCT_TYPEINFO(VariablePresentationHint); // Response to `evaluate` request. struct EvaluateResponse : public Response { // The number of indexed child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional indexedVariables; // A memory reference to a location appropriate for this result. // For pointer type eval results, this is generally a reference to the memory // address contained in the pointer. This attribute should be returned by a // debug adapter if corresponding capability `supportsMemoryReferences` is // true. optional memoryReference; // The number of named child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional namedVariables; // Properties of an evaluate result that can be used to determine how to // render the result in the UI. optional presentationHint; // The result of the evaluate request. string result; // The type of the evaluate result. // This attribute should only be returned by a debug adapter if the // corresponding capability `supportsVariableType` is true. optional type; // If `variablesReference` is > 0, the evaluate result is structured and its // children can be retrieved by passing `variablesReference` to the // `variables` request as long as execution remains suspended. See 'Lifetime // of Object References' in the Overview section for details. integer variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(EvaluateResponse); // Provides formatting information for a value. struct ValueFormat { // Display the value in hex. optional hex; }; DAP_DECLARE_STRUCT_TYPEINFO(ValueFormat); // Evaluates the given expression in the context of the topmost stack frame. // The expression has access to any variables and arguments that are in scope. struct EvaluateRequest : public Request { using Response = EvaluateResponse; // The context in which the evaluate request is used. // // May be one of the following enumeration values: // 'watch', 'repl', 'hover', 'clipboard', 'variables' optional context; // The expression to evaluate. string expression; // Specifies details on how to format the result. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsValueFormattingOptions` is true. optional format; // Evaluate the expression in the scope of this stack frame. If not specified, // the expression is evaluated in the global scope. optional frameId; }; DAP_DECLARE_STRUCT_TYPEINFO(EvaluateRequest); // This enumeration defines all possible conditions when a thrown exception // should result in a break. never: never breaks, always: always breaks, // unhandled: breaks when exception unhandled, // userUnhandled: breaks if the exception is not handled by user code. // // Must be one of the following enumeration values: // 'never', 'always', 'unhandled', 'userUnhandled' using ExceptionBreakMode = string; // Detailed information about an exception that has occurred. struct ExceptionDetails { // An expression that can be evaluated in the current scope to obtain the // exception object. optional evaluateName; // Fully-qualified type name of the exception object. optional fullTypeName; // Details of the exception contained by this exception, if any. optional> innerException; // Message contained in the exception. optional message; // Stack trace at the time the exception was thrown. optional stackTrace; // Short type name of the exception object. optional typeName; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionDetails); // Response to `exceptionInfo` request. struct ExceptionInfoResponse : public Response { // Mode that caused the exception notification to be raised. ExceptionBreakMode breakMode = "never"; // Descriptive text for the exception. optional description; // Detailed information about the exception. optional details; // ID of the exception that was thrown. string exceptionId; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionInfoResponse); // Retrieves the details of the exception that caused this event to be raised. // Clients should only call this request if the corresponding capability // `supportsExceptionInfoRequest` is true. struct ExceptionInfoRequest : public Request { using Response = ExceptionInfoResponse; // Thread for which exception information should be retrieved. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionInfoRequest); // The event indicates that the debuggee has exited and returns its exit code. struct ExitedEvent : public Event { // The exit code returned from the debuggee. integer exitCode; }; DAP_DECLARE_STRUCT_TYPEINFO(ExitedEvent); // Response to `goto` request. This is just an acknowledgement, so no body field // is required. struct GotoResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(GotoResponse); // The request sets the location where the debuggee will continue to run. // This makes it possible to skip the execution of code or to execute code // again. The code between the current location and the goto target is not // executed but skipped. The debug adapter first sends the response and then a // `stopped` event with reason `goto`. Clients should only call this request if // the corresponding capability `supportsGotoTargetsRequest` is true (because // only then goto targets exist that can be passed as arguments). struct GotoRequest : public Request { using Response = GotoResponse; // The location where the debuggee will continue to run. integer targetId; // Set the goto target for this thread. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(GotoRequest); // A `GotoTarget` describes a code location that can be used as a target in the // `goto` request. The possible goto targets can be determined via the // `gotoTargets` request. struct GotoTarget { // The column of the goto target. optional column; // The end column of the range covered by the goto target. optional endColumn; // The end line of the range covered by the goto target. optional endLine; // Unique identifier for a goto target. This is used in the `goto` request. integer id; // A memory reference for the instruction pointer value represented by this // target. optional instructionPointerReference; // The name of the goto target (shown in the UI). string label; // The line of the goto target. integer line; }; DAP_DECLARE_STRUCT_TYPEINFO(GotoTarget); // Response to `gotoTargets` request. struct GotoTargetsResponse : public Response { // The possible goto targets of the specified location. array targets; }; DAP_DECLARE_STRUCT_TYPEINFO(GotoTargetsResponse); // This request retrieves the possible goto targets for the specified source // location. These targets can be used in the `goto` request. Clients should // only call this request if the corresponding capability // `supportsGotoTargetsRequest` is true. struct GotoTargetsRequest : public Request { using Response = GotoTargetsResponse; // The position within `line` for which the goto targets are determined. It is // measured in UTF-16 code units and the client capability `columnsStartAt1` // determines whether it is 0- or 1-based. optional column; // The line location for which the goto targets are determined. integer line; // The source location for which the goto targets are determined. Source source; }; DAP_DECLARE_STRUCT_TYPEINFO(GotoTargetsRequest); // Response to `initialize` request. struct InitializeResponse : public Response { // The set of additional module information exposed by the debug adapter. optional> additionalModuleColumns; // The set of characters that should trigger completion in a REPL. If not // specified, the UI should assume the `.` character. optional> completionTriggerCharacters; // Available exception filter options for the `setExceptionBreakpoints` // request. optional> exceptionBreakpointFilters; // The debug adapter supports the `suspendDebuggee` attribute on the // `disconnect` request. optional supportSuspendDebuggee; // The debug adapter supports the `terminateDebuggee` attribute on the // `disconnect` request. optional supportTerminateDebuggee; // Checksum algorithms supported by the debug adapter. optional> supportedChecksumAlgorithms; // The debug adapter supports the `breakpointLocations` request. optional supportsBreakpointLocationsRequest; // The debug adapter supports the `cancel` request. optional supportsCancelRequest; // The debug adapter supports the `clipboard` context value in the `evaluate` // request. optional supportsClipboardContext; // The debug adapter supports the `completions` request. optional supportsCompletionsRequest; // The debug adapter supports conditional breakpoints. optional supportsConditionalBreakpoints; // The debug adapter supports the `configurationDone` request. optional supportsConfigurationDoneRequest; // The debug adapter supports data breakpoints. optional supportsDataBreakpoints; // The debug adapter supports the delayed loading of parts of the stack, which // requires that both the `startFrame` and `levels` arguments and the // `totalFrames` result of the `stackTrace` request are supported. optional supportsDelayedStackTraceLoading; // The debug adapter supports the `disassemble` request. optional supportsDisassembleRequest; // The debug adapter supports a (side effect free) `evaluate` request for data // hovers. optional supportsEvaluateForHovers; // The debug adapter supports `filterOptions` as an argument on the // `setExceptionBreakpoints` request. optional supportsExceptionFilterOptions; // The debug adapter supports the `exceptionInfo` request. optional supportsExceptionInfoRequest; // The debug adapter supports `exceptionOptions` on the // `setExceptionBreakpoints` request. optional supportsExceptionOptions; // The debug adapter supports function breakpoints. optional supportsFunctionBreakpoints; // The debug adapter supports the `gotoTargets` request. optional supportsGotoTargetsRequest; // The debug adapter supports breakpoints that break execution after a // specified number of hits. optional supportsHitConditionalBreakpoints; // The debug adapter supports adding breakpoints based on instruction // references. optional supportsInstructionBreakpoints; // The debug adapter supports the `loadedSources` request. optional supportsLoadedSourcesRequest; // The debug adapter supports log points by interpreting the `logMessage` // attribute of the `SourceBreakpoint`. optional supportsLogPoints; // The debug adapter supports the `modules` request. optional supportsModulesRequest; // The debug adapter supports the `readMemory` request. optional supportsReadMemoryRequest; // The debug adapter supports restarting a frame. optional supportsRestartFrame; // The debug adapter supports the `restart` request. In this case a client // should not implement `restart` by terminating and relaunching the adapter // but by calling the `restart` request. optional supportsRestartRequest; // The debug adapter supports the `setExpression` request. optional supportsSetExpression; // The debug adapter supports setting a variable to a value. optional supportsSetVariable; // The debug adapter supports the `singleThread` property on the execution // requests (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, // `stepBack`). optional supportsSingleThreadExecutionRequests; // The debug adapter supports stepping back via the `stepBack` and // `reverseContinue` requests. optional supportsStepBack; // The debug adapter supports the `stepInTargets` request. optional supportsStepInTargetsRequest; // The debug adapter supports stepping granularities (argument `granularity`) // for the stepping requests. optional supportsSteppingGranularity; // The debug adapter supports the `terminate` request. optional supportsTerminateRequest; // The debug adapter supports the `terminateThreads` request. optional supportsTerminateThreadsRequest; // The debug adapter supports a `format` attribute on the `stackTrace`, // `variables`, and `evaluate` requests. optional supportsValueFormattingOptions; // The debug adapter supports the `writeMemory` request. optional supportsWriteMemoryRequest; }; DAP_DECLARE_STRUCT_TYPEINFO(InitializeResponse); // The `initialize` request is sent as the first request from the client to the // debug adapter in order to configure it with client capabilities and to // retrieve capabilities from the debug adapter. Until the debug adapter has // responded with an `initialize` response, the client must not send any // additional requests or events to the debug adapter. In addition the debug // adapter is not allowed to send any requests or events to the client until it // has responded with an `initialize` response. The `initialize` request may // only be sent once. struct InitializeRequest : public Request { using Response = InitializeResponse; // The ID of the debug adapter. string adapterID; // The ID of the client using this adapter. optional clientID; // The human-readable name of the client using this adapter. optional clientName; // If true all column numbers are 1-based (default). optional columnsStartAt1; // If true all line numbers are 1-based (default). optional linesStartAt1; // The ISO-639 locale of the client using this adapter, e.g. en-US or de-CH. optional locale; // Determines in what format paths are specified. The default is `path`, which // is the native format. // // May be one of the following enumeration values: // 'path', 'uri' optional pathFormat; // Client supports the `argsCanBeInterpretedByShell` attribute on the // `runInTerminal` request. optional supportsArgsCanBeInterpretedByShell; // Client supports the `invalidated` event. optional supportsInvalidatedEvent; // Client supports the `memory` event. optional supportsMemoryEvent; // Client supports memory references. optional supportsMemoryReferences; // Client supports progress reporting. optional supportsProgressReporting; // Client supports the `runInTerminal` request. optional supportsRunInTerminalRequest; // Client supports the `startDebugging` request. optional supportsStartDebuggingRequest; // Client supports the paging of variables. optional supportsVariablePaging; // Client supports the `type` attribute for variables. optional supportsVariableType; }; DAP_DECLARE_STRUCT_TYPEINFO(InitializeRequest); // This event indicates that the debug adapter is ready to accept configuration // requests (e.g. `setBreakpoints`, `setExceptionBreakpoints`). A debug adapter // is expected to send this event when it is ready to accept configuration // requests (but not before the `initialize` request has finished). The sequence // of events/requests is as follows: // - adapters sends `initialized` event (after the `initialize` request has // returned) // - client sends zero or more `setBreakpoints` requests // - client sends one `setFunctionBreakpoints` request (if corresponding // capability `supportsFunctionBreakpoints` is true) // - client sends a `setExceptionBreakpoints` request if one or more // `exceptionBreakpointFilters` have been defined (or if // `supportsConfigurationDoneRequest` is not true) // - client sends other future configuration requests // - client sends one `configurationDone` request to indicate the end of the // configuration. struct InitializedEvent : public Event {}; DAP_DECLARE_STRUCT_TYPEINFO(InitializedEvent); // Logical areas that can be invalidated by the `invalidated` event. using InvalidatedAreas = string; // This event signals that some state in the debug adapter has changed and // requires that the client needs to re-render the data snapshot previously // requested. Debug adapters do not have to emit this event for runtime changes // like stopped or thread events because in that case the client refetches the // new state anyway. But the event can be used for example to refresh the UI // after rendering formatting has changed in the debug adapter. This event // should only be sent if the corresponding capability // `supportsInvalidatedEvent` is true. struct InvalidatedEvent : public Event { // Set of logical areas that got invalidated. This property has a hint // characteristic: a client can only be expected to make a 'best effort' in // honoring the areas but there are no guarantees. If this property is // missing, empty, or if values are not understood, the client should assume a // single value `all`. optional> areas; // If specified, the client only needs to refetch data related to this stack // frame (and the `threadId` is ignored). optional stackFrameId; // If specified, the client only needs to refetch data related to this thread. optional threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(InvalidatedEvent); // Response to `launch` request. This is just an acknowledgement, so no body // field is required. struct LaunchResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(LaunchResponse); // This launch request is sent from the client to the debug adapter to start the // debuggee with or without debugging (if `noDebug` is true). Since launching is // debugger/runtime specific, the arguments for this request are not part of // this specification. struct LaunchRequest : public Request { using Response = LaunchResponse; // 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; // If true, the launch request should launch the program without enabling // debugging. optional noDebug; }; DAP_DECLARE_STRUCT_TYPEINFO(LaunchRequest); // The event indicates that some source has been added, changed, or removed from // the set of all loaded sources. struct LoadedSourceEvent : public Event { // The reason for the event. // // Must be one of the following enumeration values: // 'new', 'changed', 'removed' string reason = "new"; // The new, changed, or removed source. Source source; }; DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourceEvent); // Response to `loadedSources` request. struct LoadedSourcesResponse : public Response { // Set of loaded sources. array sources; }; DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourcesResponse); // Retrieves the set of all sources currently loaded by the debugged process. // Clients should only call this request if the corresponding capability // `supportsLoadedSourcesRequest` is true. struct LoadedSourcesRequest : public Request { using Response = LoadedSourcesResponse; }; DAP_DECLARE_STRUCT_TYPEINFO(LoadedSourcesRequest); // This event indicates that some memory range has been updated. It should only // be sent if the corresponding capability `supportsMemoryEvent` is true. // Clients typically react to the event by re-issuing a `readMemory` request if // they show the memory identified by the `memoryReference` and if the updated // memory range overlaps the displayed range. Clients should not make // assumptions how individual memory references relate to each other, so they // should not assume that they are part of a single continuous address range and // might overlap. Debug adapters can use this event to indicate that the // contents of a memory range has changed due to some other request like // `setVariable` or `setExpression`. Debug adapters are not expected to emit // this event for each and every memory change of a running program, because // that information is typically not available from debuggers and it would flood // clients with too many events. struct MemoryEvent : public Event { // Number of bytes updated. integer count; // Memory reference of a memory range that has been updated. string memoryReference; // Starting offset in bytes where memory has been updated. Can be negative. integer offset; }; DAP_DECLARE_STRUCT_TYPEINFO(MemoryEvent); // A Module object represents a row in the modules view. // The `id` attribute identifies a module in the modules view and is used in a // `module` event for identifying a module for adding, updating or deleting. The // `name` attribute is used to minimally render the module in the UI. // // Additional attributes can be added to the module. They show up in the module // view if they have a corresponding `ColumnDescriptor`. // // To avoid an unnecessary proliferation of additional attributes with similar // semantics but different names, we recommend to re-use attributes from the // 'recommended' list below first, and only introduce new attributes if nothing // appropriate could be found. struct Module { // Address range covered by this module. optional addressRange; // Module created or modified, encoded as a RFC 3339 timestamp. optional dateTimeStamp; // Unique identifier for the module. variant id; // True if the module is optimized. optional isOptimized; // True if the module is considered 'user code' by a debugger that supports // 'Just My Code'. optional isUserCode; // A name of the module. string name; // Logical full path to the module. The exact definition is implementation // defined, but usually this would be a full path to the on-disk file for the // module. optional path; // Logical full path to the symbol file. The exact definition is // implementation defined. optional symbolFilePath; // User-understandable description of if symbols were found for the module // (ex: 'Symbols Loaded', 'Symbols not found', etc.) optional symbolStatus; // Version of Module. optional version; }; DAP_DECLARE_STRUCT_TYPEINFO(Module); // The event indicates that some information about a module has changed. struct ModuleEvent : public Event { // The new, changed, or removed module. In case of `removed` only the module // id is used. Module module; // The reason for the event. // // Must be one of the following enumeration values: // 'new', 'changed', 'removed' string reason = "new"; }; DAP_DECLARE_STRUCT_TYPEINFO(ModuleEvent); // Response to `modules` request. struct ModulesResponse : public Response { // All modules or range of modules. array modules; // The total number of modules available. optional totalModules; }; DAP_DECLARE_STRUCT_TYPEINFO(ModulesResponse); // Modules can be retrieved from the debug adapter with this request which can // either return all modules or a range of modules to support paging. Clients // should only call this request if the corresponding capability // `supportsModulesRequest` is true. struct ModulesRequest : public Request { using Response = ModulesResponse; // The number of modules to return. If `moduleCount` is not specified or 0, // all modules are returned. optional moduleCount; // The index of the first module to return; if omitted modules start at 0. optional startModule; }; DAP_DECLARE_STRUCT_TYPEINFO(ModulesRequest); // Response to `next` request. This is just an acknowledgement, so no body field // is required. struct NextResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(NextResponse); // The granularity of one 'step' in the stepping requests `next`, `stepIn`, // `stepOut`, and `stepBack`. // // Must be one of the following enumeration values: // 'statement', 'line', 'instruction' using SteppingGranularity = string; // The request executes one step (in the given granularity) for the specified // thread and allows all other threads to run freely by resuming them. If the // debug adapter supports single thread execution (see capability // `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument // to true prevents other suspended threads from resuming. The debug adapter // first sends the response and then a `stopped` event (with reason `step`) // after the step has completed. struct NextRequest : public Request { using Response = NextResponse; // Stepping granularity. If no granularity is specified, a granularity of // `statement` is assumed. optional granularity; // If this flag is true, all other suspended threads are not resumed. optional singleThread; // Specifies the thread for which to resume execution for one step (of the // given granularity). integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(NextRequest); // The event indicates that the target has produced some output. struct OutputEvent : public Event { // The output category. If not specified or if the category is not understood // by the client, `console` is assumed. // // May be one of the following enumeration values: // 'console', 'important', 'stdout', 'stderr', 'telemetry' optional category; // The position in `line` where the output was produced. It is measured in // UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. optional column; // Additional data to report. For the `telemetry` category the data is sent to // telemetry, for the other categories the data is shown in JSON format. optional, boolean, integer, null, number, object, string>> data; // Support for keeping an output log organized by grouping related messages. // // Must be one of the following enumeration values: // 'start', 'startCollapsed', 'end' optional group; // The source location's line where the output was produced. optional line; // The output to report. string output; // The source location where the output was produced. optional source; // If an attribute `variablesReference` exists and its value is > 0, the // output contains objects which can be retrieved by passing // `variablesReference` to the `variables` request as long as execution // remains suspended. See 'Lifetime of Object References' in the Overview // section for details. optional variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(OutputEvent); // Response to `pause` request. This is just an acknowledgement, so no body // field is required. struct PauseResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(PauseResponse); // The request suspends the debuggee. // The debug adapter first sends the response and then a `stopped` event (with // reason `pause`) after the thread has been paused successfully. struct PauseRequest : public Request { using Response = PauseResponse; // Pause execution for this thread. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(PauseRequest); // The event indicates that the debugger has begun debugging a new process. // Either one that it has launched, or one that it has attached to. struct ProcessEvent : public Event { // If true, the process is running on the same computer as the debug adapter. optional isLocalProcess; // The logical name of the process. This is usually the full path to process's // executable file. Example: /home/example/myproj/program.js. string name; // The size of a pointer or address for this process, in bits. This value may // be used by clients when formatting addresses for display. optional pointerSize; // Describes how the debug engine started debugging this process. // // Must be one of the following enumeration values: // 'launch', 'attach', 'attachForSuspendedLaunch' optional startMethod; // The system process id of the debugged process. This property is missing for // non-system processes. optional systemProcessId; }; DAP_DECLARE_STRUCT_TYPEINFO(ProcessEvent); // The event signals the end of the progress reporting with a final message. // This event should only be sent if the corresponding capability // `supportsProgressReporting` is true. struct ProgressEndEvent : public Event { // More detailed progress message. If omitted, the previous message (if any) // is used. optional message; // The ID that was introduced in the initial `ProgressStartEvent`. string progressId; }; DAP_DECLARE_STRUCT_TYPEINFO(ProgressEndEvent); // The event signals that a long running operation is about to start and // provides additional information for the client to set up a corresponding // progress and cancellation UI. The client is free to delay the showing of the // UI in order to reduce flicker. This event should only be sent if the // corresponding capability `supportsProgressReporting` is true. struct ProgressStartEvent : public Event { // If true, the request that reports progress may be cancelled with a `cancel` // request. So this property basically controls whether the client should use // UX that supports cancellation. Clients that don't support cancellation are // allowed to ignore the setting. optional cancellable; // More detailed progress message. optional message; // Progress percentage to display (value range: 0 to 100). If omitted no // percentage is shown. optional percentage; // An ID that can be used in subsequent `progressUpdate` and `progressEnd` // events to make them refer to the same progress reporting. IDs must be // unique within a debug session. string progressId; // The request ID that this progress report is related to. If specified a // debug adapter is expected to emit progress events for the long running // request until the request has been either completed or cancelled. If the // request ID is omitted, the progress report is assumed to be related to some // general activity of the debug adapter. optional requestId; // Short title of the progress reporting. Shown in the UI to describe the long // running operation. string title; }; DAP_DECLARE_STRUCT_TYPEINFO(ProgressStartEvent); // The event signals that the progress reporting needs to be updated with a new // message and/or percentage. The client does not have to update the UI // immediately, but the clients needs to keep track of the message and/or // percentage values. This event should only be sent if the corresponding // capability `supportsProgressReporting` is true. struct ProgressUpdateEvent : public Event { // More detailed progress message. If omitted, the previous message (if any) // is used. optional message; // Progress percentage to display (value range: 0 to 100). If omitted no // percentage is shown. optional percentage; // The ID that was introduced in the initial `progressStart` event. string progressId; }; DAP_DECLARE_STRUCT_TYPEINFO(ProgressUpdateEvent); // Response to `readMemory` request. struct ReadMemoryResponse : public Response { // The address of the first byte of data returned. // Treated as a hex value if prefixed with `0x`, or as a decimal value // otherwise. string address; // The bytes read from memory, encoded using base64. If the decoded length of // `data` is less than the requested `count` in the original `readMemory` // request, and `unreadableBytes` is zero or omitted, then the client should // assume it's reached the end of readable memory. optional data; // The number of unreadable bytes encountered after the last successfully read // byte. This can be used to determine the number of bytes that should be // skipped before a subsequent `readMemory` request succeeds. optional unreadableBytes; }; DAP_DECLARE_STRUCT_TYPEINFO(ReadMemoryResponse); // Reads bytes from memory at the provided location. // Clients should only call this request if the corresponding capability // `supportsReadMemoryRequest` is true. struct ReadMemoryRequest : public Request { using Response = ReadMemoryResponse; // Number of bytes to read at the specified location and offset. integer count; // Memory reference to the base location from which data should be read. string memoryReference; // Offset (in bytes) to be applied to the reference location before reading // data. Can be negative. optional offset; }; DAP_DECLARE_STRUCT_TYPEINFO(ReadMemoryRequest); // Response to `restartFrame` request. This is just an acknowledgement, so no // body field is required. struct RestartFrameResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(RestartFrameResponse); // The request restarts execution of the specified stack frame. // The debug adapter first sends the response and then a `stopped` event (with // reason `restart`) after the restart has completed. Clients should only call // this request if the corresponding capability `supportsRestartFrame` is true. struct RestartFrameRequest : public Request { using Response = RestartFrameResponse; // Restart the stack frame identified by `frameId`. The `frameId` must have // been obtained in the current suspended state. See 'Lifetime of Object // References' in the Overview section for details. integer frameId; }; DAP_DECLARE_STRUCT_TYPEINFO(RestartFrameRequest); // Response to `restart` request. This is just an acknowledgement, so no body // field is required. struct RestartResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(RestartResponse); // Restarts a debug session. Clients should only call this request if the // corresponding capability `supportsRestartRequest` is true. If the capability // is missing or has the value false, a typical client emulates `restart` by // terminating the debug adapter first and then launching it anew. struct RestartRequest : public Request { using Response = RestartResponse; // The latest version of the `launch` or `attach` configuration. optional arguments; }; DAP_DECLARE_STRUCT_TYPEINFO(RestartRequest); // Response to `reverseContinue` request. This is just an acknowledgement, so no // body field is required. struct ReverseContinueResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(ReverseContinueResponse); // The request resumes backward execution of all threads. If the debug adapter // supports single thread execution (see capability // `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument // to true resumes only the specified thread. If not all threads were resumed, // the `allThreadsContinued` attribute of the response should be set to false. // Clients should only call this request if the corresponding capability // `supportsStepBack` is true. struct ReverseContinueRequest : public Request { using Response = ReverseContinueResponse; // If this flag is true, backward execution is resumed only for the thread // with given `threadId`. optional singleThread; // Specifies the active thread. If the debug adapter supports single thread // execution (see `supportsSingleThreadExecutionRequests`) and the // `singleThread` argument is true, only the thread with this ID is resumed. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(ReverseContinueRequest); // Response to `runInTerminal` request. struct RunInTerminalResponse : public Response { // The process ID. The value should be less than or equal to 2147483647 // (2^31-1). optional processId; // The process ID of the terminal shell. The value should be less than or // equal to 2147483647 (2^31-1). optional shellProcessId; }; DAP_DECLARE_STRUCT_TYPEINFO(RunInTerminalResponse); // This request is sent from the debug adapter to the client to run a command in // a terminal. This is typically used to launch the debuggee in a terminal // provided by the client. This request should only be called if the // corresponding client capability `supportsRunInTerminalRequest` is true. // Client implementations of `runInTerminal` are free to run the command however // they choose including issuing the command to a command line interpreter (aka // 'shell'). Argument strings passed to the `runInTerminal` request must arrive // verbatim in the command to be run. As a consequence, clients which use a // shell are responsible for escaping any special shell characters in the // argument strings to prevent them from being interpreted (and modified) by the // shell. Some users may wish to take advantage of shell processing in the // argument strings. For clients which implement `runInTerminal` using an // intermediary shell, the `argsCanBeInterpretedByShell` property can be set to // true. In this case the client is requested not to escape any special shell // characters in the argument strings. struct RunInTerminalRequest : public Request { using Response = RunInTerminalResponse; // List of arguments. The first argument is the command to run. array args; // This property should only be set if the corresponding capability // `supportsArgsCanBeInterpretedByShell` is true. If the client uses an // intermediary shell to launch the application, then the client must not // attempt to escape characters with special meanings for the shell. The user // is fully responsible for escaping as needed and that arguments using // special characters may not be portable across shells. optional argsCanBeInterpretedByShell; // Working directory for the command. For non-empty, valid paths this // typically results in execution of a change directory command. string cwd; // Environment key-value pairs that are added to or removed from the default // environment. optional env; // What kind of terminal to launch. Defaults to `integrated` if not specified. // // Must be one of the following enumeration values: // 'integrated', 'external' optional kind; // Title of the terminal. optional title; }; DAP_DECLARE_STRUCT_TYPEINFO(RunInTerminalRequest); // A `Scope` is a named container for variables. Optionally a scope can map to a // source or a range within a source. struct Scope { // Start position of the range covered by the scope. It is measured in UTF-16 // code units and the client capability `columnsStartAt1` determines whether // it is 0- or 1-based. optional column; // End position of the range covered by the scope. It is measured in UTF-16 // code units and the client capability `columnsStartAt1` determines whether // it is 0- or 1-based. optional endColumn; // The end line of the range covered by this scope. optional endLine; // If true, the number of variables in this scope is large or expensive to // retrieve. boolean expensive; // The number of indexed variables in this scope. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. optional indexedVariables; // The start line of the range covered by this scope. optional line; // Name of the scope such as 'Arguments', 'Locals', or 'Registers'. This // string is shown in the UI as is and can be translated. string name; // The number of named variables in this scope. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. optional namedVariables; // A hint for how to present this scope in the UI. If this attribute is // missing, the scope is shown with a generic UI. // // May be one of the following enumeration values: // 'arguments', 'locals', 'registers' optional presentationHint; // The source for this scope. optional source; // The variables of this scope can be retrieved by passing the value of // `variablesReference` to the `variables` request as long as execution // remains suspended. See 'Lifetime of Object References' in the Overview // section for details. integer variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(Scope); // Response to `scopes` request. struct ScopesResponse : public Response { // The scopes of the stack frame. If the array has length zero, there are no // scopes available. array scopes; }; DAP_DECLARE_STRUCT_TYPEINFO(ScopesResponse); // The request returns the variable scopes for a given stack frame ID. struct ScopesRequest : public Request { using Response = ScopesResponse; // Retrieve the scopes for the stack frame identified by `frameId`. The // `frameId` must have been obtained in the current suspended state. See // 'Lifetime of Object References' in the Overview section for details. integer frameId; }; DAP_DECLARE_STRUCT_TYPEINFO(ScopesRequest); // Response to `setBreakpoints` request. // Returned is information about each breakpoint created by this request. // This includes the actual code location and whether the breakpoint could be // verified. The breakpoints returned are in the same order as the elements of // the `breakpoints` (or the deprecated `lines`) array in the arguments. struct SetBreakpointsResponse : public Response { // Information about the breakpoints. // The array elements are in the same order as the elements of the // `breakpoints` (or the deprecated `lines`) array in the arguments. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetBreakpointsResponse); // Properties of a breakpoint or logpoint passed to the `setBreakpoints` // request. struct SourceBreakpoint { // Start position within source line of the breakpoint or logpoint. It is // measured in UTF-16 code units and the client capability `columnsStartAt1` // determines whether it is 0- or 1-based. optional column; // The expression for conditional breakpoints. // It is only honored by a debug adapter if the corresponding capability // `supportsConditionalBreakpoints` is true. optional condition; // The expression that controls how many hits of the breakpoint are ignored. // The debug adapter is expected to interpret the expression as needed. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsHitConditionalBreakpoints` is true. If both this // property and `condition` are specified, `hitCondition` should be evaluated // only if the `condition` is met, and the debug adapter should stop only if // both conditions are met. optional hitCondition; // The source line of the breakpoint or logpoint. integer line; // If this attribute exists and is non-empty, the debug adapter must not // 'break' (stop) but log the message instead. Expressions within `{}` are // interpolated. The attribute is only honored by a debug adapter if the // corresponding capability `supportsLogPoints` is true. If either // `hitCondition` or `condition` is specified, then the message should only be // logged if those conditions are met. optional logMessage; }; DAP_DECLARE_STRUCT_TYPEINFO(SourceBreakpoint); // Sets multiple breakpoints for a single source and clears all previous // breakpoints in that source. To clear all breakpoint for a source, specify an // empty array. When a breakpoint is hit, a `stopped` event (with reason // `breakpoint`) is generated. struct SetBreakpointsRequest : public Request { using Response = SetBreakpointsResponse; // The code locations of the breakpoints. optional> breakpoints; // Deprecated: The code locations of the breakpoints. optional> lines; // The source location of the breakpoints; either `source.path` or // `source.sourceReference` must be specified. Source source; // A value of true indicates that the underlying source has been modified // which results in new breakpoint locations. optional sourceModified; }; DAP_DECLARE_STRUCT_TYPEINFO(SetBreakpointsRequest); // Response to `setDataBreakpoints` request. // Returned is information about each breakpoint created by this request. struct SetDataBreakpointsResponse : public Response { // Information about the data breakpoints. The array elements correspond to // the elements of the input argument `breakpoints` array. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetDataBreakpointsResponse); // Properties of a data breakpoint passed to the `setDataBreakpoints` request. struct DataBreakpoint { // The access type of the data. optional accessType; // An expression for conditional breakpoints. optional condition; // An id representing the data. This id is returned from the // `dataBreakpointInfo` request. string dataId; // An expression that controls how many hits of the breakpoint are ignored. // The debug adapter is expected to interpret the expression as needed. optional hitCondition; }; DAP_DECLARE_STRUCT_TYPEINFO(DataBreakpoint); // Replaces all existing data breakpoints with new data breakpoints. // To clear all data breakpoints, specify an empty array. // When a data breakpoint is hit, a `stopped` event (with reason `data // breakpoint`) is generated. Clients should only call this request if the // corresponding capability `supportsDataBreakpoints` is true. struct SetDataBreakpointsRequest : public Request { using Response = SetDataBreakpointsResponse; // The contents of this array replaces all existing data breakpoints. An empty // array clears all data breakpoints. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetDataBreakpointsRequest); // Response to `setExceptionBreakpoints` request. // The response contains an array of `Breakpoint` objects with information about // each exception breakpoint or filter. The `Breakpoint` objects are in the same // order as the elements of the `filters`, `filterOptions`, `exceptionOptions` // arrays given as arguments. If both `filters` and `filterOptions` are given, // the returned array must start with `filters` information first, followed by // `filterOptions` information. The `verified` property of a `Breakpoint` object // signals whether the exception breakpoint or filter could be successfully // created and whether the condition or hit count expressions are valid. In case // of an error the `message` property explains the problem. The `id` property // can be used to introduce a unique ID for the exception breakpoint or filter // so that it can be updated subsequently by sending breakpoint events. For // backward compatibility both the `breakpoints` array and the enclosing `body` // are optional. If these elements are missing a client is not able to show // problems for individual exception breakpoints or filters. struct SetExceptionBreakpointsResponse : public Response { // Information about the exception breakpoints or filters. // The breakpoints returned are in the same order as the elements of the // `filters`, `filterOptions`, `exceptionOptions` arrays in the arguments. If // both `filters` and `filterOptions` are given, the returned array must start // with `filters` information first, followed by `filterOptions` information. optional> breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse); // An `ExceptionPathSegment` represents a segment in a path that is used to // match leafs or nodes in a tree of exceptions. If a segment consists of more // than one name, it matches the names provided if `negate` is false or missing, // or it matches anything except the names provided if `negate` is true. struct ExceptionPathSegment { // Depending on the value of `negate` the names that should match or not // match. array names; // If false or missing this segment matches the names provided, otherwise it // matches anything except the names provided. optional negate; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionPathSegment); // An `ExceptionOptions` assigns configuration options to a set of exceptions. struct ExceptionOptions { // Condition when a thrown exception should result in a break. ExceptionBreakMode breakMode = "never"; // A path that selects a single or multiple exceptions in a tree. If `path` is // missing, the whole tree is selected. By convention the first segment of the // path is a category that is used to group exceptions in the UI. optional> path; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionOptions); // An `ExceptionFilterOptions` is used to specify an exception filter together // with a condition for the `setExceptionBreakpoints` request. struct ExceptionFilterOptions { // An expression for conditional exceptions. // The exception breaks into the debugger if the result of the condition is // true. optional condition; // ID of an exception filter returned by the `exceptionBreakpointFilters` // capability. string filterId; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionFilterOptions); // The request configures the debugger's response to thrown exceptions. // If an exception is configured to break, a `stopped` event is fired (with // reason `exception`). Clients should only call this request if the // corresponding capability `exceptionBreakpointFilters` returns one or more // filters. struct SetExceptionBreakpointsRequest : public Request { using Response = SetExceptionBreakpointsResponse; // Configuration options for selected exceptions. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsExceptionOptions` is true. optional> exceptionOptions; // Set of exception filters and their options. The set of all possible // exception filters is defined by the `exceptionBreakpointFilters` // capability. This attribute is only honored by a debug adapter if the // corresponding capability `supportsExceptionFilterOptions` is true. The // `filter` and `filterOptions` sets are additive. optional> filterOptions; // Set of exception filters specified by their ID. The set of all possible // exception filters is defined by the `exceptionBreakpointFilters` // capability. The `filter` and `filterOptions` sets are additive. array filters; }; DAP_DECLARE_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest); // Response to `setExpression` request. struct SetExpressionResponse : public Response { // The number of indexed child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional indexedVariables; // The number of named child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional namedVariables; // Properties of a value that can be used to determine how to render the // result in the UI. optional presentationHint; // The type of the value. // This attribute should only be returned by a debug adapter if the // corresponding capability `supportsVariableType` is true. optional type; // The new value of the expression. string value; // If `variablesReference` is > 0, the evaluate result is structured and its // children can be retrieved by passing `variablesReference` to the // `variables` request as long as execution remains suspended. See 'Lifetime // of Object References' in the Overview section for details. optional variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(SetExpressionResponse); // Evaluates the given `value` expression and assigns it to the `expression` // which must be a modifiable l-value. The expressions have access to any // variables and arguments that are in scope of the specified frame. Clients // should only call this request if the corresponding capability // `supportsSetExpression` is true. If a debug adapter implements both // `setExpression` and `setVariable`, a client uses `setExpression` if the // variable has an `evaluateName` property. struct SetExpressionRequest : public Request { using Response = SetExpressionResponse; // The l-value expression to assign to. string expression; // Specifies how the resulting value should be formatted. optional format; // Evaluate the expressions in the scope of this stack frame. If not // specified, the expressions are evaluated in the global scope. optional frameId; // The value expression to assign to the l-value expression. string value; }; DAP_DECLARE_STRUCT_TYPEINFO(SetExpressionRequest); // Response to `setFunctionBreakpoints` request. // Returned is information about each breakpoint created by this request. struct SetFunctionBreakpointsResponse : public Response { // Information about the breakpoints. The array elements correspond to the // elements of the `breakpoints` array. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse); // Properties of a breakpoint passed to the `setFunctionBreakpoints` request. struct FunctionBreakpoint { // An expression for conditional breakpoints. // It is only honored by a debug adapter if the corresponding capability // `supportsConditionalBreakpoints` is true. optional condition; // An expression that controls how many hits of the breakpoint are ignored. // The debug adapter is expected to interpret the expression as needed. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsHitConditionalBreakpoints` is true. optional hitCondition; // The name of the function. string name; }; DAP_DECLARE_STRUCT_TYPEINFO(FunctionBreakpoint); // Replaces all existing function breakpoints with new function breakpoints. // To clear all function breakpoints, specify an empty array. // When a function breakpoint is hit, a `stopped` event (with reason `function // breakpoint`) is generated. Clients should only call this request if the // corresponding capability `supportsFunctionBreakpoints` is true. struct SetFunctionBreakpointsRequest : public Request { using Response = SetFunctionBreakpointsResponse; // The function names of the breakpoints. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest); // Response to `setInstructionBreakpoints` request struct SetInstructionBreakpointsResponse : public Response { // Information about the breakpoints. The array elements correspond to the // elements of the `breakpoints` array. array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse); // Properties of a breakpoint passed to the `setInstructionBreakpoints` request struct InstructionBreakpoint { // An expression for conditional breakpoints. // It is only honored by a debug adapter if the corresponding capability // `supportsConditionalBreakpoints` is true. optional condition; // An expression that controls how many hits of the breakpoint are ignored. // The debug adapter is expected to interpret the expression as needed. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsHitConditionalBreakpoints` is true. optional hitCondition; // The instruction reference of the breakpoint. // This should be a memory or instruction pointer reference from an // `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or // `Breakpoint`. string instructionReference; // The offset from the instruction reference. // This can be negative. optional offset; }; DAP_DECLARE_STRUCT_TYPEINFO(InstructionBreakpoint); // Replaces all existing instruction breakpoints. Typically, instruction // breakpoints would be set from a disassembly window. To clear all instruction // breakpoints, specify an empty array. When an instruction breakpoint is hit, a // `stopped` event (with reason `instruction breakpoint`) is generated. Clients // should only call this request if the corresponding capability // `supportsInstructionBreakpoints` is true. struct SetInstructionBreakpointsRequest : public Request { using Response = SetInstructionBreakpointsResponse; // The instruction references of the breakpoints array breakpoints; }; DAP_DECLARE_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest); // Response to `setVariable` request. struct SetVariableResponse : public Response { // The number of indexed child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional indexedVariables; // The number of named child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional namedVariables; // The type of the new value. Typically shown in the UI when hovering over the // value. optional type; // The new value of the variable. string value; // If `variablesReference` is > 0, the new value is structured and its // children can be retrieved by passing `variablesReference` to the // `variables` request as long as execution remains suspended. See 'Lifetime // of Object References' in the Overview section for details. optional variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(SetVariableResponse); // Set the variable with the given name in the variable container to a new // value. Clients should only call this request if the corresponding capability // `supportsSetVariable` is true. If a debug adapter implements both // `setVariable` and `setExpression`, a client will only use `setExpression` if // the variable has an `evaluateName` property. struct SetVariableRequest : public Request { using Response = SetVariableResponse; // Specifies details on how to format the response value. optional format; // The name of the variable in the container. string name; // The value of the variable. string value; // The reference of the variable container. The `variablesReference` must have // been obtained in the current suspended state. See 'Lifetime of Object // References' in the Overview section for details. integer variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(SetVariableRequest); // Response to `source` request. struct SourceResponse : public Response { // Content of the source reference. string content; // Content type (MIME type) of the source. optional mimeType; }; DAP_DECLARE_STRUCT_TYPEINFO(SourceResponse); // The request retrieves the source code for a given source reference. struct SourceRequest : public Request { using Response = SourceResponse; // Specifies the source content to load. Either `source.path` or // `source.sourceReference` must be specified. optional source; // The reference to the source. This is the same as `source.sourceReference`. // This is provided for backward compatibility since old clients do not // understand the `source` attribute. integer sourceReference; }; DAP_DECLARE_STRUCT_TYPEINFO(SourceRequest); // A Stackframe contains the source location. struct StackFrame { // Indicates whether this frame can be restarted with the `restart` request. // Clients should only use this if the debug adapter supports the `restart` // request and the corresponding capability `supportsRestartRequest` is true. // If a debug adapter has this capability, then `canRestart` defaults to // `true` if the property is absent. optional canRestart; // Start position of the range covered by the stack frame. It is measured in // UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. If attribute `source` is missing or doesn't // exist, `column` is 0 and should be ignored by the client. integer column; // End position of the range covered by the stack frame. It is measured in // UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. optional endColumn; // The end line of the range covered by the stack frame. optional endLine; // An identifier for the stack frame. It must be unique across all threads. // This id can be used to retrieve the scopes of the frame with the `scopes` // request or to restart the execution of a stack frame. integer id; // A memory reference for the current instruction pointer in this frame. optional instructionPointerReference; // The line within the source of the frame. If the source attribute is missing // or doesn't exist, `line` is 0 and should be ignored by the client. integer line; // The module associated with this frame, if any. optional> moduleId; // The name of the stack frame, typically a method name. string name; // A hint for how to present this frame in the UI. // A value of `label` can be used to indicate that the frame is an artificial // frame that is used as a visual label or separator. A value of `subtle` can // be used to change the appearance of a frame in a 'subtle' way. // // Must be one of the following enumeration values: // 'normal', 'label', 'subtle' optional presentationHint; // The source of the frame. optional source; }; DAP_DECLARE_STRUCT_TYPEINFO(StackFrame); // Response to `stackTrace` request. struct StackTraceResponse : public Response { // The frames of the stack frame. If the array has length zero, there are no // stack frames available. This means that there is no location information // available. array stackFrames; // The total number of frames available in the stack. If omitted or if // `totalFrames` is larger than the available frames, a client is expected to // request frames until a request returns less frames than requested (which // indicates the end of the stack). Returning monotonically increasing // `totalFrames` values for subsequent requests can be used to enforce paging // in the client. optional totalFrames; }; DAP_DECLARE_STRUCT_TYPEINFO(StackTraceResponse); // Provides formatting information for a stack frame. struct StackFrameFormat : public ValueFormat { // Includes all stack frames, including those the debug adapter might // otherwise hide. optional includeAll; // Displays the line number of the stack frame. optional line; // Displays the module of the stack frame. optional module; // Displays the names of parameters for the stack frame. optional parameterNames; // Displays the types of parameters for the stack frame. optional parameterTypes; // Displays the values of parameters for the stack frame. optional parameterValues; // Displays parameters for the stack frame. optional parameters; }; DAP_DECLARE_STRUCT_TYPEINFO(StackFrameFormat); // The request returns a stacktrace from the current execution state of a given // thread. A client can request all stack frames by omitting the startFrame and // levels arguments. For performance-conscious clients and if the corresponding // capability `supportsDelayedStackTraceLoading` is true, stack frames can be // retrieved in a piecemeal way with the `startFrame` and `levels` arguments. // The response of the `stackTrace` request may contain a `totalFrames` property // that hints at the total number of frames in the stack. If a client needs this // total number upfront, it can issue a request for a single (first) frame and // depending on the value of `totalFrames` decide how to proceed. In any case a // client should be prepared to receive fewer frames than requested, which is an // indication that the end of the stack has been reached. struct StackTraceRequest : public Request { using Response = StackTraceResponse; // Specifies details on how to format the stack frames. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsValueFormattingOptions` is true. optional format; // The maximum number of frames to return. If levels is not specified or 0, // all frames are returned. optional levels; // The index of the first frame to return; if omitted frames start at 0. optional startFrame; // Retrieve the stacktrace for this thread. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(StackTraceRequest); // Response to `startDebugging` request. This is just an acknowledgement, so no // body field is required. struct StartDebuggingResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(StartDebuggingResponse); // This request is sent from the debug adapter to the client to start a new // debug session of the same type as the caller. This request should only be // sent if the corresponding client capability `supportsStartDebuggingRequest` // is true. A client implementation of `startDebugging` should start a new debug // session (of the same type as the caller) in the same way that the caller's // session was started. If the client supports hierarchical debug sessions, the // newly created session can be treated as a child of the caller session. struct StartDebuggingRequest : public Request { using Response = StartDebuggingResponse; // Arguments passed to the new debug session. The arguments must only contain // properties understood by the `launch` or `attach` requests of the debug // adapter and they must not contain any client-specific properties (e.g. // `type`) or client-specific features (e.g. substitutable 'variables'). object configuration; // Indicates whether the new debug session should be started with a `launch` // or `attach` request. // // Must be one of the following enumeration values: // 'launch', 'attach' string request = "launch"; }; DAP_DECLARE_STRUCT_TYPEINFO(StartDebuggingRequest); // Response to `stepBack` request. This is just an acknowledgement, so no body // field is required. struct StepBackResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(StepBackResponse); // The request executes one backward step (in the given granularity) for the // specified thread and allows all other threads to run backward freely by // resuming them. If the debug adapter supports single thread execution (see // capability `supportsSingleThreadExecutionRequests`), setting the // `singleThread` argument to true prevents other suspended threads from // resuming. The debug adapter first sends the response and then a `stopped` // event (with reason `step`) after the step has completed. Clients should only // call this request if the corresponding capability `supportsStepBack` is true. struct StepBackRequest : public Request { using Response = StepBackResponse; // Stepping granularity to step. If no granularity is specified, a granularity // of `statement` is assumed. optional granularity; // If this flag is true, all other suspended threads are not resumed. optional singleThread; // Specifies the thread for which to resume execution for one step backwards // (of the given granularity). integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(StepBackRequest); // Response to `stepIn` request. This is just an acknowledgement, so no body // field is required. struct StepInResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(StepInResponse); // The request resumes the given thread to step into a function/method and // allows all other threads to run freely by resuming them. If the debug adapter // supports single thread execution (see capability // `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument // to true prevents other suspended threads from resuming. If the request cannot // step into a target, `stepIn` behaves like the `next` request. The debug // adapter first sends the response and then a `stopped` event (with reason // `step`) after the step has completed. If there are multiple function/method // calls (or other targets) on the source line, the argument `targetId` can be // used to control into which target the `stepIn` should occur. The list of // possible targets for a given source line can be retrieved via the // `stepInTargets` request. struct StepInRequest : public Request { using Response = StepInResponse; // Stepping granularity. If no granularity is specified, a granularity of // `statement` is assumed. optional granularity; // If this flag is true, all other suspended threads are not resumed. optional singleThread; // Id of the target to step into. optional targetId; // Specifies the thread for which to resume execution for one step-into (of // the given granularity). integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(StepInRequest); // A `StepInTarget` can be used in the `stepIn` request and determines into // which single target the `stepIn` request should step. struct StepInTarget { // Start position of the range covered by the step in target. It is measured // in UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. optional column; // End position of the range covered by the step in target. It is measured in // UTF-16 code units and the client capability `columnsStartAt1` determines // whether it is 0- or 1-based. optional endColumn; // The end line of the range covered by the step-in target. optional endLine; // Unique identifier for a step-in target. integer id; // The name of the step-in target (shown in the UI). string label; // The line of the step-in target. optional line; }; DAP_DECLARE_STRUCT_TYPEINFO(StepInTarget); // Response to `stepInTargets` request. struct StepInTargetsResponse : public Response { // The possible step-in targets of the specified source location. array targets; }; DAP_DECLARE_STRUCT_TYPEINFO(StepInTargetsResponse); // This request retrieves the possible step-in targets for the specified stack // frame. These targets can be used in the `stepIn` request. Clients should only // call this request if the corresponding capability // `supportsStepInTargetsRequest` is true. struct StepInTargetsRequest : public Request { using Response = StepInTargetsResponse; // The stack frame for which to retrieve the possible step-in targets. integer frameId; }; DAP_DECLARE_STRUCT_TYPEINFO(StepInTargetsRequest); // Response to `stepOut` request. This is just an acknowledgement, so no body // field is required. struct StepOutResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(StepOutResponse); // The request resumes the given thread to step out (return) from a // function/method and allows all other threads to run freely by resuming them. // If the debug adapter supports single thread execution (see capability // `supportsSingleThreadExecutionRequests`), setting the `singleThread` argument // to true prevents other suspended threads from resuming. The debug adapter // first sends the response and then a `stopped` event (with reason `step`) // after the step has completed. struct StepOutRequest : public Request { using Response = StepOutResponse; // Stepping granularity. If no granularity is specified, a granularity of // `statement` is assumed. optional granularity; // If this flag is true, all other suspended threads are not resumed. optional singleThread; // Specifies the thread for which to resume execution for one step-out (of the // given granularity). integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(StepOutRequest); // The event indicates that the execution of the debuggee has stopped due to // some condition. This can be caused by a breakpoint previously set, a stepping // request has completed, by executing a debugger statement etc. struct StoppedEvent : public Event { // If `allThreadsStopped` is true, a debug adapter can announce that all // threads have stopped. // - The client should use this information to enable that all threads can be // expanded to access their stacktraces. // - If the attribute is missing or false, only the thread with the given // `threadId` can be expanded. optional allThreadsStopped; // The full reason for the event, e.g. 'Paused on exception'. This string is // shown in the UI as is and can be translated. optional description; // Ids of the breakpoints that triggered the event. In most cases there is // only a single breakpoint but here are some examples for multiple // breakpoints: // - Different types of breakpoints map to the same location. // - Multiple source breakpoints get collapsed to the same instruction by the // compiler/runtime. // - Multiple function breakpoints with different function names map to the // same location. optional> hitBreakpointIds; // A value of true hints to the client that this event should not change the // focus. optional preserveFocusHint; // The reason for the event. // For backward compatibility this string is shown in the UI if the // `description` attribute is missing (but it must not be translated). // // May be one of the following enumeration values: // 'step', 'breakpoint', 'exception', 'pause', 'entry', 'goto', 'function // breakpoint', 'data breakpoint', 'instruction breakpoint' string reason; // Additional information. E.g. if reason is `exception`, text contains the // exception name. This string is shown in the UI. optional text; // The thread which was stopped. optional threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(StoppedEvent); // Response to `terminate` request. This is just an acknowledgement, so no body // field is required. struct TerminateResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(TerminateResponse); // The `terminate` request is sent from the client to the debug adapter in order // to shut down the debuggee gracefully. Clients should only call this request // if the capability `supportsTerminateRequest` is true. Typically a debug // adapter implements `terminate` by sending a software signal which the // debuggee intercepts in order to clean things up properly before terminating // itself. Please note that this request does not directly affect the state of // the debug session: if the debuggee decides to veto the graceful shutdown for // any reason by not terminating itself, then the debug session just continues. // Clients can surface the `terminate` request as an explicit command or they // can integrate it into a two stage Stop command that first sends `terminate` // to request a graceful shutdown, and if that fails uses `disconnect` for a // forceful shutdown. struct TerminateRequest : public Request { using Response = TerminateResponse; // A value of true indicates that this `terminate` request is part of a // restart sequence. optional restart; }; DAP_DECLARE_STRUCT_TYPEINFO(TerminateRequest); // Response to `terminateThreads` request. This is just an acknowledgement, no // body field is required. struct TerminateThreadsResponse : public Response {}; DAP_DECLARE_STRUCT_TYPEINFO(TerminateThreadsResponse); // The request terminates the threads with the given ids. // Clients should only call this request if the corresponding capability // `supportsTerminateThreadsRequest` is true. struct TerminateThreadsRequest : public Request { using Response = TerminateThreadsResponse; // Ids of threads to be terminated. optional> threadIds; }; DAP_DECLARE_STRUCT_TYPEINFO(TerminateThreadsRequest); // The event indicates that debugging of the debuggee has terminated. This does // **not** mean that the debuggee itself has exited. struct TerminatedEvent : public Event { // A debug adapter may set `restart` to true (or to an arbitrary object) to // request that the client restarts the session. The value is not interpreted // by the client and passed unmodified as an attribute `__restart` to the // `launch` and `attach` requests. optional, boolean, integer, null, number, object, string>> restart; }; DAP_DECLARE_STRUCT_TYPEINFO(TerminatedEvent); // The event indicates that a thread has started or exited. struct ThreadEvent : public Event { // The reason for the event. // // May be one of the following enumeration values: // 'started', 'exited' string reason; // The identifier of the thread. integer threadId; }; DAP_DECLARE_STRUCT_TYPEINFO(ThreadEvent); // A Thread struct Thread { // Unique identifier for the thread. integer id; // The name of the thread. string name; }; DAP_DECLARE_STRUCT_TYPEINFO(Thread); // Response to `threads` request. struct ThreadsResponse : public Response { // All threads. array threads; }; DAP_DECLARE_STRUCT_TYPEINFO(ThreadsResponse); // The request retrieves a list of all threads. struct ThreadsRequest : public Request { using Response = ThreadsResponse; }; DAP_DECLARE_STRUCT_TYPEINFO(ThreadsRequest); // A Variable is a name/value pair. // The `type` attribute is shown if space permits or when hovering over the // variable's name. The `kind` attribute is used to render additional properties // of the variable, e.g. different icons can be used to indicate that a variable // is public or private. If the value is structured (has children), a handle is // provided to retrieve the children with the `variables` request. If the number // of named or indexed children is large, the numbers should be returned via the // `namedVariables` and `indexedVariables` attributes. The client can use this // information to present the children in a paged UI and fetch them in chunks. struct Variable { // The evaluatable name of this variable which can be passed to the `evaluate` // request to fetch the variable's value. optional evaluateName; // The number of indexed child variables. // The client can use this information to present the children in a paged UI // and fetch them in chunks. optional indexedVariables; // The memory reference for the variable if the variable represents executable // code, such as a function pointer. This attribute is only required if the // corresponding capability `supportsMemoryReferences` is true. optional memoryReference; // The variable's name. string name; // The number of named child variables. // The client can use this information to present the children in a paged UI // and fetch them in chunks. optional namedVariables; // Properties of a variable that can be used to determine how to render the // variable in the UI. optional presentationHint; // The type of the variable's value. Typically shown in the UI when hovering // over the value. This attribute should only be returned by a debug adapter // if the corresponding capability `supportsVariableType` is true. optional type; // The variable's value. // This can be a multi-line text, e.g. for a function the body of a function. // For structured variables (which do not have a simple value), it is // recommended to provide a one-line representation of the structured object. // This helps to identify the structured object in the collapsed state when // its children are not yet visible. An empty string can be used if no value // should be shown in the UI. string value; // If `variablesReference` is > 0, the variable is structured and its children // can be retrieved by passing `variablesReference` to the `variables` request // as long as execution remains suspended. See 'Lifetime of Object References' // in the Overview section for details. integer variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(Variable); // Response to `variables` request. struct VariablesResponse : public Response { // All (or a range) of variables for the given variable reference. array variables; }; DAP_DECLARE_STRUCT_TYPEINFO(VariablesResponse); // Retrieves all child variables for the given variable reference. // A filter can be used to limit the fetched children to either named or indexed // children. struct VariablesRequest : public Request { using Response = VariablesResponse; // The number of variables to return. If count is missing or 0, all variables // are returned. optional count; // Filter to limit the child variables to either named or indexed. If omitted, // both types are fetched. // // Must be one of the following enumeration values: // 'indexed', 'named' optional filter; // Specifies details on how to format the Variable values. // The attribute is only honored by a debug adapter if the corresponding // capability `supportsValueFormattingOptions` is true. optional format; // The index of the first variable to return; if omitted children start at 0. optional start; // The variable for which to retrieve its children. The `variablesReference` // must have been obtained in the current suspended state. See 'Lifetime of // Object References' in the Overview section for details. integer variablesReference; }; DAP_DECLARE_STRUCT_TYPEINFO(VariablesRequest); // Response to `writeMemory` request. struct WriteMemoryResponse : public Response { // Property that should be returned when `allowPartial` is true to indicate // the number of bytes starting from address that were successfully written. optional bytesWritten; // Property that should be returned when `allowPartial` is true to indicate // the offset of the first byte of data successfully written. Can be negative. optional offset; }; DAP_DECLARE_STRUCT_TYPEINFO(WriteMemoryResponse); // Writes bytes to memory at the provided location. // Clients should only call this request if the corresponding capability // `supportsWriteMemoryRequest` is true. struct WriteMemoryRequest : public Request { using Response = WriteMemoryResponse; // Property to control partial writes. If true, the debug adapter should // attempt to write memory even if the entire memory region is not writable. // In such a case the debug adapter should stop after hitting the first byte // of memory that cannot be written and return the number of bytes written in // the response via the `offset` and `bytesWritten` properties. If false or // missing, a debug adapter should attempt to verify the region is writable // before writing, and fail the response if it is not. optional allowPartial; // Bytes to write, encoded using base64. string data; // Memory reference to the base location to which data should be written. string memoryReference; // Offset (in bytes) to be applied to the reference location before writing // data. Can be negative. optional offset; }; DAP_DECLARE_STRUCT_TYPEINFO(WriteMemoryRequest); } // namespace dap #endif // dap_protocol_h google-cppdap-252b568/include/dap/serialization.h000066400000000000000000000206271443732122100217350ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_serialization_h #define dap_serialization_h #include "typeof.h" #include "types.h" #include // ptrdiff_t #include namespace dap { // Field describes a single field of a struct. struct Field { std::string name; // name of the field ptrdiff_t offset; // offset of the field to the base of the struct const TypeInfo* type; // type of the field }; //////////////////////////////////////////////////////////////////////////////// // Deserializer //////////////////////////////////////////////////////////////////////////////// // Deserializer is the interface used to decode data from structured storage. // Methods that return a bool use this to indicate success. class Deserializer { public: virtual ~Deserializer() = default; // deserialization methods for simple data types. // If the stored object is not of the correct type, then these function will // return false. virtual bool deserialize(boolean*) const = 0; virtual bool deserialize(integer*) const = 0; virtual bool deserialize(number*) const = 0; virtual bool deserialize(string*) const = 0; virtual bool deserialize(object*) const = 0; virtual bool deserialize(any*) const = 0; // count() returns the number of elements in the array object referenced by // this Deserializer. virtual size_t count() const = 0; // array() calls the provided std::function for deserializing each array // element in the array object referenced by this Deserializer. virtual bool array(const std::function&) const = 0; // field() calls the provided std::function for deserializing the field with // the given name from the struct object referenced by this Deserializer. virtual bool field(const std::string& name, const std::function&) const = 0; // deserialize() delegates to TypeOf::type()->deserialize(). template ::has_custom_serialization>> inline bool deserialize(T*) const; // deserialize() decodes an array. template inline bool deserialize(dap::array*) const; // deserialize() decodes an optional. template inline bool deserialize(dap::optional*) const; // deserialize() decodes an variant. template inline bool deserialize(dap::variant*) const; // deserialize() decodes the struct field f with the given name. template inline bool field(const std::string& name, T* f) const; }; template bool Deserializer::deserialize(T* ptr) const { return TypeOf::type()->deserialize(this, ptr); } template bool Deserializer::deserialize(dap::array* vec) const { auto n = count(); vec->resize(n); size_t i = 0; if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) { return false; } return true; } template bool Deserializer::deserialize(dap::optional* opt) const { T v; if (deserialize(&v)) { *opt = v; } return true; } template bool Deserializer::deserialize(dap::variant* var) const { return deserialize(&var->value); } template bool Deserializer::field(const std::string& name, T* v) const { return this->field(name, [&](const Deserializer* d) { return d->deserialize(v); }); } //////////////////////////////////////////////////////////////////////////////// // Serializer //////////////////////////////////////////////////////////////////////////////// class FieldSerializer; // Serializer is the interface used to encode data to structured storage. // A Serializer is associated with a single storage object, whos type and value // is assigned by a call to serialize(). // If serialize() is called multiple times on the same Serializer instance, // the last type and value is stored. // Methods that return a bool use this to indicate success. class Serializer { public: virtual ~Serializer() = default; // serialization methods for simple data types. virtual bool serialize(boolean) = 0; virtual bool serialize(integer) = 0; virtual bool serialize(number) = 0; virtual bool serialize(const string&) = 0; virtual bool serialize(const dap::object&) = 0; virtual bool serialize(const any&) = 0; // array() encodes count array elements to the array object referenced by this // Serializer. The std::function will be called count times, each time with a // Serializer that should be used to encode the n'th array element's data. virtual bool array(size_t count, const std::function&) = 0; // object() begins encoding the object referenced by this Serializer. // The std::function will be called with a FieldSerializer to serialize the // object's fields. virtual bool object(const std::function&) = 0; // remove() deletes the object referenced by this Serializer. // remove() can be used to serialize optionals with no value assigned. virtual void remove() = 0; // serialize() delegates to TypeOf::type()->serialize(). template ::has_custom_serialization>> inline bool serialize(const T&); // serialize() encodes the given array. template inline bool serialize(const dap::array&); // serialize() encodes the given optional. template inline bool serialize(const dap::optional& v); // serialize() encodes the given variant. template inline bool serialize(const dap::variant&); // deserialize() encodes the given string. inline bool serialize(const char* v); protected: static inline const TypeInfo* get_any_type(const any&); static inline const void* get_any_val(const any&); }; inline const TypeInfo* Serializer::get_any_type(const any& a){ return a.type; } const void* Serializer::get_any_val(const any& a) { return a.value; } template bool Serializer::serialize(const T& object) { return TypeOf::type()->serialize(this, &object); } template bool Serializer::serialize(const dap::array& vec) { auto it = vec.begin(); return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); }); } template bool Serializer::serialize(const dap::optional& opt) { if (!opt.has_value()) { remove(); return true; } return serialize(opt.value()); } template bool Serializer::serialize(const dap::variant& var) { return serialize(var.value); } bool Serializer::serialize(const char* v) { return serialize(std::string(v)); } //////////////////////////////////////////////////////////////////////////////// // FieldSerializer //////////////////////////////////////////////////////////////////////////////// // FieldSerializer is the interface used to serialize fields of an object. class FieldSerializer { public: using SerializeFunc = std::function; template using IsSerializeFunc = std::is_convertible; virtual ~FieldSerializer() = default; // field() encodes a field to the struct object referenced by this Serializer. // The SerializeFunc will be called with a Serializer used to encode the // field's data. virtual bool field(const std::string& name, const SerializeFunc&) = 0; // field() encodes the field with the given name and value. template < typename T, typename = typename std::enable_if::value>::type> inline bool field(const std::string& name, const T& v); }; template bool FieldSerializer::field(const std::string& name, const T& v) { return this->field(name, [&](Serializer* s) { return s->serialize(v); }); } } // namespace dap #endif // dap_serialization_h google-cppdap-252b568/include/dap/session.h000066400000000000000000000444111443732122100205400ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_session_h #define dap_session_h #include "future.h" #include "io.h" #include "traits.h" #include "typeinfo.h" #include "typeof.h" #include namespace dap { // Forward declarations struct Request; struct Response; struct Event; //////////////////////////////////////////////////////////////////////////////// // Error //////////////////////////////////////////////////////////////////////////////// // Error represents an error message in response to a DAP request. struct Error { Error() = default; Error(const std::string& error); Error(const char* msg, ...); // operator bool() returns true if there is an error. inline operator bool() const { return message.size() > 0; } std::string message; // empty represents success. }; //////////////////////////////////////////////////////////////////////////////// // ResponseOrError //////////////////////////////////////////////////////////////////////////////// // ResponseOrError holds either the response to a DAP request or an error // message. template struct ResponseOrError { using Request = T; inline ResponseOrError() = default; inline ResponseOrError(const T& response); inline ResponseOrError(T&& response); inline ResponseOrError(const Error& error); inline ResponseOrError(Error&& error); inline ResponseOrError(const ResponseOrError& other); inline ResponseOrError(ResponseOrError&& other); inline ResponseOrError& operator=(const ResponseOrError& other); inline ResponseOrError& operator=(ResponseOrError&& other); T response; Error error; // empty represents success. }; template ResponseOrError::ResponseOrError(const T& resp) : response(resp) {} template ResponseOrError::ResponseOrError(T&& resp) : response(std::move(resp)) {} template ResponseOrError::ResponseOrError(const Error& err) : error(err) {} template ResponseOrError::ResponseOrError(Error&& err) : error(std::move(err)) {} template ResponseOrError::ResponseOrError(const ResponseOrError& other) : response(other.response), error(other.error) {} template ResponseOrError::ResponseOrError(ResponseOrError&& other) : response(std::move(other.response)), error(std::move(other.error)) {} template ResponseOrError& ResponseOrError::operator=( const ResponseOrError& other) { response = other.response; error = other.error; return *this; } template ResponseOrError& ResponseOrError::operator=(ResponseOrError&& other) { response = std::move(other.response); error = std::move(other.error); return *this; } //////////////////////////////////////////////////////////////////////////////// // Session //////////////////////////////////////////////////////////////////////////////// // Session implements a DAP client or server endpoint. // The general usage is as follows: // (1) Create a session with Session::create(). // (2) Register request and event handlers with registerHandler(). // (3) Optionally register a protocol error handler with onError(). // (3) Bind the session to the remote endpoint with bind(). // (4) Send requests or events with send(). class Session { template using ParamType = traits::ParameterType; template using IsRequest = traits::EnableIfIsType; template using IsEvent = traits::EnableIfIsType; template using IsRequestHandlerWithoutCallback = traits::EnableIf< traits::CompatibleWith>::value>; template using IsRequestHandlerWithCallback = traits::EnableIf)>>:: value>; public: virtual ~Session(); // ErrorHandler is the type of callback function used for reporting protocol // errors. using ErrorHandler = std::function; // ClosedHandler is the type of callback function used to signal that a // connected endpoint has closed. using ClosedHandler = std::function; // create() constructs and returns a new Session. static std::unique_ptr create(); // onError() registers a error handler that will be called whenever a protocol // error is encountered. // Only one error handler can be bound at any given time, and later calls // will replace the existing error handler. virtual void onError(const ErrorHandler&) = 0; // registerHandler() registers a request handler for a specific request type. // The function F must have one of the following signatures: // ResponseOrError(const RequestType&) // ResponseType(const RequestType&) // Error(const RequestType&) template > inline IsRequestHandlerWithoutCallback registerHandler(F&& handler); // registerHandler() registers a request handler for a specific request type. // The handler has a response callback function for the second argument of the // handler function. This callback may be called after the handler has // returned. // The function F must have the following signature: // void(const RequestType& request, // std::function response) template , typename ResponseType = typename RequestType::Response> inline IsRequestHandlerWithCallback registerHandler( F&& handler); // registerHandler() registers a request handler for a specific request type. // The handler has a response callback function for the second argument of the // handler function. This callback may be called after the handler has // returned. // The function F must have the following signature: // void(const RequestType& request, // std::function)> response) template , typename ResponseType = typename RequestType::Response> inline IsRequestHandlerWithCallback> registerHandler(F&& handler); // registerHandler() registers a event handler for a specific event type. // The function F must have the following signature: // void(const EventType&) template > inline IsEvent registerHandler(F&& handler); // registerSentHandler() registers the function F to be called when a response // of the specific type has been sent. // The function F must have the following signature: // void(const ResponseOrError&) template ::Request> inline void registerSentHandler(F&& handler); // send() sends the request to the connected endpoint and returns a // future that is assigned the request response or error. template > future> send(const T& request); // send() sends the event to the connected endpoint. template > void send(const T& event); // bind() connects this Session to an endpoint using connect(), and then // starts processing incoming messages with startProcessingMessages(). // onClose is the optional callback which will be called when the session // endpoint has been closed. inline void bind(const std::shared_ptr& reader, const std::shared_ptr& writer, const ClosedHandler& onClose); inline void bind(const std::shared_ptr& readerWriter, const ClosedHandler& onClose); ////////////////////////////////////////////////////////////////////////////// // Note: // Methods and members below this point are for advanced usage, and are more // likely to change signature than the methods above. // The methods above this point should be sufficient for most use cases. ////////////////////////////////////////////////////////////////////////////// // connect() connects this Session to an endpoint. // connect() can only be called once. Repeated calls will raise an error, but // otherwise will do nothing. // Note: This method is used for explicit control over message handling. // Most users will use bind() instead of calling this method directly. virtual void connect(const std::shared_ptr&, const std::shared_ptr&) = 0; inline void connect(const std::shared_ptr&); // startProcessingMessages() starts a new thread to receive and dispatch // incoming messages. // onClose is the optional callback which will be called when the session // endpoint has been closed. // Note: This method is used for explicit control over message handling. // Most users will use bind() instead of calling this method directly. virtual void startProcessingMessages(const ClosedHandler& onClose = {}) = 0; // getPayload() blocks until the next incoming message is received, returning // the payload or an empty function if the connection was lost. The returned // payload is function that can be called on any thread to dispatch the // message to the Session handler. // Note: This method is used for explicit control over message handling. // Most users will use bind() instead of calling this method directly. virtual std::function getPayload() = 0; // The callback function type called when a request handler is invoked, and // the request returns a successful result. // 'responseTypeInfo' is the type information of the response data structure. // 'responseData' is a pointer to response payload data. using RequestHandlerSuccessCallback = std::function; // The callback function type used to notify when a DAP request fails. // 'responseTypeInfo' is the type information of the response data structure. // 'message' is the error message using RequestHandlerErrorCallback = std::function; // The callback function type used to invoke a request handler. // 'request' is a pointer to the request data structure // 'onSuccess' is the function to call if the request completed succesfully. // 'onError' is the function to call if the request failed. // For each call of the request handler, 'onSuccess' or 'onError' must be // called exactly once. using GenericRequestHandler = std::function; // The callback function type used to handle a response to a request. // 'response' is a pointer to the response data structure. May be nullptr. // 'error' is a pointer to the reponse error message. May be nullptr. // One of 'data' or 'error' will be nullptr. using GenericResponseHandler = std::function; // The callback function type used to handle an event. // 'event' is a pointer to the event data structure. using GenericEventHandler = std::function; // The callback function type used to notify when a response has been sent // from this session endpoint. // 'response' is a pointer to the response data structure. // 'error' is a pointer to the reponse error message. May be nullptr. using GenericResponseSentHandler = std::function; // registerHandler() registers 'handler' as the request handler callback for // requests of the type 'typeinfo'. virtual void registerHandler(const TypeInfo* typeinfo, const GenericRequestHandler& handler) = 0; // registerHandler() registers 'handler' as the event handler callback for // events of the type 'typeinfo'. virtual void registerHandler(const TypeInfo* typeinfo, const GenericEventHandler& handler) = 0; // registerHandler() registers 'handler' as the response-sent handler function // which is called whenever a response of the type 'typeinfo' is sent from // this session endpoint. virtual void registerHandler(const TypeInfo* typeinfo, const GenericResponseSentHandler& handler) = 0; // send() sends a request to the remote endpoint. // 'requestTypeInfo' is the type info of the request data structure. // 'requestTypeInfo' is the type info of the response data structure. // 'request' is a pointer to the request data structure. // 'responseHandler' is the handler function for the response. virtual bool send(const dap::TypeInfo* requestTypeInfo, const dap::TypeInfo* responseTypeInfo, const void* request, const GenericResponseHandler& responseHandler) = 0; // send() sends an event to the remote endpoint. // 'eventTypeInfo' is the type info for the event data structure. // 'event' is a pointer to the event data structure. virtual bool send(const TypeInfo* eventTypeInfo, const void* event) = 0; }; template Session::IsRequestHandlerWithoutCallback Session::registerHandler( F&& handler) { using ResponseType = typename RequestType::Response; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, [handler](const void* args, const RequestHandlerSuccessCallback& onSuccess, const RequestHandlerErrorCallback& onError) { ResponseOrError res = handler(*reinterpret_cast(args)); if (res.error) { onError(TypeOf::type(), res.error); } else { onSuccess(TypeOf::type(), &res.response); } }); } template Session::IsRequestHandlerWithCallback Session::registerHandler( F&& handler) { using CallbackType = ParamType; registerHandler( TypeOf::type(), [handler](const void* args, const RequestHandlerSuccessCallback& onSuccess, const RequestHandlerErrorCallback&) { CallbackType responseCallback = [onSuccess](const ResponseType& res) { onSuccess(TypeOf::type(), &res); }; handler(*reinterpret_cast(args), responseCallback); }); } template Session::IsRequestHandlerWithCallback> Session::registerHandler(F&& handler) { using CallbackType = ParamType; registerHandler( TypeOf::type(), [handler](const void* args, const RequestHandlerSuccessCallback& onSuccess, const RequestHandlerErrorCallback& onError) { CallbackType responseCallback = [onError, onSuccess](const ResponseOrError& res) { if (res.error) { onError(TypeOf::type(), res.error); } else { onSuccess(TypeOf::type(), &res.response); } }; handler(*reinterpret_cast(args), responseCallback); }); } template Session::IsEvent Session::registerHandler(F&& handler) { auto cb = [handler](const void* args) { handler(*reinterpret_cast(args)); }; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, cb); } template void Session::registerSentHandler(F&& handler) { auto cb = [handler](const void* response, const Error* error) { if (error != nullptr) { handler(ResponseOrError(*error)); } else { handler(ResponseOrError(*reinterpret_cast(response))); } }; const TypeInfo* typeinfo = TypeOf::type(); registerHandler(typeinfo, cb); } template future> Session::send(const T& request) { using Response = typename T::Response; promise> promise; auto sent = send(TypeOf::type(), TypeOf::type(), &request, [=](const void* result, const Error* error) { if (error != nullptr) { promise.set_value(ResponseOrError(*error)); } else { promise.set_value(ResponseOrError( *reinterpret_cast(result))); } }); if (!sent) { promise.set_value(Error("Failed to send request")); } return promise.get_future(); } template void Session::send(const T& event) { const TypeInfo* typeinfo = TypeOf::type(); send(typeinfo, &event); } void Session::connect(const std::shared_ptr& rw) { connect(rw, rw); } void Session::bind(const std::shared_ptr& r, const std::shared_ptr& w, const ClosedHandler& onClose = {}) { connect(r, w); startProcessingMessages(onClose); } void Session::bind(const std::shared_ptr& rw, const ClosedHandler& onClose = {}) { bind(rw, rw, onClose); } } // namespace dap #endif // dap_session_h google-cppdap-252b568/include/dap/traits.h000066400000000000000000000144431443732122100203650ustar00rootroot00000000000000// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_traits_h #define dap_traits_h #include #include namespace dap { namespace traits { // NthTypeOf returns the `N`th type in `Types` template using NthTypeOf = typename std::tuple_element>::type; // `IsTypeOrDerived::value` is true iff `T` is of type `BASE`, or // derives from `BASE`. template using IsTypeOrDerived = std::integral_constant< bool, std::is_base_of::type>::value || std::is_same::type>::value>; // `EachIsTypeOrDerived::value` is true iff all of the types in // the std::tuple `TYPES` is of, or derives from the corresponding indexed type // in the std::tuple `BASES`. // `N` must be equal to the number of types in both the std::tuple `BASES` and // `TYPES`. template struct EachIsTypeOrDerived { using base = typename std::tuple_element::type; using type = typename std::tuple_element::type; using last_matches = IsTypeOrDerived; using others_match = EachIsTypeOrDerived; static constexpr bool value = last_matches::value && others_match::value; }; // EachIsTypeOrDerived specialization for N = 1 template struct EachIsTypeOrDerived<1, BASES, TYPES> { using base = typename std::tuple_element<0, BASES>::type; using type = typename std::tuple_element<0, TYPES>::type; static constexpr bool value = IsTypeOrDerived::value; }; // EachIsTypeOrDerived specialization for N = 0 template struct EachIsTypeOrDerived<0, BASES, TYPES> { static constexpr bool value = true; }; // Signature describes the signature of a function. template struct Signature { // The return type of the function signature using ret = RETURN; // The parameters of the function signature held in a std::tuple using parameters = std::tuple; // The type of the Nth parameter of function signature template using parameter = NthTypeOf; // The total number of parameters static constexpr std::size_t parameter_count = sizeof...(PARAMETERS); }; // SignatureOf is a traits helper that infers the signature of the function, // method, static method, lambda, or function-like object `F`. template struct SignatureOf { // The signature of the function-like object `F` using type = typename SignatureOf::type; }; // SignatureOf specialization for a regular function or static method. template struct SignatureOf { // The signature of the function-like object `F` using type = Signature::type, typename std::decay::type...>; }; // SignatureOf specialization for a non-static method. template struct SignatureOf { // The signature of the function-like object `F` using type = Signature::type, typename std::decay::type...>; }; // SignatureOf specialization for a non-static, const method. template struct SignatureOf { // The signature of the function-like object `F` using type = Signature::type, typename std::decay::type...>; }; // SignatureOfT is an alias to `typename SignatureOf::type`. template using SignatureOfT = typename SignatureOf::type; // ParameterType is an alias to `typename SignatureOf::type::parameter`. template using ParameterType = typename SignatureOfT::template parameter; // `HasSignature::value` is true iff the function-like `F` has a matching // signature to the function-like `S`. template using HasSignature = std::integral_constant< bool, std::is_same, SignatureOfT>::value>; // `Min::value` resolves to the smaller value of A and B. template using Min = std::integral_constant; // `CompatibleWith::value` is true iff the function-like `F` // can be called with the argument types of the function-like `S`. Return type // of the two functions are not considered. template using CompatibleWith = std::integral_constant< bool, (SignatureOfT::parameter_count == SignatureOfT::parameter_count) && EachIsTypeOrDerived::parameter_count, SignatureOfT::parameter_count>::value, typename SignatureOfT::parameters, typename SignatureOfT::parameters>::value>; // If `CONDITION` is true then EnableIf resolves to type T, otherwise an // invalid type. template using EnableIf = typename std::enable_if::type; // If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE_TY`, // otherwise an invalid type. template using EnableIfIsType = EnableIf::value, TRUE_TY>; // If the function-like `F` has a matching signature to the function-like `S` // then EnableIfHasSignature resolves to type `TRUE_TY`, otherwise an invalid type. template using EnableIfHasSignature = EnableIf::value, TRUE_TY>; } // namespace traits } // namespace dap #endif // dap_traits_h google-cppdap-252b568/include/dap/typeinfo.h000066400000000000000000000036251443732122100207140ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_typeinfo_h #define dap_typeinfo_h #include #include namespace dap { class any; class Deserializer; class Serializer; // The TypeInfo interface provides basic runtime type information about DAP // types. TypeInfo is used by the serialization system to encode and decode DAP // requests, responses, events and structs. struct TypeInfo { virtual ~TypeInfo(); virtual std::string name() const = 0; virtual size_t size() const = 0; virtual size_t alignment() const = 0; virtual void construct(void*) const = 0; virtual void copyConstruct(void* dst, const void* src) const = 0; virtual void destruct(void*) const = 0; virtual bool deserialize(const Deserializer*, void*) const = 0; virtual bool serialize(Serializer*, const void*) const = 0; // create() allocates and constructs the TypeInfo of type T, registers the // pointer for deletion on cppdap library termination, and returns the pointer // to T. template static T* create(ARGS&&... args) { auto typeinfo = new T(std::forward(args)...); deleteOnExit(typeinfo); return typeinfo; } private: // deleteOnExit() ensures that the TypeInfo is destructed and deleted on // library termination. static void deleteOnExit(TypeInfo*); }; } // namespace dap #endif // dap_typeinfo_h google-cppdap-252b568/include/dap/typeof.h000066400000000000000000000272401443732122100203640ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_typeof_h #define dap_typeof_h #include "typeinfo.h" #include "types.h" #include "serialization.h" namespace dap { // BasicTypeInfo is an implementation of the TypeInfo interface for the simple // template type T. template struct BasicTypeInfo : public TypeInfo { constexpr BasicTypeInfo(std::string&& name) : name_(std::move(name)) {} // TypeInfo compliance inline std::string name() const override { return name_; } inline size_t size() const override { return sizeof(T); } inline size_t alignment() const override { return alignof(T); } inline void construct(void* ptr) const override { new (ptr) T(); } inline void copyConstruct(void* dst, const void* src) const override { new (dst) T(*reinterpret_cast(src)); } inline void destruct(void* ptr) const override { reinterpret_cast(ptr)->~T(); } inline bool deserialize(const Deserializer* d, void* ptr) const override { return d->deserialize(reinterpret_cast(ptr)); } inline bool serialize(Serializer* s, const void* ptr) const override { return s->serialize(*reinterpret_cast(ptr)); } private: std::string name_; }; // TypeOf has a template specialization for each DAP type, each declaring a // const TypeInfo* type() static member function that describes type T. template struct TypeOf {}; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template <> struct TypeOf { static const TypeInfo* type(); }; template struct TypeOf> { static inline const TypeInfo* type() { static auto typeinfo = TypeInfo::create>>( "array<" + TypeOf::type()->name() + ">"); return typeinfo; } }; template struct TypeOf> { static inline const TypeInfo* type() { static auto typeinfo = TypeInfo::create>>("variant"); return typeinfo; } }; template struct TypeOf> { static inline const TypeInfo* type() { static auto typeinfo = TypeInfo::create>>( "optional<" + TypeOf::type()->name() + ">"); return typeinfo; } }; // DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in // . It evaluates to the offset of the given field, with fewer // restrictions than offsetof(). We cast the address '32' and subtract it again, // because null-dereference is undefined behavior. #define DAP_OFFSETOF(s, m) \ ((int)(size_t) & reinterpret_cast((((s*)32)->m)) - 32) // internal functionality namespace detail { template M member_type(M T::*); } // namespace detail // DAP_TYPEOF() returns the type of the struct (s) member (m). #define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m)) // DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO // macro. // FIELD is the name of the struct field. // NAME is the serialized name of the field, as described by the DAP // specification. #define DAP_FIELD(FIELD, NAME) \ ::dap::Field { \ NAME, DAP_OFFSETOF(StructTy, FIELD), \ TypeOf::type(), \ } // DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT. // Must be used within the 'dap' namespace. #define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \ template <> \ struct TypeOf { \ static constexpr bool has_custom_serialization = true; \ static const TypeInfo* type(); \ static bool deserializeFields(const Deserializer*, void* obj); \ static bool serializeFields(FieldSerializer*, const void* obj); \ } // DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() implements the deserializeFields() // and serializeFields() static methods of a TypeOf<> specialization. Used // internally by DAP_IMPLEMENT_STRUCT_TYPEINFO() and // DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(). // You probably do not want to use this directly. #define DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, ...) \ bool TypeOf::deserializeFields(const Deserializer* fd, void* obj) { \ using StructTy = STRUCT; \ (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ for (auto field : std::initializer_list{__VA_ARGS__}) { \ if (!fd->field(field.name, [&](Deserializer* d) { \ auto ptr = reinterpret_cast(obj) + field.offset; \ return field.type->deserialize(d, ptr); \ })) { \ return false; \ } \ } \ return true; \ } \ bool TypeOf::serializeFields(FieldSerializer* fs, const void* obj) {\ using StructTy = STRUCT; \ (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ for (auto field : std::initializer_list{__VA_ARGS__}) { \ if (!fs->field(field.name, [&](Serializer* s) { \ auto ptr = reinterpret_cast(obj) + field.offset; \ return field.type->serialize(s, ptr); \ })) { \ return false; \ } \ } \ return true; \ } // DAP_IMPLEMENT_STRUCT_TYPEINFO() implements the type() member function for the // TypeOf<> specialization for STRUCT. // STRUCT is the structure typename. // NAME is the serialized name of the structure, as described by the DAP // specification. The variadic (...) parameters should be a repeated list of // DAP_FIELD()s, one for each field of the struct. // Must be used within the 'dap' namespace. #define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \ DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \ const ::dap::TypeInfo* TypeOf::type() { \ struct TI : BasicTypeInfo { \ TI() : BasicTypeInfo(NAME) {} \ bool deserialize(const Deserializer* d, void* obj) const override { \ return deserializeFields(d, obj); \ } \ bool serialize(Serializer* s, const void* obj) const override { \ return s->object( \ [&](FieldSerializer* fs) { return serializeFields(fs, obj); }); \ } \ }; \ static TI typeinfo; \ return &typeinfo; \ } // DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<> // specialization for STRUCT in a single statement. // Must be used within the 'dap' namespace. #define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \ DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \ DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__) // DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT() implements the type() member function for // the TypeOf<> specialization for STRUCT that derives from BASE. // STRUCT is the structure typename. // BASE is the base structure typename. // NAME is the serialized name of the structure, as described by the DAP // specification. The variadic (...) parameters should be a repeated list of // DAP_FIELD()s, one for each field of the struct. // Must be used within the 'dap' namespace. #define DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \ static_assert(std::is_base_of::value, \ #STRUCT " does not derive from " #BASE); \ DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \ const ::dap::TypeInfo* TypeOf::type() { \ struct TI : BasicTypeInfo { \ TI() : BasicTypeInfo(NAME) {} \ bool deserialize(const Deserializer* d, void* obj) const override { \ auto derived = static_cast(obj); \ auto base = static_cast(obj); \ return TypeOf::deserializeFields(d, base) && \ deserializeFields(d, derived); \ } \ bool serialize(Serializer* s, const void* obj) const override { \ return s->object([&](FieldSerializer* fs) { \ auto derived = static_cast(obj); \ auto base = static_cast(obj); \ return TypeOf::serializeFields(fs, base) && \ serializeFields(fs, derived); \ }); \ } \ }; \ static TI typeinfo; \ return &typeinfo; \ } // DAP_STRUCT_TYPEINFO_EXT() is a helper for declaring and implementing a // TypeOf<> specialization for STRUCT that derives from BASE in a single // statement. // Must be used within the 'dap' namespace. #define DAP_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \ DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \ DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, __VA_ARGS__) } // namespace dap #endif // dap_typeof_h google-cppdap-252b568/include/dap/types.h000066400000000000000000000045501443732122100202210ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This file holds the basic serializable types used by the debug adapter // protocol. #ifndef dap_types_h #define dap_types_h #include "any.h" #include "optional.h" #include "variant.h" #include #include #include namespace dap { // string is a sequence of characters. // string defaults to an empty string. using string = std::string; // boolean holds a true or false value. // boolean defaults to false. class boolean { public: inline boolean() : val(false) {} inline boolean(bool i) : val(i) {} inline operator bool() const { return val; } inline boolean& operator=(bool i) { val = i; return *this; } private: bool val; }; // integer holds a whole signed number. // integer defaults to 0. class integer { public: inline integer() : val(0) {} inline integer(int64_t i) : val(i) {} inline operator int64_t() const { return val; } inline integer& operator=(int64_t i) { val = i; return *this; } inline integer operator++(int) { auto copy = *this; val++; return copy; } private: int64_t val; }; // number holds a 64-bit floating point number. // number defaults to 0. class number { public: inline number() : val(0.0) {} inline number(double i) : val(i) {} inline operator double() const { return val; } inline number& operator=(double i) { val = i; return *this; } private: double val; }; // array is a list of items of type T. // array defaults to an empty list. template using array = std::vector; // object is a map of string to any. // object defaults to an empty map. using object = std::unordered_map; // null represents no value. // null is used by any to check for no-value. using null = std::nullptr_t; } // namespace dap #endif // dap_types_h google-cppdap-252b568/include/dap/variant.h000066400000000000000000000057341443732122100205260ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_variant_h #define dap_variant_h #include "any.h" namespace dap { // internal functionality namespace detail { template struct TypeIsIn { static constexpr bool value = false; }; template struct TypeIsIn { static constexpr bool value = std::is_same::value || TypeIsIn::value; }; } // namespace detail // variant represents a type-safe union of DAP types. // variant can hold a value of any of the template argument types. // variant defaults to a default-constructed T0. template class variant { public: // constructors inline variant(); template inline variant(const T& val); // assignment template inline variant& operator=(const T& val); // get() returns the contained value of the type T. // If the any does not contain a value of type T, then get() will assert. template inline T& get() const; // is() returns true iff the contained value is of type T. template inline bool is() const; // accepts() returns true iff the variant accepts values of type T. template static constexpr bool accepts(); private: friend class Serializer; friend class Deserializer; any value; }; template variant::variant() : value(T0()) {} template template variant::variant(const T& v) : value(v) { static_assert(accepts(), "variant does not accept template type T"); } template template variant& variant::operator=(const T& v) { static_assert(accepts(), "variant does not accept template type T"); value = v; return *this; } template template T& variant::get() const { static_assert(accepts(), "variant does not accept template type T"); return value.get(); } template template bool variant::is() const { return value.is(); } template template constexpr bool variant::accepts() { return detail::TypeIsIn::value; } } // namespace dap #endif // dap_variant_h google-cppdap-252b568/kokoro/000077500000000000000000000000001443732122100160155ustar00rootroot00000000000000google-cppdap-252b568/kokoro/license-check/000077500000000000000000000000001443732122100205125ustar00rootroot00000000000000google-cppdap-252b568/kokoro/license-check/presubmit-docker.sh000077500000000000000000000012011443732122100243220ustar00rootroot00000000000000#!/bin/bash # Copyright 2020 The Marl Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e # Fail on any error. license-checker google-cppdap-252b568/kokoro/license-check/presubmit.cfg000066400000000000000000000001561443732122100232070ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto build_file: "cppdap/kokoro/license-check/presubmit.sh" google-cppdap-252b568/kokoro/license-check/presubmit.sh000077500000000000000000000017511443732122100230670ustar00rootroot00000000000000#!/bin/bash # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e # Fail on any error. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" docker run --rm -i \ --volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \ --workdir "${ROOT_DIR}" \ --entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \ "gcr.io/shaderc-build/radial-build:latest" google-cppdap-252b568/kokoro/macos/000077500000000000000000000000001443732122100171175ustar00rootroot00000000000000google-cppdap-252b568/kokoro/macos/clang-x64/000077500000000000000000000000001443732122100206225ustar00rootroot00000000000000google-cppdap-252b568/kokoro/macos/clang-x64/cmake/000077500000000000000000000000001443732122100217025ustar00rootroot00000000000000google-cppdap-252b568/kokoro/macos/clang-x64/cmake/presubmit.cfg000066400000000000000000000004031443732122100243720ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/macos/presubmit.sh" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_TARGET_ARCH" value: "x64" } google-cppdap-252b568/kokoro/macos/presubmit.sh000077500000000000000000000017611443732122100214750ustar00rootroot00000000000000#!/bin/bash # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e # Fail on any error. set -x # Display commands being run. BUILD_ROOT=$PWD cd github/cppdap git submodule update --init if [ "$BUILD_SYSTEM" == "cmake" ]; then mkdir build cd build cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1 make -j$(sysctl -n hw.logicalcpu) ./cppdap-unittests else echo "Unknown build system: $BUILD_SYSTEM" exit 1 fi google-cppdap-252b568/kokoro/ubuntu/000077500000000000000000000000001443732122100173375ustar00rootroot00000000000000google-cppdap-252b568/kokoro/ubuntu/gcc-x64/000077500000000000000000000000001443732122100205125ustar00rootroot00000000000000google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/000077500000000000000000000000001443732122100215725ustar00rootroot00000000000000google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/asan/000077500000000000000000000000001443732122100225145ustar00rootroot00000000000000google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/asan/presubmit.cfg000066400000000000000000000004731443732122100252130ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/ubuntu/presubmit.sh" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_TARGET_ARCH" value: "x64" } env_vars { key: "BUILD_SANITIZER" value: "asan" } google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/presubmit.cfg000066400000000000000000000004041443732122100242630ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/ubuntu/presubmit.sh" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_TARGET_ARCH" value: "x64" } google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/tsan/000077500000000000000000000000001443732122100225375ustar00rootroot00000000000000google-cppdap-252b568/kokoro/ubuntu/gcc-x64/cmake/tsan/presubmit.cfg000066400000000000000000000004731443732122100252360ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/ubuntu/presubmit.sh" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_TARGET_ARCH" value: "x64" } env_vars { key: "BUILD_SANITIZER" value: "tsan" } google-cppdap-252b568/kokoro/ubuntu/presubmit-docker.sh000077500000000000000000000027771443732122100231720ustar00rootroot00000000000000#!/bin/bash # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e # Fail on any error. . /bin/using.sh # Declare the bash `using` function for configuring toolchains. set -x # Display commands being run. cd github/cppdap # Silence "fatal: unsafe repository" errors git config --global --add safe.directory '*' git submodule update --init if [ "$BUILD_SYSTEM" == "cmake" ]; then using cmake-3.17.2 using gcc-9 mkdir build cd build build_and_run() { cmake .. -DCPPDAP_BUILD_EXAMPLES=1 -DCPPDAP_BUILD_TESTS=1 -DCPPDAP_WARNINGS_AS_ERRORS=1 $1 make --jobs=$(nproc) ./cppdap-unittests } if [ "$BUILD_SANITIZER" == "asan" ]; then build_and_run "-DCPPDAP_ASAN=1" elif [ "$BUILD_SANITIZER" == "msan" ]; then build_and_run "-DCPPDAP_MSAN=1" elif [ "$BUILD_SANITIZER" == "tsan" ]; then build_and_run "-DCPPDAP_TSAN=1" else build_and_run fi else echo "Unknown build system: $BUILD_SYSTEM" exit 1 fi google-cppdap-252b568/kokoro/ubuntu/presubmit.sh000077500000000000000000000022501443732122100217070ustar00rootroot00000000000000#!/bin/bash # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e # Fail on any error. ROOT_DIR=`pwd` SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" # --privileged is required for some sanitizer builds, as they seem to require PTRACE privileges docker run --rm -i \ --privileged \ --volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \ --workdir "${ROOT_DIR}" \ --env BUILD_SYSTEM=$BUILD_SYSTEM \ --env BUILD_TARGET_ARCH=$BUILD_TARGET_ARCH \ --env BUILD_SANITIZER=$BUILD_SANITIZER \ --entrypoint "${SCRIPT_DIR}/presubmit-docker.sh" \ "gcr.io/shaderc-build/radial-build:latest" google-cppdap-252b568/kokoro/windows/000077500000000000000000000000001443732122100175075ustar00rootroot00000000000000google-cppdap-252b568/kokoro/windows/msvc-14.14-x64/000077500000000000000000000000001443732122100215435ustar00rootroot00000000000000google-cppdap-252b568/kokoro/windows/msvc-14.14-x64/cmake/000077500000000000000000000000001443732122100226235ustar00rootroot00000000000000google-cppdap-252b568/kokoro/windows/msvc-14.14-x64/cmake/presubmit.cfg000066400000000000000000000005241443732122100253170ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/windows/presubmit.bat" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_GENERATOR" value: "Visual Studio 15 2017 Win64" } env_vars { key: "BUILD_TARGET_ARCH" value: "x64" } google-cppdap-252b568/kokoro/windows/msvc-14.14-x86/000077500000000000000000000000001443732122100215475ustar00rootroot00000000000000google-cppdap-252b568/kokoro/windows/msvc-14.14-x86/cmake/000077500000000000000000000000001443732122100226275ustar00rootroot00000000000000google-cppdap-252b568/kokoro/windows/msvc-14.14-x86/cmake/presubmit.cfg000066400000000000000000000005161443732122100253240ustar00rootroot00000000000000# Format: //devtools/kokoro/config/proto/build.proto # Location of the continuous bash script in Git. build_file: "cppdap/kokoro/windows/presubmit.bat" env_vars { key: "BUILD_SYSTEM" value: "cmake" } env_vars { key: "BUILD_GENERATOR" value: "Visual Studio 15 2017" } env_vars { key: "BUILD_TARGET_ARCH" value: "x86" } google-cppdap-252b568/kokoro/windows/presubmit.bat000066400000000000000000000030471443732122100222150ustar00rootroot00000000000000REM Copyright 2020 Google LLC REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM https://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. @echo on SETLOCAL ENABLEDELAYEDEXPANSION SET BUILD_ROOT=%cd% SET PATH=C:\python36;C:\Program Files\cmake-3.23.1-windows-x86_64\bin;%PATH% SET SRC=%cd%\github\cppdap cd %SRC% if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! git submodule update --init if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! SET MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild" SET CONFIG=Release mkdir %SRC%\build cd %SRC%\build if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! IF /I "%BUILD_SYSTEM%"=="cmake" ( cmake .. -G "%BUILD_GENERATOR%" "-DCPPDAP_BUILD_TESTS=1" "-DCPPDAP_BUILD_EXAMPLES=1" "-DCPPDAP_WARNINGS_AS_ERRORS=1" if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! %MSBUILD% /p:Configuration=%CONFIG% cppdap.sln if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! Release\cppdap-unittests.exe if !ERRORLEVEL! neq 0 exit !ERRORLEVEL! ) ELSE ( echo "Unknown build system: %BUILD_SYSTEM%" exit /b 1 ) google-cppdap-252b568/license-checker.cfg000066400000000000000000000012341443732122100202160ustar00rootroot00000000000000{ "licenses": [ "Apache-2.0", "Apache-2.0-Header" ], "paths": [ { "exclude": [ ".clang-format", ".gitattributes", ".gitignore", ".gitmodules", ".vscode/*.json", "**.md", "CONTRIBUTING", "LICENSE", "build/**", "examples/vscode/package.json", "fuzz/**", "kokoro/**.cfg", "third_party/googletest/**", "third_party/json/**", "third_party/rapidjson/**" ] } ] } google-cppdap-252b568/src/000077500000000000000000000000001443732122100153005ustar00rootroot00000000000000google-cppdap-252b568/src/any_test.cpp000066400000000000000000000167641443732122100176500ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/any.h" #include "dap/typeof.h" #include "dap/types.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace dap { struct AnyTestObject { dap::integer i; dap::number n; }; DAP_STRUCT_TYPEINFO(AnyTestObject, "AnyTestObject", DAP_FIELD(i, "i"), DAP_FIELD(n, "n")); inline bool operator==(const AnyTestObject& a, const AnyTestObject& b) { return a.i == b.i && a.n == b.n; } } // namespace dap namespace { template struct TestValue {}; template <> struct TestValue { static const dap::null value; }; template <> struct TestValue { static const dap::integer value; }; template <> struct TestValue { static const dap::boolean value; }; template <> struct TestValue { static const dap::number value; }; template <> struct TestValue { static const dap::string value; }; template <> struct TestValue> { static const dap::array value; }; template <> struct TestValue { static const dap::AnyTestObject value; }; const dap::null TestValue::value = nullptr; const dap::integer TestValue::value = 20; const dap::boolean TestValue::value = true; const dap::number TestValue::value = 123.45; const dap::string TestValue::value = "hello world"; const dap::array TestValue>::value = { "one", "two", "three"}; const dap::AnyTestObject TestValue::value = {10, 20.30}; } // namespace TEST(Any, EmptyConstruct) { dap::any any; ASSERT_TRUE(any.is()); ASSERT_FALSE(any.is()); ASSERT_FALSE(any.is()); ASSERT_FALSE(any.is()); ASSERT_FALSE(any.is()); ASSERT_FALSE(any.is()); ASSERT_FALSE(any.is>()); ASSERT_FALSE(any.is()); } TEST(Any, Boolean) { dap::any any(dap::boolean(true)); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get(), dap::boolean(true)); } TEST(Any, Integer) { dap::any any(dap::integer(10)); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get(), dap::integer(10)); } TEST(Any, Number) { dap::any any(dap::number(123.0f)); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get(), dap::number(123.0f)); } TEST(Any, String) { dap::any any(dap::string("hello world")); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get(), dap::string("hello world")); } TEST(Any, Array) { using array = dap::array; dap::any any(array({10, 20, 30})); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get(), array({10, 20, 30})); } TEST(Any, Object) { dap::object o; o["one"] = dap::integer(1); o["two"] = dap::integer(2); o["three"] = dap::integer(3); dap::any any(o); ASSERT_TRUE(any.is()); if (any.is()) { auto got = any.get(); ASSERT_EQ(got.size(), 3U); ASSERT_EQ(got.count("one"), 1U); ASSERT_EQ(got.count("two"), 1U); ASSERT_EQ(got.count("three"), 1U); ASSERT_TRUE(got["one"].is()); ASSERT_TRUE(got["two"].is()); ASSERT_TRUE(got["three"].is()); ASSERT_EQ(got["one"].get(), dap::integer(1)); ASSERT_EQ(got["two"].get(), dap::integer(2)); ASSERT_EQ(got["three"].get(), dap::integer(3)); } } TEST(Any, TestObject) { dap::any any(dap::AnyTestObject{5, 3.0}); ASSERT_TRUE(any.is()); ASSERT_EQ(any.get().i, 5); ASSERT_EQ(any.get().n, 3.0); } template class AnyT : public ::testing::Test { protected: template ::value && !std::is_same::value>> void check_val(const dap::any& any, const T0& expect) { ASSERT_EQ(any.is(), any.is()); ASSERT_EQ(any.get(), expect); } // Special case for Null assignment, as we can assign nullptr_t to any but // can't `get()` it template void check_val(const dap::any& any, const dap::null& expect) { ASSERT_EQ(nullptr, expect); ASSERT_TRUE(any.is()); } void check_type(const dap::any& any) { ASSERT_EQ(any.is(), (std::is_same::value)); ASSERT_EQ(any.is(), (std::is_same::value)); ASSERT_EQ(any.is(), (std::is_same::value)); ASSERT_EQ(any.is(), (std::is_same::value)); ASSERT_EQ(any.is(), (std::is_same::value)); ASSERT_EQ(any.is>(), (std::is_same>::value)); ASSERT_EQ(any.is(), (std::is_same::value)); } }; TYPED_TEST_SUITE_P(AnyT); TYPED_TEST_P(AnyT, CopyConstruct) { auto val = TestValue::value; dap::any any(val); this->check_type(any); this->check_val(any, val); } TYPED_TEST_P(AnyT, MoveConstruct) { auto val = TestValue::value; dap::any any(std::move(val)); this->check_type(any); this->check_val(any, val); } TYPED_TEST_P(AnyT, Assign) { auto val = TestValue::value; dap::any any; any = val; this->check_type(any); this->check_val(any, val); } TYPED_TEST_P(AnyT, MoveAssign) { auto val = TestValue::value; dap::any any; any = std::move(val); this->check_type(any); this->check_val(any, val); } TYPED_TEST_P(AnyT, RepeatedAssign) { dap::string str = "hello world"; auto val = TestValue::value; dap::any any; any = str; any = val; this->check_type(any); this->check_val(any, val); } TYPED_TEST_P(AnyT, RepeatedMoveAssign) { dap::string str = "hello world"; auto val = TestValue::value; dap::any any; any = std::move(str); any = std::move(val); this->check_type(any); this->check_val(any, val); } REGISTER_TYPED_TEST_SUITE_P(AnyT, CopyConstruct, MoveConstruct, Assign, MoveAssign, RepeatedAssign, RepeatedMoveAssign); using AnyTypes = ::testing::Types, dap::AnyTestObject>; INSTANTIATE_TYPED_TEST_SUITE_P(T, AnyT, AnyTypes); TEST(Any, Reset) { dap::any any(dap::integer(10)); ASSERT_TRUE(any.is()); any.reset(); ASSERT_FALSE(any.is()); } google-cppdap-252b568/src/chan.h000066400000000000000000000037421443732122100163700ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_chan_h #define dap_chan_h #include "dap/optional.h" #include #include #include namespace dap { template struct Chan { public: void reset(); void close(); optional take(); void put(T&& in); void put(const T& in); private: bool closed = false; std::queue queue; std::condition_variable cv; std::mutex mutex; }; template void Chan::reset() { std::unique_lock lock(mutex); queue = {}; closed = false; } template void Chan::close() { std::unique_lock lock(mutex); closed = true; cv.notify_all(); } template optional Chan::take() { std::unique_lock lock(mutex); cv.wait(lock, [&] { return queue.size() > 0 || closed; }); if (queue.size() == 0) { return optional(); } auto out = std::move(queue.front()); queue.pop(); return optional(std::move(out)); } template void Chan::put(T&& in) { std::unique_lock lock(mutex); auto notify = queue.size() == 0 && !closed; queue.push(std::move(in)); if (notify) { cv.notify_all(); } } template void Chan::put(const T& in) { std::unique_lock lock(mutex); auto notify = queue.size() == 0 && !closed; queue.push(in); if (notify) { cv.notify_all(); } } } // namespace dap #endif // dap_chan_h google-cppdap-252b568/src/chan_test.cpp000066400000000000000000000020451443732122100177550ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "chan.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include TEST(ChanTest, PutTakeClose) { dap::Chan chan; auto thread = std::thread([&] { chan.put(10); chan.put(20); chan.put(30); chan.close(); }); EXPECT_EQ(chan.take(), dap::optional(10)); EXPECT_EQ(chan.take(), dap::optional(20)); EXPECT_EQ(chan.take(), dap::optional(30)); EXPECT_EQ(chan.take(), dap::optional()); thread.join(); } google-cppdap-252b568/src/content_stream.cpp000066400000000000000000000103431443732122100210320ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "content_stream.h" #include "dap/io.h" #include // strlen #include // std::min namespace dap { //////////////////////////////////////////////////////////////////////////////// // ContentReader //////////////////////////////////////////////////////////////////////////////// ContentReader::ContentReader(const std::shared_ptr& reader) : reader(reader) {} ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept { buf = std::move(rhs.buf); reader = std::move(rhs.reader); return *this; } bool ContentReader::isOpen() { return reader ? reader->isOpen() : false; } void ContentReader::close() { if (reader) { reader->close(); } } std::string ContentReader::read() { matched_idx = 0; // Find Content-Length header prefix if (!scan("Content-Length:")) { return ""; } // Skip whitespace and tabs while (matchAny(" \t")) { } // Parse length size_t len = 0; while (true) { auto c = matchAny("0123456789"); if (c == 0) { break; } len *= 10; len += size_t(c) - size_t('0'); } if (len == 0) { return ""; } // Expect \r\n\r\n if (!match("\r\n\r\n")) { return ""; } // Read message if (!buffer(len + matched_idx)) { return ""; } for (size_t i = 0; i < matched_idx; i++) { buf.pop_front(); } std::string out; out.reserve(len); for (size_t i = 0; i < len; i++) { out.push_back(static_cast(buf.front())); buf.pop_front(); } return out; } bool ContentReader::scan(const uint8_t* seq, size_t len) { while (buffer(len)) { if (match(seq, len)) { return true; } buf.pop_front(); } return false; } bool ContentReader::scan(const char* str) { auto len = strlen(str); return scan(reinterpret_cast(str), len); } bool ContentReader::match(const uint8_t* seq, size_t len) { if (!buffer(len + matched_idx)) { return false; } auto it = matched_idx; for (size_t i = 0; i < len; i++, it++) { if (buf[it] != seq[i]) { return false; } } matched_idx += len; return true; } bool ContentReader::match(const char* str) { auto len = strlen(str); return match(reinterpret_cast(str), len); } char ContentReader::matchAny(const char* chars) { if (!buffer(1 + matched_idx)) { return false; } int c = buf[matched_idx]; if (auto p = strchr(chars, c)) { matched_idx++; return *p; } return 0; } bool ContentReader::buffer(size_t bytes) { if (bytes < buf.size()) { return true; } bytes -= buf.size(); while (bytes > 0) { uint8_t chunk[256]; auto numWant = std::min(sizeof(chunk), bytes); auto numGot = reader->read(chunk, numWant); if (numGot == 0) { return false; } for (size_t i = 0; i < numGot; i++) { buf.push_back(chunk[i]); } bytes -= numGot; } return true; } //////////////////////////////////////////////////////////////////////////////// // ContentWriter //////////////////////////////////////////////////////////////////////////////// ContentWriter::ContentWriter(const std::shared_ptr& rhs) : writer(rhs) {} ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept { writer = std::move(rhs.writer); return *this; } bool ContentWriter::isOpen() { return writer ? writer->isOpen() : false; } void ContentWriter::close() { if (writer) { writer->close(); } } bool ContentWriter::write(const std::string& msg) const { auto header = std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n"; return writer->write(header.data(), header.size()) && writer->write(msg.data(), msg.size()); } } // namespace dap google-cppdap-252b568/src/content_stream.h000066400000000000000000000032111443732122100204730ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_content_stream_h #define dap_content_stream_h #include #include #include #include namespace dap { // Forward declarations class Reader; class Writer; class ContentReader { public: ContentReader() = default; ContentReader(const std::shared_ptr&); ContentReader& operator=(ContentReader&&) noexcept; bool isOpen(); void close(); std::string read(); private: bool scan(const uint8_t* seq, size_t len); bool scan(const char* str); bool match(const uint8_t* seq, size_t len); bool match(const char* str); char matchAny(const char* chars); bool buffer(size_t bytes); std::shared_ptr reader; std::deque buf; uint32_t matched_idx = 0; }; class ContentWriter { public: ContentWriter() = default; ContentWriter(const std::shared_ptr&); ContentWriter& operator=(ContentWriter&&) noexcept; bool isOpen(); void close(); bool write(const std::string&) const; private: std::shared_ptr writer; }; } // namespace dap #endif // dap_content_stream_h google-cppdap-252b568/src/content_stream_test.cpp000066400000000000000000000067021443732122100220750ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "content_stream.h" #include "string_buffer.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace { // SingleByteReader wraps a dap::Reader to only provide a single byte for each // read() call, regardless of the number of bytes actually requested. class SingleByteReader : public dap::Reader { public: SingleByteReader(std::unique_ptr&& inner) : inner(std::move(inner)) {} bool isOpen() override { return inner->isOpen(); } void close() override { return inner->close(); } size_t read(void* buffer, size_t) override { return inner->read(buffer, 1); }; private: std::unique_ptr inner; }; } // namespace TEST(ContentStreamTest, Write) { auto sb = dap::StringBuffer::create(); auto ptr = sb.get(); dap::ContentWriter cw(std::move(sb)); cw.write("Content payload number one"); cw.write("Content payload number two"); cw.write("Content payload number three"); ASSERT_EQ(ptr->string(), "Content-Length: 26\r\n\r\nContent payload number one" "Content-Length: 26\r\n\r\nContent payload number two" "Content-Length: 28\r\n\r\nContent payload number three"); } TEST(ContentStreamTest, Read) { auto sb = dap::StringBuffer::create(); sb->write("Content-Length: 26\r\n\r\nContent payload number one"); sb->write("some unrecognised garbage"); sb->write("Content-Length: 26\r\n\r\nContent payload number two"); sb->write("some more unrecognised garbage"); sb->write("Content-Length: 28\r\n\r\nContent payload number three"); dap::ContentReader cs(std::move(sb)); ASSERT_EQ(cs.read(), "Content payload number one"); ASSERT_EQ(cs.read(), "Content payload number two"); ASSERT_EQ(cs.read(), "Content payload number three"); ASSERT_EQ(cs.read(), ""); } TEST(ContentStreamTest, ShortRead) { auto sb = dap::StringBuffer::create(); sb->write("Content-Length: 26\r\n\r\nContent payload number one"); sb->write("some unrecognised garbage"); sb->write("Content-Length: 26\r\n\r\nContent payload number two"); sb->write("some more unrecognised garbage"); sb->write("Content-Length: 28\r\n\r\nContent payload number three"); dap::ContentReader cs( std::unique_ptr(new SingleByteReader(std::move(sb)))); ASSERT_EQ(cs.read(), "Content payload number one"); ASSERT_EQ(cs.read(), "Content payload number two"); ASSERT_EQ(cs.read(), "Content payload number three"); ASSERT_EQ(cs.read(), ""); } TEST(ContentStreamTest, PartialReadAndParse) { auto sb = std::make_shared(); dap::ContentReader cs(sb); sb->write("Content"); ASSERT_EQ(cs.read(), ""); sb->write("-Length: "); ASSERT_EQ(cs.read(), ""); sb->write("26"); ASSERT_EQ(cs.read(), ""); sb->write("\r\n\r\n"); ASSERT_EQ(cs.read(), ""); sb->write("Content payload number one"); ASSERT_EQ(cs.read(), "Content payload number one"); ASSERT_EQ(cs.read(), ""); } google-cppdap-252b568/src/dap_test.cpp000066400000000000000000000033351443732122100176130ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/dap.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } TEST(DAP, PairedInitializeTerminate) { dap::initialize(); dap::terminate(); } TEST(DAP, NestedInitializeTerminate) { dap::initialize(); dap::initialize(); dap::initialize(); dap::terminate(); dap::terminate(); dap::terminate(); } TEST(DAP, MultiThreadedInitializeTerminate) { const size_t numThreads = 64; std::mutex mutex; std::condition_variable cv; size_t numInits = 0; std::vector threads; threads.reserve(numThreads); for (size_t i = 0; i < numThreads; i++) { threads.emplace_back([&] { dap::initialize(); { std::unique_lock lock(mutex); numInits++; if (numInits == numThreads) { cv.notify_all(); } else { cv.wait(lock, [&] { return numInits == numThreads; }); } } dap::terminate(); }); } for (auto& thread : threads) { thread.join(); } } google-cppdap-252b568/src/io.cpp000066400000000000000000000151221443732122100164140ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/io.h" #include #include #include #include #include // strlen #include #include #include namespace { class Pipe : public dap::ReaderWriter { public: // dap::ReaderWriter compliance bool isOpen() override { std::unique_lock lock(mutex); return !closed; } void close() override { std::unique_lock lock(mutex); closed = true; cv.notify_all(); } size_t read(void* buffer, size_t bytes) override { std::unique_lock lock(mutex); auto out = reinterpret_cast(buffer); size_t n = 0; while (true) { cv.wait(lock, [&] { return closed || data.size() > 0; }); if (closed) { return n; } for (; n < bytes && data.size() > 0; n++) { out[n] = data.front(); data.pop_front(); } if (n == bytes) { return n; } } } bool write(const void* buffer, size_t bytes) override { std::unique_lock lock(mutex); if (closed) { return false; } if (bytes == 0) { return true; } auto notify = data.size() == 0; auto src = reinterpret_cast(buffer); for (size_t i = 0; i < bytes; i++) { data.emplace_back(src[i]); } if (notify) { cv.notify_all(); } return true; } private: std::mutex mutex; std::condition_variable cv; std::deque data; bool closed = false; }; class RW : public dap::ReaderWriter { public: RW(const std::shared_ptr& r, const std::shared_ptr& w) : r(r), w(w) {} // dap::ReaderWriter compliance bool isOpen() override { return r->isOpen() && w->isOpen(); } void close() override { r->close(); w->close(); } size_t read(void* buffer, size_t n) override { return r->read(buffer, n); } bool write(const void* buffer, size_t n) override { return w->write(buffer, n); } private: const std::shared_ptr r; const std::shared_ptr w; }; class File : public dap::ReaderWriter { public: File(FILE* f, bool closable) : f(f), closable(closable) {} ~File() { close(); } // dap::ReaderWriter compliance bool isOpen() override { return !closed; } void close() override { if (closable) { if (!closed.exchange(true)) { fclose(f); } } } size_t read(void* buffer, size_t n) override { std::unique_lock lock(readMutex); auto out = reinterpret_cast(buffer); for (size_t i = 0; i < n; i++) { int c = fgetc(f); if (c == EOF) { return i; } out[i] = char(c); } return n; } bool write(const void* buffer, size_t n) override { std::unique_lock lock(writeMutex); if (fwrite(buffer, 1, n, f) == n) { fflush(f); return true; } return false; } private: FILE* const f; const bool closable; std::mutex readMutex; std::mutex writeMutex; std::atomic closed = {false}; }; class ReaderSpy : public dap::Reader { public: ReaderSpy(const std::shared_ptr& r, const std::shared_ptr& s, const std::string& prefix) : r(r), s(s), prefix(prefix) {} // dap::Reader compliance bool isOpen() override { return r->isOpen(); } void close() override { r->close(); } size_t read(void* buffer, size_t n) override { auto c = r->read(buffer, n); if (c > 0) { auto chars = reinterpret_cast(buffer); std::string buf = prefix; buf.append(chars, chars + c); s->write(buf.data(), buf.size()); } return c; } private: const std::shared_ptr r; const std::shared_ptr s; const std::string prefix; }; class WriterSpy : public dap::Writer { public: WriterSpy(const std::shared_ptr& w, const std::shared_ptr& s, const std::string& prefix) : w(w), s(s), prefix(prefix) {} // dap::Writer compliance bool isOpen() override { return w->isOpen(); } void close() override { w->close(); } bool write(const void* buffer, size_t n) override { if (!w->write(buffer, n)) { return false; } auto chars = reinterpret_cast(buffer); std::string buf = prefix; buf.append(chars, chars + n); s->write(buf.data(), buf.size()); return true; } private: const std::shared_ptr w; const std::shared_ptr s; const std::string prefix; }; } // anonymous namespace namespace dap { std::shared_ptr ReaderWriter::create( const std::shared_ptr& r, const std::shared_ptr& w) { return std::make_shared(r, w); } std::shared_ptr pipe() { return std::make_shared(); } std::shared_ptr file(FILE* f, bool closable /* = true */) { return std::make_shared(f, closable); } std::shared_ptr file(const char* path) { if (auto f = fopen(path, "wb")) { return std::make_shared(f, true); } return nullptr; } // spy() returns a Reader that copies all reads from the Reader r to the Writer // s, using the given optional prefix. std::shared_ptr spy(const std::shared_ptr& r, const std::shared_ptr& s, const char* prefix /* = "\n<-" */) { return std::make_shared(r, s, prefix); } // spy() returns a Writer that copies all writes to the Writer w to the Writer // s, using the given optional prefix. std::shared_ptr spy(const std::shared_ptr& w, const std::shared_ptr& s, const char* prefix /* = "\n->" */) { return std::make_shared(w, s, prefix); } bool writef(const std::shared_ptr& w, const char* msg, ...) { char buf[2048]; va_list vararg; va_start(vararg, msg); vsnprintf(buf, sizeof(buf), msg, vararg); va_end(vararg); return w->write(buf, strlen(buf)); } } // namespace dap google-cppdap-252b568/src/json_serializer.h000066400000000000000000000026171443732122100206610ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_json_serializer_h #define dap_json_serializer_h #if defined(CPPDAP_JSON_NLOHMANN) #include "nlohmann_json_serializer.h" #elif defined(CPPDAP_JSON_RAPID) #include "rapid_json_serializer.h" #elif defined(CPPDAP_JSON_JSONCPP) #include "jsoncpp_json_serializer.h" #else #error "Unrecognised cppdap JSON library" #endif namespace dap { namespace json { #if defined(CPPDAP_JSON_NLOHMANN) using Deserializer = NlohmannDeserializer; using Serializer = NlohmannSerializer; #elif defined(CPPDAP_JSON_RAPID) using Deserializer = RapidDeserializer; using Serializer = RapidSerializer; #elif defined(CPPDAP_JSON_JSONCPP) using Deserializer = JsonCppDeserializer; using Serializer = JsonCppSerializer; #else #error "Unrecognised cppdap JSON library" #endif } // namespace json } // namespace dap #endif // dap_json_serializer_h google-cppdap-252b568/src/json_serializer_test.cpp000066400000000000000000000215401443732122100222470ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "json_serializer.h" #include "dap/typeinfo.h" #include "dap/typeof.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace dap { struct JSONInnerTestObject { integer i; }; DAP_STRUCT_TYPEINFO(JSONInnerTestObject, "json-inner-test-object", DAP_FIELD(i, "i")); struct JSONTestObject { boolean b; integer i; number n; array a; object o; string s; optional o1; optional o2; JSONInnerTestObject inner; }; DAP_STRUCT_TYPEINFO(JSONTestObject, "json-test-object", DAP_FIELD(b, "b"), DAP_FIELD(i, "i"), DAP_FIELD(n, "n"), DAP_FIELD(a, "a"), DAP_FIELD(o, "o"), DAP_FIELD(s, "s"), DAP_FIELD(o1, "o1"), DAP_FIELD(o2, "o2"), DAP_FIELD(inner, "inner")); struct JSONObjectNoFields {}; DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields"); struct SimpleJSONTestObject { boolean b; integer i; }; DAP_STRUCT_TYPEINFO(SimpleJSONTestObject, "simple-json-test-object", DAP_FIELD(b, "b"), DAP_FIELD(i, "i")); } // namespace dap class JSONSerializer : public testing::Test { protected: static dap::object GetSimpleObject() { return dap::object({{"one", dap::integer(1)}, {"two", dap::number(2)}, {"three", dap::string("three")}, {"four", dap::boolean(true)}}); } void TEST_SIMPLE_OBJECT(const dap::object& obj) { NESTED_TEST_FAILED = true; auto ref_obj = GetSimpleObject(); ASSERT_EQ(obj.size(), ref_obj.size()); ASSERT_TRUE(obj.at("one").is()); ASSERT_TRUE(obj.at("two").is()); ASSERT_TRUE(obj.at("three").is()); ASSERT_TRUE(obj.at("four").is()); ASSERT_EQ(ref_obj.at("one").get(), obj.at("one").get()); ASSERT_EQ(ref_obj.at("two").get(), obj.at("two").get()); ASSERT_EQ(ref_obj.at("three").get(), obj.at("three").get()); ASSERT_EQ(ref_obj.at("four").get(), obj.at("four").get()); NESTED_TEST_FAILED = false; } template void TEST_SERIALIZING_DESERIALIZING(const T& encoded, T& decoded) { NESTED_TEST_FAILED = true; dap::json::Serializer s; ASSERT_TRUE(s.serialize(encoded)); dap::json::Deserializer d(s.dump()); ASSERT_TRUE(d.deserialize(&decoded)); NESTED_TEST_FAILED = false; } bool NESTED_TEST_FAILED = false; #define _ASSERT_PASS(NESTED_TEST) \ NESTED_TEST; \ ASSERT_FALSE(NESTED_TEST_FAILED); }; TEST_F(JSONSerializer, SerializeDeserialize) { dap::JSONTestObject encoded; encoded.b = true; encoded.i = 32; encoded.n = 123.456; encoded.a = {2, 4, 6, 8, 0x100000000, -2, -4, -6, -8, -0x100000000}; encoded.o["one"] = dap::integer(1); encoded.o["two"] = dap::number(2); encoded.s = "hello world"; encoded.o2 = 42; encoded.inner.i = 70; dap::json::Serializer s; ASSERT_TRUE(s.serialize(encoded)); dap::JSONTestObject decoded; dap::json::Deserializer d(s.dump()); ASSERT_TRUE(d.deserialize(&decoded)); ASSERT_EQ(encoded.b, decoded.b); ASSERT_EQ(encoded.i, decoded.i); ASSERT_EQ(encoded.n, decoded.n); ASSERT_EQ(encoded.a, decoded.a); ASSERT_EQ(encoded.o["one"].get(), decoded.o["one"].get()); ASSERT_EQ(encoded.o["two"].get(), decoded.o["two"].get()); ASSERT_EQ(encoded.s, decoded.s); ASSERT_EQ(encoded.o2, decoded.o2); ASSERT_EQ(encoded.inner.i, decoded.inner.i); } TEST_F(JSONSerializer, SerializeObjectNoFields) { dap::JSONObjectNoFields obj; dap::json::Serializer s; ASSERT_TRUE(s.serialize(obj)); ASSERT_EQ(s.dump(), "{}"); } TEST_F(JSONSerializer, SerializeDeserializeObject) { dap::object encoded = GetSimpleObject(); dap::object decoded; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded)); } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObject) { dap::object encoded; dap::object decoded; // object nested inside object dap::object encoded_embed_obj = GetSimpleObject(); dap::object decoded_embed_obj; encoded["embed_obj"] = encoded_embed_obj; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); ASSERT_TRUE(decoded["embed_obj"].is()); decoded_embed_obj = decoded["embed_obj"].get(); _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_obj)); } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedStruct) { dap::object encoded; dap::object decoded; // object nested inside object dap::SimpleJSONTestObject encoded_embed_struct; encoded_embed_struct.b = true; encoded_embed_struct.i = 50; encoded["embed_struct"] = encoded_embed_struct; dap::object decoded_embed_obj; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); ASSERT_TRUE(decoded["embed_struct"].is()); decoded_embed_obj = decoded["embed_struct"].get(); ASSERT_TRUE(decoded_embed_obj.at("b").is()); ASSERT_TRUE(decoded_embed_obj.at("i").is()); ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get()); ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get()); } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) { dap::object encoded; dap::object decoded; // array nested inside object dap::array encoded_embed_arr = {1, 2, 3, 4}; dap::array decoded_embed_arr; encoded["embed_arr"] = encoded_embed_arr; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); // TODO: Deserializing array should infer basic member types ASSERT_TRUE(decoded["embed_arr"].is>()); decoded_embed_arr = decoded["embed_arr"].get>(); ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { ASSERT_TRUE(decoded_embed_arr[i].is()); ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get()); } } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) { dap::object encoded; dap::object decoded; dap::array encoded_embed_arr = {GetSimpleObject(), GetSimpleObject()}; dap::array decoded_embed_arr; encoded["embed_arr"] = encoded_embed_arr; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); // TODO: Deserializing array should infer basic member types ASSERT_TRUE(decoded["embed_arr"].is>()); decoded_embed_arr = decoded["embed_arr"].get>(); ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size()); for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) { ASSERT_TRUE(decoded_embed_arr[i].is()); _ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get())); } } TEST_F(JSONSerializer, DeserializeSerializeEmptyObject) { auto empty_obj = "{}"; dap::object decoded; dap::json::Deserializer d(empty_obj); ASSERT_TRUE(d.deserialize(&decoded)); dap::json::Serializer s; ASSERT_TRUE(s.serialize(decoded)); ASSERT_EQ(s.dump(), empty_obj); } TEST_F(JSONSerializer, SerializeDeserializeEmbeddedEmptyObject) { dap::object encoded_empty_obj; dap::object encoded = {{"empty_obj", encoded_empty_obj}}; dap::object decoded; _ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded)); ASSERT_TRUE(decoded["empty_obj"].is()); dap::object decoded_empty_obj = decoded["empty_obj"].get(); ASSERT_EQ(encoded_empty_obj.size(), decoded_empty_obj.size()); } TEST_F(JSONSerializer, SerializeDeserializeObjectWithNulledField) { auto thing = dap::any(dap::null()); dap::object encoded; encoded["nulled_field"] = dap::null(); dap::json::Serializer s; ASSERT_TRUE(s.serialize(encoded)); dap::object decoded; auto dump = s.dump(); dap::json::Deserializer d(dump); ASSERT_TRUE(d.deserialize(&decoded)); ASSERT_TRUE(encoded["nulled_field"].is()); } google-cppdap-252b568/src/jsoncpp_json_serializer.cpp000066400000000000000000000152221443732122100227440ustar00rootroot00000000000000// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "jsoncpp_json_serializer.h" #include "null_json_serializer.h" #include #include #include namespace dap { namespace json { JsonCppDeserializer::JsonCppDeserializer(const std::string& str) : json(new Json::Value(JsonCppDeserializer::parse(str))), ownsJson(true) {} JsonCppDeserializer::JsonCppDeserializer(const Json::Value* json) : json(json), ownsJson(false) {} JsonCppDeserializer::~JsonCppDeserializer() { if (ownsJson) { delete json; } } bool JsonCppDeserializer::deserialize(dap::boolean* v) const { if (!json->isBool()) { return false; } *v = json->asBool(); return true; } bool JsonCppDeserializer::deserialize(dap::integer* v) const { if (!json->isInt64()) { return false; } *v = json->asInt64(); return true; } bool JsonCppDeserializer::deserialize(dap::number* v) const { if (!json->isNumeric()) { return false; } *v = json->asDouble(); return true; } bool JsonCppDeserializer::deserialize(dap::string* v) const { if (!json->isString()) { return false; } *v = json->asString(); return true; } bool JsonCppDeserializer::deserialize(dap::object* v) const { v->reserve(json->size()); for (auto i = json->begin(); i != json->end(); i++) { JsonCppDeserializer d(&*i); dap::any val; if (!d.deserialize(&val)) { return false; } (*v)[i.name()] = val; } return true; } bool JsonCppDeserializer::deserialize(dap::any* v) const { if (json->isBool()) { *v = dap::boolean(json->asBool()); } else if (json->type() == Json::ValueType::realValue) { // json->isDouble() returns true for integers as well, so we need to // explicitly look for the realValue type. *v = dap::number(json->asDouble()); } else if (json->isInt64()) { *v = dap::integer(json->asInt64()); } else if (json->isString()) { *v = json->asString(); } else if (json->isObject()) { dap::object obj; if (!deserialize(&obj)) { return false; } *v = obj; } else if (json->isArray()) { dap::array arr; if (!deserialize(&arr)) { return false; } *v = arr; } else if (json->isNull()) { *v = null(); } else { return false; } return true; } size_t JsonCppDeserializer::count() const { return json->size(); } bool JsonCppDeserializer::array( const std::function& cb) const { if (!json->isArray()) { return false; } for (const auto& value : *json) { JsonCppDeserializer d(&value); if (!cb(&d)) { return false; } } return true; } bool JsonCppDeserializer::field( const std::string& name, const std::function& cb) const { if (!json->isObject()) { return false; } auto value = json->find(name.data(), name.data() + name.size()); if (value == nullptr) { return cb(&NullDeserializer::instance); } JsonCppDeserializer d(value); return cb(&d); } Json::Value JsonCppDeserializer::parse(const std::string& text) { Json::CharReaderBuilder builder; auto jsonReader = std::unique_ptr(builder.newCharReader()); Json::Value json; std::string error; if (!jsonReader->parse(text.data(), text.data() + text.size(), &json, &error)) { // cppdap expects that the JSON layer does not throw exceptions. std::abort(); } return json; } JsonCppSerializer::JsonCppSerializer() : json(new Json::Value()), ownsJson(true) {} JsonCppSerializer::JsonCppSerializer(Json::Value* json) : json(json), ownsJson(false) {} JsonCppSerializer::~JsonCppSerializer() { if (ownsJson) { delete json; } } std::string JsonCppSerializer::dump() const { Json::StreamWriterBuilder writer; return Json::writeString(writer, *json); } bool JsonCppSerializer::serialize(dap::boolean v) { *json = (bool)v; return true; } bool JsonCppSerializer::serialize(dap::integer v) { *json = (Json::LargestInt)v; return true; } bool JsonCppSerializer::serialize(dap::number v) { *json = (double)v; return true; } bool JsonCppSerializer::serialize(const dap::string& v) { *json = v; return true; } bool JsonCppSerializer::serialize(const dap::object& v) { if (!json->isObject()) { *json = Json::Value(Json::objectValue); } for (auto& it : v) { JsonCppSerializer s(&(*json)[it.first]); if (!s.serialize(it.second)) { return false; } } return true; } bool JsonCppSerializer::serialize(const dap::any& v) { if (v.is()) { *json = (bool)v.get(); } else if (v.is()) { *json = (Json::LargestInt)v.get(); } else if (v.is()) { *json = (double)v.get(); } else if (v.is()) { *json = v.get(); } else if (v.is()) { // reachable if dap::object nested is inside other dap::object return serialize(v.get()); } else if (v.is()) { } else { // reachable if array or custom serialized type is nested inside other auto type = get_any_type(v); auto value = get_any_val(v); if (type && value) { return type->serialize(this, value); } return false; } return true; } bool JsonCppSerializer::array(size_t count, const std::function& cb) { *json = Json::Value(Json::arrayValue); for (size_t i = 0; i < count; i++) { JsonCppSerializer s(&(*json)[Json::Value::ArrayIndex(i)]); if (!cb(&s)) { return false; } } return true; } bool JsonCppSerializer::object( const std::function& cb) { struct FS : public FieldSerializer { Json::Value* const json; FS(Json::Value* json) : json(json) {} bool field(const std::string& name, const SerializeFunc& cb) override { JsonCppSerializer s(&(*json)[name]); auto res = cb(&s); if (s.removed) { json->removeMember(name); } return res; } }; *json = Json::Value(Json::objectValue); FS fs{json}; return cb(&fs); } void JsonCppSerializer::remove() { removed = true; } } // namespace json } // namespace dap google-cppdap-252b568/src/jsoncpp_json_serializer.h000066400000000000000000000077251443732122100224220ustar00rootroot00000000000000// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_jsoncpp_json_serializer_h #define dap_jsoncpp_json_serializer_h #include "dap/protocol.h" #include "dap/serialization.h" #include "dap/types.h" #include namespace dap { namespace json { struct JsonCppDeserializer : public dap::Deserializer { explicit JsonCppDeserializer(const std::string&); ~JsonCppDeserializer(); // dap::Deserializer compliance bool deserialize(boolean* v) const override; bool deserialize(integer* v) const override; bool deserialize(number* v) const override; bool deserialize(string* v) const override; bool deserialize(object* v) const override; bool deserialize(any* v) const override; size_t count() const override; bool array(const std::function&) const override; bool field(const std::string& name, const std::function&) const override; // Unhide base overloads template inline bool field(const std::string& name, T* v) { return dap::Deserializer::field(name, v); } template ::has_custom_serialization>> inline bool deserialize(T* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::array* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::optional* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::variant* v) const { return dap::Deserializer::deserialize(v); } template inline bool field(const std::string& name, T* v) const { return dap::Deserializer::deserialize(name, v); } private: JsonCppDeserializer(const Json::Value*); static Json::Value parse(const std::string& text); const Json::Value* const json; const bool ownsJson; }; struct JsonCppSerializer : public dap::Serializer { JsonCppSerializer(); ~JsonCppSerializer(); std::string dump() const; // dap::Serializer compliance bool serialize(boolean v) override; bool serialize(integer v) override; bool serialize(number v) override; bool serialize(const string& v) override; bool serialize(const dap::object& v) override; bool serialize(const any& v) override; bool array(size_t count, const std::function&) override; bool object(const std::function&) override; void remove() override; // Unhide base overloads template ::has_custom_serialization>> inline bool serialize(const T& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::array& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::optional& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::variant& v) { return dap::Serializer::serialize(v); } inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } private: JsonCppSerializer(Json::Value*); Json::Value* const json; const bool ownsJson; bool removed = false; }; } // namespace json } // namespace dap #endif // dap_jsoncpp_json_serializer_h google-cppdap-252b568/src/network.cpp000066400000000000000000000044641443732122100175050ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/network.h" #include "socket.h" #include #include #include #include namespace { class Impl : public dap::net::Server { public: Impl() : stopped{true} {} ~Impl() { stop(); } bool start(int port, const OnConnect& onConnect, const OnError& onError) override { std::unique_lock lock(mutex); stopWithLock(); socket = std::unique_ptr( new dap::Socket("localhost", std::to_string(port).c_str())); if (!socket->isOpen()) { onError("Failed to open socket"); return false; } stopped = false; thread = std::thread([=] { while (true) { if (auto rw = socket->accept()) { onConnect(rw); continue; } if (!stopped) { onError("Failed to accept connection"); } break; }; }); return true; } void stop() override { std::unique_lock lock(mutex); stopWithLock(); } private: bool isRunning() { return !stopped; } void stopWithLock() { if (!stopped.exchange(true)) { socket->close(); thread.join(); } } std::mutex mutex; std::thread thread; std::unique_ptr socket; std::atomic stopped; OnError errorHandler; }; } // anonymous namespace namespace dap { namespace net { std::unique_ptr Server::create() { return std::unique_ptr(new Impl()); } std::shared_ptr connect(const char* addr, int port, uint32_t timeoutMillis) { return Socket::connect(addr, std::to_string(port).c_str(), timeoutMillis); } } // namespace net } // namespace dap google-cppdap-252b568/src/network_test.cpp000066400000000000000000000056361443732122100205460ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/network.h" #include "dap/io.h" #include "chan.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include namespace { constexpr int port = 19021; bool write(const std::shared_ptr& w, const std::string& s) { return w->write(s.data(), s.size()) && w->write("\0", 1); } std::string read(const std::shared_ptr& r) { char c; std::string s; while (r->read(&c, sizeof(c)) > 0) { if (c == '\0') { return s; } s += c; } return r->isOpen() ? "" : ""; } } // anonymous namespace TEST(Network, ClientServer) { dap::Chan done; auto server = dap::net::Server::create(); if (!server->start( port, [&](const std::shared_ptr& rw) { ASSERT_EQ(read(rw), "client to server"); ASSERT_TRUE(write(rw, "server to client")); done.put(true); }, [&](const char* err) { FAIL() << "Server error: " << err; })) { FAIL() << "Couldn't start server"; return; } for (int i = 0; i < 5; i++) { auto client = dap::net::connect("localhost", port); ASSERT_NE(client, nullptr) << "Failed to connect client " << i; ASSERT_TRUE(write(client, "client to server")); ASSERT_EQ(read(client), "server to client"); done.take(); std::this_thread::sleep_for(std::chrono::seconds(1)); } server.reset(); } TEST(Network, ServerRepeatStopAndRestart) { dap::Chan done; auto onConnect = [&](const std::shared_ptr& rw) { ASSERT_EQ(read(rw), "client to server"); ASSERT_TRUE(write(rw, "server to client")); done.put(true); }; auto onError = [&](const char* err) { FAIL() << "Server error: " << err; }; auto server = dap::net::Server::create(); if (!server->start(port, onConnect, onError)) { FAIL() << "Couldn't start server"; return; } server->stop(); server->stop(); server->stop(); if (!server->start(port, onConnect, onError)) { FAIL() << "Couldn't restart server"; return; } auto client = dap::net::connect("localhost", port); ASSERT_NE(client, nullptr) << "Failed to connect"; ASSERT_TRUE(write(client, "client to server")); ASSERT_EQ(read(client), "server to client"); done.take(); server->stop(); server->stop(); server->stop(); server.reset(); } google-cppdap-252b568/src/nlohmann_json_serializer.cpp000066400000000000000000000143041443732122100231020ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "nlohmann_json_serializer.h" #include "null_json_serializer.h" // Disable JSON exceptions. We should be guarding against any exceptions being // fired in this file. #define JSON_NOEXCEPTION 1 #include namespace dap { namespace json { NlohmannDeserializer::NlohmannDeserializer(const std::string& str) : json(new nlohmann::json(nlohmann::json::parse(str, nullptr, false))), ownsJson(true) {} NlohmannDeserializer::NlohmannDeserializer(const nlohmann::json* json) : json(json), ownsJson(false) {} NlohmannDeserializer::~NlohmannDeserializer() { if (ownsJson) { delete json; } } bool NlohmannDeserializer::deserialize(dap::boolean* v) const { if (!json->is_boolean()) { return false; } *v = json->get(); return true; } bool NlohmannDeserializer::deserialize(dap::integer* v) const { if (!json->is_number_integer()) { return false; } *v = json->get(); return true; } bool NlohmannDeserializer::deserialize(dap::number* v) const { if (!json->is_number()) { return false; } *v = json->get(); return true; } bool NlohmannDeserializer::deserialize(dap::string* v) const { if (!json->is_string()) { return false; } *v = json->get(); return true; } bool NlohmannDeserializer::deserialize(dap::object* v) const { v->reserve(json->size()); for (auto& el : json->items()) { NlohmannDeserializer d(&el.value()); dap::any val; if (!d.deserialize(&val)) { return false; } (*v)[el.key()] = val; } return true; } bool NlohmannDeserializer::deserialize(dap::any* v) const { if (json->is_boolean()) { *v = dap::boolean(json->get()); } else if (json->is_number_float()) { *v = dap::number(json->get()); } else if (json->is_number_integer()) { *v = dap::integer(json->get()); } else if (json->is_string()) { *v = json->get(); } else if (json->is_object()) { dap::object obj; if (!deserialize(&obj)) { return false; } *v = obj; } else if (json->is_array()) { dap::array arr; if (!deserialize(&arr)) { return false; } *v = arr; } else if (json->is_null()) { *v = null(); } else { return false; } return true; } size_t NlohmannDeserializer::count() const { return json->size(); } bool NlohmannDeserializer::array( const std::function& cb) const { if (!json->is_array()) { return false; } for (size_t i = 0; i < json->size(); i++) { NlohmannDeserializer d(&(*json)[i]); if (!cb(&d)) { return false; } } return true; } bool NlohmannDeserializer::field( const std::string& name, const std::function& cb) const { if (!json->is_structured()) { return false; } auto it = json->find(name); if (it == json->end()) { return cb(&NullDeserializer::instance); } auto obj = *it; NlohmannDeserializer d(&obj); return cb(&d); } NlohmannSerializer::NlohmannSerializer() : json(new nlohmann::json()), ownsJson(true) {} NlohmannSerializer::NlohmannSerializer(nlohmann::json* json) : json(json), ownsJson(false) {} NlohmannSerializer::~NlohmannSerializer() { if (ownsJson) { delete json; } } std::string NlohmannSerializer::dump() const { return json->dump(); } bool NlohmannSerializer::serialize(dap::boolean v) { *json = (bool)v; return true; } bool NlohmannSerializer::serialize(dap::integer v) { *json = (int64_t)v; return true; } bool NlohmannSerializer::serialize(dap::number v) { *json = (double)v; return true; } bool NlohmannSerializer::serialize(const dap::string& v) { *json = v; return true; } bool NlohmannSerializer::serialize(const dap::object& v) { if (!json->is_object()) { *json = nlohmann::json::object(); } for (auto& it : v) { NlohmannSerializer s(&(*json)[it.first]); if (!s.serialize(it.second)) { return false; } } return true; } bool NlohmannSerializer::serialize(const dap::any& v) { if (v.is()) { *json = (bool)v.get(); } else if (v.is()) { *json = (int64_t)v.get(); } else if (v.is()) { *json = (double)v.get(); } else if (v.is()) { *json = v.get(); } else if (v.is()) { // reachable if dap::object nested is inside other dap::object return serialize(v.get()); } else if (v.is()) { } else { // reachable if array or custom serialized type is nested inside other auto type = get_any_type(v); auto value = get_any_val(v); if (type && value) { return type->serialize(this, value); } return false; } return true; } bool NlohmannSerializer::array( size_t count, const std::function& cb) { *json = std::vector(); for (size_t i = 0; i < count; i++) { NlohmannSerializer s(&(*json)[i]); if (!cb(&s)) { return false; } } return true; } bool NlohmannSerializer::object( const std::function& cb) { struct FS : public FieldSerializer { nlohmann::json* const json; FS(nlohmann::json* json) : json(json) {} bool field(const std::string& name, const SerializeFunc& cb) override { NlohmannSerializer s(&(*json)[name]); auto res = cb(&s); if (s.removed) { json->erase(name); } return res; } }; *json = nlohmann::json({}, false, nlohmann::json::value_t::object); FS fs{json}; return cb(&fs); } void NlohmannSerializer::remove() { removed = true; } } // namespace json } // namespace dap google-cppdap-252b568/src/nlohmann_json_serializer.h000066400000000000000000000076741443732122100225630ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_nlohmann_json_serializer_h #define dap_nlohmann_json_serializer_h #include "dap/protocol.h" #include "dap/serialization.h" #include "dap/types.h" #include namespace dap { namespace json { struct NlohmannDeserializer : public dap::Deserializer { explicit NlohmannDeserializer(const std::string&); ~NlohmannDeserializer(); // dap::Deserializer compliance bool deserialize(boolean* v) const override; bool deserialize(integer* v) const override; bool deserialize(number* v) const override; bool deserialize(string* v) const override; bool deserialize(object* v) const override; bool deserialize(any* v) const override; size_t count() const override; bool array(const std::function&) const override; bool field(const std::string& name, const std::function&) const override; // Unhide base overloads template inline bool field(const std::string& name, T* v) { return dap::Deserializer::field(name, v); } template ::has_custom_serialization>> inline bool deserialize(T* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::array* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::optional* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::variant* v) const { return dap::Deserializer::deserialize(v); } template inline bool field(const std::string& name, T* v) const { return dap::Deserializer::deserialize(name, v); } private: NlohmannDeserializer(const nlohmann::json*); const nlohmann::json* const json; const bool ownsJson; }; struct NlohmannSerializer : public dap::Serializer { NlohmannSerializer(); ~NlohmannSerializer(); std::string dump() const; // dap::Serializer compliance bool serialize(boolean v) override; bool serialize(integer v) override; bool serialize(number v) override; bool serialize(const string& v) override; bool serialize(const dap::object& v) override; bool serialize(const any& v) override; bool array(size_t count, const std::function&) override; bool object(const std::function&) override; void remove() override; // Unhide base overloads template ::has_custom_serialization>> inline bool serialize(const T& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::array& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::optional& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::variant& v) { return dap::Serializer::serialize(v); } inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } private: NlohmannSerializer(nlohmann::json*); nlohmann::json* const json; const bool ownsJson; bool removed = false; }; } // namespace json } // namespace dap #endif // dap_nlohmann_json_serializer_hgoogle-cppdap-252b568/src/null_json_serializer.cpp000066400000000000000000000013511443732122100222400ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "null_json_serializer.h" namespace dap { namespace json { NullDeserializer NullDeserializer::instance; } // namespace json } // namespace dap google-cppdap-252b568/src/null_json_serializer.h000066400000000000000000000031271443732122100217100ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_null_json_serializer_h #define dap_null_json_serializer_h #include "dap/protocol.h" #include "dap/serialization.h" #include "dap/types.h" namespace dap { namespace json { struct NullDeserializer : public dap::Deserializer { static NullDeserializer instance; bool deserialize(dap::boolean*) const override { return false; } bool deserialize(dap::integer*) const override { return false; } bool deserialize(dap::number*) const override { return false; } bool deserialize(dap::string*) const override { return false; } bool deserialize(dap::object*) const override { return false; } bool deserialize(dap::any*) const override { return false; } size_t count() const override { return 0; } bool array(const std::function&) const override { return false; } bool field(const std::string&, const std::function&) const override { return false; } }; } // namespace json } // namespace dap #endif // dap_null_json_serializer_h google-cppdap-252b568/src/optional_test.cpp000066400000000000000000000114731443732122100206760ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/optional.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include TEST(Optional, EmptyConstruct) { dap::optional opt; ASSERT_FALSE(opt); ASSERT_FALSE(opt.has_value()); } TEST(Optional, ValueConstruct) { dap::optional opt(10); ASSERT_TRUE(opt); ASSERT_TRUE(opt.has_value()); ASSERT_EQ(opt.value(), 10); } TEST(Optional, CopyConstruct) { dap::optional a("meow"); dap::optional b(a); ASSERT_EQ(a, b); ASSERT_EQ(a.value(), "meow"); ASSERT_EQ(b.value(), "meow"); } TEST(Optional, CopyCastConstruct) { dap::optional a(10); dap::optional b(a); ASSERT_EQ(a, b); ASSERT_EQ(b.value(), (uint16_t)10); } TEST(Optional, MoveConstruct) { dap::optional a("meow"); dap::optional b(std::move(a)); ASSERT_EQ(b.value(), "meow"); } TEST(Optional, MoveCastConstruct) { dap::optional a(10); dap::optional b(std::move(a)); ASSERT_EQ(b.value(), (uint16_t)10); } TEST(Optional, AssignValue) { dap::optional a; std::string b = "meow"; a = b; ASSERT_EQ(a.value(), "meow"); ASSERT_EQ(b, "meow"); } TEST(Optional, AssignOptional) { dap::optional a; dap::optional b("meow"); a = b; ASSERT_EQ(a.value(), "meow"); ASSERT_EQ(b.value(), "meow"); } TEST(Optional, MoveAssignOptional) { dap::optional a; dap::optional b("meow"); a = std::move(b); ASSERT_EQ(a.value(), "meow"); } TEST(Optional, StarDeref) { dap::optional a("meow"); ASSERT_EQ(*a, "meow"); } TEST(Optional, StarDerefConst) { const dap::optional a("meow"); ASSERT_EQ(*a, "meow"); } TEST(Optional, ArrowDeref) { struct S { int i; }; dap::optional a(S{10}); ASSERT_EQ(a->i, 10); } TEST(Optional, ArrowDerefConst) { struct S { int i; }; const dap::optional a(S{10}); ASSERT_EQ(a->i, 10); } TEST(Optional, Value) { const dap::optional a("meow"); ASSERT_EQ(a.value(), "meow"); } TEST(Optional, ValueDefault) { const dap::optional a; const dap::optional b("woof"); ASSERT_EQ(a.value("meow"), "meow"); ASSERT_EQ(b.value("meow"), "woof"); } TEST(Optional, CompareLT) { ASSERT_FALSE(dap::optional(5) < dap::optional(3)); ASSERT_FALSE(dap::optional(5) < dap::optional(5)); ASSERT_TRUE(dap::optional(5) < dap::optional(10)); ASSERT_TRUE(dap::optional() < dap::optional(10)); ASSERT_FALSE(dap::optional() < dap::optional()); } TEST(Optional, CompareLE) { ASSERT_FALSE(dap::optional(5) <= dap::optional(3)); ASSERT_TRUE(dap::optional(5) <= dap::optional(5)); ASSERT_TRUE(dap::optional(5) <= dap::optional(10)); ASSERT_TRUE(dap::optional() <= dap::optional(10)); ASSERT_TRUE(dap::optional() <= dap::optional()); } TEST(Optional, CompareGT) { ASSERT_TRUE(dap::optional(5) > dap::optional(3)); ASSERT_FALSE(dap::optional(5) > dap::optional(5)); ASSERT_FALSE(dap::optional(5) > dap::optional(10)); ASSERT_FALSE(dap::optional() > dap::optional(10)); ASSERT_FALSE(dap::optional() > dap::optional()); } TEST(Optional, CompareGE) { ASSERT_TRUE(dap::optional(5) >= dap::optional(3)); ASSERT_TRUE(dap::optional(5) >= dap::optional(5)); ASSERT_FALSE(dap::optional(5) >= dap::optional(10)); ASSERT_FALSE(dap::optional() >= dap::optional(10)); ASSERT_TRUE(dap::optional() >= dap::optional()); } TEST(Optional, CompareEQ) { ASSERT_FALSE(dap::optional(5) == dap::optional(3)); ASSERT_TRUE(dap::optional(5) == dap::optional(5)); ASSERT_FALSE(dap::optional(5) == dap::optional(10)); ASSERT_FALSE(dap::optional() == dap::optional(10)); ASSERT_TRUE(dap::optional() == dap::optional()); } TEST(Optional, CompareNEQ) { ASSERT_TRUE(dap::optional(5) != dap::optional(3)); ASSERT_FALSE(dap::optional(5) != dap::optional(5)); ASSERT_TRUE(dap::optional(5) != dap::optional(10)); ASSERT_TRUE(dap::optional() != dap::optional(10)); ASSERT_FALSE(dap::optional() != dap::optional()); } google-cppdap-252b568/src/protocol_events.cpp000066400000000000000000000130031443732122100212260ustar00rootroot00000000000000// 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.59.0 #include "dap/protocol.h" namespace dap { DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent, "breakpoint", DAP_FIELD(breakpoint, "breakpoint"), DAP_FIELD(reason, "reason")); DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent, "capabilities", DAP_FIELD(capabilities, "capabilities")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent, "continued", DAP_FIELD(allThreadsContinued, "allThreadsContinued"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent, "exited", DAP_FIELD(exitCode, "exitCode")); DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized"); DAP_IMPLEMENT_STRUCT_TYPEINFO(InvalidatedEvent, "invalidated", DAP_FIELD(areas, "areas"), DAP_FIELD(stackFrameId, "stackFrameId"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent, "loadedSource", DAP_FIELD(reason, "reason"), DAP_FIELD(source, "source")); DAP_IMPLEMENT_STRUCT_TYPEINFO(MemoryEvent, "memory", DAP_FIELD(count, "count"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(offset, "offset")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent, "module", DAP_FIELD(module, "module"), DAP_FIELD(reason, "reason")); DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent, "output", DAP_FIELD(category, "category"), DAP_FIELD(column, "column"), DAP_FIELD(data, "data"), DAP_FIELD(group, "group"), DAP_FIELD(line, "line"), DAP_FIELD(output, "output"), DAP_FIELD(source, "source"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ProcessEvent, "process", DAP_FIELD(isLocalProcess, "isLocalProcess"), DAP_FIELD(name, "name"), DAP_FIELD(pointerSize, "pointerSize"), DAP_FIELD(startMethod, "startMethod"), DAP_FIELD(systemProcessId, "systemProcessId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressEndEvent, "progressEnd", DAP_FIELD(message, "message"), DAP_FIELD(progressId, "progressId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressStartEvent, "progressStart", DAP_FIELD(cancellable, "cancellable"), DAP_FIELD(message, "message"), DAP_FIELD(percentage, "percentage"), DAP_FIELD(progressId, "progressId"), DAP_FIELD(requestId, "requestId"), DAP_FIELD(title, "title")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressUpdateEvent, "progressUpdate", DAP_FIELD(message, "message"), DAP_FIELD(percentage, "percentage"), DAP_FIELD(progressId, "progressId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent, "stopped", DAP_FIELD(allThreadsStopped, "allThreadsStopped"), DAP_FIELD(description, "description"), DAP_FIELD(hitBreakpointIds, "hitBreakpointIds"), DAP_FIELD(preserveFocusHint, "preserveFocusHint"), DAP_FIELD(reason, "reason"), DAP_FIELD(text, "text"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent, "terminated", DAP_FIELD(restart, "restart")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent, "thread", DAP_FIELD(reason, "reason"), DAP_FIELD(threadId, "threadId")); } // namespace dap google-cppdap-252b568/src/protocol_requests.cpp000066400000000000000000000311561443732122100216060ustar00rootroot00000000000000// 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.59.0 #include "dap/protocol.h" namespace dap { DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachRequest, "attach", DAP_FIELD(restart, "__restart")); DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsRequest, "breakpointLocations", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(line, "line"), DAP_FIELD(source, "source")); DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelRequest, "cancel", DAP_FIELD(progressId, "progressId"), DAP_FIELD(requestId, "requestId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsRequest, "completions", DAP_FIELD(column, "column"), DAP_FIELD(frameId, "frameId"), DAP_FIELD(line, "line"), DAP_FIELD(text, "text")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneRequest, "configurationDone"); DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest, "continue", DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest, "dataBreakpointInfo", DAP_FIELD(frameId, "frameId"), DAP_FIELD(name, "name"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleRequest, "disassemble", DAP_FIELD(instructionCount, "instructionCount"), DAP_FIELD(instructionOffset, "instructionOffset"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(offset, "offset"), DAP_FIELD(resolveSymbols, "resolveSymbols")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectRequest, "disconnect", DAP_FIELD(restart, "restart"), DAP_FIELD(suspendDebuggee, "suspendDebuggee"), DAP_FIELD(terminateDebuggee, "terminateDebuggee")); DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateRequest, "evaluate", DAP_FIELD(context, "context"), DAP_FIELD(expression, "expression"), DAP_FIELD(format, "format"), DAP_FIELD(frameId, "frameId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoRequest, "exceptionInfo", DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoRequest, "goto", DAP_FIELD(targetId, "targetId"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsRequest, "gotoTargets", DAP_FIELD(column, "column"), DAP_FIELD(line, "line"), DAP_FIELD(source, "source")); DAP_IMPLEMENT_STRUCT_TYPEINFO( InitializeRequest, "initialize", DAP_FIELD(adapterID, "adapterID"), DAP_FIELD(clientID, "clientID"), DAP_FIELD(clientName, "clientName"), DAP_FIELD(columnsStartAt1, "columnsStartAt1"), DAP_FIELD(linesStartAt1, "linesStartAt1"), DAP_FIELD(locale, "locale"), DAP_FIELD(pathFormat, "pathFormat"), DAP_FIELD(supportsArgsCanBeInterpretedByShell, "supportsArgsCanBeInterpretedByShell"), DAP_FIELD(supportsInvalidatedEvent, "supportsInvalidatedEvent"), DAP_FIELD(supportsMemoryEvent, "supportsMemoryEvent"), DAP_FIELD(supportsMemoryReferences, "supportsMemoryReferences"), DAP_FIELD(supportsProgressReporting, "supportsProgressReporting"), DAP_FIELD(supportsRunInTerminalRequest, "supportsRunInTerminalRequest"), DAP_FIELD(supportsStartDebuggingRequest, "supportsStartDebuggingRequest"), DAP_FIELD(supportsVariablePaging, "supportsVariablePaging"), DAP_FIELD(supportsVariableType, "supportsVariableType")); DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchRequest, "launch", DAP_FIELD(restart, "__restart"), DAP_FIELD(noDebug, "noDebug")); DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesRequest, "loadedSources"); DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesRequest, "modules", DAP_FIELD(moduleCount, "moduleCount"), DAP_FIELD(startModule, "startModule")); DAP_IMPLEMENT_STRUCT_TYPEINFO(NextRequest, "next", DAP_FIELD(granularity, "granularity"), DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseRequest, "pause", DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryRequest, "readMemory", DAP_FIELD(count, "count"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(offset, "offset")); DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameRequest, "restartFrame", DAP_FIELD(frameId, "frameId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartRequest, "restart", DAP_FIELD(arguments, "arguments")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueRequest, "reverseContinue", DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalRequest, "runInTerminal", DAP_FIELD(args, "args"), DAP_FIELD(argsCanBeInterpretedByShell, "argsCanBeInterpretedByShell"), DAP_FIELD(cwd, "cwd"), DAP_FIELD(env, "env"), DAP_FIELD(kind, "kind"), DAP_FIELD(title, "title")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesRequest, "scopes", DAP_FIELD(frameId, "frameId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsRequest, "setBreakpoints", DAP_FIELD(breakpoints, "breakpoints"), DAP_FIELD(lines, "lines"), DAP_FIELD(source, "source"), DAP_FIELD(sourceModified, "sourceModified")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsRequest, "setDataBreakpoints", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest, "setExceptionBreakpoints", DAP_FIELD(exceptionOptions, "exceptionOptions"), DAP_FIELD(filterOptions, "filterOptions"), DAP_FIELD(filters, "filters")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionRequest, "setExpression", DAP_FIELD(expression, "expression"), DAP_FIELD(format, "format"), DAP_FIELD(frameId, "frameId"), DAP_FIELD(value, "value")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest, "setFunctionBreakpoints", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest, "setInstructionBreakpoints", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableRequest, "setVariable", DAP_FIELD(format, "format"), DAP_FIELD(name, "name"), DAP_FIELD(value, "value"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceRequest, "source", DAP_FIELD(source, "source"), DAP_FIELD(sourceReference, "sourceReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceRequest, "stackTrace", DAP_FIELD(format, "format"), DAP_FIELD(levels, "levels"), DAP_FIELD(startFrame, "startFrame"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingRequest, "startDebugging", DAP_FIELD(configuration, "configuration"), DAP_FIELD(request, "request")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackRequest, "stepBack", DAP_FIELD(granularity, "granularity"), DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInRequest, "stepIn", DAP_FIELD(granularity, "granularity"), DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(targetId, "targetId"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsRequest, "stepInTargets", DAP_FIELD(frameId, "frameId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutRequest, "stepOut", DAP_FIELD(granularity, "granularity"), DAP_FIELD(singleThread, "singleThread"), DAP_FIELD(threadId, "threadId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateRequest, "terminate", DAP_FIELD(restart, "restart")); DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsRequest, "terminateThreads", DAP_FIELD(threadIds, "threadIds")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsRequest, "threads"); DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesRequest, "variables", DAP_FIELD(count, "count"), DAP_FIELD(filter, "filter"), DAP_FIELD(format, "format"), DAP_FIELD(start, "start"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryRequest, "writeMemory", DAP_FIELD(allowPartial, "allowPartial"), DAP_FIELD(data, "data"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(offset, "offset")); } // namespace dap google-cppdap-252b568/src/protocol_response.cpp000066400000000000000000000251641443732122100215730ustar00rootroot00000000000000// 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.59.0 #include "dap/protocol.h" namespace dap { DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsResponse, "", DAP_FIELD(targets, "targets")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueResponse, "", DAP_FIELD(allThreadsContinued, "allThreadsContinued")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoResponse, "", DAP_FIELD(accessTypes, "accessTypes"), DAP_FIELD(canPersist, "canPersist"), DAP_FIELD(dataId, "dataId"), DAP_FIELD(description, "description")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleResponse, "", DAP_FIELD(instructions, "instructions")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(ErrorResponse, "", DAP_FIELD(error, "error")); DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateResponse, "", DAP_FIELD(indexedVariables, "indexedVariables"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(result, "result"), DAP_FIELD(type, "type"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoResponse, "", DAP_FIELD(breakMode, "breakMode"), DAP_FIELD(description, "description"), DAP_FIELD(details, "details"), DAP_FIELD(exceptionId, "exceptionId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsResponse, "", DAP_FIELD(targets, "targets")); DAP_IMPLEMENT_STRUCT_TYPEINFO( InitializeResponse, "", DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"), DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"), DAP_FIELD(supportsBreakpointLocationsRequest, "supportsBreakpointLocationsRequest"), DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"), DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"), DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"), DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"), DAP_FIELD(supportsConfigurationDoneRequest, "supportsConfigurationDoneRequest"), DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"), DAP_FIELD(supportsDelayedStackTraceLoading, "supportsDelayedStackTraceLoading"), DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"), DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"), DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"), DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"), DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"), DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"), DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"), DAP_FIELD(supportsHitConditionalBreakpoints, "supportsHitConditionalBreakpoints"), DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"), DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"), DAP_FIELD(supportsLogPoints, "supportsLogPoints"), DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"), DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"), DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"), DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"), DAP_FIELD(supportsSetExpression, "supportsSetExpression"), DAP_FIELD(supportsSetVariable, "supportsSetVariable"), DAP_FIELD(supportsSingleThreadExecutionRequests, "supportsSingleThreadExecutionRequests"), DAP_FIELD(supportsStepBack, "supportsStepBack"), DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"), DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"), DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"), DAP_FIELD(supportsTerminateThreadsRequest, "supportsTerminateThreadsRequest"), DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"), DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest")); DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesResponse, "", DAP_FIELD(sources, "sources")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesResponse, "", DAP_FIELD(modules, "modules"), DAP_FIELD(totalModules, "totalModules")); DAP_IMPLEMENT_STRUCT_TYPEINFO(NextResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryResponse, "", DAP_FIELD(address, "address"), DAP_FIELD(data, "data"), DAP_FIELD(unreadableBytes, "unreadableBytes")); DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalResponse, "", DAP_FIELD(processId, "processId"), DAP_FIELD(shellProcessId, "shellProcessId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesResponse, "", DAP_FIELD(scopes, "scopes")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionResponse, "", DAP_FIELD(indexedVariables, "indexedVariables"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(type, "type"), DAP_FIELD(value, "value"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse, "", DAP_FIELD(breakpoints, "breakpoints")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableResponse, "", DAP_FIELD(indexedVariables, "indexedVariables"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(type, "type"), DAP_FIELD(value, "value"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceResponse, "", DAP_FIELD(content, "content"), DAP_FIELD(mimeType, "mimeType")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceResponse, "", DAP_FIELD(stackFrames, "stackFrames"), DAP_FIELD(totalFrames, "totalFrames")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsResponse, "", DAP_FIELD(targets, "targets")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsResponse, ""); DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsResponse, "", DAP_FIELD(threads, "threads")); DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesResponse, "", DAP_FIELD(variables, "variables")); DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryResponse, "", DAP_FIELD(bytesWritten, "bytesWritten"), DAP_FIELD(offset, "offset")); } // namespace dap google-cppdap-252b568/src/protocol_types.cpp000066400000000000000000000375751443732122100211120ustar00rootroot00000000000000// 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.59.0 #include "dap/protocol.h" namespace dap { DAP_IMPLEMENT_STRUCT_TYPEINFO(Checksum, "", DAP_FIELD(algorithm, "algorithm"), DAP_FIELD(checksum, "checksum")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Source, "", DAP_FIELD(adapterData, "adapterData"), DAP_FIELD(checksums, "checksums"), DAP_FIELD(name, "name"), DAP_FIELD(origin, "origin"), DAP_FIELD(path, "path"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(sourceReference, "sourceReference"), DAP_FIELD(sources, "sources")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint, "", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(id, "id"), DAP_FIELD(instructionReference, "instructionReference"), DAP_FIELD(line, "line"), DAP_FIELD(message, "message"), DAP_FIELD(offset, "offset"), DAP_FIELD(source, "source"), DAP_FIELD(verified, "verified")); DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocation, "", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(line, "line")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ColumnDescriptor, "", DAP_FIELD(attributeName, "attributeName"), DAP_FIELD(format, "format"), DAP_FIELD(label, "label"), DAP_FIELD(type, "type"), DAP_FIELD(width, "width")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter, "", DAP_FIELD(conditionDescription, "conditionDescription"), DAP_FIELD(def, "default"), DAP_FIELD(description, "description"), DAP_FIELD(filter, "filter"), DAP_FIELD(label, "label"), DAP_FIELD(supportsCondition, "supportsCondition")); DAP_IMPLEMENT_STRUCT_TYPEINFO( Capabilities, "", DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"), DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"), DAP_FIELD(supportsBreakpointLocationsRequest, "supportsBreakpointLocationsRequest"), DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"), DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"), DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"), DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"), DAP_FIELD(supportsConfigurationDoneRequest, "supportsConfigurationDoneRequest"), DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"), DAP_FIELD(supportsDelayedStackTraceLoading, "supportsDelayedStackTraceLoading"), DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"), DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"), DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"), DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"), DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"), DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"), DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"), DAP_FIELD(supportsHitConditionalBreakpoints, "supportsHitConditionalBreakpoints"), DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"), DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"), DAP_FIELD(supportsLogPoints, "supportsLogPoints"), DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"), DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"), DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"), DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"), DAP_FIELD(supportsSetExpression, "supportsSetExpression"), DAP_FIELD(supportsSetVariable, "supportsSetVariable"), DAP_FIELD(supportsSingleThreadExecutionRequests, "supportsSingleThreadExecutionRequests"), DAP_FIELD(supportsStepBack, "supportsStepBack"), DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"), DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"), DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"), DAP_FIELD(supportsTerminateThreadsRequest, "supportsTerminateThreadsRequest"), DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"), DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest")); DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItem, "", DAP_FIELD(detail, "detail"), DAP_FIELD(label, "label"), DAP_FIELD(length, "length"), DAP_FIELD(selectionLength, "selectionLength"), DAP_FIELD(selectionStart, "selectionStart"), DAP_FIELD(sortText, "sortText"), DAP_FIELD(start, "start"), DAP_FIELD(text, "text"), DAP_FIELD(type, "type")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembledInstruction, "", DAP_FIELD(address, "address"), DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(instruction, "instruction"), DAP_FIELD(instructionBytes, "instructionBytes"), DAP_FIELD(line, "line"), DAP_FIELD(location, "location"), DAP_FIELD(symbol, "symbol")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Message, "", DAP_FIELD(format, "format"), DAP_FIELD(id, "id"), DAP_FIELD(sendTelemetry, "sendTelemetry"), DAP_FIELD(showUser, "showUser"), DAP_FIELD(url, "url"), DAP_FIELD(urlLabel, "urlLabel"), DAP_FIELD(variables, "variables")); DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablePresentationHint, "", DAP_FIELD(attributes, "attributes"), DAP_FIELD(kind, "kind"), DAP_FIELD(lazy, "lazy"), DAP_FIELD(visibility, "visibility")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ValueFormat, "", DAP_FIELD(hex, "hex")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionDetails, "", DAP_FIELD(evaluateName, "evaluateName"), DAP_FIELD(fullTypeName, "fullTypeName"), DAP_FIELD(innerException, "innerException"), DAP_FIELD(message, "message"), DAP_FIELD(stackTrace, "stackTrace"), DAP_FIELD(typeName, "typeName")); DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTarget, "", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(id, "id"), DAP_FIELD(instructionPointerReference, "instructionPointerReference"), DAP_FIELD(label, "label"), DAP_FIELD(line, "line")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Module, "", DAP_FIELD(addressRange, "addressRange"), DAP_FIELD(dateTimeStamp, "dateTimeStamp"), DAP_FIELD(id, "id"), DAP_FIELD(isOptimized, "isOptimized"), DAP_FIELD(isUserCode, "isUserCode"), DAP_FIELD(name, "name"), DAP_FIELD(path, "path"), DAP_FIELD(symbolFilePath, "symbolFilePath"), DAP_FIELD(symbolStatus, "symbolStatus"), DAP_FIELD(version, "version")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Scope, "", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(expensive, "expensive"), DAP_FIELD(indexedVariables, "indexedVariables"), DAP_FIELD(line, "line"), DAP_FIELD(name, "name"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(source, "source"), DAP_FIELD(variablesReference, "variablesReference")); DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceBreakpoint, "", DAP_FIELD(column, "column"), DAP_FIELD(condition, "condition"), DAP_FIELD(hitCondition, "hitCondition"), DAP_FIELD(line, "line"), DAP_FIELD(logMessage, "logMessage")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint, "", DAP_FIELD(accessType, "accessType"), DAP_FIELD(condition, "condition"), DAP_FIELD(dataId, "dataId"), DAP_FIELD(hitCondition, "hitCondition")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionPathSegment, "", DAP_FIELD(names, "names"), DAP_FIELD(negate, "negate")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions, "", DAP_FIELD(breakMode, "breakMode"), DAP_FIELD(path, "path")); DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionFilterOptions, "", DAP_FIELD(condition, "condition"), DAP_FIELD(filterId, "filterId")); DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint, "", DAP_FIELD(condition, "condition"), DAP_FIELD(hitCondition, "hitCondition"), DAP_FIELD(name, "name")); DAP_IMPLEMENT_STRUCT_TYPEINFO(InstructionBreakpoint, "", DAP_FIELD(condition, "condition"), DAP_FIELD(hitCondition, "hitCondition"), DAP_FIELD(instructionReference, "instructionReference"), DAP_FIELD(offset, "offset")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame, "", DAP_FIELD(canRestart, "canRestart"), DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(id, "id"), DAP_FIELD(instructionPointerReference, "instructionPointerReference"), DAP_FIELD(line, "line"), DAP_FIELD(moduleId, "moduleId"), DAP_FIELD(name, "name"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(source, "source")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrameFormat, "", DAP_FIELD(includeAll, "includeAll"), DAP_FIELD(line, "line"), DAP_FIELD(module, "module"), DAP_FIELD(parameterNames, "parameterNames"), DAP_FIELD(parameterTypes, "parameterTypes"), DAP_FIELD(parameterValues, "parameterValues"), DAP_FIELD(parameters, "parameters")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTarget, "", DAP_FIELD(column, "column"), DAP_FIELD(endColumn, "endColumn"), DAP_FIELD(endLine, "endLine"), DAP_FIELD(id, "id"), DAP_FIELD(label, "label"), DAP_FIELD(line, "line")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Thread, "", DAP_FIELD(id, "id"), DAP_FIELD(name, "name")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Variable, "", DAP_FIELD(evaluateName, "evaluateName"), DAP_FIELD(indexedVariables, "indexedVariables"), DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(name, "name"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(type, "type"), DAP_FIELD(value, "value"), DAP_FIELD(variablesReference, "variablesReference")); } // namespace dap google-cppdap-252b568/src/rapid_json_serializer.cpp000066400000000000000000000166501443732122100223750ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "rapid_json_serializer.h" #include "null_json_serializer.h" #include #include namespace dap { namespace json { RapidDeserializer::RapidDeserializer(const std::string& str) : doc(new rapidjson::Document()) { doc->Parse(str.c_str()); } RapidDeserializer::RapidDeserializer(rapidjson::Value* json) : val(json) {} RapidDeserializer::~RapidDeserializer() { delete doc; } bool RapidDeserializer::deserialize(dap::boolean* v) const { if (!json()->IsBool()) { return false; } *v = json()->GetBool(); return true; } bool RapidDeserializer::deserialize(dap::integer* v) const { if (json()->IsInt()) { *v = json()->GetInt(); return true; } else if (json()->IsUint()) { *v = static_cast(json()->GetUint()); return true; } else if (json()->IsInt64()) { *v = json()->GetInt64(); return true; } else if (json()->IsUint64()) { *v = static_cast(json()->GetUint64()); return true; } return false; } bool RapidDeserializer::deserialize(dap::number* v) const { if (!json()->IsNumber()) { return false; } *v = json()->GetDouble(); return true; } bool RapidDeserializer::deserialize(dap::string* v) const { if (!json()->IsString()) { return false; } *v = json()->GetString(); return true; } bool RapidDeserializer::deserialize(dap::object* v) const { v->reserve(json()->MemberCount()); for (auto el = json()->MemberBegin(); el != json()->MemberEnd(); el++) { dap::any el_val; RapidDeserializer d(&(el->value)); if (!d.deserialize(&el_val)) { return false; } (*v)[el->name.GetString()] = el_val; } return true; } bool RapidDeserializer::deserialize(dap::any* v) const { if (json()->IsBool()) { *v = dap::boolean(json()->GetBool()); } else if (json()->IsDouble()) { *v = dap::number(json()->GetDouble()); } else if (json()->IsInt()) { *v = dap::integer(json()->GetInt()); } else if (json()->IsString()) { *v = dap::string(json()->GetString()); } else if (json()->IsNull()) { *v = null(); } else if (json()->IsObject()) { dap::object obj; if (!deserialize(&obj)) { return false; } *v = obj; } else if (json()->IsArray()){ dap::array arr; if (!deserialize(&arr)){ return false; } *v = arr; } else { return false; } return true; } size_t RapidDeserializer::count() const { return json()->Size(); } bool RapidDeserializer::array( const std::function& cb) const { if (!json()->IsArray()) { return false; } for (uint32_t i = 0; i < json()->Size(); i++) { RapidDeserializer d(&(*json())[i]); if (!cb(&d)) { return false; } } return true; } bool RapidDeserializer::field( const std::string& name, const std::function& cb) const { if (!json()->IsObject()) { return false; } auto it = json()->FindMember(name.c_str()); if (it == json()->MemberEnd()) { return cb(&NullDeserializer::instance); } RapidDeserializer d(&(it->value)); return cb(&d); } RapidSerializer::RapidSerializer() : doc(new rapidjson::Document(rapidjson::kObjectType)), allocator(doc->GetAllocator()) {} RapidSerializer::RapidSerializer(rapidjson::Value* json, rapidjson::Document::AllocatorType& allocator) : val(json), allocator(allocator) {} RapidSerializer::~RapidSerializer() { delete doc; } std::string RapidSerializer::dump() const { rapidjson::StringBuffer sb; rapidjson::PrettyWriter writer(sb); json()->Accept(writer); return sb.GetString(); } bool RapidSerializer::serialize(dap::boolean v) { json()->SetBool(v); return true; } bool RapidSerializer::serialize(dap::integer v) { json()->SetInt64(v); return true; } bool RapidSerializer::serialize(dap::number v) { json()->SetDouble(v); return true; } bool RapidSerializer::serialize(const dap::string& v) { json()->SetString(v.data(), static_cast(v.length()), allocator); return true; } bool RapidSerializer::serialize(const dap::object& v) { if (!json()->IsObject()) { json()->SetObject(); } for (auto& it : v) { if (!json()->HasMember(it.first.c_str())) { rapidjson::Value name_value{it.first.c_str(), allocator}; json()->AddMember(name_value, rapidjson::Value(), allocator); } rapidjson::Value& member = (*json())[it.first.c_str()]; RapidSerializer s(&member, allocator); if (!s.serialize(it.second)) { return false; } } return true; } bool RapidSerializer::serialize(const dap::any& v) { if (v.is()) { json()->SetBool((bool)v.get()); } else if (v.is()) { json()->SetInt64(v.get()); } else if (v.is()) { json()->SetDouble((double)v.get()); } else if (v.is()) { auto s = v.get(); json()->SetString(s.data(), static_cast(s.length()), allocator); } else if (v.is()) { // reachable if dap::object nested is inside other dap::object return serialize(v.get()); } else if (v.is()) { } else { // reachable if array or custom serialized type is nested inside other dap::object auto type = get_any_type(v); auto value = get_any_val(v); if (type && value) { return type->serialize(this, value); } return false; } return true; } bool RapidSerializer::array(size_t count, const std::function& cb) { if (!json()->IsArray()) { json()->SetArray(); } while (count > json()->Size()) { json()->PushBack(rapidjson::Value(), allocator); } for (uint32_t i = 0; i < count; i++) { RapidSerializer s(&(*json())[i], allocator); if (!cb(&s)) { return false; } } return true; } bool RapidSerializer::object( const std::function& cb) { struct FS : public FieldSerializer { rapidjson::Value* const json; rapidjson::Document::AllocatorType& allocator; FS(rapidjson::Value* json, rapidjson::Document::AllocatorType& allocator) : json(json), allocator(allocator) {} bool field(const std::string& name, const SerializeFunc& cb) override { if (!json->HasMember(name.c_str())) { rapidjson::Value name_value{name.c_str(), allocator}; json->AddMember(name_value, rapidjson::Value(), allocator); } rapidjson::Value& member = (*json)[name.c_str()]; RapidSerializer s(&member, allocator); auto res = cb(&s); if (s.removed) { json->RemoveMember(name.c_str()); } return res; } }; if (!json()->IsObject()) { json()->SetObject(); } FS fs{json(), allocator}; return cb(&fs); } void RapidSerializer::remove() { removed = true; } } // namespace json } // namespace dap google-cppdap-252b568/src/rapid_json_serializer.h000066400000000000000000000103151443732122100220320ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_rapid_json_serializer_h #define dap_rapid_json_serializer_h #include "dap/protocol.h" #include "dap/serialization.h" #include "dap/types.h" #include namespace dap { namespace json { struct RapidDeserializer : public dap::Deserializer { explicit RapidDeserializer(const std::string&); ~RapidDeserializer(); // dap::Deserializer compliance bool deserialize(boolean* v) const override; bool deserialize(integer* v) const override; bool deserialize(number* v) const override; bool deserialize(string* v) const override; bool deserialize(object* v) const override; bool deserialize(any* v) const override; size_t count() const override; bool array(const std::function&) const override; bool field(const std::string& name, const std::function&) const override; // Unhide base overloads template inline bool field(const std::string& name, T* v) { return dap::Deserializer::field(name, v); } template ::has_custom_serialization>> inline bool deserialize(T* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::array* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::optional* v) const { return dap::Deserializer::deserialize(v); } template inline bool deserialize(dap::variant* v) const { return dap::Deserializer::deserialize(v); } template inline bool field(const std::string& name, T* v) const { return dap::Deserializer::deserialize(name, v); } inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; } private: RapidDeserializer(rapidjson::Value*); rapidjson::Document* const doc = nullptr; rapidjson::Value* const val = nullptr; }; struct RapidSerializer : public dap::Serializer { RapidSerializer(); ~RapidSerializer(); std::string dump() const; // dap::Serializer compliance bool serialize(boolean v) override; bool serialize(integer v) override; bool serialize(number v) override; bool serialize(const string& v) override; bool serialize(const dap::object& v) override; bool serialize(const any& v) override; bool array(size_t count, const std::function&) override; bool object(const std::function&) override; void remove() override; // Unhide base overloads template ::has_custom_serialization>> inline bool serialize(const T& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::array& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::optional& v) { return dap::Serializer::serialize(v); } template inline bool serialize(const dap::variant& v) { return dap::Serializer::serialize(v); } inline bool serialize(const char* v) { return dap::Serializer::serialize(v); } inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; } private: RapidSerializer(rapidjson::Value*, rapidjson::Document::AllocatorType&); rapidjson::Document* const doc = nullptr; rapidjson::Value* const val = nullptr; rapidjson::Document::AllocatorType& allocator; bool removed = false; }; } // namespace json } // namespace dap #endif // dap_rapid_json_serializer_h google-cppdap-252b568/src/rwmutex.h000066400000000000000000000075611443732122100171750ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_rwmutex_h #define dap_rwmutex_h #include #include namespace dap { //////////////////////////////////////////////////////////////////////////////// // RWMutex //////////////////////////////////////////////////////////////////////////////// // A RWMutex is a reader/writer mutual exclusion lock. // The lock can be held by an arbitrary number of readers or a single writer. // Also known as a shared mutex. class RWMutex { public: inline RWMutex() = default; // lockReader() locks the mutex for reading. // Multiple read locks can be held while there are no writer locks. inline void lockReader(); // unlockReader() unlocks the mutex for reading. inline void unlockReader(); // lockWriter() locks the mutex for writing. // If the lock is already locked for reading or writing, lockWriter blocks // until the lock is available. inline void lockWriter(); // unlockWriter() unlocks the mutex for writing. inline void unlockWriter(); private: RWMutex(const RWMutex&) = delete; RWMutex& operator=(const RWMutex&) = delete; int readLocks = 0; int pendingWriteLocks = 0; std::mutex mutex; std::condition_variable cv; }; void RWMutex::lockReader() { std::unique_lock lock(mutex); readLocks++; } void RWMutex::unlockReader() { std::unique_lock lock(mutex); readLocks--; if (readLocks == 0 && pendingWriteLocks > 0) { cv.notify_one(); } } void RWMutex::lockWriter() { std::unique_lock lock(mutex); if (readLocks > 0) { pendingWriteLocks++; cv.wait(lock, [&] { return readLocks == 0; }); pendingWriteLocks--; } lock.release(); // Keep lock held } void RWMutex::unlockWriter() { if (pendingWriteLocks > 0) { cv.notify_one(); } mutex.unlock(); } //////////////////////////////////////////////////////////////////////////////// // RLock //////////////////////////////////////////////////////////////////////////////// // RLock is a RAII read lock helper for a RWMutex. class RLock { public: inline RLock(RWMutex& mutex); inline ~RLock(); inline RLock(RLock&&); inline RLock& operator=(RLock&&); private: RLock(const RLock&) = delete; RLock& operator=(const RLock&) = delete; RWMutex* m; }; RLock::RLock(RWMutex& mutex) : m(&mutex) { m->lockReader(); } RLock::~RLock() { if (m != nullptr) { m->unlockReader(); } } RLock::RLock(RLock&& other) { m = other.m; other.m = nullptr; } RLock& RLock::operator=(RLock&& other) { m = other.m; other.m = nullptr; return *this; } //////////////////////////////////////////////////////////////////////////////// // WLock //////////////////////////////////////////////////////////////////////////////// // WLock is a RAII write lock helper for a RWMutex. class WLock { public: inline WLock(RWMutex& mutex); inline ~WLock(); inline WLock(WLock&&); inline WLock& operator=(WLock&&); private: WLock(const WLock&) = delete; WLock& operator=(const WLock&) = delete; RWMutex* m; }; WLock::WLock(RWMutex& mutex) : m(&mutex) { m->lockWriter(); } WLock::~WLock() { if (m != nullptr) { m->unlockWriter(); } } WLock::WLock(WLock&& other) { m = other.m; other.m = nullptr; } WLock& WLock::operator=(WLock&& other) { m = other.m; other.m = nullptr; return *this; } } // namespace dap #endif google-cppdap-252b568/src/rwmutex_test.cpp000066400000000000000000000047001443732122100205570ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "rwmutex.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include namespace { constexpr const size_t NumThreads = 8; } // Check that WLock behaves like regular mutex. TEST(RWMutex, WLock) { dap::RWMutex rwmutex; int counter = 0; std::vector threads; for (size_t i = 0; i < NumThreads; i++) { threads.emplace_back([&] { for (int j = 0; j < 1000; j++) { dap::WLock lock(rwmutex); counter++; EXPECT_EQ(counter, 1); counter--; } }); } for (auto& thread : threads) { thread.join(); } EXPECT_EQ(counter, 0); } TEST(RWMutex, NoRLockWithWLock) { dap::RWMutex rwmutex; std::vector threads; std::array counters = {}; { // With WLock held... dap::WLock wlock(rwmutex); for (size_t i = 0; i < counters.size(); i++) { int* counter = &counters[i]; threads.emplace_back([&rwmutex, counter] { dap::RLock lock(rwmutex); for (int j = 0; j < 1000; j++) { (*counter)++; } }); } // RLocks should block for (int counter : counters) { EXPECT_EQ(counter, 0); } } for (auto& thread : threads) { thread.join(); } for (int counter : counters) { EXPECT_EQ(counter, 1000); } } TEST(RWMutex, NoWLockWithRLock) { dap::RWMutex rwmutex; std::vector threads; size_t counter = 0; { // With RLocks held... dap::RLock rlockA(rwmutex); dap::RLock rlockB(rwmutex); dap::RLock rlockC(rwmutex); for (size_t i = 0; i < NumThreads; i++) { threads.emplace_back(std::thread([&] { dap::WLock lock(rwmutex); counter++; })); } // ... WLocks should block EXPECT_EQ(counter, 0U); } for (auto& thread : threads) { thread.join(); } EXPECT_EQ(counter, NumThreads); } google-cppdap-252b568/src/session.cpp000066400000000000000000000356431443732122100175020ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "content_stream.h" #include "dap/any.h" #include "dap/session.h" #include "chan.h" #include "json_serializer.h" #include "socket.h" #include #include #include #include #include #include #include #include #include namespace { class Impl : public dap::Session { public: void onError(const ErrorHandler& handler) override { handlers.put(handler); } void registerHandler(const dap::TypeInfo* typeinfo, const GenericRequestHandler& handler) override { handlers.put(typeinfo, handler); } void registerHandler(const dap::TypeInfo* typeinfo, const GenericEventHandler& handler) override { handlers.put(typeinfo, handler); } void registerHandler(const dap::TypeInfo* typeinfo, const GenericResponseSentHandler& handler) override { handlers.put(typeinfo, handler); } std::function getPayload() override { auto request = reader.read(); if (request.size() > 0) { if (auto payload = processMessage(request)) { return payload; } } return {}; } void connect(const std::shared_ptr& r, const std::shared_ptr& w) override { if (isBound.exchange(true)) { handlers.error("Session::connect called twice"); return; } reader = dap::ContentReader(r); writer = dap::ContentWriter(w); } void startProcessingMessages( const ClosedHandler& onClose /* = {} */) override { if (isProcessingMessages.exchange(true)) { handlers.error("Session::startProcessingMessages() called twice"); return; } recvThread = std::thread([this, onClose] { while (reader.isOpen()) { if (auto payload = getPayload()) { inbox.put(std::move(payload)); } } if (onClose) { onClose(); } }); dispatchThread = std::thread([this] { while (auto payload = inbox.take()) { payload.value()(); } }); } bool send(const dap::TypeInfo* requestTypeInfo, const dap::TypeInfo* responseTypeInfo, const void* request, const GenericResponseHandler& responseHandler) override { int seq = nextSeq++; handlers.put(seq, responseTypeInfo, responseHandler); dap::json::Serializer s; if (!s.object([&](dap::FieldSerializer* fs) { return fs->field("seq", dap::integer(seq)) && fs->field("type", "request") && fs->field("command", requestTypeInfo->name()) && fs->field("arguments", [&](dap::Serializer* s) { return requestTypeInfo->serialize(s, request); }); })) { return false; } return send(s.dump()); } bool send(const dap::TypeInfo* typeinfo, const void* event) override { dap::json::Serializer s; if (!s.object([&](dap::FieldSerializer* fs) { return fs->field("seq", dap::integer(nextSeq++)) && fs->field("type", "event") && fs->field("event", typeinfo->name()) && fs->field("body", [&](dap::Serializer* s) { return typeinfo->serialize(s, event); }); })) { return false; } return send(s.dump()); } ~Impl() { inbox.close(); reader.close(); writer.close(); if (recvThread.joinable()) { recvThread.join(); } if (dispatchThread.joinable()) { dispatchThread.join(); } } private: using Payload = std::function; class EventHandlers { public: void put(const ErrorHandler& handler) { std::unique_lock lock(errorMutex); errorHandler = handler; } void error(const char* format, ...) { va_list vararg; va_start(vararg, format); std::unique_lock lock(errorMutex); errorLocked(format, vararg); va_end(vararg); } std::pair request( const std::string& name) { std::unique_lock lock(requestMutex); auto it = requestMap.find(name); return (it != requestMap.end()) ? it->second : decltype(it->second){}; } void put(const dap::TypeInfo* typeinfo, const GenericRequestHandler& handler) { std::unique_lock lock(requestMutex); auto added = requestMap .emplace(typeinfo->name(), std::make_pair(typeinfo, handler)) .second; if (!added) { errorfLocked("Request handler for '%s' already registered", typeinfo->name().c_str()); } } std::pair response( int64_t seq) { std::unique_lock lock(responseMutex); auto responseIt = responseMap.find(seq); if (responseIt == responseMap.end()) { errorfLocked("Unknown response with sequence %d", seq); return {}; } auto out = std::move(responseIt->second); responseMap.erase(seq); return out; } void put(int seq, const dap::TypeInfo* typeinfo, const GenericResponseHandler& handler) { std::unique_lock lock(responseMutex); auto added = responseMap.emplace(seq, std::make_pair(typeinfo, handler)).second; if (!added) { errorfLocked("Response handler for sequence %d already registered", seq); } } std::pair event( const std::string& name) { std::unique_lock lock(eventMutex); auto it = eventMap.find(name); return (it != eventMap.end()) ? it->second : decltype(it->second){}; } void put(const dap::TypeInfo* typeinfo, const GenericEventHandler& handler) { std::unique_lock lock(eventMutex); auto added = eventMap.emplace(typeinfo->name(), std::make_pair(typeinfo, handler)) .second; if (!added) { errorfLocked("Event handler for '%s' already registered", typeinfo->name().c_str()); } } GenericResponseSentHandler responseSent(const dap::TypeInfo* typeinfo) { std::unique_lock lock(responseSentMutex); auto it = responseSentMap.find(typeinfo); return (it != responseSentMap.end()) ? it->second : decltype(it->second){}; } void put(const dap::TypeInfo* typeinfo, const GenericResponseSentHandler& handler) { std::unique_lock lock(responseSentMutex); auto added = responseSentMap.emplace(typeinfo, handler).second; if (!added) { errorfLocked("Response sent handler for '%s' already registered", typeinfo->name().c_str()); } } private: void errorfLocked(const char* format, ...) { va_list vararg; va_start(vararg, format); errorLocked(format, vararg); va_end(vararg); } void errorLocked(const char* format, va_list args) { char buf[2048]; vsnprintf(buf, sizeof(buf), format, args); if (errorHandler) { errorHandler(buf); } } std::mutex errorMutex; ErrorHandler errorHandler; std::mutex requestMutex; std::unordered_map> requestMap; std::mutex responseMutex; std::unordered_map> responseMap; std::mutex eventMutex; std::unordered_map> eventMap; std::mutex responseSentMutex; std::unordered_map responseSentMap; }; // EventHandlers Payload processMessage(const std::string& str) { auto d = dap::json::Deserializer(str); dap::string type; if (!d.field("type", &type)) { handlers.error("Message missing string 'type' field"); return {}; } dap::integer sequence = 0; if (!d.field("seq", &sequence)) { handlers.error("Message missing number 'seq' field"); return {}; } if (type == "request") { return processRequest(&d, sequence); } else if (type == "event") { return processEvent(&d); } else if (type == "response") { processResponse(&d); return {}; } else { handlers.error("Unknown message type '%s'", type.c_str()); } return {}; } Payload processRequest(dap::json::Deserializer* d, dap::integer sequence) { dap::string command; if (!d->field("command", &command)) { handlers.error("Request missing string 'command' field"); return {}; } const dap::TypeInfo* typeinfo; GenericRequestHandler handler; std::tie(typeinfo, handler) = handlers.request(command); if (!typeinfo) { handlers.error("No request handler registered for command '%s'", command.c_str()); return {}; } auto data = new uint8_t[typeinfo->size()]; typeinfo->construct(data); if (!d->field("arguments", [&](dap::Deserializer* d) { return typeinfo->deserialize(d, data); })) { handlers.error("Failed to deserialize request"); typeinfo->destruct(data); delete[] data; return {}; } return [=] { handler( data, [=](const dap::TypeInfo* typeinfo, const void* data) { // onSuccess dap::json::Serializer s; s.object([&](dap::FieldSerializer* fs) { return fs->field("seq", dap::integer(nextSeq++)) && fs->field("type", "response") && fs->field("request_seq", sequence) && fs->field("success", dap::boolean(true)) && fs->field("command", command) && fs->field("body", [&](dap::Serializer* s) { return typeinfo->serialize(s, data); }); }); send(s.dump()); if (auto handler = handlers.responseSent(typeinfo)) { handler(data, nullptr); } }, [=](const dap::TypeInfo* typeinfo, const dap::Error& error) { // onError dap::json::Serializer s; s.object([&](dap::FieldSerializer* fs) { return fs->field("seq", dap::integer(nextSeq++)) && fs->field("type", "response") && fs->field("request_seq", sequence) && fs->field("success", dap::boolean(false)) && fs->field("command", command) && fs->field("message", error.message); }); send(s.dump()); if (auto handler = handlers.responseSent(typeinfo)) { handler(nullptr, &error); } }); typeinfo->destruct(data); delete[] data; }; } Payload processEvent(dap::json::Deserializer* d) { dap::string event; if (!d->field("event", &event)) { handlers.error("Event missing string 'event' field"); return {}; } const dap::TypeInfo* typeinfo; GenericEventHandler handler; std::tie(typeinfo, handler) = handlers.event(event); if (!typeinfo) { handlers.error("No event handler registered for event '%s'", event.c_str()); return {}; } auto data = new uint8_t[typeinfo->size()]; typeinfo->construct(data); // "body" is an optional field for some events, such as "Terminated Event". bool body_ok = true; d->field("body", [&](dap::Deserializer* d) { if (!typeinfo->deserialize(d, data)) { body_ok = false; } return true; }); if (!body_ok) { handlers.error("Failed to deserialize event '%s' body", event.c_str()); typeinfo->destruct(data); delete[] data; return {}; } return [=] { handler(data); typeinfo->destruct(data); delete[] data; }; } void processResponse(const dap::Deserializer* d) { dap::integer requestSeq = 0; if (!d->field("request_seq", &requestSeq)) { handlers.error("Response missing int 'request_seq' field"); return; } const dap::TypeInfo* typeinfo; GenericResponseHandler handler; std::tie(typeinfo, handler) = handlers.response(requestSeq); if (!typeinfo) { handlers.error("Unknown response with sequence %d", requestSeq); return; } dap::boolean success = false; if (!d->field("success", &success)) { handlers.error("Response missing int 'success' field"); return; } if (success) { auto data = std::unique_ptr(new uint8_t[typeinfo->size()]); typeinfo->construct(data.get()); // "body" field in Response is an optional field. d->field("body", [&](const dap::Deserializer* d) { return typeinfo->deserialize(d, data.get()); }); handler(data.get(), nullptr); typeinfo->destruct(data.get()); } else { std::string message; if (!d->field("message", &message)) { handlers.error("Failed to deserialize message"); return; } auto error = dap::Error("%s", message.c_str()); handler(nullptr, &error); } } bool send(const std::string& s) { std::unique_lock lock(sendMutex); if (!writer.isOpen()) { handlers.error("Send failed as the writer is closed"); return false; } return writer.write(s); } std::atomic isBound = {false}; std::atomic isProcessingMessages = {false}; dap::ContentReader reader; dap::ContentWriter writer; std::atomic shutdown = {false}; EventHandlers handlers; std::thread recvThread; std::thread dispatchThread; dap::Chan inbox; std::atomic nextSeq = {1}; std::mutex sendMutex; }; } // anonymous namespace namespace dap { Error::Error(const std::string& message) : message(message) {} Error::Error(const char* msg, ...) { char buf[2048]; va_list vararg; va_start(vararg, msg); vsnprintf(buf, sizeof(buf), msg, vararg); va_end(vararg); message = buf; } Session::~Session() = default; std::unique_ptr Session::create() { return std::unique_ptr(new Impl()); } } // namespace dap google-cppdap-252b568/src/session_test.cpp000066400000000000000000000425341443732122100205360ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/session.h" #include "dap/io.h" #include "dap/protocol.h" #include "chan.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include #include namespace dap { struct TestResponse : public Response { boolean b; integer i; number n; array a; object o; string s; optional o1; optional o2; }; DAP_STRUCT_TYPEINFO(TestResponse, "test-response", DAP_FIELD(b, "res_b"), DAP_FIELD(i, "res_i"), DAP_FIELD(n, "res_n"), DAP_FIELD(a, "res_a"), DAP_FIELD(o, "res_o"), DAP_FIELD(s, "res_s"), DAP_FIELD(o1, "res_o1"), DAP_FIELD(o2, "res_o2")); struct TestRequest : public Request { using Response = TestResponse; boolean b; integer i; number n; array a; object o; string s; optional o1; optional o2; }; DAP_STRUCT_TYPEINFO(TestRequest, "test-request", DAP_FIELD(b, "req_b"), DAP_FIELD(i, "req_i"), DAP_FIELD(n, "req_n"), DAP_FIELD(a, "req_a"), DAP_FIELD(o, "req_o"), DAP_FIELD(s, "req_s"), DAP_FIELD(o1, "req_o1"), DAP_FIELD(o2, "req_o2")); struct TestEvent : public Event { boolean b; integer i; number n; array a; object o; string s; optional o1; optional o2; }; DAP_STRUCT_TYPEINFO(TestEvent, "test-event", DAP_FIELD(b, "evt_b"), DAP_FIELD(i, "evt_i"), DAP_FIELD(n, "evt_n"), DAP_FIELD(a, "evt_a"), DAP_FIELD(o, "evt_o"), DAP_FIELD(s, "evt_s"), DAP_FIELD(o1, "evt_o1"), DAP_FIELD(o2, "evt_o2")); }; // namespace dap namespace { dap::TestRequest createRequest() { dap::TestRequest request; request.b = false; request.i = 72; request.n = 9.87; request.a = {2, 5, 7, 8}; request.o = { std::make_pair("a", dap::integer(1)), std::make_pair("b", dap::number(2)), std::make_pair("c", dap::string("3")), }; request.s = "request"; request.o2 = 42; return request; } dap::TestResponse createResponse() { dap::TestResponse response; response.b = true; response.i = 99; response.n = 123.456; response.a = {5, 4, 3, 2, 1}; response.o = { std::make_pair("one", dap::integer(1)), std::make_pair("two", dap::number(2)), std::make_pair("three", dap::string("3")), }; response.s = "ROGER"; response.o1 = 50; return response; } dap::TestEvent createEvent() { dap::TestEvent event; event.b = false; event.i = 72; event.n = 9.87; event.a = {2, 5, 7, 8}; event.o = { std::make_pair("a", dap::integer(1)), std::make_pair("b", dap::number(2)), std::make_pair("c", dap::string("3")), }; event.s = "event"; event.o2 = 42; return event; } } // anonymous namespace class SessionTest : public testing::Test { public: void bind() { auto client2server = dap::pipe(); auto server2client = dap::pipe(); client->bind(server2client, client2server); server->bind(client2server, server2client); } std::unique_ptr client = dap::Session::create(); std::unique_ptr server = dap::Session::create(); }; TEST_F(SessionTest, Request) { dap::TestRequest received; server->registerHandler([&](const dap::TestRequest& req) { received = req; return createResponse(); }); bind(); auto request = createRequest(); client->send(request).get(); // Check request was received correctly. ASSERT_EQ(received.b, request.b); ASSERT_EQ(received.i, request.i); ASSERT_EQ(received.n, request.n); ASSERT_EQ(received.a, request.a); ASSERT_EQ(received.o.size(), 3U); ASSERT_EQ(received.o["a"].get(), request.o["a"].get()); ASSERT_EQ(received.o["b"].get(), request.o["b"].get()); ASSERT_EQ(received.o["c"].get(), request.o["c"].get()); ASSERT_EQ(received.s, request.s); ASSERT_EQ(received.o1, request.o1); ASSERT_EQ(received.o2, request.o2); } TEST_F(SessionTest, RequestResponseSuccess) { server->registerHandler( [&](const dap::TestRequest&) { return createResponse(); }); bind(); auto request = createRequest(); auto response = client->send(request); auto got = response.get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.b, dap::boolean(true)); ASSERT_EQ(got.response.i, dap::integer(99)); ASSERT_EQ(got.response.n, dap::number(123.456)); ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); ASSERT_EQ(got.response.o.size(), 3U); ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); ASSERT_EQ(got.response.s, "ROGER"); ASSERT_EQ(got.response.o1, dap::optional(50)); ASSERT_FALSE(got.response.o2.has_value()); } TEST_F(SessionTest, BreakPointRequestResponseSuccess) { server->registerHandler([&](const dap::SetBreakpointsRequest&) { dap::SetBreakpointsResponse response; dap::Breakpoint bp; bp.line = 2; response.breakpoints.emplace_back(std::move(bp)); return response; }); bind(); auto got = client->send(dap::SetBreakpointsRequest{}).get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.breakpoints.size(), 1U); } TEST_F(SessionTest, RequestResponseOrError) { server->registerHandler( [&](const dap::TestRequest&) -> dap::ResponseOrError { return dap::Error("Oh noes!"); }); bind(); auto response = client->send(createRequest()); auto got = response.get(); // Check response was received correctly. ASSERT_EQ(got.error, true); ASSERT_EQ(got.error.message, "Oh noes!"); } TEST_F(SessionTest, RequestResponseError) { server->registerHandler( [&](const dap::TestRequest&) { return dap::Error("Oh noes!"); }); bind(); auto response = client->send(createRequest()); auto got = response.get(); // Check response was received correctly. ASSERT_EQ(got.error, true); ASSERT_EQ(got.error.message, "Oh noes!"); } TEST_F(SessionTest, RequestCallbackResponse) { using ResponseCallback = std::function; server->registerHandler( [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { dap::SetBreakpointsResponse response; dap::Breakpoint bp; bp.line = 2; response.breakpoints.emplace_back(std::move(bp)); callback(response); }); bind(); auto got = client->send(dap::SetBreakpointsRequest{}).get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.breakpoints.size(), 1U); } TEST_F(SessionTest, RequestCallbackResponseOrError) { using ResponseCallback = std::function)>; server->registerHandler( [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { dap::SetBreakpointsResponse response; dap::Breakpoint bp; bp.line = 2; response.breakpoints.emplace_back(std::move(bp)); callback(response); }); bind(); auto got = client->send(dap::SetBreakpointsRequest{}).get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.breakpoints.size(), 1U); } TEST_F(SessionTest, RequestCallbackError) { using ResponseCallback = std::function)>; server->registerHandler( [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) { callback(dap::Error("Oh noes!")); }); bind(); auto got = client->send(dap::SetBreakpointsRequest{}).get(); // Check response was received correctly. ASSERT_EQ(got.error, true); ASSERT_EQ(got.error.message, "Oh noes!"); } TEST_F(SessionTest, RequestCallbackSuccessAfterReturn) { using ResponseCallback = std::function)>; ResponseCallback callback; std::mutex mutex; std::condition_variable cv; server->registerHandler( [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { std::unique_lock lock(mutex); callback = cb; cv.notify_all(); }); bind(); auto future = client->send(dap::SetBreakpointsRequest{}); { dap::SetBreakpointsResponse response; dap::Breakpoint bp; bp.line = 2; response.breakpoints.emplace_back(std::move(bp)); // Wait for the handler to be called. std::unique_lock lock(mutex); cv.wait(lock, [&] { return static_cast(callback); }); // Issue the callback callback(response); } auto got = future.get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.breakpoints.size(), 1U); } TEST_F(SessionTest, RequestCallbackErrorAfterReturn) { using ResponseCallback = std::function)>; ResponseCallback callback; std::mutex mutex; std::condition_variable cv; server->registerHandler( [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { std::unique_lock lock(mutex); callback = cb; cv.notify_all(); }); bind(); auto future = client->send(dap::SetBreakpointsRequest{}); { // Wait for the handler to be called. std::unique_lock lock(mutex); cv.wait(lock, [&] { return static_cast(callback); }); // Issue the callback callback(dap::Error("Oh noes!")); } auto got = future.get(); // Check response was received correctly. ASSERT_EQ(got.error, true); ASSERT_EQ(got.error.message, "Oh noes!"); } TEST_F(SessionTest, ResponseSentHandlerSuccess) { const auto response = createResponse(); dap::Chan> chan; server->registerHandler([&](const dap::TestRequest&) { return response; }); server->registerSentHandler( [&](const dap::ResponseOrError r) { chan.put(r); }); bind(); client->send(createRequest()); auto got = chan.take().value(); ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.b, dap::boolean(true)); ASSERT_EQ(got.response.i, dap::integer(99)); ASSERT_EQ(got.response.n, dap::number(123.456)); ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); ASSERT_EQ(got.response.o.size(), 3U); ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); ASSERT_EQ(got.response.s, "ROGER"); ASSERT_EQ(got.response.o1, dap::optional(50)); ASSERT_FALSE(got.response.o2.has_value()); } TEST_F(SessionTest, ResponseSentHandlerError) { dap::Chan> chan; server->registerHandler( [&](const dap::TestRequest&) { return dap::Error("Oh noes!"); }); server->registerSentHandler( [&](const dap::ResponseOrError r) { chan.put(r); }); bind(); client->send(createRequest()); auto got = chan.take().value(); ASSERT_EQ(got.error, true); ASSERT_EQ(got.error.message, "Oh noes!"); } TEST_F(SessionTest, Event) { dap::Chan received; server->registerHandler([&](const dap::TestEvent& e) { received.put(e); }); bind(); auto event = createEvent(); client->send(event); // Check event was received correctly. auto got = received.take().value(); ASSERT_EQ(got.b, event.b); ASSERT_EQ(got.i, event.i); ASSERT_EQ(got.n, event.n); ASSERT_EQ(got.a, event.a); ASSERT_EQ(got.o.size(), 3U); ASSERT_EQ(got.o["a"].get(), event.o["a"].get()); ASSERT_EQ(got.o["b"].get(), event.o["b"].get()); ASSERT_EQ(got.o["c"].get(), event.o["c"].get()); ASSERT_EQ(got.s, event.s); ASSERT_EQ(got.o1, event.o1); ASSERT_EQ(got.o2, event.o2); } TEST_F(SessionTest, RegisterHandlerFunction) { struct S { static dap::TestResponse requestA(const dap::TestRequest&) { return {}; } static dap::Error requestB(const dap::TestRequest&) { return {}; } static dap::ResponseOrError requestC( const dap::TestRequest&) { return dap::Error(); } static void event(const dap::TestEvent&) {} static void sent(const dap::ResponseOrError&) {} }; client->registerHandler(&S::requestA); client->registerHandler(&S::requestB); client->registerHandler(&S::requestC); client->registerHandler(&S::event); client->registerSentHandler(&S::sent); } TEST_F(SessionTest, SendRequestNoBind) { bool errored = false; client->onError([&](const std::string&) { errored = true; }); auto res = client->send(createRequest()).get(); ASSERT_TRUE(errored); ASSERT_TRUE(res.error); } TEST_F(SessionTest, SendEventNoBind) { bool errored = false; client->onError([&](const std::string&) { errored = true; }); client->send(createEvent()); ASSERT_TRUE(errored); } TEST_F(SessionTest, SingleThread) { server->registerHandler( [&](const dap::TestRequest&) { return createResponse(); }); // Explicitly connect and process request on this test thread instead of // calling bind() which inturn starts processing messages immediately on a new // thread. auto client2server = dap::pipe(); auto server2client = dap::pipe(); client->connect(server2client, client2server); server->connect(client2server, server2client); auto request = createRequest(); auto response = client->send(request); // Process request and response on this thread if (auto payload = server->getPayload()) { payload(); } if (auto payload = client->getPayload()) { payload(); } auto got = response.get(); // Check response was received correctly. ASSERT_EQ(got.error, false); ASSERT_EQ(got.response.b, dap::boolean(true)); ASSERT_EQ(got.response.i, dap::integer(99)); ASSERT_EQ(got.response.n, dap::number(123.456)); ASSERT_EQ(got.response.a, dap::array({5, 4, 3, 2, 1})); ASSERT_EQ(got.response.o.size(), 3U); ASSERT_EQ(got.response.o["one"].get(), dap::integer(1)); ASSERT_EQ(got.response.o["two"].get(), dap::number(2)); ASSERT_EQ(got.response.o["three"].get(), dap::string("3")); ASSERT_EQ(got.response.s, "ROGER"); ASSERT_EQ(got.response.o1, dap::optional(50)); ASSERT_FALSE(got.response.o2.has_value()); } TEST_F(SessionTest, Concurrency) { std::atomic numEventsHandled = {0}; std::atomic done = {false}; server->registerHandler( [](const dap::TestRequest&) { return dap::TestResponse(); }); server->registerHandler([&](const dap::TestEvent&) { if (numEventsHandled++ > 10000) { done = true; } }); bind(); constexpr int numThreads = 32; std::array threads; for (int i = 0; i < numThreads; i++) { threads[i] = std::thread([&] { while (!done) { client->send(createEvent()); client->send(createRequest()); } }); } for (int i = 0; i < numThreads; i++) { threads[i].join(); } client.reset(); server.reset(); } TEST_F(SessionTest, OnClientClosed) { std::mutex mutex; std::condition_variable cv; bool clientClosed = false; auto client2server = dap::pipe(); auto server2client = dap::pipe(); client->bind(server2client, client2server); server->bind(client2server, server2client, [&] { std::unique_lock lock(mutex); clientClosed = true; cv.notify_all(); }); client.reset(); // Wait for the client closed handler to be called. std::unique_lock lock(mutex); cv.wait(lock, [&] { return static_cast(clientClosed); }); } TEST_F(SessionTest, OnServerClosed) { std::mutex mutex; std::condition_variable cv; bool serverClosed = false; auto client2server = dap::pipe(); auto server2client = dap::pipe(); client->bind(server2client, client2server, [&] { std::unique_lock lock(mutex); serverClosed = true; cv.notify_all(); }); server->bind(client2server, server2client); server.reset(); // Wait for the client closed handler to be called. std::unique_lock lock(mutex); cv.wait(lock, [&] { return static_cast(serverClosed); }); } google-cppdap-252b568/src/socket.cpp000066400000000000000000000174261443732122100173060ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "socket.h" #include "rwmutex.h" #if defined(_WIN32) #include #include #else #include #include #include #include #include #include #endif #if defined(_WIN32) #include namespace { std::atomic wsaInitCount = {0}; } // anonymous namespace #else #include #include namespace { using SOCKET = int; } // anonymous namespace #endif namespace { constexpr SOCKET InvalidSocket = static_cast(-1); void init() { #if defined(_WIN32) if (wsaInitCount++ == 0) { WSADATA winsockData; (void)WSAStartup(MAKEWORD(2, 2), &winsockData); } #endif } void term() { #if defined(_WIN32) if (--wsaInitCount == 0) { WSACleanup(); } #endif } bool setBlocking(SOCKET s, bool blocking) { #if defined(_WIN32) u_long mode = blocking ? 0 : 1; return ioctlsocket(s, FIONBIO, &mode) == NO_ERROR; #else auto arg = fcntl(s, F_GETFL, nullptr); if (arg < 0) { return false; } arg = blocking ? (arg & ~O_NONBLOCK) : (arg | O_NONBLOCK); return fcntl(s, F_SETFL, arg) >= 0; #endif } bool errored(SOCKET s) { if (s == InvalidSocket) { return true; } char error = 0; socklen_t len = sizeof(error); getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len); return error != 0; } } // anonymous namespace class dap::Socket::Shared : public dap::ReaderWriter { public: static std::shared_ptr create(const char* address, const char* port) { init(); addrinfo hints = {}; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; addrinfo* info = nullptr; getaddrinfo(address, port, &hints, &info); if (info) { auto socket = ::socket(info->ai_family, info->ai_socktype, info->ai_protocol); auto out = std::make_shared(info, socket); out->setOptions(); return out; } freeaddrinfo(info); term(); return nullptr; } Shared(SOCKET socket) : info(nullptr), s(socket) {} Shared(addrinfo* info, SOCKET socket) : info(info), s(socket) {} ~Shared() { freeaddrinfo(info); close(); term(); } template void lock(FUNCTION&& f) { RLock l(mutex); f(s, info); } void setOptions() { RLock l(mutex); if (s == InvalidSocket) { return; } int enable = 1; #if !defined(_WIN32) // Prevent sockets lingering after process termination, causing // reconnection issues on the same port. setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable)); struct { int l_onoff; /* linger active */ int l_linger; /* how many seconds to linger for */ } linger = {false, 0}; setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger)); #endif // !defined(_WIN32) // Enable TCP_NODELAY. // DAP usually consists of small packet requests, with small packet // responses. When there are many frequent, blocking requests made, // Nagle's algorithm can dramatically limit the request->response rates. setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable)); } // dap::ReaderWriter compliance bool isOpen() { { RLock l(mutex); if ((s != InvalidSocket) && !errored(s)) { return true; } } WLock lock(mutex); s = InvalidSocket; return false; } void close() { { RLock l(mutex); if (s != InvalidSocket) { #if defined(_WIN32) closesocket(s); #elif __APPLE__ // ::shutdown() *should* be sufficient to unblock ::accept(), but // apparently on macos it can return ENOTCONN and ::accept() continues // to block indefinitely. // Note: There is a race here. Calling ::close() frees the socket ID, // which may be reused before `s` is assigned InvalidSocket. ::shutdown(s, SHUT_RDWR); ::close(s); #else // ::shutdown() to unblock ::accept(). We'll actually close the socket // under lock below. ::shutdown(s, SHUT_RDWR); #endif } } WLock l(mutex); if (s != InvalidSocket) { #if !defined(_WIN32) && !defined(__APPLE__) ::close(s); #endif s = InvalidSocket; } } size_t read(void* buffer, size_t bytes) { RLock lock(mutex); if (s == InvalidSocket) { return 0; } auto len = recv(s, reinterpret_cast(buffer), static_cast(bytes), 0); return (len < 0) ? 0 : len; } bool write(const void* buffer, size_t bytes) { RLock lock(mutex); if (s == InvalidSocket) { return false; } if (bytes == 0) { return true; } return ::send(s, reinterpret_cast(buffer), static_cast(bytes), 0) > 0; } private: addrinfo* const info; SOCKET s = InvalidSocket; RWMutex mutex; }; namespace dap { Socket::Socket(const char* address, const char* port) : shared(Shared::create(address, port)) { if (shared) { shared->lock([&](SOCKET socket, const addrinfo* info) { if (bind(socket, info->ai_addr, (int)info->ai_addrlen) != 0) { shared.reset(); return; } if (listen(socket, 0) != 0) { shared.reset(); return; } }); } } std::shared_ptr Socket::accept() const { std::shared_ptr out; if (shared) { shared->lock([&](SOCKET socket, const addrinfo*) { if (socket != InvalidSocket && !errored(socket)) { init(); auto accepted = ::accept(socket, 0, 0); if (accepted != InvalidSocket) { out = std::make_shared(accepted); out->setOptions(); } } }); } return out; } bool Socket::isOpen() const { if (shared) { return shared->isOpen(); } return false; } void Socket::close() const { if (shared) { shared->close(); } } std::shared_ptr Socket::connect(const char* address, const char* port, uint32_t timeoutMillis) { auto shared = Shared::create(address, port); if (!shared) { return nullptr; } std::shared_ptr out; shared->lock([&](SOCKET socket, const addrinfo* info) { if (socket == InvalidSocket) { return; } if (timeoutMillis == 0) { if (::connect(socket, info->ai_addr, (int)info->ai_addrlen) == 0) { out = shared; } return; } if (!setBlocking(socket, false)) { return; } auto res = ::connect(socket, info->ai_addr, (int)info->ai_addrlen); if (res == 0) { if (setBlocking(socket, true)) { out = shared; } } else { const auto microseconds = timeoutMillis * 1000; fd_set fdset; FD_ZERO(&fdset); FD_SET(socket, &fdset); timeval tv; tv.tv_sec = microseconds / 1000000; tv.tv_usec = microseconds - static_cast(tv.tv_sec * 1000000); res = select(static_cast(socket + 1), nullptr, &fdset, nullptr, &tv); if (res > 0 && !errored(socket) && setBlocking(socket, true)) { out = shared; } } }); if (!out) { return nullptr; } return out->isOpen() ? out : nullptr; } } // namespace dap google-cppdap-252b568/src/socket.h000066400000000000000000000025401443732122100167420ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_socket_h #define dap_socket_h #include "dap/io.h" #include #include namespace dap { class Socket { public: class Shared; // connect() connects to the given TCP address and port. // If timeoutMillis is non-zero and no connection was made before // timeoutMillis milliseconds, then nullptr is returned. static std::shared_ptr connect(const char* address, const char* port, uint32_t timeoutMillis); Socket(const char* address, const char* port); bool isOpen() const; std::shared_ptr accept() const; void close() const; private: std::shared_ptr shared; }; } // namespace dap #endif // dap_socket_h google-cppdap-252b568/src/socket_test.cpp000066400000000000000000000052671443732122100203450ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "socket.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include // Basic socket send & receive test TEST(Socket, SendRecv) { const char* port = "19021"; auto server = dap::Socket("localhost", port); auto client = dap::Socket::connect("localhost", port, 0); ASSERT_TRUE(client != nullptr); const std::string expect = "Hello World!"; std::string read; auto thread = std::thread([&] { auto conn = server.accept(); ASSERT_TRUE(conn != nullptr); char c; while (conn->read(&c, 1) != 0) { read += c; } }); ASSERT_TRUE(client->write(expect.data(), expect.size())); client->close(); thread.join(); ASSERT_EQ(read, expect); } // See https://github.com/google/cppdap/issues/37 TEST(Socket, CloseOnDifferentThread) { const char* port = "19021"; auto server = dap::Socket("localhost", port); auto client = dap::Socket::connect("localhost", port, 0); ASSERT_TRUE(client != nullptr); auto conn = server.accept(); auto thread = std::thread([&] { // Closing client on different thread should unblock client->read(). client->close(); }); char c; while (client->read(&c, 1) != 0) { } thread.join(); } TEST(Socket, ConnectTimeout) { const char* port = "19021"; const int timeoutMillis = 200; const int maxAttempts = 1024; using namespace std::chrono; auto server = dap::Socket("localhost", port); std::vector> connections; for (int i = 0; i < maxAttempts; i++) { auto start = system_clock::now(); auto connection = dap::Socket::connect("localhost", port, timeoutMillis); auto end = system_clock::now(); if (!connection) { auto timeTakenMillis = duration_cast(end - start).count(); ASSERT_GE(timeTakenMillis + 20, // +20ms for a bit of timing wiggle room timeoutMillis); return; } // Keep hold of the connections to saturate any incoming socket buffers. connections.emplace_back(std::move(connection)); } FAIL() << "Failed to test timeout after " << maxAttempts << " attempts"; } google-cppdap-252b568/src/string_buffer.h000066400000000000000000000042221443732122100203100ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef dap_string_buffer_h #define dap_string_buffer_h #include "dap/io.h" #include // std::min #include // memcpy #include // std::unique_ptr #include namespace dap { class StringBuffer : public virtual Reader, public virtual Writer { public: static inline std::unique_ptr create(); inline bool write(const std::string& s); inline std::string string() const; // Reader / Writer compilance inline bool isOpen() override; inline void close() override; inline size_t read(void* buffer, size_t bytes) override; inline bool write(const void* buffer, size_t bytes) override; private: std::string str; bool closed = false; }; bool StringBuffer::isOpen() { return !closed; } void StringBuffer::close() { closed = true; } std::unique_ptr StringBuffer::create() { return std::unique_ptr(new StringBuffer()); } bool StringBuffer::write(const std::string& s) { return write(s.data(), s.size()); } std::string StringBuffer::string() const { return str; } size_t StringBuffer::read(void* buffer, size_t bytes) { if (closed || bytes == 0 || str.size() == 0) { return 0; } auto len = std::min(bytes, str.size()); memcpy(buffer, str.data(), len); str = std::string(str.begin() + len, str.end()); return len; } bool StringBuffer::write(const void* buffer, size_t bytes) { if (closed) { return false; } auto chars = reinterpret_cast(buffer); str.append(chars, chars + bytes); return true; } } // namespace dap #endif // dap_string_buffer_h google-cppdap-252b568/src/traits_test.cpp000066400000000000000000000415201443732122100203530ustar00rootroot00000000000000// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/traits.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace dap { namespace traits { namespace { struct S {}; struct E : S {}; void F1(S) {} void F3(int, S, float) {} void E1(E) {} void E3(int, E, float) {} } // namespace TEST(ParameterType, Function) { F1({}); // Avoid unused method warning F3(0, {}, 0); // Avoid unused method warning static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(ParameterType, Method) { class C { public: void F1(S) {} void F3(int, S, float) {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(ParameterType, ConstMethod) { class C { public: void F1(S) const {} void F3(int, S, float) const {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(ParameterType, StaticMethod) { class C { public: static void F1(S) {} static void F3(int, S, float) {} }; C::F1({}); // Avoid unused method warning C::F3(0, {}, 0); // Avoid unused method warning static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(ParameterType, FunctionLike) { using F1 = std::function; using F3 = std::function; static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(ParameterType, Lambda) { auto l1 = [](S) {}; auto l3 = [](int, S, float) {}; static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, int>::value, ""); static_assert(std::is_same, S>::value, ""); static_assert(std::is_same, float>::value, ""); } TEST(HasSignature, Function) { F1({}); // Avoid unused method warning F3(0, {}, 0); // Avoid unused method warning static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } TEST(HasSignature, Method) { class C { public: void F1(S) {} void F3(int, S, float) {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } TEST(HasSignature, ConstMethod) { class C { public: void F1(S) const {} void F3(int, S, float) const {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } TEST(HasSignature, StaticMethod) { class C { public: static void F1(S) {} static void F3(int, S, float) {} }; C::F1({}); // Avoid unused method warning C::F3(0, {}, 0); // Avoid unused method warning static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } TEST(HasSignature, FunctionLike) { using f1 = std::function; using f3 = std::function; static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } TEST(HasSignature, Lambda) { auto l1 = [](S) {}; auto l3 = [](int, S, float) {}; static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); static_assert(!HasSignature::value, ""); } //// TEST(CompatibleWith, Function) { F1({}); // Avoid unused method warning F3(0, {}, 0); // Avoid unused method warning E1({}); // Avoid unused method warning E3(0, {}, 0); // Avoid unused method warning static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } TEST(CompatibleWith, Method) { class C { public: void F1(S) {} void F3(int, S, float) {} void E1(E) {} void E3(int, E, float) {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning C().E1({}); // Avoid unused method warning C().E3(0, {}, 0); // Avoid unused method warning static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } TEST(CompatibleWith, ConstMethod) { class C { public: void F1(S) const {} void F3(int, S, float) const {} void E1(E) const {} void E3(int, E, float) const {} }; C().F1({}); // Avoid unused method warning C().F3(0, {}, 0); // Avoid unused method warning C().E1({}); // Avoid unused method warning C().E3(0, {}, 0); // Avoid unused method warning static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } TEST(CompatibleWith, StaticMethod) { class C { public: static void F1(S) {} static void F3(int, S, float) {} static void E1(E) {} static void E3(int, E, float) {} }; C::F1({}); // Avoid unused method warning C::F3(0, {}, 0); // Avoid unused method warning C::E1({}); // Avoid unused method warning C::E3(0, {}, 0); // Avoid unused method warning static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } TEST(CompatibleWith, FunctionLike) { using f1 = std::function; using f3 = std::function; using e1 = std::function; using e3 = std::function; static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } TEST(CompatibleWith, Lambda) { auto f1 = [](S) {}; auto f3 = [](int, S, float) {}; auto e1 = [](E) {}; auto e3 = [](int, E, float) {}; static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); static_assert(!CompatibleWith::value, ""); } } // namespace traits } // namespace dap google-cppdap-252b568/src/typeinfo.cpp000066400000000000000000000012571443732122100176460ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/typeinfo.h" namespace dap { TypeInfo::~TypeInfo() = default; } // namespace dap google-cppdap-252b568/src/typeinfo_test.cpp000066400000000000000000000032211443732122100206760ustar00rootroot00000000000000// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/typeof.h" #include "dap/types.h" #include "json_serializer.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace dap { struct BaseStruct { dap::integer i; dap::number n; }; DAP_STRUCT_TYPEINFO(BaseStruct, "BaseStruct", DAP_FIELD(i, "i"), DAP_FIELD(n, "n")); struct DerivedStruct : public BaseStruct { dap::string s; dap::boolean b; }; DAP_STRUCT_TYPEINFO_EXT(DerivedStruct, BaseStruct, "DerivedStruct", DAP_FIELD(s, "s"), DAP_FIELD(b, "b")); } // namespace dap TEST(TypeInfo, Derived) { dap::DerivedStruct in; in.s = "hello world"; in.b = true; in.i = 42; in.n = 3.14; dap::json::Serializer s; ASSERT_TRUE(s.serialize(in)); dap::DerivedStruct out; dap::json::Deserializer d(s.dump()); ASSERT_TRUE(d.deserialize(&out)) << "Failed to deserialize\n" << s.dump(); ASSERT_EQ(out.s, "hello world"); ASSERT_EQ(out.b, true); ASSERT_EQ(out.i, 42); ASSERT_EQ(out.n, 3.14); } google-cppdap-252b568/src/typeof.cpp000066400000000000000000000076301443732122100173200ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/typeof.h" #include #include #include namespace { // TypeInfos owns all the dap::TypeInfo instances. struct TypeInfos { // get() returns the TypeInfos singleton pointer. // TypeInfos is constructed with an internal reference count of 1. static TypeInfos* get(); // reference() increments the TypeInfos reference count. inline void reference() { assert(refcount.load() > 0); refcount++; } // release() decrements the TypeInfos reference count. // If the reference count becomes 0, then the TypeInfos is destructed. inline void release() { if (--refcount == 0) { this->~TypeInfos(); } } struct NullTI : public dap::TypeInfo { using null = dap::null; inline std::string name() const override { return "null"; } inline size_t size() const override { return sizeof(null); } inline size_t alignment() const override { return alignof(null); } inline void construct(void* ptr) const override { new (ptr) null(); } inline void copyConstruct(void* dst, const void* src) const override { new (dst) null(*reinterpret_cast(src)); } inline void destruct(void* ptr) const override { reinterpret_cast(ptr)->~null(); } inline bool deserialize(const dap::Deserializer*, void*) const override { return true; } inline bool serialize(dap::Serializer*, const void*) const override { return true; } }; dap::BasicTypeInfo boolean = {"boolean"}; dap::BasicTypeInfo string = {"string"}; dap::BasicTypeInfo integer = {"integer"}; dap::BasicTypeInfo number = {"number"}; dap::BasicTypeInfo object = {"object"}; dap::BasicTypeInfo any = {"any"}; NullTI null; std::vector> types; private: TypeInfos() = default; ~TypeInfos() = default; std::atomic refcount = {1}; }; // aligned_storage() is a replacement for std::aligned_storage that isn't busted // on older versions of MSVC. template struct aligned_storage { struct alignas(ALIGNMENT) type { unsigned char data[SIZE]; }; }; TypeInfos* TypeInfos::get() { static aligned_storage::type memory; struct Instance { TypeInfos* ptr() { return reinterpret_cast(memory.data); } Instance() { new (ptr()) TypeInfos(); } ~Instance() { ptr()->release(); } }; static Instance instance; return instance.ptr(); } } // namespace namespace dap { const TypeInfo* TypeOf::type() { return &TypeInfos::get()->boolean; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->string; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->integer; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->number; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->object; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->any; } const TypeInfo* TypeOf::type() { return &TypeInfos::get()->null; } void TypeInfo::deleteOnExit(TypeInfo* ti) { TypeInfos::get()->types.emplace_back(std::unique_ptr(ti)); } void initialize() { TypeInfos::get()->reference(); } void terminate() { TypeInfos::get()->release(); } } // namespace dap google-cppdap-252b568/src/variant_test.cpp000066400000000000000000000063171443732122100205160ustar00rootroot00000000000000// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dap/variant.h" #include "dap/typeof.h" #include "dap/types.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace dap { struct VariantTestObject { dap::integer i; dap::number n; }; DAP_STRUCT_TYPEINFO(VariantTestObject, "VariantTestObject", DAP_FIELD(i, "i"), DAP_FIELD(n, "n")); } // namespace dap TEST(Variant, EmptyConstruct) { dap::variant variant; ASSERT_TRUE(variant.is()); ASSERT_FALSE(variant.is()); ASSERT_FALSE(variant.is()); } TEST(Variant, Boolean) { dap::variant variant( dap::boolean(true)); ASSERT_TRUE(variant.is()); ASSERT_EQ(variant.get(), dap::boolean(true)); } TEST(Variant, Integer) { dap::variant variant( dap::integer(10)); ASSERT_TRUE(variant.is()); ASSERT_EQ(variant.get(), dap::integer(10)); } TEST(Variant, TestObject) { dap::variant variant( dap::VariantTestObject{5, 3.0}); ASSERT_TRUE(variant.is()); ASSERT_EQ(variant.get().i, 5); ASSERT_EQ(variant.get().n, 3.0); } TEST(Variant, Assign) { dap::variant variant( dap::integer(10)); variant = dap::integer(10); ASSERT_TRUE(variant.is()); ASSERT_FALSE(variant.is()); ASSERT_FALSE(variant.is()); ASSERT_EQ(variant.get(), dap::integer(10)); variant = dap::boolean(true); ASSERT_FALSE(variant.is()); ASSERT_TRUE(variant.is()); ASSERT_FALSE(variant.is()); ASSERT_EQ(variant.get(), dap::boolean(true)); variant = dap::VariantTestObject{5, 3.0}; ASSERT_FALSE(variant.is()); ASSERT_FALSE(variant.is()); ASSERT_TRUE(variant.is()); ASSERT_EQ(variant.get().i, 5); ASSERT_EQ(variant.get().n, 3.0); } TEST(Variant, Accepts) { using variant = dap::variant; ASSERT_TRUE(variant::accepts()); ASSERT_TRUE(variant::accepts()); ASSERT_TRUE(variant::accepts()); ASSERT_FALSE(variant::accepts()); ASSERT_FALSE(variant::accepts()); } google-cppdap-252b568/third_party/000077500000000000000000000000001443732122100170425ustar00rootroot00000000000000google-cppdap-252b568/third_party/googletest/000077500000000000000000000000001443732122100212165ustar00rootroot00000000000000google-cppdap-252b568/third_party/json/000077500000000000000000000000001443732122100200135ustar00rootroot00000000000000google-cppdap-252b568/tools/000077500000000000000000000000001443732122100156515ustar00rootroot00000000000000google-cppdap-252b568/tools/protocol_gen/000077500000000000000000000000001443732122100203435ustar00rootroot00000000000000google-cppdap-252b568/tools/protocol_gen/protocol_gen.bkp.go000066400000000000000000000411521443732122100241420ustar00rootroot00000000000000// 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. // protocol_gen (re)generates the cppdap .h and .cpp files that describe the // DAP protocol. package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "io/ioutil" "net/http" "os" "os/exec" "path" "reflect" "runtime" "sort" "strings" ) const ( protocolURL = "https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/master/debugProtocol.json" packageURL = "https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/master/protocol/package.json" versionTag = "${version}" commonPrologue = `// 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 ${version} ` headerPrologue = commonPrologue + ` #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 {}; ` headerEpilogue = `} // namespace dap #endif // dap_protocol_h ` cppPrologue = commonPrologue + ` #include "dap/protocol.h" namespace dap { ` cppEpilogue = `} // namespace dap ` ) func main() { flag.Parse() if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } type root struct { Schema string `json:"$schema"` Title string `json:"title"` Description string `json:"description"` Ty string `json:"type"` Definitions map[string]definition `json:"definitions"` } func (r *root) definitions() []namedDefinition { sortedDefinitions := make([]namedDefinition, 0, len(r.Definitions)) for name, def := range r.Definitions { sortedDefinitions = append(sortedDefinitions, namedDefinition{name, def}) } sort.Slice(sortedDefinitions, func(i, j int) bool { return sortedDefinitions[i].name < sortedDefinitions[j].name }) return sortedDefinitions } func (r *root) getRef(ref string) (namedDefinition, error) { if !strings.HasPrefix(ref, "#/definitions/") { return namedDefinition{}, fmt.Errorf("Unknown $ref '%s'", ref) } name := strings.TrimPrefix(ref, "#/definitions/") def, ok := r.Definitions[name] if !ok { return namedDefinition{}, fmt.Errorf("Unknown $ref '%s'", ref) } return namedDefinition{name, def}, nil } type namedDefinition struct { name string def definition } type definition struct { Ty string `json:"type"` Title string `json:"title"` Description string `json:"description"` Properties properties `json:"properties"` Required []string `json:"required"` AllOf []definition `json:"allOf"` Ref string `json:"$ref"` ClosedEnum []string `json:"enum"` } type properties map[string]property func (p *properties) foreach(cb func(string, property) error) error { type namedProperty struct { name string property property } sorted := make([]namedProperty, 0, len(*p)) for name, property := range *p { sorted = append(sorted, namedProperty{name, property}) } sort.Slice(sorted, func(i, j int) bool { return sorted[i].name < sorted[j].name }) for _, entry := range sorted { if err := cb(entry.name, entry.property); err != nil { return err } } return nil } type property struct { typed Description string `json:"description"` } func (p *property) properties(r *root) (properties, []string, error) { if p.Ref == "" { return p.Properties, p.Required, nil } d, err := r.getRef(p.Ref) if err != nil { return nil, nil, err } return d.def.Properties, d.def.Required, nil } type typed struct { Ty interface{} `json:"type"` Items *typed `json:"items"` Ref string `json:"$ref"` Properties properties `json:"properties"` Required []string `json:"required"` ClosedEnum []string `json:"enum"` OpenEnum []string `json:"_enum"` } func (t typed) typename(r *root, refs *[]string) (string, error) { if t.Ref != "" { d, err := r.getRef(t.Ref) if err != nil { return "", err } *refs = append(*refs, d.name) return d.name, nil } if t.Ty == nil { return "", fmt.Errorf("No type specified") } var typeof func(v reflect.Value) (string, error) typeof = func(v reflect.Value) (string, error) { if v.Kind() == reflect.Interface { v = v.Elem() } switch v.Kind() { case reflect.String: ty := v.Interface().(string) switch ty { case "boolean", "string", "integer", "number", "object", "null": return ty, nil case "array": if t.Items != nil { el, err := t.Items.typename(r, refs) if err != nil { return "", err } return fmt.Sprintf("array<%s>", el), nil } return "array", nil default: return "", fmt.Errorf("Unhandled property type '%v'", ty) } case reflect.Slice, reflect.Array: ty := "variant<" for i := 0; i < v.Len(); i++ { if i > 0 { ty += ", " } el, err := typeof(v.Index(i)) if err != nil { return "", err } ty += el } ty += ">" return ty, nil } return "", fmt.Errorf("Unsupported type '%v' kind: %v", v.Interface(), v.Kind()) } return typeof(reflect.ValueOf(t.Ty)) } type cppField struct { desc string ty string name string defaultValue string optional bool } type cppType interface { Name() string Dependencies() []string File() cppFile DefaultValue() string WriteHeader(w io.Writer) WriteCPP(w io.Writer) } type cppStruct struct { desc string name string typename string base string fields []cppField deps []string emit bool typedefs []cppTypedef file cppFile } func (s *cppStruct) Name() string { return s.name } func (s *cppStruct) Dependencies() []string { return s.deps } func (s *cppStruct) File() cppFile { return s.file } func (s *cppStruct) DefaultValue() string { return "" } func (s *cppStruct) WriteHeader(w io.Writer) { if s.desc != "" { io.WriteString(w, "// ") io.WriteString(w, strings.ReplaceAll(s.desc, "\n", "\n// ")) io.WriteString(w, "\n") } io.WriteString(w, "struct ") io.WriteString(w, s.name) if s.base != "" { io.WriteString(w, " : public ") io.WriteString(w, s.base) } io.WriteString(w, " {") // typedefs for _, t := range s.typedefs { io.WriteString(w, "\n using ") io.WriteString(w, t.from) io.WriteString(w, " = ") io.WriteString(w, t.to) io.WriteString(w, ";") } for _, f := range s.fields { if f.desc != "" { io.WriteString(w, "\n // ") io.WriteString(w, strings.ReplaceAll(f.desc, "\n", "\n // ")) } io.WriteString(w, "\n ") if f.optional { io.WriteString(w, "optional<") io.WriteString(w, f.ty) io.WriteString(w, ">") } else { io.WriteString(w, f.ty) } io.WriteString(w, " ") io.WriteString(w, sanitize(f.name)) if !f.optional && f.defaultValue != "" { io.WriteString(w, " = ") io.WriteString(w, f.defaultValue) } io.WriteString(w, ";") } io.WriteString(w, "\n};\n\n") io.WriteString(w, "DAP_DECLARE_STRUCT_TYPEINFO(") io.WriteString(w, s.name) io.WriteString(w, ");\n\n") } func (s *cppStruct) WriteCPP(w io.Writer) { // typeinfo io.WriteString(w, "DAP_IMPLEMENT_STRUCT_TYPEINFO(") io.WriteString(w, s.name) io.WriteString(w, ",\n \"") io.WriteString(w, s.typename) io.WriteString(w, "\"") for _, f := range s.fields { io.WriteString(w, ",\n ") io.WriteString(w, "DAP_FIELD(") io.WriteString(w, sanitize(f.name)) io.WriteString(w, ", \"") io.WriteString(w, f.name) io.WriteString(w, "\")") } io.WriteString(w, ");\n\n") } type cppTypedef struct { from string to string deps []string desc string defaultValue string } func (ty *cppTypedef) Name() string { return ty.from } func (ty *cppTypedef) Dependencies() []string { return ty.deps } func (ty *cppTypedef) File() cppFile { return types } func (ty *cppTypedef) DefaultValue() string { return ty.defaultValue } func (ty *cppTypedef) WriteHeader(w io.Writer) { if ty.desc != "" { io.WriteString(w, "// ") io.WriteString(w, strings.ReplaceAll(ty.desc, "\n", "\n// ")) io.WriteString(w, "\n") } io.WriteString(w, "using ") io.WriteString(w, ty.from) io.WriteString(w, " = ") io.WriteString(w, ty.to) io.WriteString(w, ";\n\n") } func (ty *cppTypedef) WriteCPP(w io.Writer) { } func sanitize(s string) string { s = strings.Trim(s, "_") switch s { case "default": return "def" default: return s } } func appendEnumDetails(desc string, openEnum []string, closedEnum []string) string { if len(closedEnum) > 0 { desc += "\n\nMust be one of the following enumeration values:\n" for i, enum := range closedEnum { if i > 0 { desc += ", " } desc += "'" + enum + "'" } } if len(openEnum) > 0 { desc += "\n\nMay be one of the following enumeration values:\n" for i, enum := range openEnum { if i > 0 { desc += ", " } desc += "'" + enum + "'" } } return desc } func (r *root) buildObject(entry namedDefinition) (*cppStruct, error) { defName := entry.name def := entry.def base := "" if len(def.AllOf) > 1 && def.AllOf[0].Ref != "" { ref, err := r.getRef(def.AllOf[0].Ref) if err != nil { return nil, err } base = ref.name if len(def.AllOf) > 2 { return nil, fmt.Errorf("Cannot handle allOf with more than 2 entries") } def = def.AllOf[1] } s := &cppStruct{ desc: def.Description, name: defName, base: base, } var props properties var required []string var err error switch base { case "Request": if arguments, ok := def.Properties["arguments"]; ok { props, required, err = arguments.properties(r) } if command, ok := def.Properties["command"]; ok { s.typename = command.ClosedEnum[0] } response := strings.TrimSuffix(s.name, "Request") + "Response" s.deps = append(s.deps, response) s.typedefs = append(s.typedefs, cppTypedef{from: "Response", to: response}) s.emit = true s.file = request case "Response": if body, ok := def.Properties["body"]; ok { props, required, err = body.properties(r) } s.emit = true s.file = response case "Event": if body, ok := def.Properties["body"]; ok { props, required, err = body.properties(r) } if command, ok := def.Properties["event"]; ok { s.typename = command.ClosedEnum[0] } s.emit = true s.file = event default: props = def.Properties required = def.Required s.file = types } if err != nil { return nil, err } if err = props.foreach(func(propName string, property property) error { ty, err := property.typename(r, &s.deps) if err != nil { return fmt.Errorf("While processing %v.%v: %v", defName, propName, err) } optional := true for _, r := range required { if propName == r { optional = false } } desc := appendEnumDetails(property.Description, property.OpenEnum, property.ClosedEnum) defaultValue := "" if len(property.ClosedEnum) > 0 { defaultValue = `"` + property.ClosedEnum[0] + `"` } s.fields = append(s.fields, cppField{ desc: desc, defaultValue: defaultValue, ty: ty, name: propName, optional: optional, }) return nil }); err != nil { return nil, err } return s, nil } func (r *root) buildTypes() ([]cppType, error) { ignore := map[string]bool{ // These are handled internally. "ProtocolMessage": true, "Request": true, "Event": true, "Response": true, } out := []cppType{} for _, entry := range r.definitions() { if ignore[entry.name] { continue } switch entry.def.Ty { case "", "object": ty, err := r.buildObject(entry) if err != nil { return nil, err } out = append(out, ty) case "string": desc := appendEnumDetails(entry.def.Description, nil, entry.def.ClosedEnum) defaultValue := "" if len(entry.def.ClosedEnum) > 0 { defaultValue = entry.def.ClosedEnum[0] } ty := &cppTypedef{from: entry.name, to: "std::string", desc: desc, defaultValue: defaultValue} out = append(out, ty) default: return nil, fmt.Errorf("Unhandled type '%v' for '%v'", entry.def.Ty, entry.name) } } return out, nil } type cppFile string const ( request = cppFile("request") response = cppFile("response") event = cppFile("event") types = cppFile("types") ) type cppFilePaths map[cppFile]string type cppFiles map[cppFile]*os.File func run() error { pkg := struct { Version string `json:"version"` }{} if err := loadJSONFile(packageURL, &pkg); err != nil { return fmt.Errorf("Failed to load JSON file from '%v': %w", packageURL, err) } protocol := root{} if err := loadJSONFile(protocolURL, &protocol); err != nil { return fmt.Errorf("Failed to load JSON file from '%v': %w", protocolURL, err) } hPath, cppPaths := outputPaths() if err := emitFiles(&protocol, hPath, cppPaths, pkg.Version); err != nil { return fmt.Errorf("Failed to emit files: %w", err) } if clangfmt, err := exec.LookPath("clang-format"); err == nil { if out, err := exec.Command(clangfmt, "-i", hPath).CombinedOutput(); err != nil { return fmt.Errorf("Failed to run clang-format on '%v':\n%v\n%w", hPath, string(out), err) } for _, p := range cppPaths { if out, err := exec.Command(clangfmt, "-i", p).CombinedOutput(); err != nil { return fmt.Errorf("Failed to run clang-format on '%v':\n%v\n%w", p, string(out), err) } } } else { fmt.Printf("clang-format not found on PATH. Please format before committing.") } return nil } func emitFiles(r *root, hPath string, cppPaths map[cppFile]string, version string) error { h, err := os.Create(hPath) if err != nil { return err } defer h.Close() cppFiles := map[cppFile]*os.File{} for ty, p := range cppPaths { f, err := os.Create(p) if err != nil { return err } cppFiles[ty] = f defer f.Close() } h.WriteString(strings.ReplaceAll(headerPrologue, versionTag, version)) for _, f := range cppFiles { f.WriteString(strings.ReplaceAll(cppPrologue, versionTag, version)) } types, err := r.buildTypes() if err != nil { return err } typesByName := map[string]cppType{} for _, s := range types { typesByName[s.Name()] = s } seen := map[string]bool{} var emit func(cppType) error emit = func(ty cppType) error { name := ty.Name() if seen[name] { return nil } seen[name] = true for _, depName := range ty.Dependencies() { dep, ok := typesByName[depName] if !ok { return fmt.Errorf("'%v' depends on unknown type '%v'", name, depName) } if err := emit(dep); err != nil { return err } } ty.WriteHeader(h) ty.WriteCPP(cppFiles[ty.File()]) return nil } // emit message types. // Referenced types will be transitively emitted. for _, s := range types { switch s.File() { case request, response, event: if err := emit(s); err != nil { return err } } } h.WriteString(headerEpilogue) for _, f := range cppFiles { f.WriteString(cppEpilogue) } return nil } func loadJSONFile(url string, obj interface{}) error { resp, err := http.Get(url) if err != nil { return err } data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if err := json.NewDecoder(bytes.NewReader(data)).Decode(obj); err != nil { return err } return nil } func outputPaths() (string, cppFilePaths) { _, thisFile, _, _ := runtime.Caller(1) thisDir := path.Dir(thisFile) h := path.Join(thisDir, "../../include/dap/protocol.h") cpp := cppFilePaths{ request: path.Join(thisDir, "../../src/protocol_requests.cpp"), response: path.Join(thisDir, "../../src/protocol_response.cpp"), event: path.Join(thisDir, "../../src/protocol_events.cpp"), types: path.Join(thisDir, "../../src/protocol_types.cpp"), } return h, cpp } google-cppdap-252b568/tools/protocol_gen/protocol_gen.go000066400000000000000000000662501443732122100233750ustar00rootroot00000000000000// 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. // protocol_gen (re)generates the cppdap .h and .cpp files that describe the // DAP protocol. package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "io/ioutil" "net/http" "os" "os/exec" "path" "reflect" "runtime" "sort" "strings" ) const ( protocolURL = "https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/master/debugProtocol.json" packageURL = "https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/master/protocol/package.json" versionTag = "${version}" commonPrologue = `// 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 ${version} ` headerPrologue = commonPrologue + ` #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 {}; ` headerEpilogue = `} // namespace dap #endif // dap_protocol_h ` cppPrologue = commonPrologue + ` #include "dap/protocol.h" namespace dap { ` cppEpilogue = `} // namespace dap ` fuzzerHeaderPrologue = commonPrologue + ` #ifndef dap_fuzzer_h #define dap_fuzzer_h #include "dap/protocol.h" #define DAP_REQUEST_LIST() \ ` fuzzerHeaderEpilogue = ` #endif // dap_fuzzer_h ` ) func main() { flag.Parse() if err := run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } // root object of the parsed schema type root struct { Schema string `json:"$schema"` Title string `json:"title"` Description string `json:"description"` Ty string `json:"type"` Definitions map[string]*definition `json:"definitions"` } // definitions() returns a lexicographically-stored list of named definitions func (r *root) definitions() []namedDefinition { sortedDefinitions := make([]namedDefinition, 0, len(r.Definitions)) for name, def := range r.Definitions { sortedDefinitions = append(sortedDefinitions, namedDefinition{name, def}) } sort.Slice(sortedDefinitions, func(i, j int) bool { return sortedDefinitions[i].name < sortedDefinitions[j].name }) return sortedDefinitions } // getRef() returns the namedDefinition with the given reference string // References have the form '#/definitions/' func (r *root) getRef(ref string) (namedDefinition, error) { if !strings.HasPrefix(ref, "#/definitions/") { return namedDefinition{}, fmt.Errorf("Unknown $ref '%s'", ref) } name := strings.TrimPrefix(ref, "#/definitions/") def, ok := r.Definitions[name] if !ok { return namedDefinition{}, fmt.Errorf("Unknown $ref '%s'", ref) } return namedDefinition{name, def}, nil } // namedDefinition is a [name, definition] pair type namedDefinition struct { name string // name as defined in the schema def *definition // definition node } // definition is the core JSON object type in the schema, describing requests, // responses, events, properties and more. type definition struct { Ty interface{} `json:"type"` Title string `json:"title"` Items *definition `json:"items"` Description string `json:"description"` Properties properties `json:"properties"` Required []string `json:"required"` OneOf []*definition `json:"oneOf"` AllOf []*definition `json:"allOf"` Ref string `json:"$ref"` OpenEnum []string `json:"_enum"` ClosedEnum []string `json:"enum"` // The resolved C++ type of the definition cppType cppType } // properties is a map of property name to the property definition type properties map[string]*definition // foreach() calls cb for each property in the map. cb is called in // lexicographically-stored order for deterministic processing. func (p *properties) foreach(cb func(string, *definition) error) error { sorted := make([]namedDefinition, 0, len(*p)) for name, property := range *p { sorted = append(sorted, namedDefinition{name, property}) } sort.Slice(sorted, func(i, j int) bool { return sorted[i].name < sorted[j].name }) for _, entry := range sorted { if err := cb(entry.name, entry.def); err != nil { return err } } return nil } // cppField describes a single C++ field of a C++ structure type cppField struct { desc string ty cppType name string optional bool enumVals []string } // cppType is an interface for all C++ generated types type cppType interface { // Name() returns the type name, used to refer to the type Name() string // Dependencies() returns a list of dependent types, which must be emitted // before this type Dependencies() []cppType // File() returns the cppTargetFile that this type should be written to File() cppTargetFile // Description() returns the type description as parsed from the schema Description() string // DefaultValue() returns the default value that should be used for any // fields of this type DefaultValue() string // WriteHeader() writes the type definition to the given .h file writer WriteHeader(w io.Writer) // WriteHeader() writes the type definition to the given .cpp file writer WriteCPP(w io.Writer) // WriteFuzzerH() writes the fuzzer DAP_REQUEST() macro to the given .h writer WriteFuzzerH(w io.Writer) // GetFuzzerNames() returns a list of the protocol name, the fields, and field enum values for this type GetFuzzerNames() []string } // cppStruct implements the cppType interface, describing a C++ structure type cppStruct struct { name string // C++ type name protoname string // DAP name desc string // Description base string // Base class name fields []cppField // All fields of the structure deps []cppType // Types this structure depends on typedefs []cppTypedef // All nested typedefs file cppTargetFile // The files this type should be written to } func (s *cppStruct) Name() string { return s.name } func (s *cppStruct) Dependencies() []cppType { return s.deps } func (s *cppStruct) File() cppTargetFile { return s.file } func (s *cppStruct) Description() string { return s.desc } func (s *cppStruct) DefaultValue() string { return "" } func (s *cppStruct) WriteHeader(w io.Writer) { if s.desc != "" { io.WriteString(w, "// ") io.WriteString(w, strings.ReplaceAll(s.desc, "\n", "\n// ")) io.WriteString(w, "\n") } io.WriteString(w, "struct ") io.WriteString(w, s.name) if s.base != "" { io.WriteString(w, " : public ") io.WriteString(w, s.base) } io.WriteString(w, " {") // typedefs for _, t := range s.typedefs { io.WriteString(w, "\n using ") io.WriteString(w, t.from) io.WriteString(w, " = ") io.WriteString(w, t.to.Name()) io.WriteString(w, ";") } for _, f := range s.fields { if f.desc != "" { io.WriteString(w, "\n // ") io.WriteString(w, strings.ReplaceAll(f.desc, "\n", "\n // ")) } io.WriteString(w, "\n ") if f.optional { io.WriteString(w, "optional<") io.WriteString(w, f.ty.Name()) io.WriteString(w, ">") } else { io.WriteString(w, f.ty.Name()) } io.WriteString(w, " ") io.WriteString(w, sanitize(f.name)) if !f.optional && f.ty.DefaultValue() != "" { io.WriteString(w, " = ") io.WriteString(w, f.ty.DefaultValue()) } io.WriteString(w, ";") } io.WriteString(w, "\n};\n\n") io.WriteString(w, "DAP_DECLARE_STRUCT_TYPEINFO(") io.WriteString(w, s.name) io.WriteString(w, ");\n\n") } func (s *cppStruct) WriteCPP(w io.Writer) { // typeinfo io.WriteString(w, "DAP_IMPLEMENT_STRUCT_TYPEINFO(") io.WriteString(w, s.name) io.WriteString(w, ",\n \"") io.WriteString(w, s.protoname) io.WriteString(w, "\"") for _, f := range s.fields { io.WriteString(w, ",\n ") io.WriteString(w, "DAP_FIELD(") io.WriteString(w, sanitize(f.name)) io.WriteString(w, ", \"") io.WriteString(w, f.name) io.WriteString(w, "\")") } io.WriteString(w, ");\n\n") } func (s *cppStruct) WriteFuzzerH(header io.Writer) { // only write fuzzer macros for Request types if s.base != "Request" { return } io.WriteString(header, "DAP_REQUEST(dap::") io.WriteString(header, s.name) io.WriteString(header, ", dap::") responseType := "" // check typedefs for response for _, t := range s.typedefs { if t.from == "Response" { responseType = t.to.Name() } } // if no response, throw an error if responseType == "" { panic("No corresponding response type found for " + s.name) } io.WriteString(header, responseType) io.WriteString(header, ") \\\n") } func (s *cppStruct) GetFuzzerNames() []string { ret := []string{} if s.protoname != "" { ret = append(ret, s.protoname) } for _, f := range s.fields { ret = append(ret, f.name) if (f.enumVals != nil) && (len(f.enumVals) > 0) { ret = append(ret, f.enumVals...) } } return ret } // cppStruct implements the cppType interface, describing a C++ typedef type cppTypedef struct { from string // Name of the typedef to cppType // Target of the typedef desc string // Description enumVals []string // Enum values } func (ty *cppTypedef) Name() string { return ty.from } func (ty *cppTypedef) Dependencies() []cppType { return []cppType{ty.to} } func (ty *cppTypedef) File() cppTargetFile { return types } func (ty *cppTypedef) Description() string { return ty.desc } func (ty *cppTypedef) DefaultValue() string { return ty.to.DefaultValue() } func (ty *cppTypedef) WriteHeader(w io.Writer) { if ty.desc != "" { io.WriteString(w, "// ") io.WriteString(w, strings.ReplaceAll(ty.desc, "\n", "\n// ")) io.WriteString(w, "\n") } io.WriteString(w, "using ") io.WriteString(w, ty.from) io.WriteString(w, " = ") io.WriteString(w, ty.to.Name()) io.WriteString(w, ";\n\n") } func (ty *cppTypedef) WriteCPP(w io.Writer) {} func (ty *cppTypedef) WriteFuzzerH(w io.Writer) {} func (s *cppTypedef) GetFuzzerNames() []string { return s.enumVals } // cppStruct implements the cppType interface, describing a basic C++ type type cppBasicType struct { name string // Type name desc string // Description deps []cppType // Types this type depends on defaultValue string // Default value for fields of this type } func (ty *cppBasicType) Name() string { return ty.name } func (ty *cppBasicType) Dependencies() []cppType { return ty.deps } func (ty *cppBasicType) File() cppTargetFile { return types } func (ty *cppBasicType) Description() string { return ty.desc } func (ty *cppBasicType) DefaultValue() string { return ty.defaultValue } func (ty *cppBasicType) WriteHeader(w io.Writer) {} func (ty *cppBasicType) WriteCPP(w io.Writer) {} func (ty *cppBasicType) WriteFuzzerH(w io.Writer) {} func (ty *cppBasicType) GetFuzzerNames() []string { return []string{} } func stringify(s string) string { return "\"" + s + "\"" } func stringifyArray(s []string) []string { ret := []string{} if s == nil { return ret } for _, v := range s { ret = append(ret, stringify(v)) } return ret } func removeDuplicateStr(strSlice []string) []string { allKeys := make(map[string]bool) list := []string{} for _, item := range strSlice { if _, value := allKeys[item]; !value { allKeys[item] = true list = append(list, item) } } return list } // sanitize() returns the given identifier transformed into a legal C++ identifier func sanitize(s string) string { s = strings.Trim(s, "_") switch s { case "default": return "def" default: return s } } // appendEnumDetails() appends any enumerator details to the given description string. func appendEnumDetails(desc string, openEnum []string, closedEnum []string) string { if len(closedEnum) > 0 { desc += "\n\nMust be one of the following enumeration values:\n" for i, enum := range closedEnum { if i > 0 { desc += ", " } desc += "'" + enum + "'" } } if len(openEnum) > 0 { desc += "\n\nMay be one of the following enumeration values:\n" for i, enum := range openEnum { if i > 0 { desc += ", " } desc += "'" + enum + "'" } } return desc } // buildRootStruct() populates the cppStruct type with information found in def. // buildRootStruct() must only be called after all the root definitions have had // a type constructed (however, not necessarily fully populated) func (r *root) buildRootStruct(ty *cppStruct, def *definition) error { if len(def.AllOf) > 1 && def.AllOf[0].Ref != "" { ref, err := r.getRef(def.AllOf[0].Ref) if err != nil { return err } ty.base = ref.name if len(def.AllOf) > 2 { return fmt.Errorf("Cannot handle allOf with more than 2 entries") } def = def.AllOf[1] } if def.Ty != "object" { return fmt.Errorf("Definion '%v' was of unexpected type '%v'", ty.name, def.Ty) } ty.desc = def.Description var body *definition var err error switch ty.base { case "Request": if arguments, ok := def.Properties["arguments"]; ok { body = arguments } if command, ok := def.Properties["command"]; ok { ty.protoname = command.ClosedEnum[0] } responseName := strings.TrimSuffix(ty.name, "Request") + "Response" responseDef := r.Definitions[responseName] responseTy := responseDef.cppType if responseTy == nil { return fmt.Errorf("Failed to find response type '%v'", responseName) } ty.deps = append(ty.deps, responseTy) ty.typedefs = append(ty.typedefs, cppTypedef{from: "Response", to: responseTy}) ty.file = request case "Response": body = def.Properties["body"] ty.file = response case "Event": body = def.Properties["body"] if command, ok := def.Properties["event"]; ok { ty.protoname = command.ClosedEnum[0] } ty.file = event default: body = def ty.file = types } if err != nil { return err } if body == nil { return nil } if body.Ref != "" { ref, err := r.getRef(body.Ref) if err != nil { return err } body = ref.def } required := make(map[string]bool, len(body.Required)) for _, r := range body.Required { required[r] = true } if err = body.Properties.foreach(func(propName string, property *definition) error { propTy, err := r.getType(property) if err != nil { return fmt.Errorf("While processing %v.%v: %v", ty.name, propName, err) } optional := !required[propName] desc := appendEnumDetails(property.Description, property.OpenEnum, property.ClosedEnum) enumVals := []string{} if len(property.ClosedEnum) > 0 { enumVals = append(enumVals, property.ClosedEnum...) } if len(property.OpenEnum) > 0 { enumVals = append(enumVals, property.OpenEnum...) } ty.fields = append(ty.fields, cppField{ desc: desc, ty: propTy, name: propName, optional: optional, enumVals: enumVals, }) ty.deps = append(ty.deps, propTy) return nil }); err != nil { return err } return nil } // getType() returns the cppType for the given definition func (r *root) getType(def *definition) (builtType cppType, err error) { if def.cppType != nil { return def.cppType, nil } defer func() { def.cppType = builtType }() if def.Ref != "" { ref, err := r.getRef(def.Ref) if err != nil { return nil, err } return ref.def.cppType, nil } // The DAP spec introduces ambiguities with its particular uses of OneOf, just set to object if len(def.OneOf) != 0 { deps := make([]cppType, len(def.OneOf)) for i, oneOf := range def.OneOf { if oneOf == nil { return nil, fmt.Errorf("Item %d in oneOf is nil", i) } elTy, err := r.getType(oneOf) if err != nil { return nil, err } deps[i] = elTy } return &cppBasicType{ name: "object", desc: def.Description, deps: deps, }, nil } v := reflect.ValueOf(def.Ty) if v.Kind() == reflect.Interface { v = v.Elem() } var typeof func(reflect.Value) (cppType, error) typeof = func(v reflect.Value) (cppType, error) { if v.Kind() == reflect.Interface { v = v.Elem() } switch v.Kind() { case reflect.String: ty := v.Interface().(string) switch ty { case "string": desc := appendEnumDetails(def.Description, nil, def.ClosedEnum) defaultValue := "" if len(def.ClosedEnum) > 0 { defaultValue = `"` + def.ClosedEnum[0] + `"` } ty := &cppBasicType{ name: ty, defaultValue: defaultValue, desc: desc, } return ty, nil case "object", "boolean", "integer", "number", "null": ty := &cppBasicType{ name: ty, desc: def.Description, } return ty, nil case "array": name := "array" deps := []cppType{} if def.Items != nil { elTy, err := r.getType(def.Items) if err != nil { return nil, err } name = fmt.Sprintf("array<%s>", elTy.Name()) deps = append(deps, elTy) } return &cppBasicType{ name: name, desc: def.Description, deps: deps, }, nil default: return nil, fmt.Errorf("Unhandled property type '%v'", ty) } case reflect.Slice, reflect.Array: args := []string{} deps := []cppType{} for i := 0; i < v.Len(); i++ { elTy, err := typeof(v.Index(i)) if err != nil { return nil, err } deps = append(deps, elTy) args = append(args, elTy.Name()) } return &cppBasicType{ name: "variant<" + strings.Join(args, ", ") + ">", desc: def.Description, deps: deps, }, nil } return nil, fmt.Errorf("Unsupported type '%v' kind: %v", v.Interface(), v.Kind()) } return typeof(v) } // buildTypes() builds all the reachable types found in the schema, returning // all the root, named definition types. func (r *root) buildTypes() ([]cppType, error) { ignore := map[string]bool{ // These are handled internally. "ProtocolMessage": true, "Request": true, "Event": true, "Response": true, } // Step 1: Categorize all the named definitions by type. structDefs := []namedDefinition{} enumDefs := []namedDefinition{} for _, entry := range r.definitions() { if ignore[entry.name] { continue } switch entry.def.Ty { case nil, "object": structDefs = append(structDefs, entry) case "string": enumDefs = append(enumDefs, entry) default: return nil, fmt.Errorf("Unhandled top-level definition type: %v", entry.def.Ty) } } // Step 2: Construct, but do not build all the named object types (yet). // This allows the getType() function to resolve to the cppStruct types, // even if they're not built yet. out := []cppType{} for _, entry := range structDefs { entry.def.cppType = &cppStruct{ name: entry.name, } out = append(out, entry.def.cppType) } // Step 3: Resolve all the enum types for _, entry := range enumDefs { enumTy, err := r.getType(entry.def) if err != nil { return nil, err } ty := &cppTypedef{ from: entry.name, to: enumTy, desc: enumTy.Description(), enumVals: func() []string { ret := []string{} if len(entry.def.ClosedEnum) > 0 { ret = entry.def.ClosedEnum } if len(entry.def.OpenEnum) > 0 { ret = append(ret, entry.def.OpenEnum...) } return ret }(), } entry.def.cppType = ty out = append(out, entry.def.cppType) } // Step 4: Resolve all the structure types for _, s := range structDefs { if err := r.buildRootStruct(s.def.cppType.(*cppStruct), s.def); err != nil { return nil, err } } return out, nil } // cppTargetFile is an enumerator of target files that types should be written // to. type cppTargetFile string const ( request = cppTargetFile("request") // protocol_request.cpp response = cppTargetFile("response") // protocol_response.cpp event = cppTargetFile("event") // protocol_events.cpp types = cppTargetFile("types") // protocol_types.cpp ) // cppTargetFilePaths is a map of cppTargetFile to the target file path type cppTargetFilePaths map[cppTargetFile]string // cppFiles is a map of cppTargetFile to the open file type cppFiles map[cppTargetFile]*os.File // run() loads and parses the package and protocol JSON files, generates the // protocol types from the schema, writes the types to the C++ files, then runs // clang-format on each. func run() error { pkg := struct { Version string `json:"version"` }{} if err := loadJSONFile(packageURL, &pkg); err != nil { return fmt.Errorf("Failed to load JSON file from '%v': %w", packageURL, err) } protocol := root{} if err := loadJSONFile(protocolURL, &protocol); err != nil { return fmt.Errorf("Failed to load JSON file from '%v': %w", protocolURL, err) } hPath, cppPaths, cMakeListsPath, fuzzerhPath, fuzzerDictPath := outputPaths() if err := emitFiles(&protocol, hPath, cppPaths, fuzzerhPath, fuzzerDictPath, pkg.Version); err != nil { return fmt.Errorf("Failed to emit files: %w", err) } if err := updateCMakePackageVersion(cMakeListsPath, pkg.Version); err != nil { return fmt.Errorf("Failed to update CMakeLists.txt: %w", err) } if clangfmt, err := exec.LookPath("clang-format"); err == nil { if out, err := exec.Command(clangfmt, "-i", hPath).CombinedOutput(); err != nil { return fmt.Errorf("Failed to run clang-format on '%v':\n%v\n%w", hPath, string(out), err) } for _, p := range cppPaths { if out, err := exec.Command(clangfmt, "-i", p).CombinedOutput(); err != nil { return fmt.Errorf("Failed to run clang-format on '%v':\n%v\n%w", p, string(out), err) } } if out, err := exec.Command(clangfmt, "-i", fuzzerhPath).CombinedOutput(); err != nil { return fmt.Errorf("Failed to run clang-format on '%v':\n%v\n%w", fuzzerhPath, string(out), err) } } else { fmt.Printf("clang-format not found on PATH. Please format before committing.") } return nil } // Updates package version in CMakeLists.txt to current func updateCMakePackageVersion(cMakeListsPath string, version string) error { text, err := os.ReadFile(cMakeListsPath) if err != nil { return err } lines := strings.Split(string(text), "\n") for i, line := range lines { if strings.Contains(line, "project(cppdap") { lines[i] = "project(cppdap VERSION " + version + " LANGUAGES CXX C)" break } } output := strings.Join(lines, "\n") return os.WriteFile(cMakeListsPath, []byte(output), 0644) } // emitFiles() opens each of the C++ files, generates the cppType definitions // from the schema root, then writes the types to the C++ files in dependency // order. func emitFiles(r *root, hPath string, cppPaths map[cppTargetFile]string, fuzzerhPath string, fuzzerDictPath string, version string) error { h, err := os.Create(hPath) if err != nil { return err } defer h.Close() cppFiles := map[cppTargetFile]*os.File{} for ty, p := range cppPaths { f, err := os.Create(p) if err != nil { return err } cppFiles[ty] = f defer f.Close() } fuzzer_h, err := os.Create(fuzzerhPath) if err != nil { return err } fuzzerDict, err := os.Create(fuzzerDictPath) if err != nil { return err } h.WriteString(strings.ReplaceAll(headerPrologue, versionTag, version)) for _, f := range cppFiles { f.WriteString(strings.ReplaceAll(cppPrologue, versionTag, version)) } fuzzer_h.WriteString(strings.ReplaceAll(fuzzerHeaderPrologue, versionTag, version)) types, err := r.buildTypes() if err != nil { return err } typesByName := map[string]cppType{} for _, s := range types { typesByName[s.Name()] = s } seen := map[string]bool{} // Prepopulate the names list with the types that are not generated from the schema. ProtocolMessageFuzzerNames := []string{"seq", "type", "request", "response", "event"} RequestMessageFuzzerNames := []string{"request", "type", "command", "arguments"} EventMessageFuzzerNames := []string{"event", "type", "event", "body"} ResponseMessageFuzzerNames := []string{"response", "type", "request_seq", "success", "command", "message", "body", "cancelled", "notStopped"} fuzzerNames := []string{} fuzzerNames = append(fuzzerNames, ProtocolMessageFuzzerNames...) fuzzerNames = append(fuzzerNames, RequestMessageFuzzerNames...) fuzzerNames = append(fuzzerNames, EventMessageFuzzerNames...) fuzzerNames = append(fuzzerNames, ResponseMessageFuzzerNames...) var emit func(cppType) error emit = func(ty cppType) error { name := ty.Name() if seen[name] { return nil } seen[name] = true for _, dep := range ty.Dependencies() { if err := emit(dep); err != nil { return err } } ty.WriteHeader(h) ty.WriteCPP(cppFiles[ty.File()]) ty.WriteFuzzerH(fuzzer_h) // collect protoname, field names, and field enum values for dictionary fuzzerNames = append(fuzzerNames, ty.GetFuzzerNames()...) return nil } // emit message types. // Referenced types will be transitively emitted. for _, s := range types { switch s.File() { case request, response, event: if err := emit(s); err != nil { return err } } } // sort names alphabetically sort.Strings(fuzzerNames) // remove duplicates fuzzerNames = removeDuplicateStr(fuzzerNames) // append "" to each name fuzzerNames = stringifyArray(fuzzerNames) dict := strings.Join(fuzzerNames, "\n") if _, err := io.WriteString(fuzzerDict, dict); err != nil { return err } h.WriteString(headerEpilogue) for _, f := range cppFiles { f.WriteString(cppEpilogue) } fuzzer_h.WriteString(fuzzerHeaderEpilogue) return nil } // loadJSONFile() loads the JSON file from the given URL using a HTTP GET // request. func loadJSONFile(url string, obj interface{}) error { resp, err := http.Get(url) if err != nil { return err } data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } if err := json.NewDecoder(bytes.NewReader(data)).Decode(obj); err != nil { return err } return nil } // outputPaths() returns a path to the target C++ .h file and .cpp files, and the CMakeLists.txt func outputPaths() (string, cppTargetFilePaths, string, string, string) { _, thisFile, _, _ := runtime.Caller(1) thisDir := path.Dir(thisFile) h := path.Join(thisDir, "../../include/dap/protocol.h") cpp := cppTargetFilePaths{ request: path.Join(thisDir, "../../src/protocol_requests.cpp"), response: path.Join(thisDir, "../../src/protocol_response.cpp"), event: path.Join(thisDir, "../../src/protocol_events.cpp"), types: path.Join(thisDir, "../../src/protocol_types.cpp"), } CMakeLists := path.Join(thisDir, "../../CMakeLists.txt") fuzzer_h := path.Join(thisDir, "../../fuzz/fuzz.h") fuzzer_dict := path.Join(thisDir, "../../fuzz/dictionary.txt") return h, cpp, CMakeLists, fuzzer_h, fuzzer_dict }