Initial drop of cppdap
This commit is contained in:
126
src/any_test.cpp
Normal file
126
src/any_test.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "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
90
src/chan.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef dap_chan_h
|
||||
#define dap_chan_h
|
||||
|
||||
#include "dap/optional.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace dap {
|
||||
|
||||
template <typename T>
|
||||
struct Chan {
|
||||
public:
|
||||
void reset();
|
||||
void close();
|
||||
optional<T> take();
|
||||
void put(T&& in);
|
||||
void put(const T& in);
|
||||
|
||||
private:
|
||||
bool closed = false;
|
||||
std::queue<T> queue;
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::reset() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
queue = {};
|
||||
closed = false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::close() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
closed = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T> Chan<T>::take() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return queue.size() > 0 || closed; });
|
||||
if (queue.size() == 0) {
|
||||
return optional<T>();
|
||||
}
|
||||
auto out = std::move(queue.front());
|
||||
queue.pop();
|
||||
return optional<T>(std::move(out));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::put(T&& in) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
auto notify = queue.size() == 0 && !closed;
|
||||
queue.push(std::move(in));
|
||||
if (notify) {
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Chan<T>::put(const T& in) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
auto notify = queue.size() == 0 && !closed;
|
||||
queue.push(in);
|
||||
if (notify) {
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_chan_h
|
||||
35
src/chan_test.cpp
Normal file
35
src/chan_test.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "chan.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
TEST(ChanTest, PutTakeClose) {
|
||||
dap::Chan<int> chan;
|
||||
auto thread = std::thread([&] {
|
||||
chan.put(10);
|
||||
chan.put(20);
|
||||
chan.put(30);
|
||||
chan.close();
|
||||
});
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(10));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(20));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>(30));
|
||||
EXPECT_EQ(chan.take(), dap::optional<int>());
|
||||
thread.join();
|
||||
}
|
||||
179
src/content_stream.cpp
Normal file
179
src/content_stream.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#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
68
src/content_stream.h
Normal 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
|
||||
47
src/content_stream_test.cpp
Normal file
47
src/content_stream_test.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#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
21
src/dap_test.cpp
Normal 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
257
src/io.cpp
Normal 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
242
src/json_serializer.cpp
Normal 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
149
src/json_serializer.h
Normal 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
|
||||
93
src/json_serializer_test.cpp
Normal file
93
src/json_serializer_test.cpp
Normal 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
100
src/network.cpp
Normal 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
74
src/network_test.cpp
Normal 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
162
src/optional_test.cpp
Normal 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
114
src/protocol_events.cpp
Normal 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
321
src/protocol_requests.cpp
Normal 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
305
src/protocol_response.cpp
Normal 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
345
src/protocol_types.cpp
Normal 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
475
src/session.cpp
Normal 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
386
src/session_test.cpp
Normal 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
204
src/socket.cpp
Normal 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
43
src/socket.h
Normal 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
85
src/string_buffer.h
Normal 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
68
src/typeof.cpp
Normal 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
94
src/variant_test.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/variant.h"
|
||||
#include "dap/typeof.h"
|
||||
#include "dap/types.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct VariantTestObject {
|
||||
dap::integer i;
|
||||
dap::number n;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(VariantTestObject,
|
||||
"VariantTestObject",
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"));
|
||||
|
||||
} // namespace dap
|
||||
|
||||
TEST(Variant, EmptyConstruct) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant;
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
}
|
||||
|
||||
TEST(Variant, Boolean) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::boolean(true));
|
||||
ASSERT_TRUE(variant.is<dap::boolean>());
|
||||
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
|
||||
}
|
||||
|
||||
TEST(Variant, Integer) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::integer(10));
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
|
||||
}
|
||||
|
||||
TEST(Variant, TestObject) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::VariantTestObject{5, 3.0});
|
||||
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
|
||||
}
|
||||
|
||||
TEST(Variant, Assign) {
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject> variant(
|
||||
dap::integer(10));
|
||||
variant = dap::integer(10);
|
||||
ASSERT_TRUE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::integer>(), dap::integer(10));
|
||||
variant = dap::boolean(true);
|
||||
ASSERT_FALSE(variant.is<dap::integer>());
|
||||
ASSERT_TRUE(variant.is<dap::boolean>());
|
||||
ASSERT_FALSE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::boolean>(), dap::boolean(true));
|
||||
variant = dap::VariantTestObject{5, 3.0};
|
||||
ASSERT_FALSE(variant.is<dap::integer>());
|
||||
ASSERT_FALSE(variant.is<dap::boolean>());
|
||||
ASSERT_TRUE(variant.is<dap::VariantTestObject>());
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().i, 5);
|
||||
ASSERT_EQ(variant.get<dap::VariantTestObject>().n, 3.0);
|
||||
}
|
||||
|
||||
TEST(Variant, Accepts) {
|
||||
using variant =
|
||||
dap::variant<dap::integer, dap::boolean, dap::VariantTestObject>;
|
||||
ASSERT_TRUE(variant::accepts<dap::integer>());
|
||||
ASSERT_TRUE(variant::accepts<dap::boolean>());
|
||||
ASSERT_TRUE(variant::accepts<dap::VariantTestObject>());
|
||||
ASSERT_FALSE(variant::accepts<dap::number>());
|
||||
ASSERT_FALSE(variant::accepts<dap::string>());
|
||||
}
|
||||
Reference in New Issue
Block a user