Initial drop of cppdap

This commit is contained in:
Ben Clayton
2019-10-31 17:06:38 +00:00
committed by Ben Clayton
commit 2dfd15462f
51 changed files with 9924 additions and 0 deletions

2
.clang-format Normal file
View File

@@ -0,0 +1,2 @@
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Chromium

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
build/
.vs/
.vscode/settings.json
CMakeSettings.json

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[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

18
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"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++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}

39
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,39 @@
{
// 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/dap-unittests",
"cwd": "${workspaceRoot}",
},
{
"name": "unittests (gdb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/dap-unittests",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

84
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,84 @@
{
// 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",
},
]
}

231
CMakeLists.txt Normal file
View File

@@ -0,0 +1,231 @@
# 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.
cmake_minimum_required(VERSION 2.8)
set (CMAKE_CXX_STANDARD 11)
project(cppdap C CXX)
###########################################################
# Options
###########################################################
option(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF)
option(CPPDAP_BUILD_TESTS "Build tests" OFF)
option(CPPDAP_ASAN "Build dap with address sanitizer" OFF)
option(CPPDAP_MSAN "Build dap with memory sanitizer" OFF)
option(CPPDAP_TSAN "Build dap with thread sanitizer" OFF)
option(CPPDAP_INSTALL_VSCODE_EXAMPLES "Build and install dap examples into vscode extensions directory" OFF)
option(CPPDAP_INSTALL "Create dap install target" OFF)
###########################################################
# Directories
###########################################################
set(CPPDAP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(CPPDAP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(CPPDAP_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party)
set(JSON_DIR ${CPPDAP_THIRD_PARTY_DIR}/json)
set(GOOGLETEST_DIR ${CPPDAP_THIRD_PARTY_DIR}/googletest)
###########################################################
# Submodules
###########################################################
if(CPPDAP_BUILD_TESTS)
if(NOT EXISTS ${CPPDAP_THIRD_PARTY_DIR}/googletest/.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)
###########################################################
# File lists
###########################################################
set(CPPDAP_LIST
${CPPDAP_SRC_DIR}/content_stream.cpp
${CPPDAP_SRC_DIR}/io.cpp
${CPPDAP_SRC_DIR}/json_serializer.cpp
${CPPDAP_SRC_DIR}/network.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}/typeof.cpp
)
###########################################################
# OS libraries
###########################################################
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CPPDAP_OS_LIBS WS2_32)
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CPPDAP_OS_LIBS pthread)
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(CPPDAP_OS_LIBS)
endif()
###########################################################
# Functions
###########################################################
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")
endif()
# 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_libraries(${target} "-fsanitize=address")
elseif(CPPDAP_MSAN)
target_compile_options(${target} PUBLIC "-fsanitize=memory")
target_link_libraries(${target} "-fsanitize=memory")
elseif(CPPDAP_TSAN)
target_compile_options(${target} PUBLIC "-fsanitize=thread")
target_link_libraries(${target} "-fsanitize=thread")
endif()
# Error on undefined symbols
# if(NOT MSVC)
# target_compile_options(${target} PRIVATE "-Wl,--no-undefined")
# endif()
target_include_directories(${target} PRIVATE ${CPPDAP_INCLUDE_DIR})
endfunction(cppdap_set_target_options)
###########################################################
# Targets
###########################################################
# dap
add_library(cppdap STATIC ${CPPDAP_LIST})
set_target_properties(cppdap PROPERTIES
POSITION_INDEPENDENT_CODE 1
)
target_include_directories(cppdap PRIVATE "${JSON_DIR}/include/")
cppdap_set_target_options(cppdap)
target_link_libraries(cppdap "${CPPDAP_OS_LIBS}")
# install
if(CPPDAP_INSTALL)
include(GNUInstallDirs)
install(DIRECTORY ${CPPDAP_INCLUDE_DIR}/cppdap
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
USE_SOURCE_PERMISSIONS
)
install(TARGETS cppdap
EXPORT cppdap-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT cppdap-targets
FILE cppdap-config.cmake
NAMESPACE cppdap::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cppdap
)
endif(CPPDAP_INSTALL)
# 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}/session_test.cpp
${CPPDAP_SRC_DIR}/variant_test.cpp
${GOOGLETEST_DIR}/googletest/src/gtest-all.cc
)
set(DAP_TEST_INCLUDE_DIR
${GOOGLETEST_DIR}/googlemock/include/
${GOOGLETEST_DIR}/googletest/
${GOOGLETEST_DIR}/googletest/include/
${JSON_DIR}/include/
)
add_executable(cppdap-unittests ${DAP_TEST_LIST})
set_target_properties(cppdap-unittests PROPERTIES
INCLUDE_DIRECTORIES "${DAP_TEST_INCLUDE_DIR}"
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 cppdap "${CPPDAP_OS_LIBS}")
endif(CPPDAP_BUILD_TESTS)
# 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} cppdap "${CPPDAP_OS_LIBS}")
if(CPPDAP_INSTALL_VSCODE_EXAMPLES)
set(extroot "$ENV{HOME}/.vscode/extensions")
if(EXISTS ${extroot})
set(extdir "${extroot}/google.cppdap-example-${target}-1.0.0")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/examples/vscode/package.json ${extdir}/package.json)
add_custom_command(TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${extdir})
else()
message(WARNING "Could not install vscode example extension as '${extroot}' does not exist")
endif()
endif(CPPDAP_INSTALL_VSCODE_EXAMPLES)
endfunction(build_example)
build_example(hello_debugger)
endif(CPPDAP_BUILD_EXAMPLES)

28
CONTRIBUTING Normal file
View File

@@ -0,0 +1,28 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).

202
LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# cppdap
## About
`cppdap` is a C++11 library (["SDK"](https://microsoft.github.io/debug-adapter-protocol/implementors/sdks/)) implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/), providing an API for implementing a DAP client or server.
`cppdap` provides C++ type-safe structures for the full [DAP specification](https://microsoft.github.io/debug-adapter-protocol/specification), and provides a simple way to add custom protocol messages.
## Fetching dependencies
`cppdap` provides CMake build files to build the library, unit tests and examples.
`cppdap` depends on the [`nlohmann/json` library](https://github.com/nlohmann/json), and the unit tests depend on the [`googletest` library](https://github.com/google/googletest). Both are referenced as a git submodules.
Before building, fetch the git submodules with:
```bash
cd <path-to-cppdap>
git submodule update --init
```
## Building
### Linux and macOS
Next, generate the build files:
```bash
cd <path-to-cppdap>
mkdir build
cd build
cmake ..
```
You may wish to suffix the `cmake ..` line with any of the following flags:
* `-DCPPDAP_BUILD_TESTS=1` - Builds the `cppdap` unit tests
* `-DCPPDAP_BUILD_EXAMPLES=1` - Builds the `cppdap` examples
* `-DCPPDAP_INSTALL_VSCODE_EXAMPLES=1` - Installs the `cppdap` examples as Visual Studio Code extensions
* `-DCPPDAP_WARNINGS_AS_ERRORS=1` - Treats all compiler warnings as errors.
Finally, build the project:
`make`
### Windows
`cppdap` can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019).
### Using `cppdap` in your CMake project
You can build and link `cppdap` using `add_subdirectory()` in your project's `CMakeLists.txt` file:
```cmake
set(CPPDAP_DIR <path-to-cppdap>) # example <path-to-cppdap>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cppdap"
add_subdirectory(${CPPDAP_DIR})
```
This will define the `cppdap` library target, which you can pass to `target_link_libraries()`:
```cmake
target_link_libraries(<target> cppdap) # replace <target> with the name of your project's target
```
You will also want to add the `cppdap` public headers to your project's include search paths so you can `#include` the `cppdap` headers:
```cmake
target_include_directories($<target> PRIVATE "${CPPDAP_DIR}/include") # replace <target> with the name of your project's target
```
---
Note: This is not an officially supported Google product

453
examples/hello_debugger.cpp Normal file
View File

@@ -0,0 +1,453 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// hello_debugger is an example DAP server that provides single line stepping
// through a synthetic file.
#include "dap/io.h"
#include "dap/protocol.h"
#include "dap/session.h"
#include <condition_variable>
#include <cstdio>
#include <mutex>
#include <unordered_set>
// Uncomment the line below and change <path-to-log-file> to a file path to
// write all DAP communications to the given path.
//
// #define LOG_TO_FILE "<path-to-log-file>"
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 int numSourceLines = 7;
// Debugger holds the dummy debugger state and fires events to the EventHandler
// passed to the constructor.
class Debugger {
public:
enum class Event { BreakpointHit, Stepped, Paused };
using EventHandler = std::function<void(Event)>;
Debugger(const EventHandler&);
// run() instructs the debugger to continue execution.
void run();
// pause() instructs the debugger to pause execution.
void pause();
// currentLine() returns the currently executing line number.
int 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(int line);
private:
EventHandler onEvent;
std::mutex mutex;
int line = 1;
std::unordered_set<int> breakpoints;
};
Debugger::Debugger(const EventHandler& onEvent) : onEvent(onEvent) {}
void Debugger::run() {
std::unique_lock<std::mutex> lock(mutex);
for (int i = 0; i < numSourceLines; i++) {
auto l = ((line + i) % numSourceLines) + 1;
if (breakpoints.count(l)) {
line = l;
lock.unlock();
onEvent(Event::BreakpointHit);
return;
}
}
}
void Debugger::pause() {
onEvent(Event::Paused);
}
int Debugger::currentLine() {
std::unique_lock<std::mutex> lock(mutex);
return line;
}
void Debugger::stepForward() {
std::unique_lock<std::mutex> lock(mutex);
line = (line % numSourceLines) + 1;
lock.unlock();
onEvent(Event::Stepped);
}
void Debugger::clearBreakpoints() {
std::unique_lock<std::mutex> lock(mutex);
this->breakpoints.clear();
}
void Debugger::addBreakpoint(int l) {
std::unique_lock<std::mutex> lock(mutex);
this->breakpoints.emplace(l);
}
// Event provides a basic wait and signal synchronization primitive.
class Event {
public:
// wait() blocks until the event is fired.
void wait();
// fire() sets signals the event, and unblocks any calls to wait().
void fire();
private:
std::mutex mutex;
std::condition_variable cv;
bool fired = false;
};
void Event::wait() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return fired; });
}
void Event::fire() {
std::unique_lock<std::mutex> lock(mutex);
fired = true;
cv.notify_all();
}
} // anonymous namespace
// main() entry point to the DAP server.
int main(int, char*[]) {
std::shared_ptr<dap::Writer> log;
#ifdef LOG_TO_FILE
log = dap::file(LOG_TO_FILE);
#endif
// Create the DAP session.
// This is used to implement the DAP server.
auto session = dap::Session::create();
// Hard-coded identifiers for the one thread, frame, variable and source.
// These numbers have no meaning, and just need to remain constant for the
// duration of the service.
const dap::integer threadId = 100;
const dap::integer frameId = 200;
const dap::integer variablesReferenceId = 300;
const dap::integer sourceReferenceId = 400;
// Signal events
Event configured;
Event terminate;
// Event handlers from the Debugger.
auto onDebuggerEvent = [&](Debugger::Event onEvent) {
switch (onEvent) {
case Debugger::Event::Stepped: {
// The debugger has single-line stepped. Inform the client.
dap::StoppedEvent event;
event.reason = "step";
event.threadId = threadId;
session->send(event);
break;
}
case Debugger::Event::BreakpointHit: {
// The debugger has hit a breakpoint. Inform the client.
dap::StoppedEvent event;
event.reason = "breakpoint";
event.threadId = threadId;
session->send(event);
break;
}
case Debugger::Event::Paused: {
// The debugger has been suspended. Inform the client.
dap::StoppedEvent event;
event.reason = "pause";
event.threadId = threadId;
session->send(event);
break;
}
}
};
// Construct the debugger.
Debugger debugger(onDebuggerEvent);
// Handle errors reported by the Session. These errors include protocol
// parsing errors and receiving messages with no handler.
session->onError([&](const char* msg) {
if (log) {
dap::writef(log, "dap::Session error: %s\n", msg);
log->close();
}
terminate.fire();
});
// The Initialize request is the first message sent from the client and
// the response reports debugger capabilities.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
session->registerHandler([](const dap::InitializeRequest&) {
dap::InitializeResponse response;
response.supportsConfigurationDoneRequest = true;
return response;
});
// When the Initialize response has been sent, we need to send the initialized
// event.
// We use the registerSentHandler() to ensure the event is sent *after* the
// initialize response.
// https://microsoft.github.io/debug-adapter-protocol/specification#Events_Initialized
session->registerSentHandler(
[&](const dap::ResponseOrError<dap::InitializeResponse>&) {
session->send(dap::InitializedEvent());
});
// The Threads request queries the debugger's list of active threads.
// This example debugger only exposes a single thread.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Threads
session->registerHandler([&](const dap::ThreadsRequest&) {
dap::ThreadsResponse response;
dap::Thread thread;
thread.id = threadId;
thread.name = "TheThread";
response.threads.push_back(thread);
return response;
});
// The StackTrace request reports the stack frames (call stack) for a given
// thread. This example debugger only exposes a single stack frame for the
// single thread.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StackTrace
session->registerHandler(
[&](const dap::StackTraceRequest& request)
-> dap::ResponseOrError<dap::StackTraceResponse> {
if (request.threadId != threadId) {
return dap::Error("Unknown threadId '%d'", int(request.threadId));
}
dap::Source source;
source.sourceReference = sourceReferenceId;
source.name = "HelloDebuggerSource";
dap::StackFrame frame;
frame.line = debugger.currentLine();
frame.column = 1;
frame.name = "HelloDebugger";
frame.id = frameId;
frame.source = source;
dap::StackTraceResponse response;
response.stackFrames.push_back(frame);
return response;
});
// The Scopes request reports all the scopes of the given stack frame.
// This example debugger only exposes a single 'Locals' scope for the single
// frame.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Scopes
session->registerHandler([&](const dap::ScopesRequest& request)
-> dap::ResponseOrError<dap::ScopesResponse> {
if (request.frameId != frameId) {
return dap::Error("Unknown frameId '%d'", int(request.frameId));
}
dap::Scope scope;
scope.name = "Locals";
scope.presentationHint = "locals";
scope.variablesReference = variablesReferenceId;
dap::ScopesResponse response;
response.scopes.push_back(scope);
return response;
});
// The Variables request reports all the variables for the given scope.
// This example debugger only exposes a single 'currentLine' variable for the
// single 'Locals' scope.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Variables
session->registerHandler([&](const dap::VariablesRequest& request)
-> dap::ResponseOrError<dap::VariablesResponse> {
if (request.variablesReference != variablesReferenceId) {
return dap::Error("Unknown variablesReference '%d'",
int(request.variablesReference));
}
dap::Variable currentLineVar;
currentLineVar.name = "currentLine";
currentLineVar.value = std::to_string(debugger.currentLine());
currentLineVar.type = "int";
dap::VariablesResponse response;
response.variables.push_back(currentLineVar);
return response;
});
// The Pause request instructs the debugger to pause execution of one or all
// threads.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Pause
session->registerHandler([&](const dap::PauseRequest&) {
debugger.pause();
return dap::PauseResponse();
});
// The Continue request instructs the debugger to resume execution of one or
// all threads.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Continue
session->registerHandler([&](const dap::ContinueRequest&) {
debugger.run();
return dap::ContinueResponse();
});
// The Next request instructs the debugger to single line step for a specific
// thread.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Next
session->registerHandler([&](const dap::NextRequest&) {
debugger.stepForward();
return dap::NextResponse();
});
// The StepIn request instructs the debugger to step-in for a specific thread.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepIn
session->registerHandler([&](const dap::StepInRequest&) {
// Step-in treated as step-over as there's only one stack frame.
debugger.stepForward();
return dap::StepInResponse();
});
// The StepOut request instructs the debugger to step-out for a specific
// thread.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_StepOut
session->registerHandler([&](const dap::StepOutRequest&) {
// Step-out is not supported as there's only one stack frame.
return dap::StepOutResponse();
});
// The SetBreakpoints request instructs the debugger to clear and set a number
// of line breakpoints for a specific source file.
// This example debugger only exposes a single source file.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints
session->registerHandler([&](const dap::SetBreakpointsRequest& request) {
dap::SetBreakpointsResponse response;
auto breakpoints = request.breakpoints.value({});
if (request.source.sourceReference.value(0) == sourceReferenceId) {
debugger.clearBreakpoints();
response.breakpoints.resize(breakpoints.size());
for (size_t i = 0; i < breakpoints.size(); i++) {
debugger.addBreakpoint(breakpoints[i].line);
response.breakpoints[i].verified = breakpoints[i].line < numSourceLines;
}
} else {
response.breakpoints.resize(breakpoints.size());
}
return response;
});
// The SetExceptionBreakpoints request configures the debugger's handling of
// thrown exceptions.
// This example debugger does not use any exceptions, so this is a no-op.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetExceptionBreakpoints
session->registerHandler([&](const dap::SetExceptionBreakpointsRequest&) {
return dap::SetExceptionBreakpointsResponse();
});
// The Source request retrieves the source code for a given source file.
// This example debugger only exposes one synthetic source file.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Source
session->registerHandler([&](const dap::SourceRequest& request)
-> dap::ResponseOrError<dap::SourceResponse> {
if (request.sourceReference != sourceReferenceId) {
return dap::Error("Unknown source reference '%d'",
int(request.sourceReference));
}
dap::SourceResponse response;
response.content = sourceContent;
return response;
});
// The Launch request is made when the client instructs the debugger adapter
// to start the debuggee. This request contains the launch arguments.
// This example debugger does nothing with this request.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Launch
session->registerHandler(
[&](const dap::LaunchRequest&) { return dap::LaunchResponse(); });
// Handler for disconnect requests
session->registerHandler([&](const dap::DisconnectRequest& request) {
if (request.terminateDebuggee.value(false)) {
terminate.fire();
}
return dap::DisconnectResponse();
});
// The ConfigurationDone request is made by the client once all configuration
// requests have been made.
// This example debugger uses this request to 'start' the debugger.
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_ConfigurationDone
session->registerHandler([&](const dap::ConfigurationDoneRequest&) {
configured.fire();
return dap::ConfigurationDoneResponse();
});
// All the handlers we care about have now been registered.
// We now bind the session to stdin and stdout to connect to the client.
// After the call to bind() we should start receiving requests, starting with
// the Initialize request.
std::shared_ptr<dap::Reader> in = dap::file(stdin, false);
std::shared_ptr<dap::Writer> out = dap::file(stdout, false);
if (log) {
session->bind(spy(in, log), spy(out, log));
} else {
session->bind(in, out);
}
// Wait for the ConfigurationDone request to be made.
configured.wait();
// Broadcast the existance of the single thread to the client.
dap::ThreadEvent threadStartedEvent;
threadStartedEvent.reason = "started";
threadStartedEvent.threadId = threadId;
session->send(threadStartedEvent);
// Start the debugger in a paused state.
// This sends a stopped event to the client.
debugger.pause();
// Block until we receive a 'terminateDebuggee' request or encounter a session
// error.
terminate.wait();
return 0;
}

View File

@@ -0,0 +1,28 @@
{
"name": "cppdap-example-@target@",
"displayName": "cppdap example: @target@",
"description": "cppdap example: @target@",
"version": "1.0.0",
"preview": false,
"publisher": "Google LLC",
"author": {
"name": "Google LLC"
},
"license": "SEE LICENSE IN LICENSE.txt",
"engines": {
"vscode": "^1.32.0"
},
"categories": [
"Debuggers"
],
"contributes": {
"debuggers": [
{
"type": "@target@",
"program": "@target@",
"label": "cppdap example: @target@",
"configurationAttributes": {}
}
]
}
}

182
include/dap/any.h Normal file
View File

@@ -0,0 +1,182 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_any_h
#define dap_any_h
#include "typeinfo.h"
#include <assert.h>
namespace dap {
template <typename T>
struct TypeOf;
// any provides a type-safe container for values of any of dap type (boolean,
// integer, number, array, variant, any, null, dap-structs).
class any {
public:
// constructors
inline any() = default;
inline any(const any& other) noexcept;
inline any(any&& other) noexcept;
template <typename T>
inline any(const T& val);
// destructors
inline ~any();
// replaces the contained value with a null.
inline void reset();
// assignment
inline any& operator=(const any& rhs);
inline any& operator=(any&& rhs) noexcept;
template <typename T>
inline any& operator=(const T& val);
// get() returns the contained value of the type T.
// If the any does not contain a value of type T, then get() will assert.
template <typename T>
inline T& get() const;
// is() returns true iff the contained value is of type T.
template <typename T>
inline bool is() const;
private:
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 <typename T>
inline any::any(const T& val) {
*this = val;
}
any::any(const any& other) noexcept : type(other.type) {
if (other.value != nullptr) {
alloc(type->size(), type->alignment());
type->copyConstruct(value, other.value);
}
}
any::any(any&& other) noexcept : value(other.value), type(other.type) {
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 {
value = rhs.value;
type = rhs.type;
rhs.value = nullptr;
rhs.type = nullptr;
return *this;
}
template <typename T>
any& any::operator=(const T& val) {
if (!is<T>()) {
reset();
type = TypeOf<T>::type();
alloc(type->size(), type->alignment());
type->copyConstruct(value, &val);
} else {
*reinterpret_cast<T*>(value) = val;
}
return *this;
}
template <typename T>
T& any::get() const {
assert(is<T>());
return *reinterpret_cast<T*>(value);
}
template <typename T>
bool any::is() const {
return type == TypeOf<T>::type();
}
template <>
inline bool any::is<std::nullptr_t>() const {
return value == nullptr;
}
void* any::alignUp(void* val, size_t alignment) {
auto ptr = reinterpret_cast<uintptr_t>(val);
return reinterpret_cast<void*>(alignment *
((ptr + alignment - 1) / alignment));
}
void any::alloc(size_t size, size_t align) {
assert(value == nullptr);
value = alignUp(buffer, align);
if (isInBuffer(reinterpret_cast<uint8_t*>(value) + size - 1)) {
return;
}
heap = new uint8_t[size + align];
value = alignUp(heap, align);
}
void any::free() {
assert(value != nullptr);
if (heap != nullptr) {
delete[] reinterpret_cast<uint8_t*>(heap);
heap = nullptr;
}
value = nullptr;
}
bool any::isInBuffer(void* ptr) const {
auto addr = reinterpret_cast<uintptr_t>(ptr);
return addr >= reinterpret_cast<uintptr_t>(buffer) &&
addr < reinterpret_cast<uintptr_t>(buffer + sizeof(buffer));
}
} // namespace dap
#endif // dap_any_h

95
include/dap/io.h Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_io_h
#define dap_io_h
#include <stddef.h> // size_t
#include <memory> // std::unique_ptr
#include <utility> // std::pair
namespace dap {
class Closable {
public:
virtual ~Closable() = default;
// isOpen() returns true if the stream has not been closed.
virtual bool isOpen() = 0;
// close() closes the stream.
virtual void close() = 0;
};
// Reader is an interface for reading from a byte stream.
class Reader : virtual public Closable {
public:
// read() attempts to read at most n bytes into buffer, returning the number
// of bytes read.
// read() will block until the stream is closed or at least one byte is read.
virtual size_t read(void* buffer, size_t n) = 0;
};
// Writer is an interface for writing to a byte stream.
class Writer : virtual public Closable {
public:
// write() writes n bytes from buffer into the stream.
// Returns true on success, or false if there was an error or the stream was
// closed.
virtual bool write(const void* buffer, size_t n) = 0;
};
// ReaderWriter is an interface that combines the Reader and Writer interfaces.
class ReaderWriter : public Reader, public Writer {
// create() returns a ReaderWriter that delegates the interface methods on to
// the provided Reader and Writer.
// isOpen() returns true if the Reader and Writer both return true for
// isOpen().
// close() closes both the Reader and Writer.
static std::shared_ptr<ReaderWriter> create(const std::shared_ptr<Reader>&,
const std::shared_ptr<Writer>&);
};
// pipe() returns a ReaderWriter where the Writer streams to the Reader.
// Writes are internally buffered.
// Calling close() on either the Reader or Writer will close both ends of the
// stream.
std::shared_ptr<ReaderWriter> pipe();
// file() wraps file with a ReaderWriter.
// If closable is false, then a call to ReaderWriter::close() will not close the
// underlying file.
std::shared_ptr<ReaderWriter> file(FILE* file, bool closable = true);
// file() opens (or creates) the file with the given path.
std::shared_ptr<ReaderWriter> file(const char* path);
// spy() returns a Reader that copies all reads from the Reader r to the Writer
// s, using the given optional prefix.
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& s,
const char* prefix = "\n->");
// spy() returns a Writer that copies all writes to the Writer w to the Writer
// s, using the given optional prefix.
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
const std::shared_ptr<Writer>& s,
const char* prefix = "\n<-");
// writef writes the printf style string to the writer w.
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...);
} // namespace dap
#endif // dap_io_h

55
include/dap/network.h Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_network_h
#define dap_network_h
#include <functional>
#include <memory>
namespace dap {
class ReaderWriter;
namespace net {
// connect() connects to the given TCP address and port.
std::shared_ptr<ReaderWriter> connect(const char* addr, int port);
// Server implements a basic TCP server.
class Server {
public:
using OnError = std::function<void(const char*)>;
using OnConnect = std::function<void(const std::shared_ptr<ReaderWriter>&)>;
virtual ~Server() = default;
// create() constructs and returns a new Server.
static std::unique_ptr<Server> create();
// start() begins listening for connections on 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 = OnError()) = 0;
// stop() stops listening for connections.
// stop() is implicitly called on destruction.
virtual void stop() = 0;
};
} // namespace net
} // namespace dap
#endif // dap_network_h

271
include/dap/optional.h Normal file
View File

@@ -0,0 +1,271 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_optional_h
#define dap_optional_h
#include <assert.h>
#include <type_traits>
namespace dap {
// optional holds an 'optional' contained value.
// This is similar to C++17's std::optional.
template <typename T>
class optional {
template <typename U>
using IsConvertibleToT =
typename std::enable_if<std::is_convertible<U, T>::value>::type;
public:
using value_type = T;
// constructors
inline optional() = default;
inline optional(const optional& other);
inline optional(optional&& other);
template <typename U>
inline optional(const optional<U>& other);
template <typename U>
inline optional(optional<U>&& other);
template <typename U = value_type, typename = IsConvertibleToT<U>>
inline optional(U&& value);
// value() returns the contained value.
// If the optional does not contain a value, then value() will assert.
inline T& value();
inline const T& value() const;
// value() returns the contained value, or defaultValue if the optional does
// not contain a value.
inline T& value(const T& defaultValue);
inline const T& value(const T& defaultValue) const;
// operator bool() returns true if the optional contains a value.
inline explicit operator bool() const noexcept;
// has_value() returns true if the optional contains a value.
inline bool has_value() const;
// assignment
inline optional& operator=(const optional& other);
inline optional& operator=(optional&& other) noexcept;
template <typename U = T, typename = IsConvertibleToT<U>>
inline optional& operator=(U&& value);
template <typename U>
inline optional& operator=(const optional<U>& other);
template <typename U>
inline optional& operator=(optional<U>&& other);
// value access
inline const T* operator->() const;
inline T* operator->();
inline const T& operator*() const;
inline T& operator*();
private:
T val = {};
bool set = false;
};
template <typename T>
optional<T>::optional(const optional& other) : val(other.val), set(other.set) {}
template <typename T>
optional<T>::optional(optional&& other)
: val(std::move(other.val)), set(other.set) {}
template <typename T>
template <typename U>
optional<T>::optional(const optional<U>& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(other.value());
}
}
template <typename T>
template <typename U>
optional<T>::optional(optional<U>&& other) : set(other.has_value()) {
if (set) {
val = static_cast<T>(std::move(other.value()));
}
}
template <typename T>
template <typename U /*= T*/, typename>
optional<T>::optional(U&& value) : val(std::move(value)), set(true) {}
template <typename T>
T& optional<T>::value() {
assert(set);
return val;
}
template <typename T>
const T& optional<T>::value() const {
assert(set);
return val;
}
template <typename T>
T& optional<T>::value(const T& defaultValue) {
if (!has_value()) {
return defaultValue;
}
return val;
}
template <typename T>
const T& optional<T>::value(const T& defaultValue) const {
if (!has_value()) {
return defaultValue;
}
return val;
}
template <typename T>
optional<T>::operator bool() const noexcept {
return set;
}
template <typename T>
bool optional<T>::has_value() const {
return set;
}
template <typename T>
optional<T>& optional<T>::operator=(const optional& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
optional<T>& optional<T>::operator=(optional&& other) noexcept {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
template <typename U /* = T */, typename>
optional<T>& optional<T>::operator=(U&& value) {
val = std::move(value);
set = true;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(const optional<U>& other) {
val = other.val;
set = other.set;
return *this;
}
template <typename T>
template <typename U>
optional<T>& optional<T>::operator=(optional<U>&& other) {
val = std::move(other.val);
set = other.set;
return *this;
}
template <typename T>
const T* optional<T>::operator->() const {
assert(set);
return &val;
}
template <typename T>
T* optional<T>::operator->() {
assert(set);
return &val;
}
template <typename T>
const T& optional<T>::operator*() const {
assert(set);
return val;
}
template <typename T>
T& optional<T>::operator*() {
assert(set);
return val;
}
template <class T, class U>
inline bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value() && !rhs.has_value()) {
return true;
}
if (!lhs.has_value() || !rhs.has_value()) {
return false;
}
return lhs.value() == rhs.value();
}
template <class T, class U>
inline bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
return !(lhs == rhs);
}
template <class T, class U>
inline bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return false;
}
if (!lhs.has_value()) {
return true;
}
return lhs.value() < rhs.value();
}
template <class T, class U>
inline bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return true;
}
if (!rhs.has_value()) {
return false;
}
return lhs.value() <= rhs.value();
}
template <class T, class U>
inline bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
if (!lhs.has_value()) {
return false;
}
if (!rhs.has_value()) {
return true;
}
return lhs.value() > rhs.value();
}
template <class T, class U>
inline bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
if (!rhs.has_value()) {
return true;
}
if (!lhs.has_value()) {
return false;
}
return lhs.value() >= rhs.value();
}
} // namespace dap
#endif // dap_optional_h

2441
include/dap/protocol.h Normal file

File diff suppressed because it is too large Load Diff

256
include/dap/serialization.h Normal file
View File

@@ -0,0 +1,256 @@
// 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 <type_traits>
namespace dap {
// Field describes a single field of a struct.
struct Field {
std::string name; // name of the field
ptrdiff_t offset; // offset of the field to the base of the struct
const TypeInfo* type; // type of the field
};
////////////////////////////////////////////////////////////////////////////////
// Deserializer
////////////////////////////////////////////////////////////////////////////////
// Deserializer is the interface used to decode data from structured storage.
// Methods that return a bool use this to indicate success.
class Deserializer {
public:
// deserialization methods for simple data types.
// If the stored object is not of the correct type, then these function will
// return false.
virtual bool deserialize(boolean*) const = 0;
virtual bool deserialize(integer*) const = 0;
virtual bool deserialize(number*) const = 0;
virtual bool deserialize(string*) const = 0;
virtual bool deserialize(object*) const = 0;
virtual bool deserialize(any*) const = 0;
// count() returns the number of elements in the array object referenced by
// this Deserializer.
virtual size_t count() const = 0;
// array() calls the provided std::function for deserializing each array
// element in the array object referenced by this Deserializer.
virtual bool array(const std::function<bool(Deserializer*)>&) const = 0;
// field() calls the provided std::function for deserializing the field with
// the given name from the struct object referenced by this Deserializer.
virtual bool field(const std::string& name,
const std::function<bool(Deserializer*)>&) const = 0;
// deserialize() delegates to TypeOf<T>::type()->deserialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T*) const;
// deserialize() decodes an array.
template <typename T>
inline bool deserialize(dap::array<T>*) const;
// deserialize() decodes an optional.
template <typename T>
inline bool deserialize(dap::optional<T>*) const;
// deserialize() decodes an variant.
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>*) const;
// deserialize() decodes a list of fields and stores them into the object.
inline bool deserialize(void* object,
const std::initializer_list<Field>&) const;
// deserialize() decodes the struct field f with the given name.
template <typename T>
inline bool field(const std::string& name, T* f) const;
};
template <typename T, typename>
bool Deserializer::deserialize(T* ptr) const {
return TypeOf<T>::type()->deserialize(this, ptr);
}
template <typename T>
bool Deserializer::deserialize(dap::array<T>* vec) const {
auto n = count();
vec->resize(n);
size_t i = 0;
if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) {
return false;
}
return true;
}
template <typename T>
bool Deserializer::deserialize(dap::optional<T>* opt) const {
T v;
if (deserialize(&v)) {
*opt = v;
};
return true;
}
template <typename T0, typename... Types>
bool Deserializer::deserialize(dap::variant<T0, Types...>* var) const {
return deserialize(&var->value);
}
bool Deserializer::deserialize(
void* object,
const std::initializer_list<Field>& fields) const {
for (auto const& f : fields) {
if (!field(f.name, [&](Deserializer* d) {
auto ptr = reinterpret_cast<uint8_t*>(object) + f.offset;
return f.type->deserialize(d, ptr);
})) {
return false;
}
}
return true;
}
template <typename T>
bool Deserializer::field(const std::string& name, T* v) const {
return this->field(name,
[&](const Deserializer* d) { return d->deserialize(v); });
}
////////////////////////////////////////////////////////////////////////////////
// Serializer
////////////////////////////////////////////////////////////////////////////////
// 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:
using FieldSerializer = std::function<bool(Serializer*)>;
template <typename T>
using IsFieldSerializer = std::is_convertible<T, FieldSerializer>;
// 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 object&) = 0;
virtual bool serialize(const any&) = 0;
// array() encodes count array elements to the array object referenced by this
// Serializer. The std::function will be called count times, each time with a
// Serializer that should be used to encode the n'th array element's data.
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
// field() encodes a field to the struct object referenced by this Serializer.
// The FieldSerializer will be called with a Serializer used to encode the
// field's data.
virtual bool field(const std::string& name, const FieldSerializer&) = 0;
// remove() deletes the object referenced by this Serializer.
// remove() can be used to serialize optionals with no value assigned.
virtual void remove() = 0;
// serialize() delegates to TypeOf<T>::type()->serialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T&);
// serialize() encodes the given array.
template <typename T>
inline bool serialize(const dap::array<T>&);
// serialize() encodes the given optional.
template <typename T>
inline bool serialize(const dap::optional<T>& v);
// serialize() encodes the given variant.
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>&);
// serialize() encodes all the provided fields of the given object.
inline bool serialize(const void* object,
const std::initializer_list<Field>&);
// deserialize() encodes the given string.
inline bool serialize(const char* v);
// field() encodes the field with the given name and value.
template <
typename T,
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
inline bool field(const std::string& name, const T& v);
};
template <typename T, typename>
bool Serializer::serialize(const T& object) {
return TypeOf<T>::type()->serialize(this, &object);
}
template <typename T>
bool Serializer::serialize(const dap::array<T>& vec) {
auto it = vec.begin();
return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); });
}
template <typename T>
bool Serializer::serialize(const dap::optional<T>& opt) {
if (!opt.has_value()) {
remove();
return true;
}
return serialize(opt.value());
}
template <typename T0, typename... Types>
bool Serializer::serialize(const dap::variant<T0, Types...>& var) {
return serialize(var.value);
}
bool Serializer::serialize(const void* object,
const std::initializer_list<Field>& fields) {
for (auto const& f : fields) {
if (!field(f.name, [&](Serializer* d) {
auto ptr = reinterpret_cast<const uint8_t*>(object) + f.offset;
return f.type->serialize(d, ptr);
}))
return false;
}
return true;
}
bool Serializer::serialize(const char* v) {
return serialize(std::string(v));
}
template <typename T, typename>
bool Serializer::field(const std::string& name, const T& v) {
return this->field(name, [&](Serializer* s) { return s->serialize(v); });
}
} // namespace dap
#endif // dap_serialization_h

283
include/dap/session.h Normal file
View File

@@ -0,0 +1,283 @@
// 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 "io.h"
#include "typeinfo.h"
#include "typeof.h"
#include <functional>
#include <future>
namespace dap {
// Forward declarations
struct Request;
struct Response;
struct Event;
// internal functionality
namespace detail {
template <typename T>
struct traits {
static constexpr bool isRequest = std::is_base_of<dap::Request, T>::value;
static constexpr bool isResponse = std::is_base_of<dap::Response, T>::value;
static constexpr bool isEvent = std::is_base_of<dap::Event, T>::value;
};
// ArgTy<F>::type resolves to the first argument type of the function F.
// F can be a function, static member function, or lambda.
template <typename F>
struct ArgTy {
using type = typename ArgTy<decltype(&F::operator())>::type;
};
template <typename R, typename Arg>
struct ArgTy<R (*)(Arg)> {
using type = typename std::decay<Arg>::type;
};
template <typename R, typename C, typename Arg>
struct ArgTy<R (C::*)(Arg) const> {
using type = typename std::decay<Arg>::type;
};
} // namespace detail
////////////////////////////////////////////////////////////////////////////////
// Error
////////////////////////////////////////////////////////////////////////////////
// Error represents an error message in response to a DAP request.
struct Error {
Error() = default;
Error(const std::string& error);
Error(const char* msg, ...);
// operator bool() returns true if there is an error.
inline operator bool() const { return message.size() > 0; }
std::string message; // empty represents success.
};
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError<T>
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError holds either the response to a DAP request or an error
// message.
template <typename T>
struct ResponseOrError {
using Request = T;
inline ResponseOrError() = default;
inline ResponseOrError(const T& response);
inline ResponseOrError(const Error& error);
inline ResponseOrError(const ResponseOrError& other);
T response;
Error error; // empty represents success.
};
template <typename T>
ResponseOrError<T>::ResponseOrError(const T& response) : response(response) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const Error& error) : error(error) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const ResponseOrError& other)
: response(other.response), error(other.error) {}
////////////////////////////////////////////////////////////////////////////////
// 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 <typename T>
using IsRequest = typename std::enable_if<detail::traits<T>::isRequest>::type;
template <typename T>
using IsEvent = typename std::enable_if<detail::traits<T>::isEvent>::type;
template <typename F>
using ArgTy = typename detail::ArgTy<F>::type;
public:
virtual ~Session() = default;
// ErrorHandler is the type of callback function used for reporting protocol
// errors.
using ErrorHandler = std::function<void(const char*)>;
// create() constructs and returns a new Session.
static std::unique_ptr<Session> 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<ResponseType>(const RequestType&)
// ResponseType(const RequestType&)
// Error(const RequestType&)
template <typename F, typename RequestType = ArgTy<F>>
inline IsRequest<RequestType> registerHandler(F&& handler);
// registerHandler() registers a event handler for a specific event type.
// The function F must have the following signature:
// void(const EventType&)
template <typename F, typename EventType = ArgTy<F>>
inline IsEvent<EventType> registerHandler(F&& handler);
// registerSentHandler() registers the function F to be called when a response
// of the specific type has been sent.
// The function F must have the following signature:
// void(const ResponseOrError<ResponseType>&)
template <typename F, typename ResponseType = typename ArgTy<F>::Request>
inline void registerSentHandler(F&& handler);
// send() sends the request to the connected endpoint and returns a
// std::future that is assigned the request response or error.
template <typename T, typename = IsRequest<T>>
std::future<ResponseOrError<typename T::Response>> send(const T& request);
// send() sends the event to the connected endpoint.
template <typename T, typename = IsEvent<T>>
void send(const T& event);
// bind() connects this Session to an endpoint.
// bind() can only be called once. Repeated calls will raise an error, but
// otherwise will do nothing.
virtual void bind(const std::shared_ptr<Reader>&,
const std::shared_ptr<Writer>&) = 0;
inline void bind(const std::shared_ptr<ReaderWriter>&);
protected:
using RequestSuccessCallback =
std::function<void(const TypeInfo*, const void*)>;
using RequestErrorCallback =
std::function<void(const TypeInfo*, const Error& message)>;
using GenericResponseHandler = std::function<void(const void*, const Error*)>;
using GenericRequestHandler =
std::function<void(const void* args,
const RequestSuccessCallback& onSuccess,
const RequestErrorCallback& onError)>;
using GenericEventHandler = std::function<void(const void* args)>;
using GenericResponseSentHandler =
std::function<void(const void* response, const Error* error)>;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericRequestHandler& handler) = 0;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericEventHandler& handler) = 0;
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericResponseSentHandler& handler) = 0;
virtual bool send(const dap::TypeInfo* typeinfo,
const void* request,
const GenericResponseHandler& responseHandler) = 0;
virtual bool send(const TypeInfo*, const void* event) = 0;
};
template <typename F, typename T>
Session::IsRequest<T> Session::registerHandler(F&& handler) {
using ResponseType = typename T::Response;
auto cb = [handler](const void* args, const RequestSuccessCallback& onSuccess,
const RequestErrorCallback& onError) {
ResponseOrError<ResponseType> res =
handler(*reinterpret_cast<const T*>(args));
if (res.error) {
onError(TypeOf<ResponseType>::type(), res.error);
} else {
onSuccess(TypeOf<ResponseType>::type(), &res.response);
}
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename F, typename T>
Session::IsEvent<T> Session::registerHandler(F&& handler) {
auto cb = [handler](const void* args) {
handler(*reinterpret_cast<const T*>(args));
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename F, typename T>
void Session::registerSentHandler(F&& handler) {
auto cb = [handler](const void* response, const Error* error) {
if (error != nullptr) {
handler(ResponseOrError<T>(*error));
} else {
handler(ResponseOrError<T>(*reinterpret_cast<const T*>(response)));
}
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename T, typename>
std::future<ResponseOrError<typename T::Response>> Session::send(
const T& request) {
using Response = typename T::Response;
auto promise = std::make_shared<std::promise<ResponseOrError<Response>>>();
const TypeInfo* typeinfo = TypeOf<T>::type();
auto sent =
send(typeinfo, &request, [=](const void* result, const Error* error) {
if (error != nullptr) {
promise->set_value(ResponseOrError<Response>(*error));
} else {
promise->set_value(ResponseOrError<Response>(
*reinterpret_cast<const Response*>(result)));
}
});
if (!sent) {
promise->set_value(Error("Failed to send request"));
}
return promise->get_future();
}
template <typename T, typename>
void Session::send(const T& event) {
const TypeInfo* typeinfo = TypeOf<T>::type();
send(typeinfo, &event);
}
void Session::bind(const std::shared_ptr<ReaderWriter>& rw) {
bind(rw, rw);
}
} // namespace dap
#endif // dap_session_h

43
include/dap/typeinfo.h Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeinfo_h
#define dap_typeinfo_h
#include <functional>
#include <string>
namespace dap {
class any;
class Deserializer;
class Serializer;
// The TypeInfo interface provides basic runtime type information about DAP
// types. TypeInfo is used by the serialization system to encode and decode DAP
// requests, responses, events and structs.
struct TypeInfo {
virtual 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;
};
} // namespace dap
#endif // dap_typeinfo_h

182
include/dap/typeof.h Normal file
View File

@@ -0,0 +1,182 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeof_h
#define dap_typeof_h
#include "typeinfo.h"
#include "types.h"
#include "serialization.h"
namespace dap {
// BasicTypeInfo is an implementation of the TypeInfo interface for the simple
// template type T.
template <typename T>
struct BasicTypeInfo : public TypeInfo {
BasicTypeInfo(const std::string& name) : name_(name) {}
// TypeInfo compliance
inline std::string name() const { return name_; }
inline size_t size() const { return sizeof(T); }
inline size_t alignment() const { return alignof(T); }
inline void construct(void* ptr) const { new (ptr) T(); }
inline void copyConstruct(void* dst, const void* src) const {
new (dst) T(*reinterpret_cast<const T*>(src));
}
inline void destruct(void* ptr) const { reinterpret_cast<T*>(ptr)->~T(); }
inline bool deserialize(const Deserializer* d, void* ptr) const {
return d->deserialize(reinterpret_cast<T*>(ptr));
}
inline bool serialize(Serializer* s, const void* ptr) const {
return s->serialize(*reinterpret_cast<const T*>(ptr));
}
private:
std::string name_;
};
// TypeOf has a template specialization for each DAP type, each declaring a
// const TypeInfo* type() static member function that describes type T.
template <typename T>
struct TypeOf {};
template <>
struct TypeOf<boolean> {
static const TypeInfo* type();
};
template <>
struct TypeOf<string> {
static const TypeInfo* type();
};
template <>
struct TypeOf<integer> {
static const TypeInfo* type();
};
template <>
struct TypeOf<number> {
static const TypeInfo* type();
};
template <>
struct TypeOf<object> {
static const TypeInfo* type();
};
template <>
struct TypeOf<any> {
static const TypeInfo* type();
};
template <>
struct TypeOf<null> {
static const TypeInfo* type();
};
template <typename T>
struct TypeOf<array<T>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<array<T>> typeinfo("array<" +
TypeOf<T>::type()->name() + ">");
return &typeinfo;
}
};
template <typename T0, typename... Types>
struct TypeOf<variant<T0, Types...>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<variant<T0, Types...>> typeinfo("variant");
return &typeinfo;
}
};
template <typename T>
struct TypeOf<optional<T>> {
static inline const TypeInfo* type() {
static BasicTypeInfo<optional<T>> typeinfo("optional<" +
TypeOf<T>::type()->name() + ">");
return &typeinfo;
}
};
// DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in
// <cstddef>. It evaluates to the offset of the given field, with fewer
// restrictions than offsetof(). We cast the address '32' and subtract it again,
// because null-dereference is undefined behavior.
#define DAP_OFFSETOF(s, m) \
((int)(size_t) & reinterpret_cast<const volatile char&>((((s*)32)->m)) - 32)
// internal functionality
namespace detail {
template <class T, class M>
M member_type(M T::*);
} // namespace detail
// DAP_TYPEOF() returns the type of the struct (s) member (m).
#define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m))
// DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO
// macro.
// FIELD is the name of the struct field.
// NAME is the serialized name of the field, as described by the DAP
// specification.
#define DAP_FIELD(FIELD, NAME) \
dap::Field { \
NAME, DAP_OFFSETOF(StructTy, FIELD), \
TypeOf<DAP_TYPEOF(StructTy, FIELD)>::type(), \
}
// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT.
#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \
template <> \
struct TypeOf<STRUCT> { \
static constexpr bool has_custom_serialization = true; \
static const TypeInfo* type(); \
}
// DAP_DECLARE_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.
#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
const TypeInfo* TypeOf<STRUCT>::type() { \
using StructTy = STRUCT; \
struct TI : BasicTypeInfo<StructTy> { \
TI() : BasicTypeInfo<StructTy>(NAME) {} \
bool deserialize(const Deserializer* d, void* ptr) const override { \
return d->deserialize(ptr, {__VA_ARGS__}); \
} \
bool serialize(Serializer* s, const void* ptr) const override { \
return s->serialize(ptr, {__VA_ARGS__}); \
} \
}; \
static TI typeinfo; \
return &typeinfo; \
}
// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<>
// specialization for STRUCT in a single statement.
#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__)
} // namespace dap
#endif // dap_typeof_h

102
include/dap/types.h Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This file holds the basic serializable types used by the debug adapter
// protocol.
#ifndef dap_types_h
#define dap_types_h
#include "any.h"
#include "optional.h"
#include "variant.h"
#include <unordered_map>
#include <vector>
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(int i) : val(i) {}
inline operator int() const { return val; }
inline integer& operator=(int i) {
val = i;
return *this;
}
inline integer operator++(int) {
auto copy = *this;
val++;
return copy;
}
private:
int val;
};
// number holds a 64-bit floating point number.
// number defaults to 0.
class number {
public:
inline number() : val(0.0) {}
inline number(double i) : val(i) {}
inline operator double() const { return val; }
inline number& operator=(double i) {
val = i;
return *this;
}
private:
double val;
};
// array is a list of items of type T.
// array defaults to an empty list.
template <typename T>
using array = std::vector<T>;
// object is a map of string to any.
// object defaults to an empty map.
using object = std::unordered_map<string, any>;
// null represents no value.
// null is used by any to check for no-value.
using null = std::nullptr_t;
} // namespace dap
#endif // dap_types_h

108
include/dap/variant.h Normal file
View File

@@ -0,0 +1,108 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_variant_h
#define dap_variant_h
#include "any.h"
namespace dap {
// internal functionality
namespace detail {
template <typename T, typename...>
struct TypeIsIn {
static constexpr bool value = false;
};
template <typename T, typename List0, typename... ListN>
struct TypeIsIn<T, List0, ListN...> {
static constexpr bool value =
std::is_same<T, List0>::value || TypeIsIn<T, ListN...>::value;
};
} // namespace detail
// variant represents a type-safe union of DAP types.
// variant can hold a value of any of the template argument types.
// variant defaults to a default-constructed T0.
template <typename T0, typename... Types>
class variant {
public:
// constructors
inline variant();
template <typename T>
inline variant(const T& val);
// assignment
template <typename T>
inline variant& operator=(const T& val);
// get() returns the contained value of the type T.
// If the any does not contain a value of type T, then get() will assert.
template <typename T>
inline T& get() const;
// is() returns true iff the contained value is of type T.
template <typename T>
inline bool is() const;
// accepts() returns true iff the variant accepts values of type T.
template <typename T>
static constexpr bool accepts();
private:
friend class Serializer;
friend class Deserializer;
any value;
};
template <typename T0, typename... Types>
variant<T0, Types...>::variant() : value(T0()) {}
template <typename T0, typename... Types>
template <typename T>
variant<T0, Types...>::variant(const T& value) : value(value) {
static_assert(accepts<T>(), "variant does not accept template type T");
}
template <typename T0, typename... Types>
template <typename T>
variant<T0, Types...>& variant<T0, Types...>::operator=(const T& v) {
static_assert(accepts<T>(), "variant does not accept template type T");
value = v;
return *this;
}
template <typename T0, typename... Types>
template <typename T>
T& variant<T0, Types...>::get() const {
static_assert(accepts<T>(), "variant does not accept template type T");
return value.get<T>();
}
template <typename T0, typename... Types>
template <typename T>
bool variant<T0, Types...>::is() const {
return value.is<T>();
}
template <typename T0, typename... Types>
template <typename T>
constexpr bool variant<T0, Types...>::accepts() {
return detail::TypeIsIn<T, T0, Types...>::value;
}
} // namespace dap
#endif // dap_variant_h

126
src/any_test.cpp Normal file
View File

@@ -0,0 +1,126 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "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"));
} // namespace dap
TEST(Any, EmptyConstruct) {
dap::any any;
ASSERT_TRUE(any.is<dap::null>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::number>());
ASSERT_FALSE(any.is<dap::object>());
ASSERT_FALSE(any.is<dap::array<dap::integer>>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
}
TEST(Any, Boolean) {
dap::any any(dap::boolean(true));
ASSERT_TRUE(any.is<dap::boolean>());
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
}
TEST(Any, Integer) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
}
TEST(Any, Number) {
dap::any any(dap::number(123.0f));
ASSERT_TRUE(any.is<dap::number>());
ASSERT_EQ(any.get<dap::number>(), dap::number(123.0f));
}
TEST(Any, Array) {
using array = dap::array<dap::integer>;
dap::any any(array({10, 20, 30}));
ASSERT_TRUE(any.is<array>());
ASSERT_EQ(any.get<array>(), array({10, 20, 30}));
}
TEST(Any, Object) {
dap::object o;
o["one"] = dap::integer(1);
o["two"] = dap::integer(2);
o["three"] = dap::integer(3);
dap::any any(o);
ASSERT_TRUE(any.is<dap::object>());
if (any.is<dap::object>()) {
auto got = any.get<dap::object>();
ASSERT_EQ(got.size(), 3);
ASSERT_EQ(got.count("one"), 1);
ASSERT_EQ(got.count("two"), 1);
ASSERT_EQ(got.count("three"), 1);
ASSERT_TRUE(got["one"].is<dap::integer>());
ASSERT_TRUE(got["two"].is<dap::integer>());
ASSERT_TRUE(got["three"].is<dap::integer>());
ASSERT_EQ(got["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got["two"].get<dap::integer>(), dap::integer(2));
ASSERT_EQ(got["three"].get<dap::integer>(), dap::integer(3));
}
}
TEST(Any, TestObject) {
dap::any any(dap::AnyTestObject{5, 3.0});
ASSERT_TRUE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
}
TEST(Any, Assign) {
dap::any any;
any = dap::integer(10);
ASSERT_TRUE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
any = dap::boolean(true);
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_TRUE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
any = dap::AnyTestObject{5, 3.0};
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_TRUE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
}
TEST(Any, Reset) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
any.reset();
ASSERT_FALSE(any.is<dap::integer>());
}

90
src/chan.h Normal file
View File

@@ -0,0 +1,90 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_chan_h
#define dap_chan_h
#include "dap/optional.h"
#include <condition_variable>
#include <mutex>
#include <queue>
namespace dap {
template <typename T>
struct Chan {
public:
void reset();
void close();
optional<T> take();
void put(T&& in);
void put(const T& in);
private:
bool closed = false;
std::queue<T> queue;
std::condition_variable cv;
std::mutex mutex;
};
template <typename T>
void Chan<T>::reset() {
std::unique_lock<std::mutex> lock(mutex);
queue = {};
closed = false;
}
template <typename T>
void Chan<T>::close() {
std::unique_lock<std::mutex> lock(mutex);
closed = true;
cv.notify_all();
}
template <typename T>
optional<T> Chan<T>::take() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return queue.size() > 0 || closed; });
if (queue.size() == 0) {
return optional<T>();
}
auto out = std::move(queue.front());
queue.pop();
return optional<T>(std::move(out));
}
template <typename T>
void Chan<T>::put(T&& in) {
std::unique_lock<std::mutex> lock(mutex);
auto notify = queue.size() == 0 && !closed;
queue.push(std::move(in));
if (notify) {
cv.notify_all();
}
}
template <typename T>
void Chan<T>::put(const T& in) {
std::unique_lock<std::mutex> lock(mutex);
auto notify = queue.size() == 0 && !closed;
queue.push(in);
if (notify) {
cv.notify_all();
}
}
} // namespace dap
#endif // dap_chan_h

35
src/chan_test.cpp Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <thread>
TEST(ChanTest, PutTakeClose) {
dap::Chan<int> chan;
auto thread = std::thread([&] {
chan.put(10);
chan.put(20);
chan.put(30);
chan.close();
});
EXPECT_EQ(chan.take(), dap::optional<int>(10));
EXPECT_EQ(chan.take(), dap::optional<int>(20));
EXPECT_EQ(chan.take(), dap::optional<int>(30));
EXPECT_EQ(chan.take(), dap::optional<int>());
thread.join();
}

179
src/content_stream.cpp Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "dap/io.h"
#include <string.h> // strlen
#include <algorithm> // std::min
namespace dap {
////////////////////////////////////////////////////////////////////////////////
// ContentReader
////////////////////////////////////////////////////////////////////////////////
ContentReader::ContentReader(const std::shared_ptr<Reader>& reader)
: 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() {
// 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)) {
return "";
}
std::string out;
out.reserve(len);
for (size_t i = 0; i < len; i++) {
out.push_back(static_cast<char>(buf.front()));
buf.pop_front();
}
return out;
}
bool ContentReader::scan(const uint8_t* seq, size_t len) {
while (buffer(len)) {
if (match(seq, len)) {
return true;
}
buf.pop_front();
}
return false;
}
bool ContentReader::scan(const char* str) {
auto len = strlen(str);
return scan(reinterpret_cast<const uint8_t*>(str), len);
}
bool ContentReader::match(const uint8_t* seq, size_t len) {
if (!buffer(len)) {
return false;
}
auto it = buf.begin();
for (size_t i = 0; i < len; i++, it++) {
if (*it != seq[i]) {
return false;
}
}
for (size_t i = 0; i < len; i++) {
buf.pop_front();
}
return true;
}
bool ContentReader::match(const char* str) {
auto len = strlen(str);
return match(reinterpret_cast<const uint8_t*>(str), len);
}
char ContentReader::matchAny(const char* chars) {
if (!buffer(1)) {
return false;
}
int c = buf.front();
if (auto p = strchr(chars, c)) {
buf.pop_front();
return *p;
}
return 0;
}
bool ContentReader::buffer(size_t bytes) {
if (bytes < buf.size()) {
return true;
}
bytes -= buf.size();
while (bytes > 0) {
uint8_t chunk[256];
auto c = std::min(sizeof(chunk), bytes);
if (reader->read(chunk, c) <= 0) {
return false;
}
for (size_t i = 0; i < c; i++) {
buf.push_back(chunk[i]);
}
bytes -= c;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ContentWriter
////////////////////////////////////////////////////////////////////////////////
ContentWriter::ContentWriter(const std::shared_ptr<Writer>& rhs)
: writer(rhs) {}
ContentWriter& ContentWriter::operator=(ContentWriter&& rhs) noexcept {
writer = std::move(rhs.writer);
return *this;
}
bool ContentWriter::isOpen() {
return writer ? writer->isOpen() : false;
}
void ContentWriter::close() {
if (writer) {
writer->close();
}
}
bool ContentWriter::write(const std::string& msg) const {
auto header =
std::string("Content-Length: ") + std::to_string(msg.size()) + "\r\n\r\n";
return writer->write(header.data(), header.size()) &&
writer->write(msg.data(), msg.size());
}
} // namespace dap

68
src/content_stream.h Normal file
View File

@@ -0,0 +1,68 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_content_stream_h
#define dap_content_stream_h
#include <deque>
#include <memory>
#include <string>
#include <stdint.h>
namespace dap {
// Forward declarations
class Reader;
class Writer;
class ContentReader {
public:
ContentReader() = default;
ContentReader(const std::shared_ptr<Reader>&);
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> reader;
std::deque<uint8_t> buf;
};
class ContentWriter {
public:
ContentWriter() = default;
ContentWriter(const std::shared_ptr<Writer>&);
ContentWriter& operator=(ContentWriter&&) noexcept;
bool isOpen();
void close();
bool write(const std::string&) const;
private:
std::shared_ptr<Writer> writer;
};
} // namespace dap
#endif // dap_content_stream_h

View File

@@ -0,0 +1,47 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "string_buffer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
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(), "");
}

21
src/dap_test.cpp Normal file
View File

@@ -0,0 +1,21 @@
// 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 "gmock/gmock.h"
#include "gtest/gtest.h"
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

257
src/io.cpp Normal file
View File

@@ -0,0 +1,257 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/io.h"
#include <atomic>
#include <condition_variable>
#include <cstdarg>
#include <cstdio>
#include <deque>
#include <mutex>
namespace {
class Pipe : public dap::ReaderWriter {
public:
// dap::ReaderWriter compliance
bool isOpen() override {
std::unique_lock<std::mutex> lock(mutex);
return !closed;
}
void close() override {
std::unique_lock<std::mutex> lock(mutex);
closed = true;
cv.notify_all();
}
size_t read(void* buffer, size_t bytes) override {
std::unique_lock<std::mutex> lock(mutex);
auto out = reinterpret_cast<uint8_t*>(buffer);
size_t n = 0;
while (true) {
cv.wait(lock, [&] { return closed || data.size() > 0; });
if (closed) {
return n;
}
for (; n < bytes && data.size() > 0; n++) {
out[n] = data.front();
data.pop_front();
}
if (n == bytes) {
return n;
}
}
}
bool write(const void* buffer, size_t bytes) override {
std::unique_lock<std::mutex> lock(mutex);
if (closed) {
return false;
}
if (bytes == 0) {
return true;
}
auto notify = data.size() == 0;
auto src = reinterpret_cast<const uint8_t*>(buffer);
for (size_t i = 0; i < bytes; i++) {
data.emplace_back(src[i]);
}
if (notify) {
cv.notify_all();
}
return true;
}
private:
std::mutex mutex;
std::condition_variable cv;
std::deque<uint8_t> data;
bool closed = false;
};
class RW : public dap::ReaderWriter {
public:
RW(const std::shared_ptr<Reader>& r, const std::shared_ptr<Writer>& w)
: r(r), w(w) {}
// dap::ReaderWriter compliance
bool isOpen() override { return r->isOpen() && w->isOpen(); }
void close() override {
r->close();
w->close();
}
size_t read(void* buffer, size_t n) override { return r->read(buffer, n); }
bool write(const void* buffer, size_t n) override {
return w->write(buffer, n);
}
private:
const std::shared_ptr<dap::Reader> r;
const std::shared_ptr<dap::Writer> w;
};
class File : public dap::ReaderWriter {
public:
File(FILE* f, bool closable) : f(f), closable(closable) {}
~File() { close(); }
// dap::ReaderWriter compliance
bool isOpen() override { return !closed; }
void close() override {
if (closable) {
if (!closed.exchange(true)) {
fclose(f);
}
}
}
size_t read(void* buffer, size_t n) override {
std::unique_lock<std::mutex> lock(readMutex);
auto out = reinterpret_cast<char*>(buffer);
for (size_t i = 0; i < n; i++) {
int c = fgetc(f);
if (c == EOF) {
return i;
}
out[i] = char(c);
}
return n;
// return fread(buffer, 1, n, f);
}
bool write(const void* buffer, size_t n) override {
std::unique_lock<std::mutex> lock(writeMutex);
if (fwrite(buffer, 1, n, f) == n) {
fflush(f);
return true;
}
return false;
}
private:
FILE* const f;
std::mutex readMutex;
std::mutex writeMutex;
std::atomic<bool> closed;
const bool closable;
};
class ReaderSpy : public dap::Reader {
public:
ReaderSpy(const std::shared_ptr<dap::Reader>& r,
const std::shared_ptr<dap::Writer>& s,
const std::string& prefix)
: r(r), s(s), prefix(prefix) {}
// dap::Reader compliance
bool isOpen() override { return r->isOpen(); }
void close() override { r->close(); }
size_t read(void* buffer, size_t n) override {
auto c = r->read(buffer, n);
if (c > 0) {
auto chars = reinterpret_cast<const char*>(buffer);
std::string buf = prefix;
buf.append(chars, chars + c);
s->write(buf.data(), buf.size());
}
return c;
}
private:
const std::shared_ptr<dap::Reader> r;
const std::shared_ptr<dap::Writer> s;
const std::string prefix;
};
class WriterSpy : public dap::Writer {
public:
WriterSpy(const std::shared_ptr<dap::Writer>& w,
const std::shared_ptr<dap::Writer>& s,
const std::string& prefix)
: w(w), s(s), prefix(prefix) {}
// dap::Writer compliance
bool isOpen() override { return w->isOpen(); }
void close() override { w->close(); }
bool write(const void* buffer, size_t n) override {
if (!w->write(buffer, n)) {
return false;
}
auto chars = reinterpret_cast<const char*>(buffer);
std::string buf = prefix;
buf.append(chars, chars + n);
s->write(buf.data(), buf.size());
return true;
}
private:
const std::shared_ptr<dap::Writer> w;
const std::shared_ptr<dap::Writer> s;
const std::string prefix;
};
} // anonymous namespace
namespace dap {
std::shared_ptr<ReaderWriter> ReaderWriter::create(
const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& w) {
return std::make_shared<RW>(r, w);
}
std::shared_ptr<ReaderWriter> pipe() {
return std::make_shared<Pipe>();
}
std::shared_ptr<ReaderWriter> file(FILE* f, bool closable /* = true */) {
return std::make_shared<File>(f, closable);
}
std::shared_ptr<ReaderWriter> file(const char* path) {
if (auto f = fopen(path, "wb")) {
return std::make_shared<File>(f, true);
}
return nullptr;
}
// spy() returns a Reader that copies all reads from the Reader r to the Writer
// s, using the given optional prefix.
std::shared_ptr<Reader> spy(const std::shared_ptr<Reader>& r,
const std::shared_ptr<Writer>& s,
const char* prefix /* = "\n<-" */) {
return std::make_shared<ReaderSpy>(r, s, prefix);
}
// spy() returns a Writer that copies all writes to the Writer w to the Writer
// s, using the given optional prefix.
std::shared_ptr<Writer> spy(const std::shared_ptr<Writer>& w,
const std::shared_ptr<Writer>& s,
const char* prefix /* = "\n->" */) {
return std::make_shared<WriterSpy>(w, s, prefix);
}
bool writef(const std::shared_ptr<Writer>& w, const char* msg, ...) {
char buf[2048];
va_list vararg;
va_start(vararg, msg);
vsnprintf(buf, sizeof(buf), msg, vararg);
va_end(vararg);
return w->write(buf, strlen(buf));
}
} // namespace dap

242
src/json_serializer.cpp Normal file
View File

@@ -0,0 +1,242 @@
// 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 <nlohmann/json.hpp>
namespace {
struct NullDeserializer : public dap::Deserializer {
static NullDeserializer instance;
bool deserialize(dap::boolean*) const override { return false; }
bool deserialize(dap::integer*) const override { return false; }
bool deserialize(dap::number*) const override { return false; }
bool deserialize(dap::string*) const override { return false; }
bool deserialize(dap::object*) const override { return false; }
bool deserialize(dap::any*) const override { return false; }
size_t count() const override { return 0; }
bool array(const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
bool field(const std::string&,
const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
};
NullDeserializer NullDeserializer::instance;
} // anonymous namespace
namespace dap {
namespace json {
Deserializer::Deserializer(const std::string& str)
: json(new nlohmann::json(nlohmann::json::parse(str))), ownsJson(true) {}
Deserializer::Deserializer(const nlohmann::json* json)
: json(json), ownsJson(false) {}
Deserializer::~Deserializer() {
if (ownsJson) {
delete json;
}
}
bool Deserializer::deserialize(dap::boolean* v) const {
if (!json->is_boolean()) {
return false;
}
*v = json->get<bool>();
return true;
}
bool Deserializer::deserialize(dap::integer* v) const {
if (!json->is_number_integer()) {
return false;
}
*v = json->get<int>();
return true;
}
bool Deserializer::deserialize(dap::number* v) const {
if (!json->is_number()) {
return false;
}
*v = json->get<double>();
return true;
}
bool Deserializer::deserialize(dap::string* v) const {
if (!json->is_string()) {
return false;
}
*v = json->get<std::string>();
return true;
}
bool Deserializer::deserialize(dap::object* v) const {
v->reserve(json->size());
for (auto& el : json->items()) {
Deserializer d(&el.value());
dap::any val;
if (!d.deserialize(&val)) {
return false;
}
(*v)[el.key()] = val;
}
return true;
}
bool Deserializer::deserialize(dap::any* v) const {
if (json->is_boolean()) {
*v = dap::boolean(json->get<bool>());
} else if (json->is_number_float()) {
*v = dap::number(json->get<double>());
} else if (json->is_number_integer()) {
*v = dap::integer(json->get<int>());
} else if (json->is_string()) {
*v = json->get<std::string>();
} else if (json->is_null()) {
*v = null();
} else {
return false;
}
return true;
}
size_t Deserializer::count() const {
return json->size();
}
bool Deserializer::array(
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_array()) {
return false;
}
for (size_t i = 0; i < json->size(); i++) {
Deserializer d(&(*json)[i]);
if (!cb(&d)) {
return false;
}
}
return true;
}
bool Deserializer::field(
const std::string& name,
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_structured()) {
return false;
}
auto it = json->find(name);
if (it == json->end()) {
return cb(&NullDeserializer::instance);
}
auto obj = *it;
Deserializer d(&obj);
return cb(&d);
}
Serializer::Serializer() : json(new nlohmann::json()), ownsJson(true) {}
Serializer::Serializer(nlohmann::json* json) : json(json), ownsJson(false) {}
Serializer::~Serializer() {
if (ownsJson) {
delete json;
}
}
std::string Serializer::dump() const {
return json->dump();
}
bool Serializer::serialize(dap::boolean v) {
*json = (bool)v;
return true;
}
bool Serializer::serialize(dap::integer v) {
*json = (int)v;
return true;
}
bool Serializer::serialize(dap::number v) {
*json = (double)v;
return true;
}
bool Serializer::serialize(const dap::string& v) {
*json = v;
return true;
}
bool Serializer::serialize(const dap::object& v) {
for (auto& it : v) {
Serializer s(&(*json)[it.first]);
if (!s.serialize(it.second)) {
return false;
}
}
return true;
}
bool Serializer::serialize(const dap::any& v) {
if (v.is<dap::boolean>()) {
*json = (bool)v.get<dap::boolean>();
} else if (v.is<dap::integer>()) {
*json = (int)v.get<dap::integer>();
} else if (v.is<dap::number>()) {
*json = (double)v.get<dap::number>();
} else if (v.is<dap::string>()) {
*json = v.get<dap::string>();
} else if (v.is<dap::null>()) {
} else {
return false;
}
return true;
}
bool Serializer::array(size_t count,
const std::function<bool(dap::Serializer*)>& cb) {
*json = std::vector<int>();
for (size_t i = 0; i < count; i++) {
Serializer s(&(*json)[i]);
if (!cb(&s)) {
return false;
}
}
return true;
}
bool Serializer::field(const std::string& name,
const std::function<bool(dap::Serializer*)>& cb) {
Serializer s(&(*json)[name]);
auto res = cb(&s);
if (s.removed) {
json->erase(name);
}
return res;
}
void Serializer::remove() {
removed = true;
}
} // namespace json
} // namespace dap

149
src/json_serializer.h Normal file
View File

@@ -0,0 +1,149 @@
// 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_json_serializer_h
#define dap_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
#include <nlohmann/json_fwd.hpp>
namespace dap {
namespace json {
struct Deserializer : public dap::Deserializer {
explicit Deserializer(const std::string&);
~Deserializer();
// dap::Deserializer compliance
bool deserialize(boolean* v) const override;
bool deserialize(integer* v) const override;
bool deserialize(number* v) const override;
bool deserialize(string* v) const override;
bool deserialize(object* v) const override;
bool deserialize(any* v) const override;
size_t count() const override;
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
bool field(const std::string& name,
const std::function<bool(dap::Deserializer*)>&) const override;
// Unhide base overloads
template <typename T>
inline bool field(const std::string& name, T* v) {
return dap::Deserializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::array<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::optional<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>* v) const {
return dap::Deserializer::deserialize(v);
}
inline bool deserialize(void* o,
const std::initializer_list<Field>& f) const {
return dap::Deserializer::deserialize(o, f);
}
template <typename T>
inline bool field(const std::string& name, T* v) const {
return dap::Deserializer::deserialize(name, v);
}
private:
Deserializer(const nlohmann::json*);
const nlohmann::json* const json;
const bool ownsJson;
};
struct Serializer : public dap::Serializer {
Serializer();
~Serializer();
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 object& v) override;
bool serialize(const any& v) override;
bool array(size_t count,
const std::function<bool(dap::Serializer*)>&) override;
bool field(const std::string& name, const FieldSerializer&) override;
void remove() override;
// Unhide base overloads
template <
typename T,
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
inline bool field(const std::string& name, const T& v) {
return dap::Serializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::array<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::optional<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>& v) {
return dap::Serializer::serialize(v);
}
inline bool serialize(const void* o, const std::initializer_list<Field>& f) {
return dap::Serializer::serialize(o, f);
}
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
private:
Serializer(nlohmann::json*);
nlohmann::json* const json;
const bool ownsJson;
bool removed = false;
};
} // namespace json
} // namespace dap
#endif // dap_json_serializer_h

View File

@@ -0,0 +1,93 @@
// 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"
#include <nlohmann/json.hpp>
namespace dap {
struct JSONInnerTestObject {
integer i;
};
DAP_STRUCT_TYPEINFO(JSONInnerTestObject,
"json-inner-test-object",
DAP_FIELD(i, "i"));
struct JSONTestObject {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
JSONInnerTestObject inner;
};
DAP_STRUCT_TYPEINFO(JSONTestObject,
"json-test-object",
DAP_FIELD(b, "b"),
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"),
DAP_FIELD(a, "a"),
DAP_FIELD(o, "o"),
DAP_FIELD(s, "s"),
DAP_FIELD(o1, "o1"),
DAP_FIELD(o2, "o2"),
DAP_FIELD(inner, "inner"));
TEST(JSONSerializer, Decode) {}
} // namespace dap
TEST(JSONSerializer, SerializeDeserialize) {
dap::JSONTestObject encoded;
encoded.b = true;
encoded.i = 32;
encoded.n = 123.456;
encoded.a = {2, 4, 6, 8};
encoded.o["one"] = dap::integer(1);
encoded.o["two"] = dap::number(2);
encoded.s = "hello world";
encoded.o2 = 42;
encoded.inner.i = 70;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(encoded));
dap::JSONTestObject decoded;
dap::json::Deserializer d(s.dump());
ASSERT_TRUE(d.deserialize(&decoded));
ASSERT_EQ(encoded.b, decoded.b);
ASSERT_EQ(encoded.i, decoded.i);
ASSERT_EQ(encoded.n, decoded.n);
ASSERT_EQ(encoded.a, decoded.a);
ASSERT_EQ(encoded.o["one"].get<dap::integer>(),
decoded.o["one"].get<dap::integer>());
ASSERT_EQ(encoded.o["two"].get<dap::number>(),
decoded.o["two"].get<dap::number>());
ASSERT_EQ(encoded.s, decoded.s);
ASSERT_EQ(encoded.o2, decoded.o2);
ASSERT_EQ(encoded.inner.i, decoded.inner.i);
}

100
src/network.cpp Normal file
View File

@@ -0,0 +1,100 @@
// 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 <mutex>
#include <string>
#include <thread>
namespace {
class Impl : public dap::net::Server {
public:
Impl() {}
~Impl() { stop(); }
bool start(int port,
const OnConnect& onConnect,
const OnError& onError) override {
std::unique_lock<std::mutex> lock(mutex);
stopWithLock();
socket = std::unique_ptr<dap::Socket>(
new dap::Socket("localhost", std::to_string(port).c_str()));
if (!socket->isOpen()) {
onError("Failed to open socket");
return false;
}
running = true;
thread = std::thread([=] {
do {
if (auto rw = socket->accept()) {
onConnect(rw);
continue;
}
if (!isRunning()) {
onError("Failed to accept connection");
}
} while (false);
});
return true;
}
void stop() override {
std::unique_lock<std::mutex> lock(mutex);
stopWithLock();
}
private:
bool isRunning() {
std::unique_lock<std::mutex> lock(mutex);
return running;
}
void stopWithLock() {
if (running) {
socket->close();
thread.join();
running = false;
}
}
std::mutex mutex;
std::thread thread;
std::unique_ptr<dap::Socket> socket;
bool running = false;
OnError errorHandler;
};
} // anonymous namespace
namespace dap {
namespace net {
std::unique_ptr<Server> Server::create() {
return std::unique_ptr<Server>(new Impl());
}
std::shared_ptr<ReaderWriter> connect(const char* addr, int port) {
return Socket::connect(addr, std::to_string(port).c_str());
}
} // namespace net
} // namespace dap

74
src/network_test.cpp Normal file
View File

@@ -0,0 +1,74 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/network.h"
#include "dap/io.h"
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <chrono>
#include <thread>
namespace {
bool write(const std::shared_ptr<dap::Writer>& w, const std::string& s) {
return w->write(s.data(), s.size()) && w->write("\0", 1);
}
std::string read(const std::shared_ptr<dap::Reader>& r) {
char c;
std::string s;
while (r->read(&c, sizeof(c)) > 0) {
if (c == '\0') {
return s;
}
s += c;
}
return r->isOpen() ? "<read failed>" : "<stream closed>";
}
} // anonymous namespace
TEST(Network, ClientServer) {
const int port = 19021;
dap::Chan<bool> done;
auto server = dap::net::Server::create();
if (!server->start(port,
[&](const std::shared_ptr<dap::ReaderWriter>& rw) {
ASSERT_EQ(read(rw), "client to server");
ASSERT_TRUE(write(rw, "server to client"));
done.put(true);
},
[&](const char* err) {
FAIL() << "Server error: " << err;
})) {
FAIL() << "Couldn't start server";
return;
}
for (int i = 0; i < 10; i++) {
if (auto client = dap::net::connect("localhost", port)) {
ASSERT_TRUE(write(client, "client to server"));
ASSERT_EQ(read(client), "server to client");
break;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
done.take();
server.reset();
}

162
src/optional_test.cpp Normal file
View File

@@ -0,0 +1,162 @@
// 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"
TEST(Optional, EmptyConstruct) {
dap::optional<int> opt;
ASSERT_FALSE(opt);
ASSERT_FALSE(opt.has_value());
}
TEST(Optional, ValueConstruct) {
dap::optional<int> opt(0);
ASSERT_TRUE(opt);
ASSERT_TRUE(opt.has_value());
}
TEST(Optional, CopyConstruct) {
dap::optional<int> a(10);
dap::optional<int> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(b.value(), 10);
}
TEST(Optional, CopyCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, MoveConstruct) {
dap::optional<int> a(10);
dap::optional<int> b(std::move(a));
ASSERT_EQ(b.value(), 10);
}
TEST(Optional, MoveCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(std::move(a));
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, AssignValue) {
dap::optional<int> a;
a = 10;
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, AssignOptional) {
dap::optional<int> a;
dap::optional<int> b(10);
a = b;
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, MoveAssignOptional) {
dap::optional<int> a;
dap::optional<int> b(10);
a = std::move(b);
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, StarDeref) {
dap::optional<int> a(10);
ASSERT_EQ(*a, 10);
}
TEST(Optional, StarDerefConst) {
const dap::optional<int> a(10);
ASSERT_EQ(*a, 10);
}
TEST(Optional, ArrowDeref) {
struct S {
int i;
};
dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, ArrowDerefConst) {
struct S {
int i;
};
const dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, Value) {
const dap::optional<int> a(10);
ASSERT_EQ(a.value(), 10);
}
TEST(Optional, ValueDefault) {
const dap::optional<int> a;
const dap::optional<int> b(20);
ASSERT_EQ(a.value(10), 10);
ASSERT_EQ(b.value(10), 20);
}
TEST(Optional, CompareLT) {
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) < dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() < dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() < dap::optional<int>());
}
TEST(Optional, CompareLE) {
ASSERT_FALSE(dap::optional<int>(5) <= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>());
}
TEST(Optional, CompareGT) {
ASSERT_TRUE(dap::optional<int>(5) > dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>());
}
TEST(Optional, CompareGE) {
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) >= dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() >= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() >= dap::optional<int>());
}
TEST(Optional, CompareEQ) {
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) == dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() == dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() == dap::optional<int>());
}
TEST(Optional, CompareNEQ) {
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) != dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() != dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() != dap::optional<int>());
}

114
src/protocol_events.cpp Normal file
View File

@@ -0,0 +1,114 @@
// 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
#include "dap/protocol.h"
namespace dap {
BreakpointEvent::BreakpointEvent() = default;
BreakpointEvent::~BreakpointEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent,
"breakpoint",
DAP_FIELD(breakpoint, "breakpoint"),
DAP_FIELD(reason, "reason"));
CapabilitiesEvent::CapabilitiesEvent() = default;
CapabilitiesEvent::~CapabilitiesEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent,
"capabilities",
DAP_FIELD(capabilities, "capabilities"));
ContinuedEvent::ContinuedEvent() = default;
ContinuedEvent::~ContinuedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent,
"continued",
DAP_FIELD(allThreadsContinued,
"allThreadsContinued"),
DAP_FIELD(threadId, "threadId"));
ExitedEvent::ExitedEvent() = default;
ExitedEvent::~ExitedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent,
"exited",
DAP_FIELD(exitCode, "exitCode"));
InitializedEvent::InitializedEvent() = default;
InitializedEvent::~InitializedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized");
LoadedSourceEvent::LoadedSourceEvent() = default;
LoadedSourceEvent::~LoadedSourceEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent,
"loadedSource",
DAP_FIELD(reason, "reason"),
DAP_FIELD(source, "source"));
ModuleEvent::ModuleEvent() = default;
ModuleEvent::~ModuleEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent,
"module",
DAP_FIELD(module, "module"),
DAP_FIELD(reason, "reason"));
OutputEvent::OutputEvent() = default;
OutputEvent::~OutputEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent,
"output",
DAP_FIELD(category, "category"),
DAP_FIELD(column, "column"),
DAP_FIELD(data, "data"),
DAP_FIELD(line, "line"),
DAP_FIELD(output, "output"),
DAP_FIELD(source, "source"),
DAP_FIELD(variablesReference,
"variablesReference"));
ProcessEvent::ProcessEvent() = default;
ProcessEvent::~ProcessEvent() = default;
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"));
StoppedEvent::StoppedEvent() = default;
StoppedEvent::~StoppedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent,
"stopped",
DAP_FIELD(allThreadsStopped, "allThreadsStopped"),
DAP_FIELD(description, "description"),
DAP_FIELD(preserveFocusHint, "preserveFocusHint"),
DAP_FIELD(reason, "reason"),
DAP_FIELD(text, "text"),
DAP_FIELD(threadId, "threadId"));
TerminatedEvent::TerminatedEvent() = default;
TerminatedEvent::~TerminatedEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent,
"terminated",
DAP_FIELD(restart, "restart"));
ThreadEvent::ThreadEvent() = default;
ThreadEvent::~ThreadEvent() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent,
"thread",
DAP_FIELD(reason, "reason"),
DAP_FIELD(threadId, "threadId"));
} // namespace dap

321
src/protocol_requests.cpp Normal file
View File

@@ -0,0 +1,321 @@
// 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
#include "dap/protocol.h"
namespace dap {
AttachRequest::AttachRequest() = default;
AttachRequest::~AttachRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachRequest,
"attach",
DAP_FIELD(restart, "__restart"));
BreakpointLocationsRequest::BreakpointLocationsRequest() = default;
BreakpointLocationsRequest::~BreakpointLocationsRequest() = default;
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"));
CancelRequest::CancelRequest() = default;
CancelRequest::~CancelRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelRequest,
"cancel",
DAP_FIELD(requestId, "requestId"));
CompletionsRequest::CompletionsRequest() = default;
CompletionsRequest::~CompletionsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsRequest,
"completions",
DAP_FIELD(column, "column"),
DAP_FIELD(frameId, "frameId"),
DAP_FIELD(line, "line"),
DAP_FIELD(text, "text"));
ConfigurationDoneRequest::ConfigurationDoneRequest() = default;
ConfigurationDoneRequest::~ConfigurationDoneRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneRequest, "configurationDone");
ContinueRequest::ContinueRequest() = default;
ContinueRequest::~ContinueRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest,
"continue",
DAP_FIELD(threadId, "threadId"));
DataBreakpointInfoRequest::DataBreakpointInfoRequest() = default;
DataBreakpointInfoRequest::~DataBreakpointInfoRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest,
"dataBreakpointInfo",
DAP_FIELD(name, "name"),
DAP_FIELD(variablesReference,
"variablesReference"));
DisassembleRequest::DisassembleRequest() = default;
DisassembleRequest::~DisassembleRequest() = default;
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"));
DisconnectRequest::DisconnectRequest() = default;
DisconnectRequest::~DisconnectRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectRequest,
"disconnect",
DAP_FIELD(restart, "restart"),
DAP_FIELD(terminateDebuggee,
"terminateDebuggee"));
EvaluateRequest::EvaluateRequest() = default;
EvaluateRequest::~EvaluateRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateRequest,
"evaluate",
DAP_FIELD(context, "context"),
DAP_FIELD(expression, "expression"),
DAP_FIELD(format, "format"),
DAP_FIELD(frameId, "frameId"));
ExceptionInfoRequest::ExceptionInfoRequest() = default;
ExceptionInfoRequest::~ExceptionInfoRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoRequest,
"exceptionInfo",
DAP_FIELD(threadId, "threadId"));
GotoRequest::GotoRequest() = default;
GotoRequest::~GotoRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoRequest,
"goto",
DAP_FIELD(targetId, "targetId"),
DAP_FIELD(threadId, "threadId"));
GotoTargetsRequest::GotoTargetsRequest() = default;
GotoTargetsRequest::~GotoTargetsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsRequest,
"gotoTargets",
DAP_FIELD(column, "column"),
DAP_FIELD(line, "line"),
DAP_FIELD(source, "source"));
InitializeRequest::InitializeRequest() = default;
InitializeRequest::~InitializeRequest() = default;
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(supportsMemoryReferences, "supportsMemoryReferences"),
DAP_FIELD(supportsRunInTerminalRequest, "supportsRunInTerminalRequest"),
DAP_FIELD(supportsVariablePaging, "supportsVariablePaging"),
DAP_FIELD(supportsVariableType, "supportsVariableType"));
LaunchRequest::LaunchRequest() = default;
LaunchRequest::~LaunchRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchRequest,
"launch",
DAP_FIELD(restart, "__restart"),
DAP_FIELD(noDebug, "noDebug"));
LoadedSourcesRequest::LoadedSourcesRequest() = default;
LoadedSourcesRequest::~LoadedSourcesRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesRequest, "loadedSources");
ModulesRequest::ModulesRequest() = default;
ModulesRequest::~ModulesRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesRequest,
"modules",
DAP_FIELD(moduleCount, "moduleCount"),
DAP_FIELD(startModule, "startModule"));
NextRequest::NextRequest() = default;
NextRequest::~NextRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextRequest,
"next",
DAP_FIELD(threadId, "threadId"));
PauseRequest::PauseRequest() = default;
PauseRequest::~PauseRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseRequest,
"pause",
DAP_FIELD(threadId, "threadId"));
ReadMemoryRequest::ReadMemoryRequest() = default;
ReadMemoryRequest::~ReadMemoryRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryRequest,
"readMemory",
DAP_FIELD(count, "count"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(offset, "offset"));
RestartFrameRequest::RestartFrameRequest() = default;
RestartFrameRequest::~RestartFrameRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameRequest,
"restartFrame",
DAP_FIELD(frameId, "frameId"));
RestartRequest::RestartRequest() = default;
RestartRequest::~RestartRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartRequest, "restart");
ReverseContinueRequest::ReverseContinueRequest() = default;
ReverseContinueRequest::~ReverseContinueRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueRequest,
"reverseContinue",
DAP_FIELD(threadId, "threadId"));
RunInTerminalRequest::RunInTerminalRequest() = default;
RunInTerminalRequest::~RunInTerminalRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalRequest,
"runInTerminal",
DAP_FIELD(args, "args"),
DAP_FIELD(cwd, "cwd"),
DAP_FIELD(env, "env"),
DAP_FIELD(kind, "kind"),
DAP_FIELD(title, "title"));
ScopesRequest::ScopesRequest() = default;
ScopesRequest::~ScopesRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesRequest,
"scopes",
DAP_FIELD(frameId, "frameId"));
SetBreakpointsRequest::SetBreakpointsRequest() = default;
SetBreakpointsRequest::~SetBreakpointsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsRequest,
"setBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"),
DAP_FIELD(lines, "lines"),
DAP_FIELD(source, "source"),
DAP_FIELD(sourceModified, "sourceModified"));
SetDataBreakpointsRequest::SetDataBreakpointsRequest() = default;
SetDataBreakpointsRequest::~SetDataBreakpointsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsRequest,
"setDataBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"));
SetExceptionBreakpointsRequest::SetExceptionBreakpointsRequest() = default;
SetExceptionBreakpointsRequest::~SetExceptionBreakpointsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest,
"setExceptionBreakpoints",
DAP_FIELD(exceptionOptions, "exceptionOptions"),
DAP_FIELD(filters, "filters"));
SetExpressionRequest::SetExpressionRequest() = default;
SetExpressionRequest::~SetExpressionRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionRequest,
"setExpression",
DAP_FIELD(expression, "expression"),
DAP_FIELD(format, "format"),
DAP_FIELD(frameId, "frameId"),
DAP_FIELD(value, "value"));
SetFunctionBreakpointsRequest::SetFunctionBreakpointsRequest() = default;
SetFunctionBreakpointsRequest::~SetFunctionBreakpointsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest,
"setFunctionBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"));
SetVariableRequest::SetVariableRequest() = default;
SetVariableRequest::~SetVariableRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableRequest,
"setVariable",
DAP_FIELD(format, "format"),
DAP_FIELD(name, "name"),
DAP_FIELD(value, "value"),
DAP_FIELD(variablesReference,
"variablesReference"));
SourceRequest::SourceRequest() = default;
SourceRequest::~SourceRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceRequest,
"source",
DAP_FIELD(source, "source"),
DAP_FIELD(sourceReference, "sourceReference"));
StackTraceRequest::StackTraceRequest() = default;
StackTraceRequest::~StackTraceRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceRequest,
"stackTrace",
DAP_FIELD(format, "format"),
DAP_FIELD(levels, "levels"),
DAP_FIELD(startFrame, "startFrame"),
DAP_FIELD(threadId, "threadId"));
StepBackRequest::StepBackRequest() = default;
StepBackRequest::~StepBackRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackRequest,
"stepBack",
DAP_FIELD(threadId, "threadId"));
StepInRequest::StepInRequest() = default;
StepInRequest::~StepInRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInRequest,
"stepIn",
DAP_FIELD(targetId, "targetId"),
DAP_FIELD(threadId, "threadId"));
StepInTargetsRequest::StepInTargetsRequest() = default;
StepInTargetsRequest::~StepInTargetsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsRequest,
"stepInTargets",
DAP_FIELD(frameId, "frameId"));
StepOutRequest::StepOutRequest() = default;
StepOutRequest::~StepOutRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutRequest,
"stepOut",
DAP_FIELD(threadId, "threadId"));
TerminateRequest::TerminateRequest() = default;
TerminateRequest::~TerminateRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateRequest,
"terminate",
DAP_FIELD(restart, "restart"));
TerminateThreadsRequest::TerminateThreadsRequest() = default;
TerminateThreadsRequest::~TerminateThreadsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsRequest,
"terminateThreads",
DAP_FIELD(threadIds, "threadIds"));
ThreadsRequest::ThreadsRequest() = default;
ThreadsRequest::~ThreadsRequest() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsRequest, "threads");
VariablesRequest::VariablesRequest() = default;
VariablesRequest::~VariablesRequest() = default;
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"));
} // namespace dap

305
src/protocol_response.cpp Normal file
View File

@@ -0,0 +1,305 @@
// 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
#include "dap/protocol.h"
namespace dap {
AttachResponse::AttachResponse() = default;
AttachResponse::~AttachResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachResponse, "");
BreakpointLocationsResponse::BreakpointLocationsResponse() = default;
BreakpointLocationsResponse::~BreakpointLocationsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
CancelResponse::CancelResponse() = default;
CancelResponse::~CancelResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelResponse, "");
CompletionsResponse::CompletionsResponse() = default;
CompletionsResponse::~CompletionsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsResponse,
"",
DAP_FIELD(targets, "targets"));
ConfigurationDoneResponse::ConfigurationDoneResponse() = default;
ConfigurationDoneResponse::~ConfigurationDoneResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneResponse, "");
ContinueResponse::ContinueResponse() = default;
ContinueResponse::~ContinueResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueResponse,
"",
DAP_FIELD(allThreadsContinued,
"allThreadsContinued"));
DataBreakpointInfoResponse::DataBreakpointInfoResponse() = default;
DataBreakpointInfoResponse::~DataBreakpointInfoResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoResponse,
"",
DAP_FIELD(accessTypes, "accessTypes"),
DAP_FIELD(canPersist, "canPersist"),
DAP_FIELD(dataId, "dataId"),
DAP_FIELD(description, "description"));
DisassembleResponse::DisassembleResponse() = default;
DisassembleResponse::~DisassembleResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleResponse,
"",
DAP_FIELD(instructions, "instructions"));
DisconnectResponse::DisconnectResponse() = default;
DisconnectResponse::~DisconnectResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectResponse, "");
ErrorResponse::ErrorResponse() = default;
ErrorResponse::~ErrorResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ErrorResponse, "", DAP_FIELD(error, "error"));
EvaluateResponse::EvaluateResponse() = default;
EvaluateResponse::~EvaluateResponse() = default;
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"));
ExceptionInfoResponse::ExceptionInfoResponse() = default;
ExceptionInfoResponse::~ExceptionInfoResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoResponse,
"",
DAP_FIELD(breakMode, "breakMode"),
DAP_FIELD(description, "description"),
DAP_FIELD(details, "details"),
DAP_FIELD(exceptionId, "exceptionId"));
GotoResponse::GotoResponse() = default;
GotoResponse::~GotoResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoResponse, "");
GotoTargetsResponse::GotoTargetsResponse() = default;
GotoTargetsResponse::~GotoTargetsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsResponse,
"",
DAP_FIELD(targets, "targets"));
InitializeResponse::InitializeResponse() = default;
InitializeResponse::~InitializeResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(
InitializeResponse,
"",
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
DAP_FIELD(supportsBreakpointLocationsRequest,
"supportsBreakpointLocationsRequest"),
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
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(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
DAP_FIELD(supportsHitConditionalBreakpoints,
"supportsHitConditionalBreakpoints"),
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(supportsStepBack, "supportsStepBack"),
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
DAP_FIELD(supportsTerminateThreadsRequest,
"supportsTerminateThreadsRequest"),
DAP_FIELD(supportsValueFormattingOptions,
"supportsValueFormattingOptions"));
LaunchResponse::LaunchResponse() = default;
LaunchResponse::~LaunchResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchResponse, "");
LoadedSourcesResponse::LoadedSourcesResponse() = default;
LoadedSourcesResponse::~LoadedSourcesResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesResponse,
"",
DAP_FIELD(sources, "sources"));
ModulesResponse::ModulesResponse() = default;
ModulesResponse::~ModulesResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesResponse,
"",
DAP_FIELD(modules, "modules"),
DAP_FIELD(totalModules, "totalModules"));
NextResponse::NextResponse() = default;
NextResponse::~NextResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextResponse, "");
PauseResponse::PauseResponse() = default;
PauseResponse::~PauseResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseResponse, "");
ReadMemoryResponse::ReadMemoryResponse() = default;
ReadMemoryResponse::~ReadMemoryResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryResponse,
"",
DAP_FIELD(address, "address"),
DAP_FIELD(data, "data"),
DAP_FIELD(unreadableBytes, "unreadableBytes"));
RestartFrameResponse::RestartFrameResponse() = default;
RestartFrameResponse::~RestartFrameResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameResponse, "");
RestartResponse::RestartResponse() = default;
RestartResponse::~RestartResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartResponse, "");
ReverseContinueResponse::ReverseContinueResponse() = default;
ReverseContinueResponse::~ReverseContinueResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueResponse, "");
RunInTerminalResponse::RunInTerminalResponse() = default;
RunInTerminalResponse::~RunInTerminalResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalResponse,
"",
DAP_FIELD(processId, "processId"),
DAP_FIELD(shellProcessId, "shellProcessId"));
ScopesResponse::ScopesResponse() = default;
ScopesResponse::~ScopesResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesResponse, "", DAP_FIELD(scopes, "scopes"));
SetBreakpointsResponse::SetBreakpointsResponse() = default;
SetBreakpointsResponse::~SetBreakpointsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
SetDataBreakpointsResponse::SetDataBreakpointsResponse() = default;
SetDataBreakpointsResponse::~SetDataBreakpointsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
SetExceptionBreakpointsResponse::SetExceptionBreakpointsResponse() = default;
SetExceptionBreakpointsResponse::~SetExceptionBreakpointsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse, "");
SetExpressionResponse::SetExpressionResponse() = default;
SetExpressionResponse::~SetExpressionResponse() = default;
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"));
SetFunctionBreakpointsResponse::SetFunctionBreakpointsResponse() = default;
SetFunctionBreakpointsResponse::~SetFunctionBreakpointsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
SetVariableResponse::SetVariableResponse() = default;
SetVariableResponse::~SetVariableResponse() = default;
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"));
SourceResponse::SourceResponse() = default;
SourceResponse::~SourceResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceResponse,
"",
DAP_FIELD(content, "content"),
DAP_FIELD(mimeType, "mimeType"));
StackTraceResponse::StackTraceResponse() = default;
StackTraceResponse::~StackTraceResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceResponse,
"",
DAP_FIELD(stackFrames, "stackFrames"),
DAP_FIELD(totalFrames, "totalFrames"));
StepBackResponse::StepBackResponse() = default;
StepBackResponse::~StepBackResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackResponse, "");
StepInResponse::StepInResponse() = default;
StepInResponse::~StepInResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInResponse, "");
StepInTargetsResponse::StepInTargetsResponse() = default;
StepInTargetsResponse::~StepInTargetsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsResponse,
"",
DAP_FIELD(targets, "targets"));
StepOutResponse::StepOutResponse() = default;
StepOutResponse::~StepOutResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutResponse, "");
TerminateResponse::TerminateResponse() = default;
TerminateResponse::~TerminateResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateResponse, "");
TerminateThreadsResponse::TerminateThreadsResponse() = default;
TerminateThreadsResponse::~TerminateThreadsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsResponse, "");
ThreadsResponse::ThreadsResponse() = default;
ThreadsResponse::~ThreadsResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsResponse,
"",
DAP_FIELD(threads, "threads"));
VariablesResponse::VariablesResponse() = default;
VariablesResponse::~VariablesResponse() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesResponse,
"",
DAP_FIELD(variables, "variables"));
} // namespace dap

345
src/protocol_types.cpp Normal file
View File

@@ -0,0 +1,345 @@
// 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
#include "dap/protocol.h"
namespace dap {
ChecksumAlgorithm::ChecksumAlgorithm() = default;
ChecksumAlgorithm::~ChecksumAlgorithm() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ChecksumAlgorithm, "");
Checksum::Checksum() = default;
Checksum::~Checksum() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(Checksum,
"",
DAP_FIELD(algorithm, "algorithm"),
DAP_FIELD(checksum, "checksum"));
Source::Source() = default;
Source::~Source() = default;
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"));
Breakpoint::Breakpoint() = default;
Breakpoint::~Breakpoint() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(id, "id"),
DAP_FIELD(line, "line"),
DAP_FIELD(message, "message"),
DAP_FIELD(source, "source"),
DAP_FIELD(verified, "verified"));
BreakpointLocation::BreakpointLocation() = default;
BreakpointLocation::~BreakpointLocation() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocation,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(line, "line"));
ColumnDescriptor::ColumnDescriptor() = default;
ColumnDescriptor::~ColumnDescriptor() = default;
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"));
ExceptionBreakpointsFilter::ExceptionBreakpointsFilter() = default;
ExceptionBreakpointsFilter::~ExceptionBreakpointsFilter() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter,
"",
DAP_FIELD(def, "default"),
DAP_FIELD(filter, "filter"),
DAP_FIELD(label, "label"));
Capabilities::Capabilities() = default;
Capabilities::~Capabilities() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(
Capabilities,
"",
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
DAP_FIELD(supportsBreakpointLocationsRequest,
"supportsBreakpointLocationsRequest"),
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
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(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
DAP_FIELD(supportsHitConditionalBreakpoints,
"supportsHitConditionalBreakpoints"),
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(supportsStepBack, "supportsStepBack"),
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
DAP_FIELD(supportsTerminateThreadsRequest,
"supportsTerminateThreadsRequest"),
DAP_FIELD(supportsValueFormattingOptions,
"supportsValueFormattingOptions"));
CompletionItemType::CompletionItemType() = default;
CompletionItemType::~CompletionItemType() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItemType, "");
CompletionItem::CompletionItem() = default;
CompletionItem::~CompletionItem() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItem,
"",
DAP_FIELD(label, "label"),
DAP_FIELD(length, "length"),
DAP_FIELD(sortText, "sortText"),
DAP_FIELD(start, "start"),
DAP_FIELD(text, "text"),
DAP_FIELD(type, "type"));
DataBreakpointAccessType::DataBreakpointAccessType() = default;
DataBreakpointAccessType::~DataBreakpointAccessType() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointAccessType, "");
DisassembledInstruction::DisassembledInstruction() = default;
DisassembledInstruction::~DisassembledInstruction() = default;
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"));
Message::Message() = default;
Message::~Message() = default;
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"));
VariablePresentationHint::VariablePresentationHint() = default;
VariablePresentationHint::~VariablePresentationHint() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablePresentationHint,
"",
DAP_FIELD(attributes, "attributes"),
DAP_FIELD(kind, "kind"),
DAP_FIELD(visibility, "visibility"));
ValueFormat::ValueFormat() = default;
ValueFormat::~ValueFormat() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ValueFormat, "", DAP_FIELD(hex, "hex"));
ExceptionBreakMode::ExceptionBreakMode() = default;
ExceptionBreakMode::~ExceptionBreakMode() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakMode, "");
ExceptionDetails::ExceptionDetails() = default;
ExceptionDetails::~ExceptionDetails() = default;
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"));
GotoTarget::GotoTarget() = default;
GotoTarget::~GotoTarget() = default;
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"));
Module::Module() = default;
Module::~Module() = default;
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"));
Scope::Scope() = default;
Scope::~Scope() = default;
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"));
SourceBreakpoint::SourceBreakpoint() = default;
SourceBreakpoint::~SourceBreakpoint() = default;
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"));
DataBreakpoint::DataBreakpoint() = default;
DataBreakpoint::~DataBreakpoint() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint,
"",
DAP_FIELD(accessType, "accessType"),
DAP_FIELD(condition, "condition"),
DAP_FIELD(dataId, "dataId"),
DAP_FIELD(hitCondition, "hitCondition"));
ExceptionPathSegment::ExceptionPathSegment() = default;
ExceptionPathSegment::~ExceptionPathSegment() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionPathSegment,
"",
DAP_FIELD(names, "names"),
DAP_FIELD(negate, "negate"));
ExceptionOptions::ExceptionOptions() = default;
ExceptionOptions::~ExceptionOptions() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions,
"",
DAP_FIELD(breakMode, "breakMode"),
DAP_FIELD(path, "path"));
FunctionBreakpoint::FunctionBreakpoint() = default;
FunctionBreakpoint::~FunctionBreakpoint() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint,
"",
DAP_FIELD(condition, "condition"),
DAP_FIELD(hitCondition, "hitCondition"),
DAP_FIELD(name, "name"));
StackFrame::StackFrame() = default;
StackFrame::~StackFrame() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame,
"",
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"));
StackFrameFormat::StackFrameFormat() = default;
StackFrameFormat::~StackFrameFormat() = default;
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"));
StepInTarget::StepInTarget() = default;
StepInTarget::~StepInTarget() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTarget,
"",
DAP_FIELD(id, "id"),
DAP_FIELD(label, "label"));
Thread::Thread() = default;
Thread::~Thread() = default;
DAP_IMPLEMENT_STRUCT_TYPEINFO(Thread,
"",
DAP_FIELD(id, "id"),
DAP_FIELD(name, "name"));
Variable::Variable() = default;
Variable::~Variable() = default;
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

475
src/session.cpp Normal file
View File

@@ -0,0 +1,475 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "dap/any.h"
#include "dap/session.h"
#include "chan.h"
#include "json_serializer.h"
#include "socket.h"
#include <stdarg.h>
#include <stdio.h>
#include <atomic>
#include <deque>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
namespace {
class Impl : public dap::Session {
public:
void 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);
}
void bind(const std::shared_ptr<dap::Reader>& r,
const std::shared_ptr<dap::Writer>& w) override {
if (isBound.exchange(true)) {
handlers.error("Session is already bound!");
return;
}
reader = dap::ContentReader(r);
writer = dap::ContentWriter(w);
recvThread = std::thread([this] {
while (reader.isOpen()) {
auto request = reader.read();
if (request.size() > 0) {
if (auto payload = processMessage(request)) {
inbox.put(std::move(payload));
}
}
}
});
dispatchThread = std::thread([this] {
while (auto payload = inbox.take()) {
payload.value()();
}
});
}
bool send(const dap::TypeInfo* typeinfo,
const void* request,
const GenericResponseHandler& responseHandler) override {
int seq = nextSeq++;
handlers.put(seq, typeinfo, responseHandler);
dap::json::Serializer s;
s.field("seq", dap::integer(seq));
s.field("type", "request");
s.field("command", typeinfo->name());
s.field("arguments", [&](dap::Serializer* s) {
return typeinfo->serialize(s, request);
});
return send(s.dump());
}
bool send(const dap::TypeInfo* typeinfo, const void* event) override {
dap::json::Serializer s;
s.field("seq", dap::integer(nextSeq++));
s.field("type", "event");
s.field("event", typeinfo->name());
s.field("body",
[&](dap::Serializer* s) { return typeinfo->serialize(s, event); });
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<void()>;
class EventHandlers {
public:
void put(const ErrorHandler& handler) {
std::unique_lock<std::mutex> lock(errorMutex);
errorHandler = handler;
}
void error(const char* format, ...) {
va_list vararg;
va_start(vararg, format);
std::unique_lock<std::mutex> lock(errorMutex);
errorLocked(format, vararg);
va_end(vararg);
}
std::pair<const dap::TypeInfo*, GenericRequestHandler> request(
const std::string& name) {
std::unique_lock<std::mutex> lock(requestMutex);
auto it = requestMap.find(name);
return (it != requestMap.end()) ? it->second : decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericRequestHandler& handler) {
std::unique_lock<std::mutex> lock(requestMutex);
auto added =
requestMap
.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
.second;
if (!added) {
errorfLocked("Request handler for '%s' already registered",
typeinfo->name().c_str());
}
}
std::pair<const dap::TypeInfo*, GenericResponseHandler> response(int seq) {
std::unique_lock<std::mutex> lock(responseMutex);
auto responseIt = responseMap.find(seq);
if (responseIt == responseMap.end()) {
errorfLocked("Unknown response with sequence %d", seq);
return {};
}
auto out = std::move(responseIt->second);
responseMap.erase(seq);
return out;
}
void put(int seq,
const dap::TypeInfo* typeinfo,
const GenericResponseHandler& handler) {
std::unique_lock<std::mutex> lock(responseMutex);
auto added =
responseMap.emplace(seq, std::make_pair(typeinfo, handler)).second;
if (!added) {
errorfLocked("Response handler for sequence %d already registered",
seq);
}
}
std::pair<const dap::TypeInfo*, GenericEventHandler> event(
const std::string& name) {
std::unique_lock<std::mutex> lock(eventMutex);
auto it = eventMap.find(name);
return (it != eventMap.end()) ? it->second : decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericEventHandler& handler) {
std::unique_lock<std::mutex> lock(eventMutex);
auto added =
eventMap.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
.second;
if (!added) {
errorfLocked("Event handler for '%s' already registered",
typeinfo->name().c_str());
}
}
GenericResponseSentHandler responseSent(const dap::TypeInfo* typeinfo) {
std::unique_lock<std::mutex> lock(responseSentMutex);
auto it = responseSentMap.find(typeinfo);
return (it != responseSentMap.end()) ? it->second
: decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericResponseSentHandler& handler) {
std::unique_lock<std::mutex> lock(responseSentMutex);
auto added = responseSentMap.emplace(typeinfo, handler).second;
if (!added) {
errorfLocked("Response sent handler for '%s' already registered",
typeinfo->name().c_str());
}
}
private:
void errorfLocked(const char* format, ...) {
va_list vararg;
va_start(vararg, format);
errorLocked(format, vararg);
va_end(vararg);
}
void errorLocked(const char* format, va_list args) {
char buf[2048];
vsnprintf(buf, sizeof(buf), format, args);
if (errorHandler) {
errorHandler(buf);
}
}
std::mutex errorMutex;
ErrorHandler errorHandler;
std::mutex requestMutex;
std::unordered_map<std::string,
std::pair<const dap::TypeInfo*, GenericRequestHandler>>
requestMap;
std::mutex responseMutex;
std::unordered_map<int,
std::pair<const dap::TypeInfo*, GenericResponseHandler>>
responseMap;
std::mutex eventMutex;
std::unordered_map<std::string,
std::pair<const dap::TypeInfo*, GenericEventHandler>>
eventMap;
std::mutex responseSentMutex;
std::unordered_map<const dap::TypeInfo*, GenericResponseSentHandler>
responseSentMap;
}; // EventHandlers
Payload processMessage(const std::string& str) {
auto d = dap::json::Deserializer(str);
dap::string type;
if (!d.field("type", &type)) {
handlers.error("Message missing string 'type' field");
return {};
}
dap::integer sequence = 0;
if (!d.field("seq", &sequence)) {
handlers.error("Message missing number 'seq' field");
return {};
}
if (type == "request") {
return processRequest(&d, sequence);
} else if (type == "event") {
return processEvent(&d);
} else if (type == "response") {
processResponse(&d);
return {};
} else {
handlers.error("Unknown message type '%s'", type.c_str());
}
return {};
}
Payload processRequest(dap::json::Deserializer* d, dap::integer sequence) {
dap::string command;
if (!d->field("command", &command)) {
handlers.error("Request missing string 'command' field");
return {};
}
const dap::TypeInfo* typeinfo;
GenericRequestHandler handler;
std::tie(typeinfo, handler) = handlers.request(command);
if (!typeinfo) {
handlers.error("No request handler registered for command '%s'",
command.c_str());
return {};
}
auto data = new uint8_t[typeinfo->size()];
typeinfo->construct(data);
if (!d->field("arguments", [&](dap::Deserializer* d) {
return typeinfo->deserialize(d, data);
})) {
handlers.error("Failed to deserialize request");
typeinfo->destruct(data);
delete[] data;
return {};
}
return [=] {
handler(data,
[&](const dap::TypeInfo* typeinfo, const void* data) {
// onSuccess
dap::json::Serializer s;
s.field("seq", dap::integer(nextSeq++));
s.field("type", "response");
s.field("request_seq", sequence);
s.field("success", dap::boolean(true));
s.field("command", command);
s.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.field("seq", dap::integer(nextSeq++));
s.field("type", "response");
s.field("request_seq", sequence);
s.field("success", dap::boolean(false));
s.field("command", command);
s.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);
if (!d->field("body", [&](dap::Deserializer* d) {
return typeinfo->deserialize(d, data);
})) {
handlers.error("Failed to deserialize event '%s' body", event.c_str());
typeinfo->destruct(data);
delete[] data;
return {};
}
return [=] {
handler(data);
typeinfo->destruct(data);
delete[] data;
};
}
void processResponse(const dap::Deserializer* d) {
dap::integer requestSeq = 0;
if (!d->field("request_seq", &requestSeq)) {
handlers.error("Response missing int 'request_seq' field");
return;
}
const dap::TypeInfo* typeinfo;
GenericResponseHandler handler;
std::tie(typeinfo, handler) = handlers.response(requestSeq);
if (!typeinfo) {
handlers.error("Unknown response with sequence %d", requestSeq);
return;
}
dap::boolean success = false;
if (!d->field("success", &success)) {
handlers.error("Response missing int 'success' field");
return;
}
if (success) {
auto data = std::unique_ptr<uint8_t[]>(new uint8_t[typeinfo->size()]);
typeinfo->construct(data.get());
if (!d->field("body", [&](const dap::Deserializer* d) {
return typeinfo->deserialize(d, data.get());
})) {
handlers.error("Failed to deserialize request");
return;
}
handler(data.get(), nullptr);
typeinfo->destruct(data.get());
} else {
std::string message;
if (!d->field("message", &message)) {
handlers.error("Failed to deserialize message");
return;
}
auto error = dap::Error("%s", message.c_str());
handler(nullptr, &error);
}
}
bool send(const std::string& s) {
std::unique_lock<std::mutex> lock(sendMutex);
if (!writer.isOpen()) {
handlers.error("Send failed as the writer is closed");
return false;
}
return writer.write(s);
}
std::atomic<bool> isBound = {false};
dap::ContentReader reader;
dap::ContentWriter writer;
std::atomic<bool> shutdown = {false};
EventHandlers handlers;
std::thread recvThread;
std::thread dispatchThread;
dap::Chan<Payload> inbox;
std::atomic<uint32_t> nextSeq = {1};
std::mutex sendMutex;
};
} // 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;
}
std::unique_ptr<Session> Session::create() {
return std::unique_ptr<Session>(new Impl());
}
} // namespace dap

386
src/session_test.cpp Normal file
View File

@@ -0,0 +1,386 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/session.h"
#include "dap/io.h"
#include "dap/protocol.h"
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <array>
#include <condition_variable>
#include <mutex>
namespace dap {
struct TestResponse : public Response {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestResponse,
"test-response",
DAP_FIELD(b, "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"));
struct TestRequest : public Request {
using Response = TestResponse;
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestRequest,
"test-request",
DAP_FIELD(b, "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"));
struct TestEvent : public Event {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestEvent,
"test-event",
DAP_FIELD(b, "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"));
}; // namespace dap
namespace {
dap::TestRequest createRequest() {
dap::TestRequest request;
request.b = false;
request.i = 72;
request.n = 9.87;
request.a = {2, 5, 7, 8};
request.o = {
std::make_pair("a", dap::integer(1)),
std::make_pair("b", dap::number(2)),
std::make_pair("c", dap::string("3")),
};
request.s = "request";
request.o2 = 42;
return request;
}
dap::TestResponse createResponse() {
dap::TestResponse response;
response.b = true;
response.i = 99;
response.n = 123.456;
response.a = {5, 4, 3, 2, 1};
response.o = {
std::make_pair("one", dap::integer(1)),
std::make_pair("two", dap::number(2)),
std::make_pair("three", dap::string("3")),
};
response.s = "ROGER";
response.o1 = 50;
return response;
}
dap::TestEvent createEvent() {
dap::TestEvent event;
event.b = false;
event.i = 72;
event.n = 9.87;
event.a = {2, 5, 7, 8};
event.o = {
std::make_pair("a", dap::integer(1)),
std::make_pair("b", dap::number(2)),
std::make_pair("c", dap::string("3")),
};
event.s = "event";
event.o2 = 42;
return event;
}
} // anonymous namespace
class SessionTest : public testing::Test {
public:
void bind() {
auto client2server = dap::pipe();
auto server2client = dap::pipe();
client->bind(server2client, client2server);
server->bind(client2server, server2client);
}
std::unique_ptr<dap::Session> client = dap::Session::create();
std::unique_ptr<dap::Session> server = dap::Session::create();
};
TEST_F(SessionTest, Request) {
dap::TestRequest received;
server->registerHandler([&](const dap::TestRequest& req) {
received = req;
return createResponse();
});
bind();
auto request = createRequest();
client->send(request).get();
// Check request was received correctly.
ASSERT_EQ(received.b, request.b);
ASSERT_EQ(received.i, request.i);
ASSERT_EQ(received.n, request.n);
ASSERT_EQ(received.a, request.a);
ASSERT_EQ(received.o.size(), 3);
ASSERT_EQ(received.o["a"].get<dap::integer>(),
request.o["a"].get<dap::integer>());
ASSERT_EQ(received.o["b"].get<dap::number>(),
request.o["b"].get<dap::number>());
ASSERT_EQ(received.o["c"].get<dap::string>(),
request.o["c"].get<dap::string>());
ASSERT_EQ(received.s, request.s);
ASSERT_EQ(received.o1, request.o1);
ASSERT_EQ(received.o2, request.o2);
}
TEST_F(SessionTest, RequestResponseSuccess) {
server->registerHandler(
[&](const dap::TestRequest&) { return createResponse(); });
bind();
auto request = createRequest();
auto response = client->send(request);
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.b, dap::boolean(true));
ASSERT_EQ(got.response.i, dap::integer(99));
ASSERT_EQ(got.response.n, dap::number(123.456));
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
ASSERT_EQ(got.response.o.size(), 3);
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
ASSERT_EQ(got.response.s, "ROGER");
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
ASSERT_FALSE(got.response.o2.has_value());
}
TEST_F(SessionTest, RequestResponseOrError) {
server->registerHandler(
[&](const dap::TestRequest&) -> dap::ResponseOrError<dap::TestResponse> {
return dap::Error("Oh noes!");
});
bind();
auto response = client->send(createRequest());
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, RequestResponseError) {
server->registerHandler(
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
bind();
auto response = client->send(createRequest());
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, ResponseSentHandlerSuccess) {
const auto response = createResponse();
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
server->registerHandler([&](const dap::TestRequest&) { return response; });
server->registerSentHandler(
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
bind();
client->send(createRequest());
auto got = chan.take().value();
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.b, dap::boolean(true));
ASSERT_EQ(got.response.i, dap::integer(99));
ASSERT_EQ(got.response.n, dap::number(123.456));
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
ASSERT_EQ(got.response.o.size(), 3);
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
ASSERT_EQ(got.response.s, "ROGER");
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
ASSERT_FALSE(got.response.o2.has_value());
}
TEST_F(SessionTest, ResponseSentHandlerError) {
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
server->registerHandler(
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
server->registerSentHandler(
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
bind();
client->send(createRequest());
auto got = chan.take().value();
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, Event) {
dap::Chan<dap::TestEvent> received;
server->registerHandler([&](const dap::TestEvent& e) { received.put(e); });
bind();
auto event = createEvent();
client->send(event);
// Check event was received correctly.
auto got = received.take().value();
ASSERT_EQ(got.b, event.b);
ASSERT_EQ(got.i, event.i);
ASSERT_EQ(got.n, event.n);
ASSERT_EQ(got.a, event.a);
ASSERT_EQ(got.o.size(), 3);
ASSERT_EQ(got.o["a"].get<dap::integer>(), event.o["a"].get<dap::integer>());
ASSERT_EQ(got.o["b"].get<dap::number>(), event.o["b"].get<dap::number>());
ASSERT_EQ(got.o["c"].get<dap::string>(), event.o["c"].get<dap::string>());
ASSERT_EQ(got.s, event.s);
ASSERT_EQ(got.o1, event.o1);
ASSERT_EQ(got.o2, event.o2);
}
TEST_F(SessionTest, RegisterHandlerFunction) {
struct S {
static dap::TestResponse requestA(const dap::TestRequest&) { return {}; }
static dap::Error requestB(const dap::TestRequest&) { return {}; }
static dap::ResponseOrError<dap::TestResponse> requestC(
const dap::TestRequest&) {
return dap::Error();
}
static void event(const dap::TestEvent&) {}
static void sent(const dap::ResponseOrError<dap::TestResponse>&) {}
};
client->registerHandler(&S::requestA);
client->registerHandler(&S::requestB);
client->registerHandler(&S::requestC);
client->registerHandler(&S::event);
client->registerSentHandler(&S::sent);
}
TEST_F(SessionTest, SendRequestNoBind) {
bool errored = false;
client->onError([&](const std::string&) { errored = true; });
auto res = client->send(createRequest()).get();
ASSERT_TRUE(errored);
ASSERT_TRUE(res.error);
}
TEST_F(SessionTest, SendEventNoBind) {
bool errored = false;
client->onError([&](const std::string&) { errored = true; });
client->send(createEvent());
ASSERT_TRUE(errored);
}
TEST_F(SessionTest, Concurrency) {
std::atomic<int> numEventsHandled = {0};
std::atomic<bool> done = {false};
server->registerHandler(
[](const dap::TestRequest&) { return dap::TestResponse(); });
server->registerHandler([&](const dap::TestEvent&) {
if (numEventsHandled++ > 10000) {
done = true;
}
});
bind();
constexpr int numThreads = 32;
std::array<std::thread, numThreads> threads;
for (int i = 0; i < numThreads; i++) {
threads[i] = std::thread([&] {
while (!done) {
client->send(createEvent());
client->send(createRequest());
}
});
}
for (int i = 0; i < numThreads; i++) {
threads[i].join();
}
client.reset();
server.reset();
}

204
src/socket.cpp Normal file
View File

@@ -0,0 +1,204 @@
// 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"
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#if defined(_WIN32)
#include <atomic>
namespace {
std::atomic<int> wsaInitCount = {0};
} // anonymous namespace
#else
namespace {
using SOCKET = int;
} // anonymous namespace
#endif
namespace {
constexpr SOCKET InvalidSocket = static_cast<SOCKET>(-1);
} // anonymous namespace
class dap::Socket::Shared : public dap::ReaderWriter {
public:
static void init() {
#if defined(_WIN32)
if (wsaInitCount++ == 0) {
WSADATA winsockData;
(void)WSAStartup(MAKEWORD(2, 2), &winsockData);
}
#endif
}
static void term() {
#if defined(_WIN32)
if (--wsaInitCount == 0) {
WSACleanup();
}
#endif
}
static std::shared_ptr<Shared> create(const char* address, const char* port) {
init();
addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
addrinfo* info = nullptr;
getaddrinfo(address, port, &hints, &info);
if (info) {
auto socket =
::socket(info->ai_family, info->ai_socktype, info->ai_protocol);
return std::make_shared<Shared>(*info, socket);
}
term();
return nullptr;
}
Shared(SOCKET socket) : info({}), sock(socket) {}
Shared(const addrinfo& info, SOCKET socket) : info(info), sock(socket) {}
~Shared() {
close();
term();
}
SOCKET socket() { return sock.load(); }
// dap::ReaderWriter compliance
bool isOpen() {
SOCKET s = socket();
if (s == InvalidSocket) {
return false;
}
char error = 0;
socklen_t len = sizeof(error);
getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len);
if (error != 0) {
sock.compare_exchange_weak(s, InvalidSocket);
return false;
}
return true;
}
void close() {
SOCKET s = sock.exchange(InvalidSocket);
if (s != InvalidSocket) {
#if defined(_WIN32)
closesocket(s);
#else
::shutdown(s, SHUT_RDWR);
::close(s);
#endif
}
}
size_t read(void* buffer, size_t bytes) {
SOCKET s = socket();
if (s == InvalidSocket) {
return 0;
}
return recv(s, reinterpret_cast<char*>(buffer), static_cast<int>(bytes), 0);
}
bool write(const void* buffer, size_t bytes) {
SOCKET s = socket();
if (s == InvalidSocket) {
return false;
}
if (bytes == 0) {
return true;
}
return ::send(s, reinterpret_cast<const char*>(buffer),
static_cast<int>(bytes), 0) > 0;
}
const addrinfo info;
private:
std::atomic<SOCKET> sock = {InvalidSocket};
};
namespace dap {
Socket::Socket(const char* address, const char* port)
: shared(Shared::create(address, port)) {
if (!shared) {
return;
}
auto socket = shared->socket();
if (bind(socket, shared->info.ai_addr, (int)shared->info.ai_addrlen) != 0) {
shared.reset();
return;
}
if (listen(socket, 1) != 0) {
shared.reset();
return;
}
}
std::shared_ptr<ReaderWriter> Socket::accept() const {
if (shared) {
SOCKET socket = shared->socket();
if (socket != InvalidSocket) {
return std::make_shared<Shared>(::accept(socket, 0, 0));
}
}
return {};
}
bool Socket::isOpen() const {
if (shared) {
return shared->isOpen();
}
return false;
}
void Socket::close() const {
if (shared) {
shared->close();
}
}
std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
const char* port) {
auto shared = Shared::create(address, port);
if (::connect(shared->socket(), shared->info.ai_addr,
(int)shared->info.ai_addrlen) == 0) {
return shared;
}
return {};
}
} // namespace dap

43
src/socket.h Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_socket_h
#define dap_socket_h
#include "dap/io.h"
#include <atomic>
#include <memory>
namespace dap {
class Socket {
public:
class Shared;
static std::shared_ptr<ReaderWriter> connect(const char* address,
const char* port);
Socket(const char* address, const char* port);
bool isOpen() const;
std::shared_ptr<ReaderWriter> accept() const;
void close() const;
private:
std::shared_ptr<Shared> shared;
};
} // namespace dap
#endif // dap_socket_h

85
src/string_buffer.h Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_string_buffer_h
#define dap_string_buffer_h
#include "dap/io.h"
#include <algorithm> // std::min
#include <cstring> // memcpy
#include <memory> // std::unique_ptr
#include <string>
namespace dap {
class StringBuffer : public virtual Reader, public virtual Writer {
public:
static inline std::unique_ptr<StringBuffer> create();
inline bool write(const std::string& s);
inline std::string string() const;
// Reader / Writer compilance
inline bool isOpen() override;
inline void close() override;
inline size_t read(void* buffer, size_t bytes) override;
inline bool write(const void* buffer, size_t bytes) override;
private:
std::string str;
bool closed = false;
};
bool StringBuffer::isOpen() {
return !closed;
}
void StringBuffer::close() {
closed = true;
}
std::unique_ptr<StringBuffer> StringBuffer::create() {
return std::unique_ptr<StringBuffer>(new StringBuffer());
}
bool StringBuffer::write(const std::string& s) {
return write(s.data(), s.size());
}
std::string StringBuffer::string() const {
return str;
}
size_t StringBuffer::read(void* buffer, size_t bytes) {
if (closed || bytes == 0 || str.size() == 0) {
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<const char*>(buffer);
str.append(chars, chars + bytes);
return true;
}
} // namespace dap
#endif // dap_string_buffer_h

68
src/typeof.cpp Normal file
View File

@@ -0,0 +1,68 @@
// 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"
namespace dap {
const TypeInfo* TypeOf<boolean>::type() {
static BasicTypeInfo<boolean> typeinfo("boolean");
return &typeinfo;
}
const TypeInfo* TypeOf<string>::type() {
static BasicTypeInfo<string> typeinfo("string");
return &typeinfo;
}
const TypeInfo* TypeOf<integer>::type() {
static BasicTypeInfo<integer> typeinfo("integer");
return &typeinfo;
}
const TypeInfo* TypeOf<number>::type() {
static BasicTypeInfo<number> typeinfo("number");
return &typeinfo;
}
const TypeInfo* TypeOf<object>::type() {
static BasicTypeInfo<object> typeinfo("object");
return &typeinfo;
}
const TypeInfo* TypeOf<any>::type() {
static BasicTypeInfo<any> typeinfo("any");
return &typeinfo;
}
const TypeInfo* TypeOf<null>::type() {
struct TI : public TypeInfo {
inline std::string name() const { return "null"; }
inline size_t size() const { return sizeof(null); }
inline size_t alignment() const { return alignof(null); }
inline void construct(void* ptr) const { new (ptr) null(); }
inline void copyConstruct(void* dst, const void* src) const {
new (dst) null(*reinterpret_cast<const null*>(src));
}
inline void destruct(void* ptr) const {
reinterpret_cast<null*>(ptr)->~null();
}
inline bool deserialize(const Deserializer*, void*) const { return true; }
inline bool serialize(Serializer*, const void*) const { return true; }
};
static TI typeinfo;
return &typeinfo;
} // namespace dap
} // namespace dap

94
src/variant_test.cpp Normal file
View File

@@ -0,0 +1,94 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/variant.h"
#include "dap/typeof.h"
#include "dap/types.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct VariantTestObject {
dap::integer i;
dap::number n;
};
DAP_STRUCT_TYPEINFO(VariantTestObject,
"VariantTestObject",
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"));
} // namespace dap
TEST(Variant, EmptyConstruct) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant;
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
}
TEST(Variant, Boolean) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::boolean(true));
ASSERT_TRUE(variant.is<dap::boolean>());
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
}
TEST(Variant, Integer) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::integer(10));
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
}
TEST(Variant, TestObject) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::VariantTestObject{5, 3.0});
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
}
TEST(Variant, Assign) {
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
dap::integer(10));
variant = dap::integer(10);
ASSERT_TRUE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
variant = dap::boolean(true);
ASSERT_FALSE(variant.is<dap::integer>());
ASSERT_TRUE(variant.is<dap::boolean>());
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
variant = dap::VariantTestObject{5, 3.0};
ASSERT_FALSE(variant.is<dap::integer>());
ASSERT_FALSE(variant.is<dap::boolean>());
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
}
TEST(Variant, Accepts) {
using variant =
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject>;
ASSERT_TRUE(variant::accepts<dap::integer>());
ASSERT_TRUE(variant::accepts<dap::boolean>());
ASSERT_TRUE(variant::accepts<dap::VariantTestObject>());
ASSERT_FALSE(variant::accepts<dap::number>());
ASSERT_FALSE(variant::accepts<dap::string>());
}

1
third_party/googletest vendored Submodule

Submodule third_party/googletest added at 0a03480824

1
third_party/json vendored Submodule

Submodule third_party/json added at f272ad533d

View File

@@ -0,0 +1,653 @@
// 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"
)
var (
cache = flag.String("cache", "", "File cache of the .json schema")
)
const (
jsonURL = "https://raw.githubusercontent.com/microsoft/vscode-debugadapter-node/master/debugProtocol.json"
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
`
headerPrologue = commonPrologue + `
#ifndef dap_protocol_h
#define dap_protocol_h
#include "optional.h"
#include "typeinfo.h"
#include "typeof.h"
#include "variant.h"
#include <string>
#include <type_traits>
#include <vector>
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"`
}
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<any>", 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 cppStruct struct {
desc string
name string
typename string
base string
fields []cppField
deps []string
emit bool
typedefs []cppTypedef
ty structType
}
type cppTypedef struct {
from string
to string
}
func sanitize(s string) string {
s = strings.Trim(s, "_")
switch s {
case "default":
return "def"
default:
return s
}
}
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, ";")
}
// constructor
io.WriteString(w, "\n\n ")
io.WriteString(w, s.name)
io.WriteString(w, "();")
// destructor
io.WriteString(w, "\n ~")
io.WriteString(w, s.name)
io.WriteString(w, "();\n")
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) {
// constructor
io.WriteString(w, s.name)
io.WriteString(w, "::")
io.WriteString(w, s.name)
io.WriteString(w, "() = default;\n")
// destructor
io.WriteString(w, s.name)
io.WriteString(w, "::~")
io.WriteString(w, s.name)
io.WriteString(w, "() = default;\n")
// 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")
}
func buildStructs(r *root) ([]*cppStruct, error) {
ignore := map[string]bool{
// These are handled internally.
"ProtocolMessage": true,
"Request": true,
"Event": true,
"Response": true,
}
out := []*cppStruct{}
for _, entry := range r.definitions() {
defName, def := entry.name, entry.def
if ignore[defName] {
continue
}
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{"Response", response})
s.emit = true
s.ty = request
case "Response":
if body, ok := def.Properties["body"]; ok {
props, required, err = body.properties(r)
}
s.emit = true
s.ty = 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.ty = event
default:
props = def.Properties
required = def.Required
s.ty = 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 := property.Description
defaultValue := ""
if len(property.ClosedEnum) > 0 {
desc += "\n\nMust be one of the following enumeration values:\n"
for i, enum := range property.ClosedEnum {
if i > 0 {
desc += ", "
}
desc += "'" + enum + "'"
}
defaultValue = `"` + property.ClosedEnum[0] + `"`
}
if len(property.OpenEnum) > 0 {
desc += "\n\nMay be one of the following enumeration values:\n"
for i, enum := range property.OpenEnum {
if i > 0 {
desc += ", "
}
desc += "'" + enum + "'"
}
}
s.fields = append(s.fields, cppField{
desc: desc,
defaultValue: defaultValue,
ty: ty,
name: propName,
optional: optional,
})
return nil
}); err != nil {
return nil, err
}
out = append(out, &s)
}
return out, nil
}
type structType string
const (
request = structType("request")
response = structType("response")
event = structType("event")
types = structType("types")
)
type cppFilePaths map[structType]string
type cppFiles map[structType]*os.File
func run() error {
data, err := loadJSONFile()
if err != nil {
return err
}
r := root{}
d := json.NewDecoder(bytes.NewReader(data))
if err := d.Decode(&r); err != nil {
return err
}
hPath, cppPaths := outputPaths()
if err := emitFiles(&r, hPath, cppPaths); err != nil {
return err
}
if clangfmt, err := exec.LookPath("clang-format"); err == nil {
if err := exec.Command(clangfmt, "-i", hPath).Run(); err != nil {
return err
}
for _, p := range cppPaths {
if err := exec.Command(clangfmt, "-i", p).Run(); err != nil {
return err
}
}
}
return nil
}
func emitFiles(r *root, hPath string, cppPaths map[structType]string) error {
h, err := os.Create(hPath)
if err != nil {
return err
}
defer h.Close()
cppFiles := map[structType]*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(headerPrologue)
for _, f := range cppFiles {
f.WriteString(cppPrologue)
}
structs, err := buildStructs(r)
if err != nil {
return err
}
structsByName := map[string]*cppStruct{}
for _, s := range structs {
structsByName[s.name] = s
}
seen := map[string]bool{}
var emit func(*cppStruct)
emit = func(s *cppStruct) {
if seen[s.name] {
return
}
seen[s.name] = true
for _, dep := range s.deps {
emit(structsByName[dep])
}
s.writeHeader(h)
s.writeCPP(cppFiles[s.ty])
}
// emit message types.
// Referenced structs will be transitively emitted.
for _, s := range structs {
switch s.ty {
case request, response, event:
emit(s)
}
}
h.WriteString(headerEpilogue)
for _, f := range cppFiles {
f.WriteString(cppEpilogue)
}
return nil
}
func loadJSONFile() ([]byte, error) {
if *cache != "" {
data, err := ioutil.ReadFile(*cache)
if err == nil {
return data, nil
}
}
resp, err := http.Get(jsonURL)
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if *cache != "" {
ioutil.WriteFile(*cache, data, 0777)
}
return data, 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
}