Merge branch 'upstream-cppdap' into import-cppdap

* upstream-cppdap:
  cppdap 2023-05-26 (03cc1867)
This commit is contained in:
Glen Chung
2023-05-26 09:33:57 -04:00
committed by Brad King
54 changed files with 11861 additions and 0 deletions

1
Utilities/cmcppdap/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* -whitespace

202
Utilities/cmcppdap/LICENSE Normal file
View File

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

View File

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

View File

@@ -0,0 +1,35 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_dap_h
#define dap_dap_h
namespace dap {
// Explicit library initialization and termination functions.
//
// cppdap automatically initializes and terminates its internal state using lazy
// static initialization, and so will usually work fine without explicit calls
// to these functions.
// However, if you use cppdap types in global state, you may need to call these
// functions to ensure that cppdap is not uninitialized before the last usage.
//
// Each call to initialize() must have a corresponding call to terminate().
// It is undefined behaviour to call initialize() after terminate().
void initialize();
void terminate();
} // namespace dap
#endif // dap_dap_h

View File

@@ -0,0 +1,179 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_future_h
#define dap_future_h
#include <condition_variable>
#include <memory>
#include <mutex>
namespace dap {
// internal functionality
namespace detail {
template <typename T>
struct promise_state {
T val;
std::mutex mutex;
std::condition_variable cv;
bool hasVal = false;
};
} // namespace detail
// forward declaration
template <typename T>
class promise;
// future_status is the enumeration returned by future::wait_for and
// future::wait_until.
enum class future_status {
ready,
timeout,
};
// future is a minimal reimplementation of std::future, that does not suffer
// from TSAN false positives. See:
// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
template <typename T>
class future {
public:
using State = detail::promise_state<T>;
// constructors
inline future() = default;
inline future(future&&) = default;
// valid() returns true if the future has an internal state.
bool valid() const;
// get() blocks until the future has a valid result, and returns it.
// The future must have a valid internal state to call this method.
inline T get();
// wait() blocks until the future has a valid result.
// The future must have a valid internal state to call this method.
void wait() const;
// wait_for() blocks until the future has a valid result, or the timeout is
// reached.
// The future must have a valid internal state to call this method.
template <class Rep, class Period>
future_status wait_for(
const std::chrono::duration<Rep, Period>& timeout) const;
// wait_until() blocks until the future has a valid result, or the timeout is
// reached.
// The future must have a valid internal state to call this method.
template <class Clock, class Duration>
future_status wait_until(
const std::chrono::time_point<Clock, Duration>& timeout) const;
private:
friend promise<T>;
future(const future&) = delete;
inline future(const std::shared_ptr<State>& state);
std::shared_ptr<State> state = std::make_shared<State>();
};
template <typename T>
future<T>::future(const std::shared_ptr<State>& s) : state(s) {}
template <typename T>
bool future<T>::valid() const {
return static_cast<bool>(state);
}
template <typename T>
T future<T>::get() {
std::unique_lock<std::mutex> lock(state->mutex);
state->cv.wait(lock, [&] { return state->hasVal; });
return state->val;
}
template <typename T>
void future<T>::wait() const {
std::unique_lock<std::mutex> lock(state->mutex);
state->cv.wait(lock, [&] { return state->hasVal; });
}
template <typename T>
template <class Rep, class Period>
future_status future<T>::wait_for(
const std::chrono::duration<Rep, Period>& timeout) const {
std::unique_lock<std::mutex> lock(state->mutex);
return state->cv.wait_for(lock, timeout, [&] { return state->hasVal; })
? future_status::ready
: future_status::timeout;
}
template <typename T>
template <class Clock, class Duration>
future_status future<T>::wait_until(
const std::chrono::time_point<Clock, Duration>& timeout) const {
std::unique_lock<std::mutex> lock(state->mutex);
return state->cv.wait_until(lock, timeout, [&] { return state->hasVal; })
? future_status::ready
: future_status::timeout;
}
// promise is a minimal reimplementation of std::promise, that does not suffer
// from TSAN false positives. See:
// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69204
template <typename T>
class promise {
public:
// constructors
inline promise() = default;
inline promise(promise&& other) = default;
inline promise(const promise& other) = default;
// set_value() stores value to the shared state.
// set_value() must only be called once.
inline void set_value(const T& value) const;
inline void set_value(T&& value) const;
// get_future() returns a future sharing this promise's state.
future<T> get_future();
private:
using State = detail::promise_state<T>;
std::shared_ptr<State> state = std::make_shared<State>();
};
template <typename T>
future<T> promise<T>::get_future() {
return future<T>(state);
}
template <typename T>
void promise<T>::set_value(const T& value) const {
std::unique_lock<std::mutex> lock(state->mutex);
state->val = value;
state->hasVal = true;
state->cv.notify_all();
}
template <typename T>
void promise<T>::set_value(T&& value) const {
std::unique_lock<std::mutex> lock(state->mutex);
state->val = std::move(value);
state->hasVal = true;
state->cv.notify_all();
}
} // namespace dap
#endif // dap_future_h

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_serialization_h
#define dap_serialization_h
#include "typeof.h"
#include "types.h"
#include <cstddef> // ptrdiff_t
#include <type_traits>
namespace dap {
// Field describes a single field of a struct.
struct Field {
std::string name; // name of the field
ptrdiff_t offset; // offset of the field to the base of the struct
const TypeInfo* type; // type of the field
};
////////////////////////////////////////////////////////////////////////////////
// Deserializer
////////////////////////////////////////////////////////////////////////////////
// Deserializer is the interface used to decode data from structured storage.
// Methods that return a bool use this to indicate success.
class Deserializer {
public:
virtual ~Deserializer() = default;
// deserialization methods for simple data types.
// If the stored object is not of the correct type, then these function will
// return false.
virtual bool deserialize(boolean*) const = 0;
virtual bool deserialize(integer*) const = 0;
virtual bool deserialize(number*) const = 0;
virtual bool deserialize(string*) const = 0;
virtual bool deserialize(object*) const = 0;
virtual bool deserialize(any*) const = 0;
// count() returns the number of elements in the array object referenced by
// this Deserializer.
virtual size_t count() const = 0;
// array() calls the provided std::function for deserializing each array
// element in the array object referenced by this Deserializer.
virtual bool array(const std::function<bool(Deserializer*)>&) const = 0;
// field() calls the provided std::function for deserializing the field with
// the given name from the struct object referenced by this Deserializer.
virtual bool field(const std::string& name,
const std::function<bool(Deserializer*)>&) const = 0;
// deserialize() delegates to TypeOf<T>::type()->deserialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T*) const;
// deserialize() decodes an array.
template <typename T>
inline bool deserialize(dap::array<T>*) const;
// deserialize() decodes an optional.
template <typename T>
inline bool deserialize(dap::optional<T>*) const;
// deserialize() decodes an variant.
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>*) const;
// deserialize() decodes the struct field f with the given name.
template <typename T>
inline bool field(const std::string& name, T* f) const;
};
template <typename T, typename>
bool Deserializer::deserialize(T* ptr) const {
return TypeOf<T>::type()->deserialize(this, ptr);
}
template <typename T>
bool Deserializer::deserialize(dap::array<T>* vec) const {
auto n = count();
vec->resize(n);
size_t i = 0;
if (!array([&](Deserializer* d) { return d->deserialize(&(*vec)[i++]); })) {
return false;
}
return true;
}
template <typename T>
bool Deserializer::deserialize(dap::optional<T>* opt) const {
T v;
if (deserialize(&v)) {
*opt = v;
}
return true;
}
template <typename T0, typename... Types>
bool Deserializer::deserialize(dap::variant<T0, Types...>* var) const {
return deserialize(&var->value);
}
template <typename T>
bool Deserializer::field(const std::string& name, T* v) const {
return this->field(name,
[&](const Deserializer* d) { return d->deserialize(v); });
}
////////////////////////////////////////////////////////////////////////////////
// Serializer
////////////////////////////////////////////////////////////////////////////////
class FieldSerializer;
// Serializer is the interface used to encode data to structured storage.
// A Serializer is associated with a single storage object, whos type and value
// is assigned by a call to serialize().
// If serialize() is called multiple times on the same Serializer instance,
// the last type and value is stored.
// Methods that return a bool use this to indicate success.
class Serializer {
public:
virtual ~Serializer() = default;
// serialization methods for simple data types.
virtual bool serialize(boolean) = 0;
virtual bool serialize(integer) = 0;
virtual bool serialize(number) = 0;
virtual bool serialize(const string&) = 0;
virtual bool serialize(const dap::object&) = 0;
virtual bool serialize(const any&) = 0;
// array() encodes count array elements to the array object referenced by this
// Serializer. The std::function will be called count times, each time with a
// Serializer that should be used to encode the n'th array element's data.
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
// object() begins encoding the object referenced by this Serializer.
// The std::function will be called with a FieldSerializer to serialize the
// object's fields.
virtual bool object(const std::function<bool(dap::FieldSerializer*)>&) = 0;
// remove() deletes the object referenced by this Serializer.
// remove() can be used to serialize optionals with no value assigned.
virtual void remove() = 0;
// serialize() delegates to TypeOf<T>::type()->serialize().
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T&);
// serialize() encodes the given array.
template <typename T>
inline bool serialize(const dap::array<T>&);
// serialize() encodes the given optional.
template <typename T>
inline bool serialize(const dap::optional<T>& v);
// serialize() encodes the given variant.
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>&);
// deserialize() encodes the given string.
inline bool serialize(const char* v);
protected:
static inline const TypeInfo* get_any_type(const any&);
static inline const void* get_any_val(const any&);
};
inline const TypeInfo* Serializer::get_any_type(const any& a){
return a.type;
}
const void* Serializer::get_any_val(const any& a) {
return a.value;
}
template <typename T, typename>
bool Serializer::serialize(const T& object) {
return TypeOf<T>::type()->serialize(this, &object);
}
template <typename T>
bool Serializer::serialize(const dap::array<T>& vec) {
auto it = vec.begin();
return array(vec.size(), [&](Serializer* s) { return s->serialize(*it++); });
}
template <typename T>
bool Serializer::serialize(const dap::optional<T>& opt) {
if (!opt.has_value()) {
remove();
return true;
}
return serialize(opt.value());
}
template <typename T0, typename... Types>
bool Serializer::serialize(const dap::variant<T0, Types...>& var) {
return serialize(var.value);
}
bool Serializer::serialize(const char* v) {
return serialize(std::string(v));
}
////////////////////////////////////////////////////////////////////////////////
// FieldSerializer
////////////////////////////////////////////////////////////////////////////////
// FieldSerializer is the interface used to serialize fields of an object.
class FieldSerializer {
public:
using SerializeFunc = std::function<bool(Serializer*)>;
template <typename T>
using IsSerializeFunc = std::is_convertible<T, SerializeFunc>;
virtual ~FieldSerializer() = default;
// field() encodes a field to the struct object referenced by this Serializer.
// The SerializeFunc will be called with a Serializer used to encode the
// field's data.
virtual bool field(const std::string& name, const SerializeFunc&) = 0;
// field() encodes the field with the given name and value.
template <
typename T,
typename = typename std::enable_if<!IsSerializeFunc<T>::value>::type>
inline bool field(const std::string& name, const T& v);
};
template <typename T, typename>
bool FieldSerializer::field(const std::string& name, const T& v) {
return this->field(name, [&](Serializer* s) { return s->serialize(v); });
}
} // namespace dap
#endif // dap_serialization_h

View File

@@ -0,0 +1,449 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_session_h
#define dap_session_h
#include "future.h"
#include "io.h"
#include "traits.h"
#include "typeinfo.h"
#include "typeof.h"
#include <functional>
namespace dap {
// Forward declarations
struct Request;
struct Response;
struct Event;
////////////////////////////////////////////////////////////////////////////////
// Error
////////////////////////////////////////////////////////////////////////////////
// Error represents an error message in response to a DAP request.
struct Error {
Error() = default;
Error(const std::string& error);
Error(const char* msg, ...);
// operator bool() returns true if there is an error.
inline operator bool() const { return message.size() > 0; }
std::string message; // empty represents success.
};
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError<T>
////////////////////////////////////////////////////////////////////////////////
// ResponseOrError holds either the response to a DAP request or an error
// message.
template <typename T>
struct ResponseOrError {
using Request = T;
inline ResponseOrError() = default;
inline ResponseOrError(const T& response);
inline ResponseOrError(T&& response);
inline ResponseOrError(const Error& error);
inline ResponseOrError(Error&& error);
inline ResponseOrError(const ResponseOrError& other);
inline ResponseOrError(ResponseOrError&& other);
inline ResponseOrError& operator=(const ResponseOrError& other);
inline ResponseOrError& operator=(ResponseOrError&& other);
T response;
Error error; // empty represents success.
};
template <typename T>
ResponseOrError<T>::ResponseOrError(const T& resp) : response(resp) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(T&& resp) : response(std::move(resp)) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const Error& err) : error(err) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(Error&& err) : error(std::move(err)) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(const ResponseOrError& other)
: response(other.response), error(other.error) {}
template <typename T>
ResponseOrError<T>::ResponseOrError(ResponseOrError&& other)
: response(std::move(other.response)), error(std::move(other.error)) {}
template <typename T>
ResponseOrError<T>& ResponseOrError<T>::operator=(
const ResponseOrError& other) {
response = other.response;
error = other.error;
return *this;
}
template <typename T>
ResponseOrError<T>& ResponseOrError<T>::operator=(ResponseOrError&& other) {
response = std::move(other.response);
error = std::move(other.error);
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// Session
////////////////////////////////////////////////////////////////////////////////
// Session implements a DAP client or server endpoint.
// The general usage is as follows:
// (1) Create a session with Session::create().
// (2) Register request and event handlers with registerHandler().
// (3) Optionally register a protocol error handler with onError().
// (3) Bind the session to the remote endpoint with bind().
// (4) Send requests or events with send().
class Session {
template <typename F, int N>
using ParamType = traits::ParameterType<F, N>;
template <typename T>
using IsRequest = traits::EnableIfIsType<dap::Request, T>;
template <typename T>
using IsEvent = traits::EnableIfIsType<dap::Event, T>;
template <typename F>
using IsRequestHandlerWithoutCallback = traits::EnableIf<
traits::CompatibleWith<F, std::function<void(dap::Request)>>::value>;
template <typename F, typename CallbackType>
using IsRequestHandlerWithCallback = traits::EnableIf<traits::CompatibleWith<
F,
std::function<void(dap::Request, std::function<void(CallbackType)>)>>::
value>;
public:
virtual ~Session();
// ErrorHandler is the type of callback function used for reporting protocol
// errors.
using ErrorHandler = std::function<void(const char*)>;
// ClosedHandler is the type of callback function used to signal that a
// connected endpoint has closed.
using ClosedHandler = std::function<void()>;
// create() constructs and returns a new Session.
static std::unique_ptr<Session> create();
// onError() registers a error handler that will be called whenever a protocol
// error is encountered.
// Only one error handler can be bound at any given time, and later calls
// will replace the existing error handler.
virtual void onError(const ErrorHandler&) = 0;
// registerHandler() registers a request handler for a specific request type.
// The function F must have one of the following signatures:
// ResponseOrError<ResponseType>(const RequestType&)
// ResponseType(const RequestType&)
// Error(const RequestType&)
template <typename F, typename RequestType = ParamType<F, 0>>
inline IsRequestHandlerWithoutCallback<F> registerHandler(F&& handler);
// registerHandler() registers a request handler for a specific request type.
// The handler has a response callback function for the second argument of the
// handler function. This callback may be called after the handler has
// returned.
// The function F must have the following signature:
// void(const RequestType& request,
// std::function<void(ResponseType)> response)
template <typename F,
typename RequestType = ParamType<F, 0>,
typename ResponseType = typename RequestType::Response>
inline IsRequestHandlerWithCallback<F, ResponseType> registerHandler(
F&& handler);
// registerHandler() registers a request handler for a specific request type.
// The handler has a response callback function for the second argument of the
// handler function. This callback may be called after the handler has
// returned.
// The function F must have the following signature:
// void(const RequestType& request,
// std::function<void(ResponseOrError<ResponseType>)> response)
template <typename F,
typename RequestType = ParamType<F, 0>,
typename ResponseType = typename RequestType::Response>
inline IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
registerHandler(F&& handler);
// registerHandler() registers a event handler for a specific event type.
// The function F must have the following signature:
// void(const EventType&)
template <typename F, typename EventType = ParamType<F, 0>>
inline IsEvent<EventType> registerHandler(F&& handler);
// registerSentHandler() registers the function F to be called when a response
// of the specific type has been sent.
// The function F must have the following signature:
// void(const ResponseOrError<ResponseType>&)
template <typename F,
typename ResponseType = typename ParamType<F, 0>::Request>
inline void registerSentHandler(F&& handler);
// send() sends the request to the connected endpoint and returns a
// future that is assigned the request response or error.
template <typename T, typename = IsRequest<T>>
future<ResponseOrError<typename T::Response>> send(const T& request);
// send() sends the event to the connected endpoint.
template <typename T, typename = IsEvent<T>>
void send(const T& event);
// bind() connects this Session to an endpoint using connect(), and then
// starts processing incoming messages with startProcessingMessages().
// onClose is the optional callback which will be called when the session
// endpoint has been closed.
inline void bind(const std::shared_ptr<Reader>& reader,
const std::shared_ptr<Writer>& writer,
const ClosedHandler& onClose);
inline void bind(const std::shared_ptr<ReaderWriter>& readerWriter,
const ClosedHandler& onClose);
//////////////////////////////////////////////////////////////////////////////
// Note:
// Methods and members below this point are for advanced usage, and are more
// likely to change signature than the methods above.
// The methods above this point should be sufficient for most use cases.
//////////////////////////////////////////////////////////////////////////////
// connect() connects this Session to an endpoint.
// connect() can only be called once. Repeated calls will raise an error, but
// otherwise will do nothing.
// Note: This method is used for explicit control over message handling.
// Most users will use bind() instead of calling this method directly.
virtual void connect(const std::shared_ptr<Reader>&,
const std::shared_ptr<Writer>&) = 0;
inline void connect(const std::shared_ptr<ReaderWriter>&);
// startProcessingMessages() starts a new thread to receive and dispatch
// incoming messages.
// onClose is the optional callback which will be called when the session
// endpoint has been closed.
// Note: This method is used for explicit control over message handling.
// Most users will use bind() instead of calling this method directly.
virtual void startProcessingMessages(const ClosedHandler& onClose = {}) = 0;
// getPayload() blocks until the next incoming message is received, returning
// the payload or an empty function if the connection was lost. The returned
// payload is function that can be called on any thread to dispatch the
// message to the Session handler.
// Note: This method is used for explicit control over message handling.
// Most users will use bind() instead of calling this method directly.
virtual std::function<void()> getPayload() = 0;
// The callback function type called when a request handler is invoked, and
// the request returns a successful result.
// 'responseTypeInfo' is the type information of the response data structure.
// 'responseData' is a pointer to response payload data.
using RequestHandlerSuccessCallback =
std::function<void(const TypeInfo* responseTypeInfo,
const void* responseData)>;
// The callback function type used to notify when a DAP request fails.
// 'responseTypeInfo' is the type information of the response data structure.
// 'message' is the error message
using RequestHandlerErrorCallback =
std::function<void(const TypeInfo* responseTypeInfo,
const Error& message)>;
// The callback function type used to invoke a request handler.
// 'request' is a pointer to the request data structure
// 'onSuccess' is the function to call if the request completed succesfully.
// 'onError' is the function to call if the request failed.
// For each call of the request handler, 'onSuccess' or 'onError' must be
// called exactly once.
using GenericRequestHandler =
std::function<void(const void* request,
const RequestHandlerSuccessCallback& onSuccess,
const RequestHandlerErrorCallback& onError)>;
// The callback function type used to handle a response to a request.
// 'response' is a pointer to the response data structure. May be nullptr.
// 'error' is a pointer to the reponse error message. May be nullptr.
// One of 'data' or 'error' will be nullptr.
using GenericResponseHandler =
std::function<void(const void* response, const Error* error)>;
// The callback function type used to handle an event.
// 'event' is a pointer to the event data structure.
using GenericEventHandler = std::function<void(const void* event)>;
// The callback function type used to notify when a response has been sent
// from this session endpoint.
// 'response' is a pointer to the response data structure.
// 'error' is a pointer to the reponse error message. May be nullptr.
using GenericResponseSentHandler =
std::function<void(const void* response, const Error* error)>;
// registerHandler() registers 'handler' as the request handler callback for
// requests of the type 'typeinfo'.
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericRequestHandler& handler) = 0;
// registerHandler() registers 'handler' as the event handler callback for
// events of the type 'typeinfo'.
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericEventHandler& handler) = 0;
// registerHandler() registers 'handler' as the response-sent handler function
// which is called whenever a response of the type 'typeinfo' is sent from
// this session endpoint.
virtual void registerHandler(const TypeInfo* typeinfo,
const GenericResponseSentHandler& handler) = 0;
// send() sends a request to the remote endpoint.
// 'requestTypeInfo' is the type info of the request data structure.
// 'requestTypeInfo' is the type info of the response data structure.
// 'request' is a pointer to the request data structure.
// 'responseHandler' is the handler function for the response.
virtual bool send(const dap::TypeInfo* requestTypeInfo,
const dap::TypeInfo* responseTypeInfo,
const void* request,
const GenericResponseHandler& responseHandler) = 0;
// send() sends an event to the remote endpoint.
// 'eventTypeInfo' is the type info for the event data structure.
// 'event' is a pointer to the event data structure.
virtual bool send(const TypeInfo* eventTypeInfo, const void* event) = 0;
};
template <typename F, typename RequestType>
Session::IsRequestHandlerWithoutCallback<F> Session::registerHandler(
F&& handler) {
using ResponseType = typename RequestType::Response;
const TypeInfo* typeinfo = TypeOf<RequestType>::type();
registerHandler(typeinfo,
[handler](const void* args,
const RequestHandlerSuccessCallback& onSuccess,
const RequestHandlerErrorCallback& onError) {
ResponseOrError<ResponseType> res =
handler(*reinterpret_cast<const RequestType*>(args));
if (res.error) {
onError(TypeOf<ResponseType>::type(), res.error);
} else {
onSuccess(TypeOf<ResponseType>::type(), &res.response);
}
});
}
template <typename F, typename RequestType, typename ResponseType>
Session::IsRequestHandlerWithCallback<F, ResponseType> Session::registerHandler(
F&& handler) {
using CallbackType = ParamType<F, 1>;
registerHandler(
TypeOf<RequestType>::type(),
[handler](const void* args,
const RequestHandlerSuccessCallback& onSuccess,
const RequestHandlerErrorCallback&) {
CallbackType responseCallback = [onSuccess](const ResponseType& res) {
onSuccess(TypeOf<ResponseType>::type(), &res);
};
handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
});
}
template <typename F, typename RequestType, typename ResponseType>
Session::IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
Session::registerHandler(F&& handler) {
using CallbackType = ParamType<F, 1>;
registerHandler(
TypeOf<RequestType>::type(),
[handler](const void* args,
const RequestHandlerSuccessCallback& onSuccess,
const RequestHandlerErrorCallback& onError) {
CallbackType responseCallback =
[onError, onSuccess](const ResponseOrError<ResponseType>& res) {
if (res.error) {
onError(TypeOf<ResponseType>::type(), res.error);
} else {
onSuccess(TypeOf<ResponseType>::type(), &res.response);
}
};
handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
});
}
template <typename F, typename T>
Session::IsEvent<T> Session::registerHandler(F&& handler) {
auto cb = [handler](const void* args) {
handler(*reinterpret_cast<const T*>(args));
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename F, typename T>
void Session::registerSentHandler(F&& handler) {
auto cb = [handler](const void* response, const Error* error) {
if (error != nullptr) {
handler(ResponseOrError<T>(*error));
} else {
handler(ResponseOrError<T>(*reinterpret_cast<const T*>(response)));
}
};
const TypeInfo* typeinfo = TypeOf<T>::type();
registerHandler(typeinfo, cb);
}
template <typename T, typename>
future<ResponseOrError<typename T::Response>> Session::send(const T& request) {
using Response = typename T::Response;
promise<ResponseOrError<Response>> promise;
auto sent = send(TypeOf<T>::type(), TypeOf<Response>::type(), &request,
[=](const void* result, const Error* error) {
if (error != nullptr) {
promise.set_value(ResponseOrError<Response>(*error));
} else {
promise.set_value(ResponseOrError<Response>(
*reinterpret_cast<const Response*>(result)));
}
});
if (!sent) {
promise.set_value(Error("Failed to send request"));
}
return promise.get_future();
}
template <typename T, typename>
void Session::send(const T& event) {
const TypeInfo* typeinfo = TypeOf<T>::type();
send(typeinfo, &event);
}
void Session::connect(const std::shared_ptr<ReaderWriter>& rw) {
connect(rw, rw);
}
void Session::bind(const std::shared_ptr<dap::Reader>& r,
const std::shared_ptr<dap::Writer>& w,
const ClosedHandler& onClose = {}) {
connect(r, w);
startProcessingMessages(onClose);
}
void Session::bind(const std::shared_ptr<ReaderWriter>& rw,
const ClosedHandler& onClose = {}) {
bind(rw, rw, onClose);
}
} // namespace dap
#endif // dap_session_h

View File

@@ -0,0 +1,159 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_traits_h
#define dap_traits_h
#include <tuple>
#include <type_traits>
namespace dap {
namespace traits {
// NthTypeOf returns the `N`th type in `Types`
template <int N, typename... Types>
using NthTypeOf = typename std::tuple_element<N, std::tuple<Types...>>::type;
// `IsTypeOrDerived<BASE, T>::value` is true iff `T` is of type `BASE`, or
// derives from `BASE`.
template <typename BASE, typename T>
using IsTypeOrDerived = std::integral_constant<
bool,
std::is_base_of<BASE, typename std::decay<T>::type>::value ||
std::is_same<BASE, typename std::decay<T>::type>::value>;
// `EachIsTypeOrDerived<N, BASES, TYPES>::value` is true iff all of the types in
// the std::tuple `TYPES` is of, or derives from the corresponding indexed type
// in the std::tuple `BASES`.
// `N` must be equal to the number of types in both the std::tuple `BASES` and
// `TYPES`.
template <int N, typename BASES, typename TYPES>
struct EachIsTypeOrDerived {
using base = typename std::tuple_element<N - 1, BASES>::type;
using type = typename std::tuple_element<N - 1, TYPES>::type;
using last_matches = IsTypeOrDerived<base, type>;
using others_match = EachIsTypeOrDerived<N - 1, BASES, TYPES>;
static constexpr bool value = last_matches::value && others_match::value;
};
// EachIsTypeOrDerived specialization for N = 1
template <typename BASES, typename TYPES>
struct EachIsTypeOrDerived<1, BASES, TYPES> {
using base = typename std::tuple_element<0, BASES>::type;
using type = typename std::tuple_element<0, TYPES>::type;
static constexpr bool value = IsTypeOrDerived<base, type>::value;
};
// EachIsTypeOrDerived specialization for N = 0
template <typename BASES, typename TYPES>
struct EachIsTypeOrDerived<0, BASES, TYPES> {
static constexpr bool value = true;
};
// Signature describes the signature of a function.
template <typename RETURN, typename... PARAMETERS>
struct Signature {
// The return type of the function signature
using ret = RETURN;
// The parameters of the function signature held in a std::tuple
using parameters = std::tuple<PARAMETERS...>;
// The type of the Nth parameter of function signature
template <std::size_t N>
using parameter = NthTypeOf<N, PARAMETERS...>;
// The total number of parameters
static constexpr std::size_t parameter_count = sizeof...(PARAMETERS);
};
// SignatureOf is a traits helper that infers the signature of the function,
// method, static method, lambda, or function-like object `F`.
template <typename F>
struct SignatureOf {
// The signature of the function-like object `F`
using type = typename SignatureOf<decltype(&F::operator())>::type;
};
// SignatureOf specialization for a regular function or static method.
template <typename R, typename... ARGS>
struct SignatureOf<R (*)(ARGS...)> {
// The signature of the function-like object `F`
using type = Signature<typename std::decay<R>::type,
typename std::decay<ARGS>::type...>;
};
// SignatureOf specialization for a non-static method.
template <typename R, typename C, typename... ARGS>
struct SignatureOf<R (C::*)(ARGS...)> {
// The signature of the function-like object `F`
using type = Signature<typename std::decay<R>::type,
typename std::decay<ARGS>::type...>;
};
// SignatureOf specialization for a non-static, const method.
template <typename R, typename C, typename... ARGS>
struct SignatureOf<R (C::*)(ARGS...) const> {
// The signature of the function-like object `F`
using type = Signature<typename std::decay<R>::type,
typename std::decay<ARGS>::type...>;
};
// SignatureOfT is an alias to `typename SignatureOf<F>::type`.
template <typename F>
using SignatureOfT = typename SignatureOf<F>::type;
// ParameterType is an alias to `typename SignatureOf<F>::type::parameter<N>`.
template <typename F, std::size_t N>
using ParameterType = typename SignatureOfT<F>::template parameter<N>;
// `HasSignature<F, S>::value` is true iff the function-like `F` has a matching
// signature to the function-like `S`.
template <typename F, typename S>
using HasSignature = std::integral_constant<
bool,
std::is_same<SignatureOfT<F>, SignatureOfT<S>>::value>;
// `Min<A, B>::value` resolves to the smaller value of A and B.
template <std::size_t A, std::size_t B>
using Min = std::integral_constant<std::size_t, (A < B ? A : B)>;
// `CompatibleWith<F, S>::value` is true iff the function-like `F`
// can be called with the argument types of the function-like `S`. Return type
// of the two functions are not considered.
template <typename F, typename S>
using CompatibleWith = std::integral_constant<
bool,
(SignatureOfT<S>::parameter_count == SignatureOfT<F>::parameter_count) &&
EachIsTypeOrDerived<Min<SignatureOfT<S>::parameter_count,
SignatureOfT<F>::parameter_count>::value,
typename SignatureOfT<S>::parameters,
typename SignatureOfT<F>::parameters>::value>;
// If `CONDITION` is true then EnableIf resolves to type T, otherwise an
// invalid type.
template <bool CONDITION, typename T = void>
using EnableIf = typename std::enable_if<CONDITION, T>::type;
// If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE_TY`,
// otherwise an invalid type.
template <typename BASE, typename T, typename TRUE_TY = void>
using EnableIfIsType = EnableIf<IsTypeOrDerived<BASE, T>::value, TRUE_TY>;
// If the function-like `F` has a matching signature to the function-like `S`
// then EnableIfHasSignature resolves to type `TRUE_TY`, otherwise an invalid type.
template <typename F, typename S, typename TRUE_TY = void>
using EnableIfHasSignature = EnableIf<HasSignature<F, S>::value, TRUE_TY>;
} // namespace traits
} // namespace dap
#endif // dap_traits_h

View File

@@ -0,0 +1,59 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeinfo_h
#define dap_typeinfo_h
#include <functional>
#include <string>
namespace dap {
class any;
class Deserializer;
class Serializer;
// The TypeInfo interface provides basic runtime type information about DAP
// types. TypeInfo is used by the serialization system to encode and decode DAP
// requests, responses, events and structs.
struct TypeInfo {
virtual ~TypeInfo();
virtual std::string name() const = 0;
virtual size_t size() const = 0;
virtual size_t alignment() const = 0;
virtual void construct(void*) const = 0;
virtual void copyConstruct(void* dst, const void* src) const = 0;
virtual void destruct(void*) const = 0;
virtual bool deserialize(const Deserializer*, void*) const = 0;
virtual bool serialize(Serializer*, const void*) const = 0;
// create() allocates and constructs the TypeInfo of type T, registers the
// pointer for deletion on cppdap library termination, and returns the pointer
// to T.
template <typename T, typename... ARGS>
static T* create(ARGS&&... args) {
auto typeinfo = new T(std::forward<ARGS>(args)...);
deleteOnExit(typeinfo);
return typeinfo;
}
private:
// deleteOnExit() ensures that the TypeInfo is destructed and deleted on
// library termination.
static void deleteOnExit(TypeInfo*);
};
} // namespace dap
#endif // dap_typeinfo_h

View File

@@ -0,0 +1,266 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_typeof_h
#define dap_typeof_h
#include "typeinfo.h"
#include "types.h"
#include "serialization.h"
namespace dap {
// BasicTypeInfo is an implementation of the TypeInfo interface for the simple
// template type T.
template <typename T>
struct BasicTypeInfo : public TypeInfo {
constexpr BasicTypeInfo(std::string&& name) : name_(std::move(name)) {}
// TypeInfo compliance
inline std::string name() const override { return name_; }
inline size_t size() const override { return sizeof(T); }
inline size_t alignment() const override { return alignof(T); }
inline void construct(void* ptr) const override { new (ptr) T(); }
inline void copyConstruct(void* dst, const void* src) const override {
new (dst) T(*reinterpret_cast<const T*>(src));
}
inline void destruct(void* ptr) const override {
reinterpret_cast<T*>(ptr)->~T();
}
inline bool deserialize(const Deserializer* d, void* ptr) const override {
return d->deserialize(reinterpret_cast<T*>(ptr));
}
inline bool serialize(Serializer* s, const void* ptr) const override {
return s->serialize(*reinterpret_cast<const T*>(ptr));
}
private:
std::string name_;
};
// TypeOf has a template specialization for each DAP type, each declaring a
// const TypeInfo* type() static member function that describes type T.
template <typename T>
struct TypeOf {};
template <>
struct TypeOf<boolean> {
static const TypeInfo* type();
};
template <>
struct TypeOf<string> {
static const TypeInfo* type();
};
template <>
struct TypeOf<integer> {
static const TypeInfo* type();
};
template <>
struct TypeOf<number> {
static const TypeInfo* type();
};
template <>
struct TypeOf<object> {
static const TypeInfo* type();
};
template <>
struct TypeOf<any> {
static const TypeInfo* type();
};
template <>
struct TypeOf<null> {
static const TypeInfo* type();
};
template <typename T>
struct TypeOf<array<T>> {
static inline const TypeInfo* type() {
static auto typeinfo = TypeInfo::create<BasicTypeInfo<array<T>>>(
"array<" + TypeOf<T>::type()->name() + ">");
return typeinfo;
}
};
template <typename T0, typename... Types>
struct TypeOf<variant<T0, Types...>> {
static inline const TypeInfo* type() {
static auto typeinfo =
TypeInfo::create<BasicTypeInfo<variant<T0, Types...>>>("variant");
return typeinfo;
}
};
template <typename T>
struct TypeOf<optional<T>> {
static inline const TypeInfo* type() {
static auto typeinfo = TypeInfo::create<BasicTypeInfo<optional<T>>>(
"optional<" + TypeOf<T>::type()->name() + ">");
return typeinfo;
}
};
// DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in
// <cstddef>. It evaluates to the offset of the given field, with fewer
// restrictions than offsetof(). We cast the address '32' and subtract it again,
// because null-dereference is undefined behavior.
#define DAP_OFFSETOF(s, m) \
((int)(size_t) & reinterpret_cast<const volatile char&>((((s*)32)->m)) - 32)
// internal functionality
namespace detail {
template <class T, class M>
M member_type(M T::*);
} // namespace detail
// DAP_TYPEOF() returns the type of the struct (s) member (m).
#define DAP_TYPEOF(s, m) decltype(detail::member_type(&s::m))
// DAP_FIELD() declares a structure field for the DAP_IMPLEMENT_STRUCT_TYPEINFO
// macro.
// FIELD is the name of the struct field.
// NAME is the serialized name of the field, as described by the DAP
// specification.
#define DAP_FIELD(FIELD, NAME) \
::dap::Field { \
NAME, DAP_OFFSETOF(StructTy, FIELD), \
TypeOf<DAP_TYPEOF(StructTy, FIELD)>::type(), \
}
// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT.
// Must be used within the 'dap' namespace.
#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \
template <> \
struct TypeOf<STRUCT> { \
static constexpr bool has_custom_serialization = true; \
static const TypeInfo* type(); \
static bool deserializeFields(const Deserializer*, void* obj); \
static bool serializeFields(FieldSerializer*, const void* obj); \
}
// DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() implements the deserializeFields()
// and serializeFields() static methods of a TypeOf<> specialization. Used
// internally by DAP_IMPLEMENT_STRUCT_TYPEINFO() and
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT().
// You probably do not want to use this directly.
#define DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, ...) \
bool TypeOf<STRUCT>::deserializeFields(const Deserializer* fd, void* obj) { \
using StructTy = STRUCT; \
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
for (auto field : std::initializer_list<Field>{__VA_ARGS__}) { \
if (!fd->field(field.name, [&](Deserializer* d) { \
auto ptr = reinterpret_cast<uint8_t*>(obj) + field.offset; \
return field.type->deserialize(d, ptr); \
})) { \
return false; \
} \
} \
return true; \
} \
bool TypeOf<STRUCT>::serializeFields(FieldSerializer* fs, const void* obj) {\
using StructTy = STRUCT; \
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
for (auto field : std::initializer_list<Field>{__VA_ARGS__}) { \
if (!fs->field(field.name, [&](Serializer* s) { \
auto ptr = reinterpret_cast<const uint8_t*>(obj) + field.offset; \
return field.type->serialize(s, ptr); \
})) { \
return false; \
} \
} \
return true; \
}
// DAP_IMPLEMENT_STRUCT_TYPEINFO() implements the type() member function for the
// TypeOf<> specialization for STRUCT.
// STRUCT is the structure typename.
// NAME is the serialized name of the structure, as described by the DAP
// specification. The variadic (...) parameters should be a repeated list of
// DAP_FIELD()s, one for each field of the struct.
// Must be used within the 'dap' namespace.
#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
struct TI : BasicTypeInfo<STRUCT> { \
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
bool deserialize(const Deserializer* d, void* obj) const override { \
return deserializeFields(d, obj); \
} \
bool serialize(Serializer* s, const void* obj) const override { \
return s->object( \
[&](FieldSerializer* fs) { return serializeFields(fs, obj); }); \
} \
}; \
static TI typeinfo; \
return &typeinfo; \
}
// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<>
// specialization for STRUCT in a single statement.
// Must be used within the 'dap' namespace.
#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__)
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT() implements the type() member function for
// the TypeOf<> specialization for STRUCT that derives from BASE.
// STRUCT is the structure typename.
// BASE is the base structure typename.
// NAME is the serialized name of the structure, as described by the DAP
// specification. The variadic (...) parameters should be a repeated list of
// DAP_FIELD()s, one for each field of the struct.
// Must be used within the 'dap' namespace.
#define DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
static_assert(std::is_base_of<BASE, STRUCT>::value, \
#STRUCT " does not derive from " #BASE); \
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
struct TI : BasicTypeInfo<STRUCT> { \
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
bool deserialize(const Deserializer* d, void* obj) const override { \
auto derived = static_cast<STRUCT*>(obj); \
auto base = static_cast<BASE*>(obj); \
return TypeOf<BASE>::deserializeFields(d, base) && \
deserializeFields(d, derived); \
} \
bool serialize(Serializer* s, const void* obj) const override { \
return s->object([&](FieldSerializer* fs) { \
auto derived = static_cast<const STRUCT*>(obj); \
auto base = static_cast<const BASE*>(obj); \
return TypeOf<BASE>::serializeFields(fs, base) && \
serializeFields(fs, derived); \
}); \
} \
}; \
static TI typeinfo; \
return &typeinfo; \
}
// DAP_STRUCT_TYPEINFO_EXT() is a helper for declaring and implementing a
// TypeOf<> specialization for STRUCT that derives from BASE in a single
// statement.
// Must be used within the 'dap' namespace.
#define DAP_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, __VA_ARGS__)
} // namespace dap
#endif // dap_typeof_h

View File

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

View File

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

View File

@@ -0,0 +1,262 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/any.h"
#include "dap/typeof.h"
#include "dap/types.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct AnyTestObject {
dap::integer i;
dap::number n;
};
DAP_STRUCT_TYPEINFO(AnyTestObject,
"AnyTestObject",
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"));
inline bool operator==(const AnyTestObject& a, const AnyTestObject& b) {
return a.i == b.i && a.n == b.n;
}
} // namespace dap
namespace {
template <typename T>
struct TestValue {};
template <>
struct TestValue<dap::null> {
static const dap::null value;
};
template <>
struct TestValue<dap::integer> {
static const dap::integer value;
};
template <>
struct TestValue<dap::boolean> {
static const dap::boolean value;
};
template <>
struct TestValue<dap::number> {
static const dap::number value;
};
template <>
struct TestValue<dap::string> {
static const dap::string value;
};
template <>
struct TestValue<dap::array<dap::string>> {
static const dap::array<dap::string> value;
};
template <>
struct TestValue<dap::AnyTestObject> {
static const dap::AnyTestObject value;
};
const dap::null TestValue<dap::null>::value = nullptr;
const dap::integer TestValue<dap::integer>::value = 20;
const dap::boolean TestValue<dap::boolean>::value = true;
const dap::number TestValue<dap::number>::value = 123.45;
const dap::string TestValue<dap::string>::value = "hello world";
const dap::array<dap::string> TestValue<dap::array<dap::string>>::value = {
"one", "two", "three"};
const dap::AnyTestObject TestValue<dap::AnyTestObject>::value = {10, 20.30};
} // namespace
TEST(Any, EmptyConstruct) {
dap::any any;
ASSERT_TRUE(any.is<dap::null>());
ASSERT_FALSE(any.is<dap::boolean>());
ASSERT_FALSE(any.is<dap::integer>());
ASSERT_FALSE(any.is<dap::number>());
ASSERT_FALSE(any.is<dap::object>());
ASSERT_FALSE(any.is<dap::string>());
ASSERT_FALSE(any.is<dap::array<dap::integer>>());
ASSERT_FALSE(any.is<dap::AnyTestObject>());
}
TEST(Any, Boolean) {
dap::any any(dap::boolean(true));
ASSERT_TRUE(any.is<dap::boolean>());
ASSERT_EQ(any.get<dap::boolean>(), dap::boolean(true));
}
TEST(Any, Integer) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
ASSERT_EQ(any.get<dap::integer>(), dap::integer(10));
}
TEST(Any, Number) {
dap::any any(dap::number(123.0f));
ASSERT_TRUE(any.is<dap::number>());
ASSERT_EQ(any.get<dap::number>(), dap::number(123.0f));
}
TEST(Any, String) {
dap::any any(dap::string("hello world"));
ASSERT_TRUE(any.is<dap::string>());
ASSERT_EQ(any.get<dap::string>(), dap::string("hello world"));
}
TEST(Any, Array) {
using array = dap::array<dap::integer>;
dap::any any(array({10, 20, 30}));
ASSERT_TRUE(any.is<array>());
ASSERT_EQ(any.get<array>(), array({10, 20, 30}));
}
TEST(Any, Object) {
dap::object o;
o["one"] = dap::integer(1);
o["two"] = dap::integer(2);
o["three"] = dap::integer(3);
dap::any any(o);
ASSERT_TRUE(any.is<dap::object>());
if (any.is<dap::object>()) {
auto got = any.get<dap::object>();
ASSERT_EQ(got.size(), 3U);
ASSERT_EQ(got.count("one"), 1U);
ASSERT_EQ(got.count("two"), 1U);
ASSERT_EQ(got.count("three"), 1U);
ASSERT_TRUE(got["one"].is<dap::integer>());
ASSERT_TRUE(got["two"].is<dap::integer>());
ASSERT_TRUE(got["three"].is<dap::integer>());
ASSERT_EQ(got["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got["two"].get<dap::integer>(), dap::integer(2));
ASSERT_EQ(got["three"].get<dap::integer>(), dap::integer(3));
}
}
TEST(Any, TestObject) {
dap::any any(dap::AnyTestObject{5, 3.0});
ASSERT_TRUE(any.is<dap::AnyTestObject>());
ASSERT_EQ(any.get<dap::AnyTestObject>().i, 5);
ASSERT_EQ(any.get<dap::AnyTestObject>().n, 3.0);
}
template <typename T>
class AnyT : public ::testing::Test {
protected:
template <typename T0,
typename = std::enable_if<std::is_same<T, T0>::value &&
!std::is_same<T0, dap::null>::value>>
void check_val(const dap::any& any, const T0& expect) {
ASSERT_EQ(any.is<T>(), any.is<T0>());
ASSERT_EQ(any.get<T>(), expect);
}
// Special case for Null assignment, as we can assign nullptr_t to any but
// can't `get()` it
template <typename = dap::null>
void check_val(const dap::any& any, const dap::null& expect) {
ASSERT_EQ(nullptr, expect);
ASSERT_TRUE(any.is<dap::null>());
}
void check_type(const dap::any& any) {
ASSERT_EQ(any.is<dap::null>(), (std::is_same<T, dap::null>::value));
ASSERT_EQ(any.is<dap::integer>(), (std::is_same<T, dap::integer>::value));
ASSERT_EQ(any.is<dap::boolean>(), (std::is_same<T, dap::boolean>::value));
ASSERT_EQ(any.is<dap::number>(), (std::is_same<T, dap::number>::value));
ASSERT_EQ(any.is<dap::string>(), (std::is_same<T, dap::string>::value));
ASSERT_EQ(any.is<dap::array<dap::string>>(),
(std::is_same<T, dap::array<dap::string>>::value));
ASSERT_EQ(any.is<dap::AnyTestObject>(),
(std::is_same<T, dap::AnyTestObject>::value));
}
};
TYPED_TEST_SUITE_P(AnyT);
TYPED_TEST_P(AnyT, CopyConstruct) {
auto val = TestValue<TypeParam>::value;
dap::any any(val);
this->check_type(any);
this->check_val(any, val);
}
TYPED_TEST_P(AnyT, MoveConstruct) {
auto val = TestValue<TypeParam>::value;
dap::any any(std::move(val));
this->check_type(any);
this->check_val(any, val);
}
TYPED_TEST_P(AnyT, Assign) {
auto val = TestValue<TypeParam>::value;
dap::any any;
any = val;
this->check_type(any);
this->check_val(any, val);
}
TYPED_TEST_P(AnyT, MoveAssign) {
auto val = TestValue<TypeParam>::value;
dap::any any;
any = std::move(val);
this->check_type(any);
this->check_val(any, val);
}
TYPED_TEST_P(AnyT, RepeatedAssign) {
dap::string str = "hello world";
auto val = TestValue<TypeParam>::value;
dap::any any;
any = str;
any = val;
this->check_type(any);
this->check_val(any, val);
}
TYPED_TEST_P(AnyT, RepeatedMoveAssign) {
dap::string str = "hello world";
auto val = TestValue<TypeParam>::value;
dap::any any;
any = std::move(str);
any = std::move(val);
this->check_type(any);
this->check_val(any, val);
}
REGISTER_TYPED_TEST_SUITE_P(AnyT,
CopyConstruct,
MoveConstruct,
Assign,
MoveAssign,
RepeatedAssign,
RepeatedMoveAssign);
using AnyTypes = ::testing::Types<dap::null,
dap::integer,
dap::boolean,
dap::number,
dap::string,
dap::array<dap::string>,
dap::AnyTestObject>;
INSTANTIATE_TYPED_TEST_SUITE_P(T, AnyT, AnyTypes);
TEST(Any, Reset) {
dap::any any(dap::integer(10));
ASSERT_TRUE(any.is<dap::integer>());
any.reset();
ASSERT_FALSE(any.is<dap::integer>());
}

View File

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

View File

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

View File

@@ -0,0 +1,189 @@
// 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() {
matched_idx = 0;
// Find Content-Length header prefix
if (!scan("Content-Length:")) {
return "";
}
// Skip whitespace and tabs
while (matchAny(" \t")) {
}
// Parse length
size_t len = 0;
while (true) {
auto c = matchAny("0123456789");
if (c == 0) {
break;
}
len *= 10;
len += size_t(c) - size_t('0');
}
if (len == 0) {
return "";
}
// Expect \r\n\r\n
if (!match("\r\n\r\n")) {
return "";
}
// Read message
if (!buffer(len + matched_idx)) {
return "";
}
for (size_t i = 0; i < matched_idx; i++) {
buf.pop_front();
}
std::string out;
out.reserve(len);
for (size_t i = 0; i < len; i++) {
out.push_back(static_cast<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 + matched_idx)) {
return false;
}
auto it = matched_idx;
for (size_t i = 0; i < len; i++, it++) {
if (buf[it] != seq[i]) {
return false;
}
}
matched_idx += len;
return true;
}
bool ContentReader::match(const char* str) {
auto len = strlen(str);
return match(reinterpret_cast<const uint8_t*>(str), len);
}
char ContentReader::matchAny(const char* chars) {
if (!buffer(1 + matched_idx)) {
return false;
}
int c = buf[matched_idx];
if (auto p = strchr(chars, c)) {
matched_idx++;
return *p;
}
return 0;
}
bool ContentReader::buffer(size_t bytes) {
if (bytes < buf.size()) {
return true;
}
bytes -= buf.size();
while (bytes > 0) {
uint8_t chunk[256];
auto numWant = std::min(sizeof(chunk), bytes);
auto numGot = reader->read(chunk, numWant);
if (numGot == 0) {
return false;
}
for (size_t i = 0; i < numGot; i++) {
buf.push_back(chunk[i]);
}
bytes -= numGot;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ContentWriter
////////////////////////////////////////////////////////////////////////////////
ContentWriter::ContentWriter(const std::shared_ptr<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

View File

@@ -0,0 +1,69 @@
// 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;
uint32_t matched_idx = 0;
};
class ContentWriter {
public:
ContentWriter() = default;
ContentWriter(const std::shared_ptr<Writer>&);
ContentWriter& operator=(ContentWriter&&) noexcept;
bool isOpen();
void close();
bool write(const std::string&) const;
private:
std::shared_ptr<Writer> writer;
};
} // namespace dap
#endif // dap_content_stream_h

View File

@@ -0,0 +1,99 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "content_stream.h"
#include "string_buffer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
namespace {
// SingleByteReader wraps a dap::Reader to only provide a single byte for each
// read() call, regardless of the number of bytes actually requested.
class SingleByteReader : public dap::Reader {
public:
SingleByteReader(std::unique_ptr<dap::Reader>&& inner)
: inner(std::move(inner)) {}
bool isOpen() override { return inner->isOpen(); }
void close() override { return inner->close(); }
size_t read(void* buffer, size_t) override { return inner->read(buffer, 1); };
private:
std::unique_ptr<dap::Reader> inner;
};
} // namespace
TEST(ContentStreamTest, Write) {
auto sb = dap::StringBuffer::create();
auto ptr = sb.get();
dap::ContentWriter cw(std::move(sb));
cw.write("Content payload number one");
cw.write("Content payload number two");
cw.write("Content payload number three");
ASSERT_EQ(ptr->string(),
"Content-Length: 26\r\n\r\nContent payload number one"
"Content-Length: 26\r\n\r\nContent payload number two"
"Content-Length: 28\r\n\r\nContent payload number three");
}
TEST(ContentStreamTest, Read) {
auto sb = dap::StringBuffer::create();
sb->write("Content-Length: 26\r\n\r\nContent payload number one");
sb->write("some unrecognised garbage");
sb->write("Content-Length: 26\r\n\r\nContent payload number two");
sb->write("some more unrecognised garbage");
sb->write("Content-Length: 28\r\n\r\nContent payload number three");
dap::ContentReader cs(std::move(sb));
ASSERT_EQ(cs.read(), "Content payload number one");
ASSERT_EQ(cs.read(), "Content payload number two");
ASSERT_EQ(cs.read(), "Content payload number three");
ASSERT_EQ(cs.read(), "");
}
TEST(ContentStreamTest, ShortRead) {
auto sb = dap::StringBuffer::create();
sb->write("Content-Length: 26\r\n\r\nContent payload number one");
sb->write("some unrecognised garbage");
sb->write("Content-Length: 26\r\n\r\nContent payload number two");
sb->write("some more unrecognised garbage");
sb->write("Content-Length: 28\r\n\r\nContent payload number three");
dap::ContentReader cs(
std::unique_ptr<SingleByteReader>(new SingleByteReader(std::move(sb))));
ASSERT_EQ(cs.read(), "Content payload number one");
ASSERT_EQ(cs.read(), "Content payload number two");
ASSERT_EQ(cs.read(), "Content payload number three");
ASSERT_EQ(cs.read(), "");
}
TEST(ContentStreamTest, PartialReadAndParse) {
auto sb = std::make_shared<dap::StringBuffer>();
dap::ContentReader cs(sb);
sb->write("Content");
ASSERT_EQ(cs.read(), "");
sb->write("-Length: ");
ASSERT_EQ(cs.read(), "");
sb->write("26");
ASSERT_EQ(cs.read(), "");
sb->write("\r\n\r\n");
ASSERT_EQ(cs.read(), "");
sb->write("Content payload number one");
ASSERT_EQ(cs.read(), "Content payload number one");
ASSERT_EQ(cs.read(), "");
}

View File

@@ -0,0 +1,72 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/dap.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
TEST(DAP, PairedInitializeTerminate) {
dap::initialize();
dap::terminate();
}
TEST(DAP, NestedInitializeTerminate) {
dap::initialize();
dap::initialize();
dap::initialize();
dap::terminate();
dap::terminate();
dap::terminate();
}
TEST(DAP, MultiThreadedInitializeTerminate) {
const size_t numThreads = 64;
std::mutex mutex;
std::condition_variable cv;
size_t numInits = 0;
std::vector<std::thread> threads;
threads.reserve(numThreads);
for (size_t i = 0; i < numThreads; i++) {
threads.emplace_back([&] {
dap::initialize();
{
std::unique_lock<std::mutex> lock(mutex);
numInits++;
if (numInits == numThreads) {
cv.notify_all();
} else {
cv.wait(lock, [&] { return numInits == numThreads; });
}
}
dap::terminate();
});
}
for (auto& thread : threads) {
thread.join();
}
}

View File

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

View File

@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_json_serializer_h
#define dap_json_serializer_h
#if defined(CPPDAP_JSON_NLOHMANN)
#include "nlohmann_json_serializer.h"
#elif defined(CPPDAP_JSON_RAPID)
#include "rapid_json_serializer.h"
#elif defined(CPPDAP_JSON_JSONCPP)
#include "jsoncpp_json_serializer.h"
#else
#error "Unrecognised cppdap JSON library"
#endif
namespace dap {
namespace json {
#if defined(CPPDAP_JSON_NLOHMANN)
using Deserializer = NlohmannDeserializer;
using Serializer = NlohmannSerializer;
#elif defined(CPPDAP_JSON_RAPID)
using Deserializer = RapidDeserializer;
using Serializer = RapidSerializer;
#elif defined(CPPDAP_JSON_JSONCPP)
using Deserializer = JsonCppDeserializer;
using Serializer = JsonCppSerializer;
#else
#error "Unrecognised cppdap JSON library"
#endif
} // namespace json
} // namespace dap
#endif // dap_json_serializer_h

View File

@@ -0,0 +1,266 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "json_serializer.h"
#include "dap/typeinfo.h"
#include "dap/typeof.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct JSONInnerTestObject {
integer i;
};
DAP_STRUCT_TYPEINFO(JSONInnerTestObject,
"json-inner-test-object",
DAP_FIELD(i, "i"));
struct JSONTestObject {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
JSONInnerTestObject inner;
};
DAP_STRUCT_TYPEINFO(JSONTestObject,
"json-test-object",
DAP_FIELD(b, "b"),
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"),
DAP_FIELD(a, "a"),
DAP_FIELD(o, "o"),
DAP_FIELD(s, "s"),
DAP_FIELD(o1, "o1"),
DAP_FIELD(o2, "o2"),
DAP_FIELD(inner, "inner"));
struct JSONObjectNoFields {};
DAP_STRUCT_TYPEINFO(JSONObjectNoFields, "json-object-no-fields");
struct SimpleJSONTestObject {
boolean b;
integer i;
};
DAP_STRUCT_TYPEINFO(SimpleJSONTestObject,
"simple-json-test-object",
DAP_FIELD(b, "b"),
DAP_FIELD(i, "i"));
} // namespace dap
class JSONSerializer : public testing::Test {
protected:
static dap::object GetSimpleObject() {
return dap::object({{"one", dap::integer(1)},
{"two", dap::number(2)},
{"three", dap::string("three")},
{"four", dap::boolean(true)}});
}
void TEST_SIMPLE_OBJECT(const dap::object& obj) {
NESTED_TEST_FAILED = true;
auto ref_obj = GetSimpleObject();
ASSERT_EQ(obj.size(), ref_obj.size());
ASSERT_TRUE(obj.at("one").is<dap::integer>());
ASSERT_TRUE(obj.at("two").is<dap::number>());
ASSERT_TRUE(obj.at("three").is<dap::string>());
ASSERT_TRUE(obj.at("four").is<dap::boolean>());
ASSERT_EQ(ref_obj.at("one").get<dap::integer>(),
obj.at("one").get<dap::integer>());
ASSERT_EQ(ref_obj.at("two").get<dap::number>(),
obj.at("two").get<dap::number>());
ASSERT_EQ(ref_obj.at("three").get<dap::string>(),
obj.at("three").get<dap::string>());
ASSERT_EQ(ref_obj.at("four").get<dap::boolean>(),
obj.at("four").get<dap::boolean>());
NESTED_TEST_FAILED = false;
}
template <typename T>
void TEST_SERIALIZING_DESERIALIZING(const T& encoded, T& decoded) {
NESTED_TEST_FAILED = true;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(encoded));
dap::json::Deserializer d(s.dump());
ASSERT_TRUE(d.deserialize(&decoded));
NESTED_TEST_FAILED = false;
}
bool NESTED_TEST_FAILED = false;
#define _ASSERT_PASS(NESTED_TEST) \
NESTED_TEST; \
ASSERT_FALSE(NESTED_TEST_FAILED);
};
TEST_F(JSONSerializer, SerializeDeserialize) {
dap::JSONTestObject encoded;
encoded.b = true;
encoded.i = 32;
encoded.n = 123.456;
encoded.a = {2, 4, 6, 8, 0x100000000, -2, -4, -6, -8, -0x100000000};
encoded.o["one"] = dap::integer(1);
encoded.o["two"] = dap::number(2);
encoded.s = "hello world";
encoded.o2 = 42;
encoded.inner.i = 70;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(encoded));
dap::JSONTestObject decoded;
dap::json::Deserializer d(s.dump());
ASSERT_TRUE(d.deserialize(&decoded));
ASSERT_EQ(encoded.b, decoded.b);
ASSERT_EQ(encoded.i, decoded.i);
ASSERT_EQ(encoded.n, decoded.n);
ASSERT_EQ(encoded.a, decoded.a);
ASSERT_EQ(encoded.o["one"].get<dap::integer>(),
decoded.o["one"].get<dap::integer>());
ASSERT_EQ(encoded.o["two"].get<dap::number>(),
decoded.o["two"].get<dap::number>());
ASSERT_EQ(encoded.s, decoded.s);
ASSERT_EQ(encoded.o2, decoded.o2);
ASSERT_EQ(encoded.inner.i, decoded.inner.i);
}
TEST_F(JSONSerializer, SerializeObjectNoFields) {
dap::JSONObjectNoFields obj;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(obj));
ASSERT_EQ(s.dump(), "{}");
}
TEST_F(JSONSerializer, SerializeDeserializeObject) {
dap::object encoded = GetSimpleObject();
dap::object decoded;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded));
}
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObject) {
dap::object encoded;
dap::object decoded;
// object nested inside object
dap::object encoded_embed_obj = GetSimpleObject();
dap::object decoded_embed_obj;
encoded["embed_obj"] = encoded_embed_obj;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
ASSERT_TRUE(decoded["embed_obj"].is<dap::object>());
decoded_embed_obj = decoded["embed_obj"].get<dap::object>();
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_obj));
}
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedStruct) {
dap::object encoded;
dap::object decoded;
// object nested inside object
dap::SimpleJSONTestObject encoded_embed_struct;
encoded_embed_struct.b = true;
encoded_embed_struct.i = 50;
encoded["embed_struct"] = encoded_embed_struct;
dap::object decoded_embed_obj;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
ASSERT_TRUE(decoded["embed_struct"].is<dap::object>());
decoded_embed_obj = decoded["embed_struct"].get<dap::object>();
ASSERT_TRUE(decoded_embed_obj.at("b").is<dap::boolean>());
ASSERT_TRUE(decoded_embed_obj.at("i").is<dap::integer>());
ASSERT_EQ(encoded_embed_struct.b, decoded_embed_obj["b"].get<dap::boolean>());
ASSERT_EQ(encoded_embed_struct.i, decoded_embed_obj["i"].get<dap::integer>());
}
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedIntArray) {
dap::object encoded;
dap::object decoded;
// array nested inside object
dap::array<dap::integer> encoded_embed_arr = {1, 2, 3, 4};
dap::array<dap::any> decoded_embed_arr;
encoded["embed_arr"] = encoded_embed_arr;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
// TODO: Deserializing array should infer basic member types
ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>());
decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>();
ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size());
for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) {
ASSERT_TRUE(decoded_embed_arr[i].is<dap::integer>());
ASSERT_EQ(encoded_embed_arr[i], decoded_embed_arr[i].get<dap::integer>());
}
}
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedObjectArray) {
dap::object encoded;
dap::object decoded;
dap::array<dap::object> encoded_embed_arr = {GetSimpleObject(),
GetSimpleObject()};
dap::array<dap::any> decoded_embed_arr;
encoded["embed_arr"] = encoded_embed_arr;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
// TODO: Deserializing array should infer basic member types
ASSERT_TRUE(decoded["embed_arr"].is<dap::array<dap::any>>());
decoded_embed_arr = decoded["embed_arr"].get<dap::array<dap::any>>();
ASSERT_EQ(encoded_embed_arr.size(), decoded_embed_arr.size());
for (std::size_t i = 0; i < decoded_embed_arr.size(); i++) {
ASSERT_TRUE(decoded_embed_arr[i].is<dap::object>());
_ASSERT_PASS(TEST_SIMPLE_OBJECT(decoded_embed_arr[i].get<dap::object>()));
}
}
TEST_F(JSONSerializer, DeserializeSerializeEmptyObject) {
auto empty_obj = "{}";
dap::object decoded;
dap::json::Deserializer d(empty_obj);
ASSERT_TRUE(d.deserialize(&decoded));
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(decoded));
ASSERT_EQ(s.dump(), empty_obj);
}
TEST_F(JSONSerializer, SerializeDeserializeEmbeddedEmptyObject) {
dap::object encoded_empty_obj;
dap::object encoded = {{"empty_obj", encoded_empty_obj}};
dap::object decoded;
_ASSERT_PASS(TEST_SERIALIZING_DESERIALIZING(encoded, decoded));
ASSERT_TRUE(decoded["empty_obj"].is<dap::object>());
dap::object decoded_empty_obj = decoded["empty_obj"].get<dap::object>();
ASSERT_EQ(encoded_empty_obj.size(), decoded_empty_obj.size());
}
TEST_F(JSONSerializer, SerializeDeserializeObjectWithNulledField) {
auto thing = dap::any(dap::null());
dap::object encoded;
encoded["nulled_field"] = dap::null();
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(encoded));
dap::object decoded;
auto dump = s.dump();
dap::json::Deserializer d(dump);
ASSERT_TRUE(d.deserialize(&decoded));
ASSERT_TRUE(encoded["nulled_field"].is<dap::null>());
}

View File

@@ -0,0 +1,272 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "jsoncpp_json_serializer.h"
#include "null_json_serializer.h"
#include <json/json.h>
#include <cstdlib>
#include <memory>
namespace dap {
namespace json {
JsonCppDeserializer::JsonCppDeserializer(const std::string& str)
: json(new Json::Value(JsonCppDeserializer::parse(str))), ownsJson(true) {}
JsonCppDeserializer::JsonCppDeserializer(const Json::Value* json)
: json(json), ownsJson(false) {}
JsonCppDeserializer::~JsonCppDeserializer() {
if (ownsJson) {
delete json;
}
}
bool JsonCppDeserializer::deserialize(dap::boolean* v) const {
if (!json->isBool()) {
return false;
}
*v = json->asBool();
return true;
}
bool JsonCppDeserializer::deserialize(dap::integer* v) const {
if (!json->isInt64()) {
return false;
}
*v = json->asInt64();
return true;
}
bool JsonCppDeserializer::deserialize(dap::number* v) const {
if (!json->isNumeric()) {
return false;
}
*v = json->asDouble();
return true;
}
bool JsonCppDeserializer::deserialize(dap::string* v) const {
if (!json->isString()) {
return false;
}
*v = json->asString();
return true;
}
bool JsonCppDeserializer::deserialize(dap::object* v) const {
v->reserve(json->size());
for (auto i = json->begin(); i != json->end(); i++) {
JsonCppDeserializer d(&*i);
dap::any val;
if (!d.deserialize(&val)) {
return false;
}
(*v)[i.name()] = val;
}
return true;
}
bool JsonCppDeserializer::deserialize(dap::any* v) const {
if (json->isBool()) {
*v = dap::boolean(json->asBool());
} else if (json->type() == Json::ValueType::realValue) {
// json->isDouble() returns true for integers as well, so we need to
// explicitly look for the realValue type.
*v = dap::number(json->asDouble());
} else if (json->isInt64()) {
*v = dap::integer(json->asInt64());
} else if (json->isString()) {
*v = json->asString();
} else if (json->isObject()) {
dap::object obj;
if (!deserialize(&obj)) {
return false;
}
*v = obj;
} else if (json->isArray()) {
dap::array<any> arr;
if (!deserialize(&arr)) {
return false;
}
*v = arr;
} else if (json->isNull()) {
*v = null();
} else {
return false;
}
return true;
}
size_t JsonCppDeserializer::count() const {
return json->size();
}
bool JsonCppDeserializer::array(
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->isArray()) {
return false;
}
for (const auto& value : *json) {
JsonCppDeserializer d(&value);
if (!cb(&d)) {
return false;
}
}
return true;
}
bool JsonCppDeserializer::field(
const std::string& name,
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->isObject()) {
return false;
}
auto value = json->find(name.data(), name.data() + name.size());
if (value == nullptr) {
return cb(&NullDeserializer::instance);
}
JsonCppDeserializer d(value);
return cb(&d);
}
Json::Value JsonCppDeserializer::parse(const std::string& text) {
Json::CharReaderBuilder builder;
auto jsonReader = std::unique_ptr<Json::CharReader>(builder.newCharReader());
Json::Value json;
std::string error;
if (!jsonReader->parse(text.data(), text.data() + text.size(), &json,
&error)) {
// cppdap expects that the JSON layer does not throw exceptions.
std::abort();
}
return json;
}
JsonCppSerializer::JsonCppSerializer()
: json(new Json::Value()), ownsJson(true) {}
JsonCppSerializer::JsonCppSerializer(Json::Value* json)
: json(json), ownsJson(false) {}
JsonCppSerializer::~JsonCppSerializer() {
if (ownsJson) {
delete json;
}
}
std::string JsonCppSerializer::dump() const {
Json::StreamWriterBuilder writer;
return Json::writeString(writer, *json);
}
bool JsonCppSerializer::serialize(dap::boolean v) {
*json = (bool)v;
return true;
}
bool JsonCppSerializer::serialize(dap::integer v) {
*json = (Json::LargestInt)v;
return true;
}
bool JsonCppSerializer::serialize(dap::number v) {
*json = (double)v;
return true;
}
bool JsonCppSerializer::serialize(const dap::string& v) {
*json = v;
return true;
}
bool JsonCppSerializer::serialize(const dap::object& v) {
if (!json->isObject()) {
*json = Json::Value(Json::objectValue);
}
for (auto& it : v) {
JsonCppSerializer s(&(*json)[it.first]);
if (!s.serialize(it.second)) {
return false;
}
}
return true;
}
bool JsonCppSerializer::serialize(const dap::any& v) {
if (v.is<dap::boolean>()) {
*json = (bool)v.get<dap::boolean>();
} else if (v.is<dap::integer>()) {
*json = (Json::LargestInt)v.get<dap::integer>();
} else if (v.is<dap::number>()) {
*json = (double)v.get<dap::number>();
} else if (v.is<dap::string>()) {
*json = v.get<dap::string>();
} else if (v.is<dap::object>()) {
// reachable if dap::object nested is inside other dap::object
return serialize(v.get<dap::object>());
} else if (v.is<dap::null>()) {
} else {
// reachable if array or custom serialized type is nested inside other
auto type = get_any_type(v);
auto value = get_any_val(v);
if (type && value) {
return type->serialize(this, value);
}
return false;
}
return true;
}
bool JsonCppSerializer::array(size_t count,
const std::function<bool(dap::Serializer*)>& cb) {
*json = Json::Value(Json::arrayValue);
for (size_t i = 0; i < count; i++) {
JsonCppSerializer s(&(*json)[Json::Value::ArrayIndex(i)]);
if (!cb(&s)) {
return false;
}
}
return true;
}
bool JsonCppSerializer::object(
const std::function<bool(dap::FieldSerializer*)>& cb) {
struct FS : public FieldSerializer {
Json::Value* const json;
FS(Json::Value* json) : json(json) {}
bool field(const std::string& name, const SerializeFunc& cb) override {
JsonCppSerializer s(&(*json)[name]);
auto res = cb(&s);
if (s.removed) {
json->removeMember(name);
}
return res;
}
};
*json = Json::Value(Json::objectValue);
FS fs{json};
return cb(&fs);
}
void JsonCppSerializer::remove() {
removed = true;
}
} // namespace json
} // namespace dap

View File

@@ -0,0 +1,134 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_jsoncpp_json_serializer_h
#define dap_jsoncpp_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
#include <json/forwards.h>
namespace dap {
namespace json {
struct JsonCppDeserializer : public dap::Deserializer {
explicit JsonCppDeserializer(const std::string&);
~JsonCppDeserializer();
// dap::Deserializer compliance
bool deserialize(boolean* v) const override;
bool deserialize(integer* v) const override;
bool deserialize(number* v) const override;
bool deserialize(string* v) const override;
bool deserialize(object* v) const override;
bool deserialize(any* v) const override;
size_t count() const override;
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
bool field(const std::string& name,
const std::function<bool(dap::Deserializer*)>&) const override;
// Unhide base overloads
template <typename T>
inline bool field(const std::string& name, T* v) {
return dap::Deserializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::array<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::optional<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool field(const std::string& name, T* v) const {
return dap::Deserializer::deserialize(name, v);
}
private:
JsonCppDeserializer(const Json::Value*);
static Json::Value parse(const std::string& text);
const Json::Value* const json;
const bool ownsJson;
};
struct JsonCppSerializer : public dap::Serializer {
JsonCppSerializer();
~JsonCppSerializer();
std::string dump() const;
// dap::Serializer compliance
bool serialize(boolean v) override;
bool serialize(integer v) override;
bool serialize(number v) override;
bool serialize(const string& v) override;
bool serialize(const dap::object& v) override;
bool serialize(const any& v) override;
bool array(size_t count,
const std::function<bool(dap::Serializer*)>&) override;
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
void remove() override;
// Unhide base overloads
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::array<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::optional<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>& v) {
return dap::Serializer::serialize(v);
}
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
private:
JsonCppSerializer(Json::Value*);
Json::Value* const json;
const bool ownsJson;
bool removed = false;
};
} // namespace json
} // namespace dap
#endif // dap_jsoncpp_json_serializer_h

View File

@@ -0,0 +1,100 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/network.h"
#include "socket.h"
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
namespace {
class Impl : public dap::net::Server {
public:
Impl() : stopped{true} {}
~Impl() { stop(); }
bool start(int port,
const OnConnect& onConnect,
const OnError& onError) override {
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;
}
stopped = false;
thread = std::thread([=] {
while (true) {
if (auto rw = socket->accept()) {
onConnect(rw);
continue;
}
if (!stopped) {
onError("Failed to accept connection");
}
break;
};
});
return true;
}
void stop() override {
std::unique_lock<std::mutex> lock(mutex);
stopWithLock();
}
private:
bool isRunning() { return !stopped; }
void stopWithLock() {
if (!stopped.exchange(true)) {
socket->close();
thread.join();
}
}
std::mutex mutex;
std::thread thread;
std::unique_ptr<dap::Socket> socket;
std::atomic<bool> stopped;
OnError errorHandler;
};
} // anonymous namespace
namespace dap {
namespace net {
std::unique_ptr<Server> Server::create() {
return std::unique_ptr<Server>(new Impl());
}
std::shared_ptr<ReaderWriter> connect(const char* addr,
int port,
uint32_t timeoutMillis) {
return Socket::connect(addr, std::to_string(port).c_str(), timeoutMillis);
}
} // namespace net
} // namespace dap

View File

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

View File

@@ -0,0 +1,260 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nlohmann_json_serializer.h"
#include "null_json_serializer.h"
// Disable JSON exceptions. We should be guarding against any exceptions being
// fired in this file.
#define JSON_NOEXCEPTION 1
#include <nlohmann/json.hpp>
namespace dap {
namespace json {
NlohmannDeserializer::NlohmannDeserializer(const std::string& str)
: json(new nlohmann::json(nlohmann::json::parse(str, nullptr, false))),
ownsJson(true) {}
NlohmannDeserializer::NlohmannDeserializer(const nlohmann::json* json)
: json(json), ownsJson(false) {}
NlohmannDeserializer::~NlohmannDeserializer() {
if (ownsJson) {
delete json;
}
}
bool NlohmannDeserializer::deserialize(dap::boolean* v) const {
if (!json->is_boolean()) {
return false;
}
*v = json->get<bool>();
return true;
}
bool NlohmannDeserializer::deserialize(dap::integer* v) const {
if (!json->is_number_integer()) {
return false;
}
*v = json->get<int64_t>();
return true;
}
bool NlohmannDeserializer::deserialize(dap::number* v) const {
if (!json->is_number()) {
return false;
}
*v = json->get<double>();
return true;
}
bool NlohmannDeserializer::deserialize(dap::string* v) const {
if (!json->is_string()) {
return false;
}
*v = json->get<std::string>();
return true;
}
bool NlohmannDeserializer::deserialize(dap::object* v) const {
v->reserve(json->size());
for (auto& el : json->items()) {
NlohmannDeserializer d(&el.value());
dap::any val;
if (!d.deserialize(&val)) {
return false;
}
(*v)[el.key()] = val;
}
return true;
}
bool NlohmannDeserializer::deserialize(dap::any* v) const {
if (json->is_boolean()) {
*v = dap::boolean(json->get<bool>());
} else if (json->is_number_float()) {
*v = dap::number(json->get<double>());
} else if (json->is_number_integer()) {
*v = dap::integer(json->get<int64_t>());
} else if (json->is_string()) {
*v = json->get<std::string>();
} else if (json->is_object()) {
dap::object obj;
if (!deserialize(&obj)) {
return false;
}
*v = obj;
} else if (json->is_array()) {
dap::array<any> arr;
if (!deserialize(&arr)) {
return false;
}
*v = arr;
} else if (json->is_null()) {
*v = null();
} else {
return false;
}
return true;
}
size_t NlohmannDeserializer::count() const {
return json->size();
}
bool NlohmannDeserializer::array(
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_array()) {
return false;
}
for (size_t i = 0; i < json->size(); i++) {
NlohmannDeserializer d(&(*json)[i]);
if (!cb(&d)) {
return false;
}
}
return true;
}
bool NlohmannDeserializer::field(
const std::string& name,
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json->is_structured()) {
return false;
}
auto it = json->find(name);
if (it == json->end()) {
return cb(&NullDeserializer::instance);
}
auto obj = *it;
NlohmannDeserializer d(&obj);
return cb(&d);
}
NlohmannSerializer::NlohmannSerializer()
: json(new nlohmann::json()), ownsJson(true) {}
NlohmannSerializer::NlohmannSerializer(nlohmann::json* json)
: json(json), ownsJson(false) {}
NlohmannSerializer::~NlohmannSerializer() {
if (ownsJson) {
delete json;
}
}
std::string NlohmannSerializer::dump() const {
return json->dump();
}
bool NlohmannSerializer::serialize(dap::boolean v) {
*json = (bool)v;
return true;
}
bool NlohmannSerializer::serialize(dap::integer v) {
*json = (int64_t)v;
return true;
}
bool NlohmannSerializer::serialize(dap::number v) {
*json = (double)v;
return true;
}
bool NlohmannSerializer::serialize(const dap::string& v) {
*json = v;
return true;
}
bool NlohmannSerializer::serialize(const dap::object& v) {
if (!json->is_object()) {
*json = nlohmann::json::object();
}
for (auto& it : v) {
NlohmannSerializer s(&(*json)[it.first]);
if (!s.serialize(it.second)) {
return false;
}
}
return true;
}
bool NlohmannSerializer::serialize(const dap::any& v) {
if (v.is<dap::boolean>()) {
*json = (bool)v.get<dap::boolean>();
} else if (v.is<dap::integer>()) {
*json = (int64_t)v.get<dap::integer>();
} else if (v.is<dap::number>()) {
*json = (double)v.get<dap::number>();
} else if (v.is<dap::string>()) {
*json = v.get<dap::string>();
} else if (v.is<dap::object>()) {
// reachable if dap::object nested is inside other dap::object
return serialize(v.get<dap::object>());
} else if (v.is<dap::null>()) {
} else {
// reachable if array or custom serialized type is nested inside other
auto type = get_any_type(v);
auto value = get_any_val(v);
if (type && value) {
return type->serialize(this, value);
}
return false;
}
return true;
}
bool NlohmannSerializer::array(
size_t count,
const std::function<bool(dap::Serializer*)>& cb) {
*json = std::vector<int>();
for (size_t i = 0; i < count; i++) {
NlohmannSerializer s(&(*json)[i]);
if (!cb(&s)) {
return false;
}
}
return true;
}
bool NlohmannSerializer::object(
const std::function<bool(dap::FieldSerializer*)>& cb) {
struct FS : public FieldSerializer {
nlohmann::json* const json;
FS(nlohmann::json* json) : json(json) {}
bool field(const std::string& name, const SerializeFunc& cb) override {
NlohmannSerializer s(&(*json)[name]);
auto res = cb(&s);
if (s.removed) {
json->erase(name);
}
return res;
}
};
*json = nlohmann::json({}, false, nlohmann::json::value_t::object);
FS fs{json};
return cb(&fs);
}
void NlohmannSerializer::remove() {
removed = true;
}
} // namespace json
} // namespace dap

View File

@@ -0,0 +1,133 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_nlohmann_json_serializer_h
#define dap_nlohmann_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
#include <nlohmann/json_fwd.hpp>
namespace dap {
namespace json {
struct NlohmannDeserializer : public dap::Deserializer {
explicit NlohmannDeserializer(const std::string&);
~NlohmannDeserializer();
// dap::Deserializer compliance
bool deserialize(boolean* v) const override;
bool deserialize(integer* v) const override;
bool deserialize(number* v) const override;
bool deserialize(string* v) const override;
bool deserialize(object* v) const override;
bool deserialize(any* v) const override;
size_t count() const override;
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
bool field(const std::string& name,
const std::function<bool(dap::Deserializer*)>&) const override;
// Unhide base overloads
template <typename T>
inline bool field(const std::string& name, T* v) {
return dap::Deserializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::array<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::optional<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool field(const std::string& name, T* v) const {
return dap::Deserializer::deserialize(name, v);
}
private:
NlohmannDeserializer(const nlohmann::json*);
const nlohmann::json* const json;
const bool ownsJson;
};
struct NlohmannSerializer : public dap::Serializer {
NlohmannSerializer();
~NlohmannSerializer();
std::string dump() const;
// dap::Serializer compliance
bool serialize(boolean v) override;
bool serialize(integer v) override;
bool serialize(number v) override;
bool serialize(const string& v) override;
bool serialize(const dap::object& v) override;
bool serialize(const any& v) override;
bool array(size_t count,
const std::function<bool(dap::Serializer*)>&) override;
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
void remove() override;
// Unhide base overloads
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::array<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::optional<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>& v) {
return dap::Serializer::serialize(v);
}
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
private:
NlohmannSerializer(nlohmann::json*);
nlohmann::json* const json;
const bool ownsJson;
bool removed = false;
};
} // namespace json
} // namespace dap
#endif // dap_nlohmann_json_serializer_h

View File

@@ -0,0 +1,23 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "null_json_serializer.h"
namespace dap {
namespace json {
NullDeserializer NullDeserializer::instance;
} // namespace json
} // namespace dap

View File

@@ -0,0 +1,47 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_null_json_serializer_h
#define dap_null_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
namespace dap {
namespace json {
struct NullDeserializer : public dap::Deserializer {
static NullDeserializer instance;
bool deserialize(dap::boolean*) const override { return false; }
bool deserialize(dap::integer*) const override { return false; }
bool deserialize(dap::number*) const override { return false; }
bool deserialize(dap::string*) const override { return false; }
bool deserialize(dap::object*) const override { return false; }
bool deserialize(dap::any*) const override { return false; }
size_t count() const override { return 0; }
bool array(const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
bool field(const std::string&,
const std::function<bool(dap::Deserializer*)>&) const override {
return false;
}
};
} // namespace json
} // namespace dap
#endif // dap_null_json_serializer_h

View File

@@ -0,0 +1,169 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/optional.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <string>
TEST(Optional, EmptyConstruct) {
dap::optional<std::string> opt;
ASSERT_FALSE(opt);
ASSERT_FALSE(opt.has_value());
}
TEST(Optional, ValueConstruct) {
dap::optional<int> opt(10);
ASSERT_TRUE(opt);
ASSERT_TRUE(opt.has_value());
ASSERT_EQ(opt.value(), 10);
}
TEST(Optional, CopyConstruct) {
dap::optional<std::string> a("meow");
dap::optional<std::string> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(a.value(), "meow");
ASSERT_EQ(b.value(), "meow");
}
TEST(Optional, CopyCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(a);
ASSERT_EQ(a, b);
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, MoveConstruct) {
dap::optional<std::string> a("meow");
dap::optional<std::string> b(std::move(a));
ASSERT_EQ(b.value(), "meow");
}
TEST(Optional, MoveCastConstruct) {
dap::optional<int> a(10);
dap::optional<uint16_t> b(std::move(a));
ASSERT_EQ(b.value(), (uint16_t)10);
}
TEST(Optional, AssignValue) {
dap::optional<std::string> a;
std::string b = "meow";
a = b;
ASSERT_EQ(a.value(), "meow");
ASSERT_EQ(b, "meow");
}
TEST(Optional, AssignOptional) {
dap::optional<std::string> a;
dap::optional<std::string> b("meow");
a = b;
ASSERT_EQ(a.value(), "meow");
ASSERT_EQ(b.value(), "meow");
}
TEST(Optional, MoveAssignOptional) {
dap::optional<std::string> a;
dap::optional<std::string> b("meow");
a = std::move(b);
ASSERT_EQ(a.value(), "meow");
}
TEST(Optional, StarDeref) {
dap::optional<std::string> a("meow");
ASSERT_EQ(*a, "meow");
}
TEST(Optional, StarDerefConst) {
const dap::optional<std::string> a("meow");
ASSERT_EQ(*a, "meow");
}
TEST(Optional, ArrowDeref) {
struct S {
int i;
};
dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, ArrowDerefConst) {
struct S {
int i;
};
const dap::optional<S> a(S{10});
ASSERT_EQ(a->i, 10);
}
TEST(Optional, Value) {
const dap::optional<std::string> a("meow");
ASSERT_EQ(a.value(), "meow");
}
TEST(Optional, ValueDefault) {
const dap::optional<std::string> a;
const dap::optional<std::string> b("woof");
ASSERT_EQ(a.value("meow"), "meow");
ASSERT_EQ(b.value("meow"), "woof");
}
TEST(Optional, CompareLT) {
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) < dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) < dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() < dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() < dap::optional<int>());
}
TEST(Optional, CompareLE) {
ASSERT_FALSE(dap::optional<int>(5) <= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() <= dap::optional<int>());
}
TEST(Optional, CompareGT) {
ASSERT_TRUE(dap::optional<int>(5) > dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() > dap::optional<int>());
}
TEST(Optional, CompareGE) {
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) >= dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) >= dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() >= dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() >= dap::optional<int>());
}
TEST(Optional, CompareEQ) {
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(3));
ASSERT_TRUE(dap::optional<int>(5) == dap::optional<int>(5));
ASSERT_FALSE(dap::optional<int>(5) == dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() == dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() == dap::optional<int>());
}
TEST(Optional, CompareNEQ) {
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(3));
ASSERT_FALSE(dap::optional<int>(5) != dap::optional<int>(5));
ASSERT_TRUE(dap::optional<int>(5) != dap::optional<int>(10));
ASSERT_TRUE(dap::optional<int>() != dap::optional<int>(10));
ASSERT_FALSE(dap::optional<int>() != dap::optional<int>());
}

View File

@@ -0,0 +1,126 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Generated with protocol_gen.go -- do not edit this file.
// go run scripts/protocol_gen/protocol_gen.go
//
// DAP version 1.59.0
#include "dap/protocol.h"
namespace dap {
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointEvent,
"breakpoint",
DAP_FIELD(breakpoint, "breakpoint"),
DAP_FIELD(reason, "reason"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(CapabilitiesEvent,
"capabilities",
DAP_FIELD(capabilities, "capabilities"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinuedEvent,
"continued",
DAP_FIELD(allThreadsContinued,
"allThreadsContinued"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExitedEvent,
"exited",
DAP_FIELD(exitCode, "exitCode"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(InitializedEvent, "initialized");
DAP_IMPLEMENT_STRUCT_TYPEINFO(InvalidatedEvent,
"invalidated",
DAP_FIELD(areas, "areas"),
DAP_FIELD(stackFrameId, "stackFrameId"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourceEvent,
"loadedSource",
DAP_FIELD(reason, "reason"),
DAP_FIELD(source, "source"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(MemoryEvent,
"memory",
DAP_FIELD(count, "count"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(offset, "offset"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModuleEvent,
"module",
DAP_FIELD(module, "module"),
DAP_FIELD(reason, "reason"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(OutputEvent,
"output",
DAP_FIELD(category, "category"),
DAP_FIELD(column, "column"),
DAP_FIELD(data, "data"),
DAP_FIELD(group, "group"),
DAP_FIELD(line, "line"),
DAP_FIELD(output, "output"),
DAP_FIELD(source, "source"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProcessEvent,
"process",
DAP_FIELD(isLocalProcess, "isLocalProcess"),
DAP_FIELD(name, "name"),
DAP_FIELD(pointerSize, "pointerSize"),
DAP_FIELD(startMethod, "startMethod"),
DAP_FIELD(systemProcessId, "systemProcessId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressEndEvent,
"progressEnd",
DAP_FIELD(message, "message"),
DAP_FIELD(progressId, "progressId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressStartEvent,
"progressStart",
DAP_FIELD(cancellable, "cancellable"),
DAP_FIELD(message, "message"),
DAP_FIELD(percentage, "percentage"),
DAP_FIELD(progressId, "progressId"),
DAP_FIELD(requestId, "requestId"),
DAP_FIELD(title, "title"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ProgressUpdateEvent,
"progressUpdate",
DAP_FIELD(message, "message"),
DAP_FIELD(percentage, "percentage"),
DAP_FIELD(progressId, "progressId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StoppedEvent,
"stopped",
DAP_FIELD(allThreadsStopped, "allThreadsStopped"),
DAP_FIELD(description, "description"),
DAP_FIELD(hitBreakpointIds, "hitBreakpointIds"),
DAP_FIELD(preserveFocusHint, "preserveFocusHint"),
DAP_FIELD(reason, "reason"),
DAP_FIELD(text, "text"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminatedEvent,
"terminated",
DAP_FIELD(restart, "restart"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadEvent,
"thread",
DAP_FIELD(reason, "reason"),
DAP_FIELD(threadId, "threadId"));
} // namespace dap

View File

@@ -0,0 +1,281 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Generated with protocol_gen.go -- do not edit this file.
// go run scripts/protocol_gen/protocol_gen.go
//
// DAP version 1.59.0
#include "dap/protocol.h"
namespace dap {
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachRequest,
"attach",
DAP_FIELD(restart, "__restart"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsRequest,
"breakpointLocations",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(line, "line"),
DAP_FIELD(source, "source"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelRequest,
"cancel",
DAP_FIELD(progressId, "progressId"),
DAP_FIELD(requestId, "requestId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsRequest,
"completions",
DAP_FIELD(column, "column"),
DAP_FIELD(frameId, "frameId"),
DAP_FIELD(line, "line"),
DAP_FIELD(text, "text"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneRequest, "configurationDone");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest,
"continue",
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest,
"dataBreakpointInfo",
DAP_FIELD(frameId, "frameId"),
DAP_FIELD(name, "name"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleRequest,
"disassemble",
DAP_FIELD(instructionCount, "instructionCount"),
DAP_FIELD(instructionOffset, "instructionOffset"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(offset, "offset"),
DAP_FIELD(resolveSymbols, "resolveSymbols"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectRequest,
"disconnect",
DAP_FIELD(restart, "restart"),
DAP_FIELD(suspendDebuggee, "suspendDebuggee"),
DAP_FIELD(terminateDebuggee,
"terminateDebuggee"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateRequest,
"evaluate",
DAP_FIELD(context, "context"),
DAP_FIELD(expression, "expression"),
DAP_FIELD(format, "format"),
DAP_FIELD(frameId, "frameId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoRequest,
"exceptionInfo",
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoRequest,
"goto",
DAP_FIELD(targetId, "targetId"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsRequest,
"gotoTargets",
DAP_FIELD(column, "column"),
DAP_FIELD(line, "line"),
DAP_FIELD(source, "source"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(
InitializeRequest,
"initialize",
DAP_FIELD(adapterID, "adapterID"),
DAP_FIELD(clientID, "clientID"),
DAP_FIELD(clientName, "clientName"),
DAP_FIELD(columnsStartAt1, "columnsStartAt1"),
DAP_FIELD(linesStartAt1, "linesStartAt1"),
DAP_FIELD(locale, "locale"),
DAP_FIELD(pathFormat, "pathFormat"),
DAP_FIELD(supportsArgsCanBeInterpretedByShell,
"supportsArgsCanBeInterpretedByShell"),
DAP_FIELD(supportsInvalidatedEvent, "supportsInvalidatedEvent"),
DAP_FIELD(supportsMemoryEvent, "supportsMemoryEvent"),
DAP_FIELD(supportsMemoryReferences, "supportsMemoryReferences"),
DAP_FIELD(supportsProgressReporting, "supportsProgressReporting"),
DAP_FIELD(supportsRunInTerminalRequest, "supportsRunInTerminalRequest"),
DAP_FIELD(supportsStartDebuggingRequest, "supportsStartDebuggingRequest"),
DAP_FIELD(supportsVariablePaging, "supportsVariablePaging"),
DAP_FIELD(supportsVariableType, "supportsVariableType"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchRequest,
"launch",
DAP_FIELD(restart, "__restart"),
DAP_FIELD(noDebug, "noDebug"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesRequest, "loadedSources");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesRequest,
"modules",
DAP_FIELD(moduleCount, "moduleCount"),
DAP_FIELD(startModule, "startModule"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextRequest,
"next",
DAP_FIELD(granularity, "granularity"),
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseRequest,
"pause",
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryRequest,
"readMemory",
DAP_FIELD(count, "count"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(offset, "offset"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameRequest,
"restartFrame",
DAP_FIELD(frameId, "frameId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartRequest,
"restart",
DAP_FIELD(arguments, "arguments"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueRequest,
"reverseContinue",
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalRequest,
"runInTerminal",
DAP_FIELD(args, "args"),
DAP_FIELD(argsCanBeInterpretedByShell,
"argsCanBeInterpretedByShell"),
DAP_FIELD(cwd, "cwd"),
DAP_FIELD(env, "env"),
DAP_FIELD(kind, "kind"),
DAP_FIELD(title, "title"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesRequest,
"scopes",
DAP_FIELD(frameId, "frameId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsRequest,
"setBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"),
DAP_FIELD(lines, "lines"),
DAP_FIELD(source, "source"),
DAP_FIELD(sourceModified, "sourceModified"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsRequest,
"setDataBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsRequest,
"setExceptionBreakpoints",
DAP_FIELD(exceptionOptions, "exceptionOptions"),
DAP_FIELD(filterOptions, "filterOptions"),
DAP_FIELD(filters, "filters"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionRequest,
"setExpression",
DAP_FIELD(expression, "expression"),
DAP_FIELD(format, "format"),
DAP_FIELD(frameId, "frameId"),
DAP_FIELD(value, "value"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsRequest,
"setFunctionBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsRequest,
"setInstructionBreakpoints",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableRequest,
"setVariable",
DAP_FIELD(format, "format"),
DAP_FIELD(name, "name"),
DAP_FIELD(value, "value"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceRequest,
"source",
DAP_FIELD(source, "source"),
DAP_FIELD(sourceReference, "sourceReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceRequest,
"stackTrace",
DAP_FIELD(format, "format"),
DAP_FIELD(levels, "levels"),
DAP_FIELD(startFrame, "startFrame"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingRequest,
"startDebugging",
DAP_FIELD(configuration, "configuration"),
DAP_FIELD(request, "request"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackRequest,
"stepBack",
DAP_FIELD(granularity, "granularity"),
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInRequest,
"stepIn",
DAP_FIELD(granularity, "granularity"),
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(targetId, "targetId"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsRequest,
"stepInTargets",
DAP_FIELD(frameId, "frameId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutRequest,
"stepOut",
DAP_FIELD(granularity, "granularity"),
DAP_FIELD(singleThread, "singleThread"),
DAP_FIELD(threadId, "threadId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateRequest,
"terminate",
DAP_FIELD(restart, "restart"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsRequest,
"terminateThreads",
DAP_FIELD(threadIds, "threadIds"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsRequest, "threads");
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesRequest,
"variables",
DAP_FIELD(count, "count"),
DAP_FIELD(filter, "filter"),
DAP_FIELD(format, "format"),
DAP_FIELD(start, "start"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryRequest,
"writeMemory",
DAP_FIELD(allowPartial, "allowPartial"),
DAP_FIELD(data, "data"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(offset, "offset"));
} // namespace dap

View File

@@ -0,0 +1,243 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Generated with protocol_gen.go -- do not edit this file.
// go run scripts/protocol_gen/protocol_gen.go
//
// DAP version 1.59.0
#include "dap/protocol.h"
namespace dap {
DAP_IMPLEMENT_STRUCT_TYPEINFO(AttachResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocationsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(CancelResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionsResponse,
"",
DAP_FIELD(targets, "targets"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ConfigurationDoneResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueResponse,
"",
DAP_FIELD(allThreadsContinued,
"allThreadsContinued"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoResponse,
"",
DAP_FIELD(accessTypes, "accessTypes"),
DAP_FIELD(canPersist, "canPersist"),
DAP_FIELD(dataId, "dataId"),
DAP_FIELD(description, "description"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembleResponse,
"",
DAP_FIELD(instructions, "instructions"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisconnectResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ErrorResponse, "", DAP_FIELD(error, "error"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(EvaluateResponse,
"",
DAP_FIELD(indexedVariables, "indexedVariables"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(namedVariables, "namedVariables"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(result, "result"),
DAP_FIELD(type, "type"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionInfoResponse,
"",
DAP_FIELD(breakMode, "breakMode"),
DAP_FIELD(description, "description"),
DAP_FIELD(details, "details"),
DAP_FIELD(exceptionId, "exceptionId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTargetsResponse,
"",
DAP_FIELD(targets, "targets"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(
InitializeResponse,
"",
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"),
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
DAP_FIELD(supportsBreakpointLocationsRequest,
"supportsBreakpointLocationsRequest"),
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"),
DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"),
DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"),
DAP_FIELD(supportsConfigurationDoneRequest,
"supportsConfigurationDoneRequest"),
DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"),
DAP_FIELD(supportsDelayedStackTraceLoading,
"supportsDelayedStackTraceLoading"),
DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"),
DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"),
DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"),
DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
DAP_FIELD(supportsHitConditionalBreakpoints,
"supportsHitConditionalBreakpoints"),
DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"),
DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"),
DAP_FIELD(supportsLogPoints, "supportsLogPoints"),
DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"),
DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"),
DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"),
DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"),
DAP_FIELD(supportsSetExpression, "supportsSetExpression"),
DAP_FIELD(supportsSetVariable, "supportsSetVariable"),
DAP_FIELD(supportsSingleThreadExecutionRequests,
"supportsSingleThreadExecutionRequests"),
DAP_FIELD(supportsStepBack, "supportsStepBack"),
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"),
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
DAP_FIELD(supportsTerminateThreadsRequest,
"supportsTerminateThreadsRequest"),
DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"),
DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(LaunchResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(LoadedSourcesResponse,
"",
DAP_FIELD(sources, "sources"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ModulesResponse,
"",
DAP_FIELD(modules, "modules"),
DAP_FIELD(totalModules, "totalModules"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(NextResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(PauseResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReadMemoryResponse,
"",
DAP_FIELD(address, "address"),
DAP_FIELD(data, "data"),
DAP_FIELD(unreadableBytes, "unreadableBytes"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartFrameResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(RestartResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ReverseContinueResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(RunInTerminalResponse,
"",
DAP_FIELD(processId, "processId"),
DAP_FIELD(shellProcessId, "shellProcessId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ScopesResponse, "", DAP_FIELD(scopes, "scopes"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetDataBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionResponse,
"",
DAP_FIELD(indexedVariables, "indexedVariables"),
DAP_FIELD(namedVariables, "namedVariables"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(type, "type"),
DAP_FIELD(value, "value"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetFunctionBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse,
"",
DAP_FIELD(breakpoints, "breakpoints"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableResponse,
"",
DAP_FIELD(indexedVariables, "indexedVariables"),
DAP_FIELD(namedVariables, "namedVariables"),
DAP_FIELD(type, "type"),
DAP_FIELD(value, "value"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceResponse,
"",
DAP_FIELD(content, "content"),
DAP_FIELD(mimeType, "mimeType"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackTraceResponse,
"",
DAP_FIELD(stackFrames, "stackFrames"),
DAP_FIELD(totalFrames, "totalFrames"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StartDebuggingResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepBackResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTargetsResponse,
"",
DAP_FIELD(targets, "targets"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepOutResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(TerminateThreadsResponse, "");
DAP_IMPLEMENT_STRUCT_TYPEINFO(ThreadsResponse,
"",
DAP_FIELD(threads, "threads"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablesResponse,
"",
DAP_FIELD(variables, "variables"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(WriteMemoryResponse,
"",
DAP_FIELD(bytesWritten, "bytesWritten"),
DAP_FIELD(offset, "offset"));
} // namespace dap

View File

@@ -0,0 +1,316 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Generated with protocol_gen.go -- do not edit this file.
// go run scripts/protocol_gen/protocol_gen.go
//
// DAP version 1.59.0
#include "dap/protocol.h"
namespace dap {
DAP_IMPLEMENT_STRUCT_TYPEINFO(Checksum,
"",
DAP_FIELD(algorithm, "algorithm"),
DAP_FIELD(checksum, "checksum"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Source,
"",
DAP_FIELD(adapterData, "adapterData"),
DAP_FIELD(checksums, "checksums"),
DAP_FIELD(name, "name"),
DAP_FIELD(origin, "origin"),
DAP_FIELD(path, "path"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(sourceReference, "sourceReference"),
DAP_FIELD(sources, "sources"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(id, "id"),
DAP_FIELD(instructionReference,
"instructionReference"),
DAP_FIELD(line, "line"),
DAP_FIELD(message, "message"),
DAP_FIELD(offset, "offset"),
DAP_FIELD(source, "source"),
DAP_FIELD(verified, "verified"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointLocation,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(line, "line"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ColumnDescriptor,
"",
DAP_FIELD(attributeName, "attributeName"),
DAP_FIELD(format, "format"),
DAP_FIELD(label, "label"),
DAP_FIELD(type, "type"),
DAP_FIELD(width, "width"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter,
"",
DAP_FIELD(conditionDescription,
"conditionDescription"),
DAP_FIELD(def, "default"),
DAP_FIELD(description, "description"),
DAP_FIELD(filter, "filter"),
DAP_FIELD(label, "label"),
DAP_FIELD(supportsCondition,
"supportsCondition"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(
Capabilities,
"",
DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"),
DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"),
DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"),
DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"),
DAP_FIELD(supportTerminateDebuggee, "supportTerminateDebuggee"),
DAP_FIELD(supportedChecksumAlgorithms, "supportedChecksumAlgorithms"),
DAP_FIELD(supportsBreakpointLocationsRequest,
"supportsBreakpointLocationsRequest"),
DAP_FIELD(supportsCancelRequest, "supportsCancelRequest"),
DAP_FIELD(supportsClipboardContext, "supportsClipboardContext"),
DAP_FIELD(supportsCompletionsRequest, "supportsCompletionsRequest"),
DAP_FIELD(supportsConditionalBreakpoints, "supportsConditionalBreakpoints"),
DAP_FIELD(supportsConfigurationDoneRequest,
"supportsConfigurationDoneRequest"),
DAP_FIELD(supportsDataBreakpoints, "supportsDataBreakpoints"),
DAP_FIELD(supportsDelayedStackTraceLoading,
"supportsDelayedStackTraceLoading"),
DAP_FIELD(supportsDisassembleRequest, "supportsDisassembleRequest"),
DAP_FIELD(supportsEvaluateForHovers, "supportsEvaluateForHovers"),
DAP_FIELD(supportsExceptionFilterOptions, "supportsExceptionFilterOptions"),
DAP_FIELD(supportsExceptionInfoRequest, "supportsExceptionInfoRequest"),
DAP_FIELD(supportsExceptionOptions, "supportsExceptionOptions"),
DAP_FIELD(supportsFunctionBreakpoints, "supportsFunctionBreakpoints"),
DAP_FIELD(supportsGotoTargetsRequest, "supportsGotoTargetsRequest"),
DAP_FIELD(supportsHitConditionalBreakpoints,
"supportsHitConditionalBreakpoints"),
DAP_FIELD(supportsInstructionBreakpoints, "supportsInstructionBreakpoints"),
DAP_FIELD(supportsLoadedSourcesRequest, "supportsLoadedSourcesRequest"),
DAP_FIELD(supportsLogPoints, "supportsLogPoints"),
DAP_FIELD(supportsModulesRequest, "supportsModulesRequest"),
DAP_FIELD(supportsReadMemoryRequest, "supportsReadMemoryRequest"),
DAP_FIELD(supportsRestartFrame, "supportsRestartFrame"),
DAP_FIELD(supportsRestartRequest, "supportsRestartRequest"),
DAP_FIELD(supportsSetExpression, "supportsSetExpression"),
DAP_FIELD(supportsSetVariable, "supportsSetVariable"),
DAP_FIELD(supportsSingleThreadExecutionRequests,
"supportsSingleThreadExecutionRequests"),
DAP_FIELD(supportsStepBack, "supportsStepBack"),
DAP_FIELD(supportsStepInTargetsRequest, "supportsStepInTargetsRequest"),
DAP_FIELD(supportsSteppingGranularity, "supportsSteppingGranularity"),
DAP_FIELD(supportsTerminateRequest, "supportsTerminateRequest"),
DAP_FIELD(supportsTerminateThreadsRequest,
"supportsTerminateThreadsRequest"),
DAP_FIELD(supportsValueFormattingOptions, "supportsValueFormattingOptions"),
DAP_FIELD(supportsWriteMemoryRequest, "supportsWriteMemoryRequest"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(CompletionItem,
"",
DAP_FIELD(detail, "detail"),
DAP_FIELD(label, "label"),
DAP_FIELD(length, "length"),
DAP_FIELD(selectionLength, "selectionLength"),
DAP_FIELD(selectionStart, "selectionStart"),
DAP_FIELD(sortText, "sortText"),
DAP_FIELD(start, "start"),
DAP_FIELD(text, "text"),
DAP_FIELD(type, "type"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembledInstruction,
"",
DAP_FIELD(address, "address"),
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(instruction, "instruction"),
DAP_FIELD(instructionBytes, "instructionBytes"),
DAP_FIELD(line, "line"),
DAP_FIELD(location, "location"),
DAP_FIELD(symbol, "symbol"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Message,
"",
DAP_FIELD(format, "format"),
DAP_FIELD(id, "id"),
DAP_FIELD(sendTelemetry, "sendTelemetry"),
DAP_FIELD(showUser, "showUser"),
DAP_FIELD(url, "url"),
DAP_FIELD(urlLabel, "urlLabel"),
DAP_FIELD(variables, "variables"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(VariablePresentationHint,
"",
DAP_FIELD(attributes, "attributes"),
DAP_FIELD(kind, "kind"),
DAP_FIELD(lazy, "lazy"),
DAP_FIELD(visibility, "visibility"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ValueFormat, "", DAP_FIELD(hex, "hex"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionDetails,
"",
DAP_FIELD(evaluateName, "evaluateName"),
DAP_FIELD(fullTypeName, "fullTypeName"),
DAP_FIELD(innerException, "innerException"),
DAP_FIELD(message, "message"),
DAP_FIELD(stackTrace, "stackTrace"),
DAP_FIELD(typeName, "typeName"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(GotoTarget,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(id, "id"),
DAP_FIELD(instructionPointerReference,
"instructionPointerReference"),
DAP_FIELD(label, "label"),
DAP_FIELD(line, "line"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Module,
"",
DAP_FIELD(addressRange, "addressRange"),
DAP_FIELD(dateTimeStamp, "dateTimeStamp"),
DAP_FIELD(id, "id"),
DAP_FIELD(isOptimized, "isOptimized"),
DAP_FIELD(isUserCode, "isUserCode"),
DAP_FIELD(name, "name"),
DAP_FIELD(path, "path"),
DAP_FIELD(symbolFilePath, "symbolFilePath"),
DAP_FIELD(symbolStatus, "symbolStatus"),
DAP_FIELD(version, "version"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Scope,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(expensive, "expensive"),
DAP_FIELD(indexedVariables, "indexedVariables"),
DAP_FIELD(line, "line"),
DAP_FIELD(name, "name"),
DAP_FIELD(namedVariables, "namedVariables"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(source, "source"),
DAP_FIELD(variablesReference,
"variablesReference"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceBreakpoint,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(condition, "condition"),
DAP_FIELD(hitCondition, "hitCondition"),
DAP_FIELD(line, "line"),
DAP_FIELD(logMessage, "logMessage"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint,
"",
DAP_FIELD(accessType, "accessType"),
DAP_FIELD(condition, "condition"),
DAP_FIELD(dataId, "dataId"),
DAP_FIELD(hitCondition, "hitCondition"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionPathSegment,
"",
DAP_FIELD(names, "names"),
DAP_FIELD(negate, "negate"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions,
"",
DAP_FIELD(breakMode, "breakMode"),
DAP_FIELD(path, "path"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionFilterOptions,
"",
DAP_FIELD(condition, "condition"),
DAP_FIELD(filterId, "filterId"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint,
"",
DAP_FIELD(condition, "condition"),
DAP_FIELD(hitCondition, "hitCondition"),
DAP_FIELD(name, "name"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(InstructionBreakpoint,
"",
DAP_FIELD(condition, "condition"),
DAP_FIELD(hitCondition, "hitCondition"),
DAP_FIELD(instructionReference,
"instructionReference"),
DAP_FIELD(offset, "offset"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame,
"",
DAP_FIELD(canRestart, "canRestart"),
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(id, "id"),
DAP_FIELD(instructionPointerReference,
"instructionPointerReference"),
DAP_FIELD(line, "line"),
DAP_FIELD(moduleId, "moduleId"),
DAP_FIELD(name, "name"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(source, "source"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrameFormat,
"",
DAP_FIELD(includeAll, "includeAll"),
DAP_FIELD(line, "line"),
DAP_FIELD(module, "module"),
DAP_FIELD(parameterNames, "parameterNames"),
DAP_FIELD(parameterTypes, "parameterTypes"),
DAP_FIELD(parameterValues, "parameterValues"),
DAP_FIELD(parameters, "parameters"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(StepInTarget,
"",
DAP_FIELD(column, "column"),
DAP_FIELD(endColumn, "endColumn"),
DAP_FIELD(endLine, "endLine"),
DAP_FIELD(id, "id"),
DAP_FIELD(label, "label"),
DAP_FIELD(line, "line"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Thread,
"",
DAP_FIELD(id, "id"),
DAP_FIELD(name, "name"));
DAP_IMPLEMENT_STRUCT_TYPEINFO(Variable,
"",
DAP_FIELD(evaluateName, "evaluateName"),
DAP_FIELD(indexedVariables, "indexedVariables"),
DAP_FIELD(memoryReference, "memoryReference"),
DAP_FIELD(name, "name"),
DAP_FIELD(namedVariables, "namedVariables"),
DAP_FIELD(presentationHint, "presentationHint"),
DAP_FIELD(type, "type"),
DAP_FIELD(value, "value"),
DAP_FIELD(variablesReference,
"variablesReference"));
} // namespace dap

View File

@@ -0,0 +1,289 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rapid_json_serializer.h"
#include "null_json_serializer.h"
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
namespace dap {
namespace json {
RapidDeserializer::RapidDeserializer(const std::string& str)
: doc(new rapidjson::Document()) {
doc->Parse(str.c_str());
}
RapidDeserializer::RapidDeserializer(rapidjson::Value* json) : val(json) {}
RapidDeserializer::~RapidDeserializer() {
delete doc;
}
bool RapidDeserializer::deserialize(dap::boolean* v) const {
if (!json()->IsBool()) {
return false;
}
*v = json()->GetBool();
return true;
}
bool RapidDeserializer::deserialize(dap::integer* v) const {
if (json()->IsInt()) {
*v = json()->GetInt();
return true;
} else if (json()->IsUint()) {
*v = static_cast<int64_t>(json()->GetUint());
return true;
} else if (json()->IsInt64()) {
*v = json()->GetInt64();
return true;
} else if (json()->IsUint64()) {
*v = static_cast<int64_t>(json()->GetUint64());
return true;
}
return false;
}
bool RapidDeserializer::deserialize(dap::number* v) const {
if (!json()->IsNumber()) {
return false;
}
*v = json()->GetDouble();
return true;
}
bool RapidDeserializer::deserialize(dap::string* v) const {
if (!json()->IsString()) {
return false;
}
*v = json()->GetString();
return true;
}
bool RapidDeserializer::deserialize(dap::object* v) const {
v->reserve(json()->MemberCount());
for (auto el = json()->MemberBegin(); el != json()->MemberEnd(); el++) {
dap::any el_val;
RapidDeserializer d(&(el->value));
if (!d.deserialize(&el_val)) {
return false;
}
(*v)[el->name.GetString()] = el_val;
}
return true;
}
bool RapidDeserializer::deserialize(dap::any* v) const {
if (json()->IsBool()) {
*v = dap::boolean(json()->GetBool());
} else if (json()->IsDouble()) {
*v = dap::number(json()->GetDouble());
} else if (json()->IsInt()) {
*v = dap::integer(json()->GetInt());
} else if (json()->IsString()) {
*v = dap::string(json()->GetString());
} else if (json()->IsNull()) {
*v = null();
} else if (json()->IsObject()) {
dap::object obj;
if (!deserialize(&obj)) {
return false;
}
*v = obj;
} else if (json()->IsArray()){
dap::array<any> arr;
if (!deserialize(&arr)){
return false;
}
*v = arr;
} else {
return false;
}
return true;
}
size_t RapidDeserializer::count() const {
return json()->Size();
}
bool RapidDeserializer::array(
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json()->IsArray()) {
return false;
}
for (uint32_t i = 0; i < json()->Size(); i++) {
RapidDeserializer d(&(*json())[i]);
if (!cb(&d)) {
return false;
}
}
return true;
}
bool RapidDeserializer::field(
const std::string& name,
const std::function<bool(dap::Deserializer*)>& cb) const {
if (!json()->IsObject()) {
return false;
}
auto it = json()->FindMember(name.c_str());
if (it == json()->MemberEnd()) {
return cb(&NullDeserializer::instance);
}
RapidDeserializer d(&(it->value));
return cb(&d);
}
RapidSerializer::RapidSerializer()
: doc(new rapidjson::Document(rapidjson::kObjectType)),
allocator(doc->GetAllocator()) {}
RapidSerializer::RapidSerializer(rapidjson::Value* json,
rapidjson::Document::AllocatorType& allocator)
: val(json), allocator(allocator) {}
RapidSerializer::~RapidSerializer() {
delete doc;
}
std::string RapidSerializer::dump() const {
rapidjson::StringBuffer sb;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
json()->Accept(writer);
return sb.GetString();
}
bool RapidSerializer::serialize(dap::boolean v) {
json()->SetBool(v);
return true;
}
bool RapidSerializer::serialize(dap::integer v) {
json()->SetInt64(v);
return true;
}
bool RapidSerializer::serialize(dap::number v) {
json()->SetDouble(v);
return true;
}
bool RapidSerializer::serialize(const dap::string& v) {
json()->SetString(v.data(), static_cast<uint32_t>(v.length()), allocator);
return true;
}
bool RapidSerializer::serialize(const dap::object& v) {
if (!json()->IsObject()) {
json()->SetObject();
}
for (auto& it : v) {
if (!json()->HasMember(it.first.c_str())) {
rapidjson::Value name_value{it.first.c_str(), allocator};
json()->AddMember(name_value, rapidjson::Value(), allocator);
}
rapidjson::Value& member = (*json())[it.first.c_str()];
RapidSerializer s(&member, allocator);
if (!s.serialize(it.second)) {
return false;
}
}
return true;
}
bool RapidSerializer::serialize(const dap::any& v) {
if (v.is<dap::boolean>()) {
json()->SetBool((bool)v.get<dap::boolean>());
} else if (v.is<dap::integer>()) {
json()->SetInt64(v.get<dap::integer>());
} else if (v.is<dap::number>()) {
json()->SetDouble((double)v.get<dap::number>());
} else if (v.is<dap::string>()) {
auto s = v.get<dap::string>();
json()->SetString(s.data(), static_cast<uint32_t>(s.length()), allocator);
} else if (v.is<dap::object>()) {
// reachable if dap::object nested is inside other dap::object
return serialize(v.get<dap::object>());
} else if (v.is<dap::null>()) {
} else {
// reachable if array or custom serialized type is nested inside other dap::object
auto type = get_any_type(v);
auto value = get_any_val(v);
if (type && value) {
return type->serialize(this, value);
}
return false;
}
return true;
}
bool RapidSerializer::array(size_t count,
const std::function<bool(dap::Serializer*)>& cb) {
if (!json()->IsArray()) {
json()->SetArray();
}
while (count > json()->Size()) {
json()->PushBack(rapidjson::Value(), allocator);
}
for (uint32_t i = 0; i < count; i++) {
RapidSerializer s(&(*json())[i], allocator);
if (!cb(&s)) {
return false;
}
}
return true;
}
bool RapidSerializer::object(
const std::function<bool(dap::FieldSerializer*)>& cb) {
struct FS : public FieldSerializer {
rapidjson::Value* const json;
rapidjson::Document::AllocatorType& allocator;
FS(rapidjson::Value* json, rapidjson::Document::AllocatorType& allocator)
: json(json), allocator(allocator) {}
bool field(const std::string& name, const SerializeFunc& cb) override {
if (!json->HasMember(name.c_str())) {
rapidjson::Value name_value{name.c_str(), allocator};
json->AddMember(name_value, rapidjson::Value(), allocator);
}
rapidjson::Value& member = (*json)[name.c_str()];
RapidSerializer s(&member, allocator);
auto res = cb(&s);
if (s.removed) {
json->RemoveMember(name.c_str());
}
return res;
}
};
if (!json()->IsObject()) {
json()->SetObject();
}
FS fs{json(), allocator};
return cb(&fs);
}
void RapidSerializer::remove() {
removed = true;
}
} // namespace json
} // namespace dap

View File

@@ -0,0 +1,138 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_rapid_json_serializer_h
#define dap_rapid_json_serializer_h
#include "dap/protocol.h"
#include "dap/serialization.h"
#include "dap/types.h"
#include <rapidjson/document.h>
namespace dap {
namespace json {
struct RapidDeserializer : public dap::Deserializer {
explicit RapidDeserializer(const std::string&);
~RapidDeserializer();
// dap::Deserializer compliance
bool deserialize(boolean* v) const override;
bool deserialize(integer* v) const override;
bool deserialize(number* v) const override;
bool deserialize(string* v) const override;
bool deserialize(object* v) const override;
bool deserialize(any* v) const override;
size_t count() const override;
bool array(const std::function<bool(dap::Deserializer*)>&) const override;
bool field(const std::string& name,
const std::function<bool(dap::Deserializer*)>&) const override;
// Unhide base overloads
template <typename T>
inline bool field(const std::string& name, T* v) {
return dap::Deserializer::field(name, v);
}
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool deserialize(T* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::array<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool deserialize(dap::optional<T>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T0, typename... Types>
inline bool deserialize(dap::variant<T0, Types...>* v) const {
return dap::Deserializer::deserialize(v);
}
template <typename T>
inline bool field(const std::string& name, T* v) const {
return dap::Deserializer::deserialize(name, v);
}
inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; }
private:
RapidDeserializer(rapidjson::Value*);
rapidjson::Document* const doc = nullptr;
rapidjson::Value* const val = nullptr;
};
struct RapidSerializer : public dap::Serializer {
RapidSerializer();
~RapidSerializer();
std::string dump() const;
// dap::Serializer compliance
bool serialize(boolean v) override;
bool serialize(integer v) override;
bool serialize(number v) override;
bool serialize(const string& v) override;
bool serialize(const dap::object& v) override;
bool serialize(const any& v) override;
bool array(size_t count,
const std::function<bool(dap::Serializer*)>&) override;
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
void remove() override;
// Unhide base overloads
template <typename T,
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
inline bool serialize(const T& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::array<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T>
inline bool serialize(const dap::optional<T>& v) {
return dap::Serializer::serialize(v);
}
template <typename T0, typename... Types>
inline bool serialize(const dap::variant<T0, Types...>& v) {
return dap::Serializer::serialize(v);
}
inline bool serialize(const char* v) { return dap::Serializer::serialize(v); }
inline rapidjson::Value* json() const { return (val == nullptr) ? doc : val; }
private:
RapidSerializer(rapidjson::Value*, rapidjson::Document::AllocatorType&);
rapidjson::Document* const doc = nullptr;
rapidjson::Value* const val = nullptr;
rapidjson::Document::AllocatorType& allocator;
bool removed = false;
};
} // namespace json
} // namespace dap
#endif // dap_rapid_json_serializer_h

View File

@@ -0,0 +1,172 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef dap_rwmutex_h
#define dap_rwmutex_h
#include <condition_variable>
#include <mutex>
namespace dap {
////////////////////////////////////////////////////////////////////////////////
// RWMutex
////////////////////////////////////////////////////////////////////////////////
// A RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers or a single writer.
// Also known as a shared mutex.
class RWMutex {
public:
inline RWMutex() = default;
// lockReader() locks the mutex for reading.
// Multiple read locks can be held while there are no writer locks.
inline void lockReader();
// unlockReader() unlocks the mutex for reading.
inline void unlockReader();
// lockWriter() locks the mutex for writing.
// If the lock is already locked for reading or writing, lockWriter blocks
// until the lock is available.
inline void lockWriter();
// unlockWriter() unlocks the mutex for writing.
inline void unlockWriter();
private:
RWMutex(const RWMutex&) = delete;
RWMutex& operator=(const RWMutex&) = delete;
int readLocks = 0;
int pendingWriteLocks = 0;
std::mutex mutex;
std::condition_variable cv;
};
void RWMutex::lockReader() {
std::unique_lock<std::mutex> lock(mutex);
readLocks++;
}
void RWMutex::unlockReader() {
std::unique_lock<std::mutex> lock(mutex);
readLocks--;
if (readLocks == 0 && pendingWriteLocks > 0) {
cv.notify_one();
}
}
void RWMutex::lockWriter() {
std::unique_lock<std::mutex> lock(mutex);
if (readLocks > 0) {
pendingWriteLocks++;
cv.wait(lock, [&] { return readLocks == 0; });
pendingWriteLocks--;
}
lock.release(); // Keep lock held
}
void RWMutex::unlockWriter() {
if (pendingWriteLocks > 0) {
cv.notify_one();
}
mutex.unlock();
}
////////////////////////////////////////////////////////////////////////////////
// RLock
////////////////////////////////////////////////////////////////////////////////
// RLock is a RAII read lock helper for a RWMutex.
class RLock {
public:
inline RLock(RWMutex& mutex);
inline ~RLock();
inline RLock(RLock&&);
inline RLock& operator=(RLock&&);
private:
RLock(const RLock&) = delete;
RLock& operator=(const RLock&) = delete;
RWMutex* m;
};
RLock::RLock(RWMutex& mutex) : m(&mutex) {
m->lockReader();
}
RLock::~RLock() {
if (m != nullptr) {
m->unlockReader();
}
}
RLock::RLock(RLock&& other) {
m = other.m;
other.m = nullptr;
}
RLock& RLock::operator=(RLock&& other) {
m = other.m;
other.m = nullptr;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// WLock
////////////////////////////////////////////////////////////////////////////////
// WLock is a RAII write lock helper for a RWMutex.
class WLock {
public:
inline WLock(RWMutex& mutex);
inline ~WLock();
inline WLock(WLock&&);
inline WLock& operator=(WLock&&);
private:
WLock(const WLock&) = delete;
WLock& operator=(const WLock&) = delete;
RWMutex* m;
};
WLock::WLock(RWMutex& mutex) : m(&mutex) {
m->lockWriter();
}
WLock::~WLock() {
if (m != nullptr) {
m->unlockWriter();
}
}
WLock::WLock(WLock&& other) {
m = other.m;
other.m = nullptr;
}
WLock& WLock::operator=(WLock&& other) {
m = other.m;
other.m = nullptr;
return *this;
}
} // namespace dap
#endif

View File

@@ -0,0 +1,113 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rwmutex.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <array>
#include <thread>
#include <vector>
namespace {
constexpr const size_t NumThreads = 8;
}
// Check that WLock behaves like regular mutex.
TEST(RWMutex, WLock) {
dap::RWMutex rwmutex;
int counter = 0;
std::vector<std::thread> threads;
for (size_t i = 0; i < NumThreads; i++) {
threads.emplace_back([&] {
for (int j = 0; j < 1000; j++) {
dap::WLock lock(rwmutex);
counter++;
EXPECT_EQ(counter, 1);
counter--;
}
});
}
for (auto& thread : threads) {
thread.join();
}
EXPECT_EQ(counter, 0);
}
TEST(RWMutex, NoRLockWithWLock) {
dap::RWMutex rwmutex;
std::vector<std::thread> threads;
std::array<int, NumThreads> counters = {};
{ // With WLock held...
dap::WLock wlock(rwmutex);
for (size_t i = 0; i < counters.size(); i++) {
int* counter = &counters[i];
threads.emplace_back([&rwmutex, counter] {
dap::RLock lock(rwmutex);
for (int j = 0; j < 1000; j++) {
(*counter)++;
}
});
}
// RLocks should block
for (int counter : counters) {
EXPECT_EQ(counter, 0);
}
}
for (auto& thread : threads) {
thread.join();
}
for (int counter : counters) {
EXPECT_EQ(counter, 1000);
}
}
TEST(RWMutex, NoWLockWithRLock) {
dap::RWMutex rwmutex;
std::vector<std::thread> threads;
size_t counter = 0;
{ // With RLocks held...
dap::RLock rlockA(rwmutex);
dap::RLock rlockB(rwmutex);
dap::RLock rlockC(rwmutex);
for (size_t i = 0; i < NumThreads; i++) {
threads.emplace_back(std::thread([&] {
dap::WLock lock(rwmutex);
counter++;
}));
}
// ... WLocks should block
EXPECT_EQ(counter, 0U);
}
for (auto& thread : threads) {
thread.join();
}
EXPECT_EQ(counter, NumThreads);
}

View File

@@ -0,0 +1,516 @@
// 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);
}
std::function<void()> getPayload() override {
auto request = reader.read();
if (request.size() > 0) {
if (auto payload = processMessage(request)) {
return payload;
}
}
return {};
}
void connect(const std::shared_ptr<dap::Reader>& r,
const std::shared_ptr<dap::Writer>& w) override {
if (isBound.exchange(true)) {
handlers.error("Session::connect called twice");
return;
}
reader = dap::ContentReader(r);
writer = dap::ContentWriter(w);
}
void startProcessingMessages(
const ClosedHandler& onClose /* = {} */) override {
if (isProcessingMessages.exchange(true)) {
handlers.error("Session::startProcessingMessages() called twice");
return;
}
recvThread = std::thread([this, onClose] {
while (reader.isOpen()) {
if (auto payload = getPayload()) {
inbox.put(std::move(payload));
}
}
if (onClose) {
onClose();
}
});
dispatchThread = std::thread([this] {
while (auto payload = inbox.take()) {
payload.value()();
}
});
}
bool send(const dap::TypeInfo* requestTypeInfo,
const dap::TypeInfo* responseTypeInfo,
const void* request,
const GenericResponseHandler& responseHandler) override {
int seq = nextSeq++;
handlers.put(seq, responseTypeInfo, responseHandler);
dap::json::Serializer s;
if (!s.object([&](dap::FieldSerializer* fs) {
return fs->field("seq", dap::integer(seq)) &&
fs->field("type", "request") &&
fs->field("command", requestTypeInfo->name()) &&
fs->field("arguments", [&](dap::Serializer* s) {
return requestTypeInfo->serialize(s, request);
});
})) {
return false;
}
return send(s.dump());
}
bool send(const dap::TypeInfo* typeinfo, const void* event) override {
dap::json::Serializer s;
if (!s.object([&](dap::FieldSerializer* fs) {
return fs->field("seq", dap::integer(nextSeq++)) &&
fs->field("type", "event") &&
fs->field("event", typeinfo->name()) &&
fs->field("body", [&](dap::Serializer* s) {
return typeinfo->serialize(s, event);
});
})) {
return false;
}
return send(s.dump());
}
~Impl() {
inbox.close();
reader.close();
writer.close();
if (recvThread.joinable()) {
recvThread.join();
}
if (dispatchThread.joinable()) {
dispatchThread.join();
}
}
private:
using Payload = std::function<void()>;
class EventHandlers {
public:
void put(const ErrorHandler& handler) {
std::unique_lock<std::mutex> lock(errorMutex);
errorHandler = handler;
}
void error(const char* format, ...) {
va_list vararg;
va_start(vararg, format);
std::unique_lock<std::mutex> lock(errorMutex);
errorLocked(format, vararg);
va_end(vararg);
}
std::pair<const dap::TypeInfo*, GenericRequestHandler> request(
const std::string& name) {
std::unique_lock<std::mutex> lock(requestMutex);
auto it = requestMap.find(name);
return (it != requestMap.end()) ? it->second : decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericRequestHandler& handler) {
std::unique_lock<std::mutex> lock(requestMutex);
auto added =
requestMap
.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
.second;
if (!added) {
errorfLocked("Request handler for '%s' already registered",
typeinfo->name().c_str());
}
}
std::pair<const dap::TypeInfo*, GenericResponseHandler> response(
int64_t seq) {
std::unique_lock<std::mutex> lock(responseMutex);
auto responseIt = responseMap.find(seq);
if (responseIt == responseMap.end()) {
errorfLocked("Unknown response with sequence %d", seq);
return {};
}
auto out = std::move(responseIt->second);
responseMap.erase(seq);
return out;
}
void put(int seq,
const dap::TypeInfo* typeinfo,
const GenericResponseHandler& handler) {
std::unique_lock<std::mutex> lock(responseMutex);
auto added =
responseMap.emplace(seq, std::make_pair(typeinfo, handler)).second;
if (!added) {
errorfLocked("Response handler for sequence %d already registered",
seq);
}
}
std::pair<const dap::TypeInfo*, GenericEventHandler> event(
const std::string& name) {
std::unique_lock<std::mutex> lock(eventMutex);
auto it = eventMap.find(name);
return (it != eventMap.end()) ? it->second : decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericEventHandler& handler) {
std::unique_lock<std::mutex> lock(eventMutex);
auto added =
eventMap.emplace(typeinfo->name(), std::make_pair(typeinfo, handler))
.second;
if (!added) {
errorfLocked("Event handler for '%s' already registered",
typeinfo->name().c_str());
}
}
GenericResponseSentHandler responseSent(const dap::TypeInfo* typeinfo) {
std::unique_lock<std::mutex> lock(responseSentMutex);
auto it = responseSentMap.find(typeinfo);
return (it != responseSentMap.end()) ? it->second
: decltype(it->second){};
}
void put(const dap::TypeInfo* typeinfo,
const GenericResponseSentHandler& handler) {
std::unique_lock<std::mutex> lock(responseSentMutex);
auto added = responseSentMap.emplace(typeinfo, handler).second;
if (!added) {
errorfLocked("Response sent handler for '%s' already registered",
typeinfo->name().c_str());
}
}
private:
void errorfLocked(const char* format, ...) {
va_list vararg;
va_start(vararg, format);
errorLocked(format, vararg);
va_end(vararg);
}
void errorLocked(const char* format, va_list args) {
char buf[2048];
vsnprintf(buf, sizeof(buf), format, args);
if (errorHandler) {
errorHandler(buf);
}
}
std::mutex errorMutex;
ErrorHandler errorHandler;
std::mutex requestMutex;
std::unordered_map<std::string,
std::pair<const dap::TypeInfo*, GenericRequestHandler>>
requestMap;
std::mutex responseMutex;
std::unordered_map<int64_t,
std::pair<const dap::TypeInfo*, GenericResponseHandler>>
responseMap;
std::mutex eventMutex;
std::unordered_map<std::string,
std::pair<const dap::TypeInfo*, GenericEventHandler>>
eventMap;
std::mutex responseSentMutex;
std::unordered_map<const dap::TypeInfo*, GenericResponseSentHandler>
responseSentMap;
}; // EventHandlers
Payload processMessage(const std::string& str) {
auto d = dap::json::Deserializer(str);
dap::string type;
if (!d.field("type", &type)) {
handlers.error("Message missing string 'type' field");
return {};
}
dap::integer sequence = 0;
if (!d.field("seq", &sequence)) {
handlers.error("Message missing number 'seq' field");
return {};
}
if (type == "request") {
return processRequest(&d, sequence);
} else if (type == "event") {
return processEvent(&d);
} else if (type == "response") {
processResponse(&d);
return {};
} else {
handlers.error("Unknown message type '%s'", type.c_str());
}
return {};
}
Payload processRequest(dap::json::Deserializer* d, dap::integer sequence) {
dap::string command;
if (!d->field("command", &command)) {
handlers.error("Request missing string 'command' field");
return {};
}
const dap::TypeInfo* typeinfo;
GenericRequestHandler handler;
std::tie(typeinfo, handler) = handlers.request(command);
if (!typeinfo) {
handlers.error("No request handler registered for command '%s'",
command.c_str());
return {};
}
auto data = new uint8_t[typeinfo->size()];
typeinfo->construct(data);
if (!d->field("arguments", [&](dap::Deserializer* d) {
return typeinfo->deserialize(d, data);
})) {
handlers.error("Failed to deserialize request");
typeinfo->destruct(data);
delete[] data;
return {};
}
return [=] {
handler(
data,
[=](const dap::TypeInfo* typeinfo, const void* data) {
// onSuccess
dap::json::Serializer s;
s.object([&](dap::FieldSerializer* fs) {
return fs->field("seq", dap::integer(nextSeq++)) &&
fs->field("type", "response") &&
fs->field("request_seq", sequence) &&
fs->field("success", dap::boolean(true)) &&
fs->field("command", command) &&
fs->field("body", [&](dap::Serializer* s) {
return typeinfo->serialize(s, data);
});
});
send(s.dump());
if (auto handler = handlers.responseSent(typeinfo)) {
handler(data, nullptr);
}
},
[=](const dap::TypeInfo* typeinfo, const dap::Error& error) {
// onError
dap::json::Serializer s;
s.object([&](dap::FieldSerializer* fs) {
return fs->field("seq", dap::integer(nextSeq++)) &&
fs->field("type", "response") &&
fs->field("request_seq", sequence) &&
fs->field("success", dap::boolean(false)) &&
fs->field("command", command) &&
fs->field("message", error.message);
});
send(s.dump());
if (auto handler = handlers.responseSent(typeinfo)) {
handler(nullptr, &error);
}
});
typeinfo->destruct(data);
delete[] data;
};
}
Payload processEvent(dap::json::Deserializer* d) {
dap::string event;
if (!d->field("event", &event)) {
handlers.error("Event missing string 'event' field");
return {};
}
const dap::TypeInfo* typeinfo;
GenericEventHandler handler;
std::tie(typeinfo, handler) = handlers.event(event);
if (!typeinfo) {
handlers.error("No event handler registered for event '%s'",
event.c_str());
return {};
}
auto data = new uint8_t[typeinfo->size()];
typeinfo->construct(data);
// "body" is an optional field for some events, such as "Terminated Event".
bool body_ok = true;
d->field("body", [&](dap::Deserializer* d) {
if (!typeinfo->deserialize(d, data)) {
body_ok = false;
}
return true;
});
if (!body_ok) {
handlers.error("Failed to deserialize event '%s' body", event.c_str());
typeinfo->destruct(data);
delete[] data;
return {};
}
return [=] {
handler(data);
typeinfo->destruct(data);
delete[] data;
};
}
void processResponse(const dap::Deserializer* d) {
dap::integer requestSeq = 0;
if (!d->field("request_seq", &requestSeq)) {
handlers.error("Response missing int 'request_seq' field");
return;
}
const dap::TypeInfo* typeinfo;
GenericResponseHandler handler;
std::tie(typeinfo, handler) = handlers.response(requestSeq);
if (!typeinfo) {
handlers.error("Unknown response with sequence %d", requestSeq);
return;
}
dap::boolean success = false;
if (!d->field("success", &success)) {
handlers.error("Response missing int 'success' field");
return;
}
if (success) {
auto data = std::unique_ptr<uint8_t[]>(new uint8_t[typeinfo->size()]);
typeinfo->construct(data.get());
// "body" field in Response is an optional field.
d->field("body", [&](const dap::Deserializer* d) {
return typeinfo->deserialize(d, data.get());
});
handler(data.get(), nullptr);
typeinfo->destruct(data.get());
} else {
std::string message;
if (!d->field("message", &message)) {
handlers.error("Failed to deserialize message");
return;
}
auto error = dap::Error("%s", message.c_str());
handler(nullptr, &error);
}
}
bool send(const std::string& s) {
std::unique_lock<std::mutex> lock(sendMutex);
if (!writer.isOpen()) {
handlers.error("Send failed as the writer is closed");
return false;
}
return writer.write(s);
}
std::atomic<bool> isBound = {false};
std::atomic<bool> isProcessingMessages = {false};
dap::ContentReader reader;
dap::ContentWriter writer;
std::atomic<bool> shutdown = {false};
EventHandlers handlers;
std::thread recvThread;
std::thread dispatchThread;
dap::Chan<Payload> inbox;
std::atomic<uint32_t> nextSeq = {1};
std::mutex sendMutex;
};
} // anonymous namespace
namespace dap {
Error::Error(const std::string& message) : message(message) {}
Error::Error(const char* msg, ...) {
char buf[2048];
va_list vararg;
va_start(vararg, msg);
vsnprintf(buf, sizeof(buf), msg, vararg);
va_end(vararg);
message = buf;
}
Session::~Session() = default;
std::unique_ptr<Session> Session::create() {
return std::unique_ptr<Session>(new Impl());
}
} // namespace dap

View File

@@ -0,0 +1,625 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/session.h"
#include "dap/io.h"
#include "dap/protocol.h"
#include "chan.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <array>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
namespace dap {
struct TestResponse : public Response {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestResponse,
"test-response",
DAP_FIELD(b, "res_b"),
DAP_FIELD(i, "res_i"),
DAP_FIELD(n, "res_n"),
DAP_FIELD(a, "res_a"),
DAP_FIELD(o, "res_o"),
DAP_FIELD(s, "res_s"),
DAP_FIELD(o1, "res_o1"),
DAP_FIELD(o2, "res_o2"));
struct TestRequest : public Request {
using Response = TestResponse;
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestRequest,
"test-request",
DAP_FIELD(b, "req_b"),
DAP_FIELD(i, "req_i"),
DAP_FIELD(n, "req_n"),
DAP_FIELD(a, "req_a"),
DAP_FIELD(o, "req_o"),
DAP_FIELD(s, "req_s"),
DAP_FIELD(o1, "req_o1"),
DAP_FIELD(o2, "req_o2"));
struct TestEvent : public Event {
boolean b;
integer i;
number n;
array<integer> a;
object o;
string s;
optional<integer> o1;
optional<integer> o2;
};
DAP_STRUCT_TYPEINFO(TestEvent,
"test-event",
DAP_FIELD(b, "evt_b"),
DAP_FIELD(i, "evt_i"),
DAP_FIELD(n, "evt_n"),
DAP_FIELD(a, "evt_a"),
DAP_FIELD(o, "evt_o"),
DAP_FIELD(s, "evt_s"),
DAP_FIELD(o1, "evt_o1"),
DAP_FIELD(o2, "evt_o2"));
}; // namespace dap
namespace {
dap::TestRequest createRequest() {
dap::TestRequest request;
request.b = false;
request.i = 72;
request.n = 9.87;
request.a = {2, 5, 7, 8};
request.o = {
std::make_pair("a", dap::integer(1)),
std::make_pair("b", dap::number(2)),
std::make_pair("c", dap::string("3")),
};
request.s = "request";
request.o2 = 42;
return request;
}
dap::TestResponse createResponse() {
dap::TestResponse response;
response.b = true;
response.i = 99;
response.n = 123.456;
response.a = {5, 4, 3, 2, 1};
response.o = {
std::make_pair("one", dap::integer(1)),
std::make_pair("two", dap::number(2)),
std::make_pair("three", dap::string("3")),
};
response.s = "ROGER";
response.o1 = 50;
return response;
}
dap::TestEvent createEvent() {
dap::TestEvent event;
event.b = false;
event.i = 72;
event.n = 9.87;
event.a = {2, 5, 7, 8};
event.o = {
std::make_pair("a", dap::integer(1)),
std::make_pair("b", dap::number(2)),
std::make_pair("c", dap::string("3")),
};
event.s = "event";
event.o2 = 42;
return event;
}
} // anonymous namespace
class SessionTest : public testing::Test {
public:
void bind() {
auto client2server = dap::pipe();
auto server2client = dap::pipe();
client->bind(server2client, client2server);
server->bind(client2server, server2client);
}
std::unique_ptr<dap::Session> client = dap::Session::create();
std::unique_ptr<dap::Session> server = dap::Session::create();
};
TEST_F(SessionTest, Request) {
dap::TestRequest received;
server->registerHandler([&](const dap::TestRequest& req) {
received = req;
return createResponse();
});
bind();
auto request = createRequest();
client->send(request).get();
// Check request was received correctly.
ASSERT_EQ(received.b, request.b);
ASSERT_EQ(received.i, request.i);
ASSERT_EQ(received.n, request.n);
ASSERT_EQ(received.a, request.a);
ASSERT_EQ(received.o.size(), 3U);
ASSERT_EQ(received.o["a"].get<dap::integer>(),
request.o["a"].get<dap::integer>());
ASSERT_EQ(received.o["b"].get<dap::number>(),
request.o["b"].get<dap::number>());
ASSERT_EQ(received.o["c"].get<dap::string>(),
request.o["c"].get<dap::string>());
ASSERT_EQ(received.s, request.s);
ASSERT_EQ(received.o1, request.o1);
ASSERT_EQ(received.o2, request.o2);
}
TEST_F(SessionTest, RequestResponseSuccess) {
server->registerHandler(
[&](const dap::TestRequest&) { return createResponse(); });
bind();
auto request = createRequest();
auto response = client->send(request);
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.b, dap::boolean(true));
ASSERT_EQ(got.response.i, dap::integer(99));
ASSERT_EQ(got.response.n, dap::number(123.456));
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
ASSERT_EQ(got.response.o.size(), 3U);
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
ASSERT_EQ(got.response.s, "ROGER");
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
ASSERT_FALSE(got.response.o2.has_value());
}
TEST_F(SessionTest, BreakPointRequestResponseSuccess) {
server->registerHandler([&](const dap::SetBreakpointsRequest&) {
dap::SetBreakpointsResponse response;
dap::Breakpoint bp;
bp.line = 2;
response.breakpoints.emplace_back(std::move(bp));
return response;
});
bind();
auto got = client->send(dap::SetBreakpointsRequest{}).get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.breakpoints.size(), 1U);
}
TEST_F(SessionTest, RequestResponseOrError) {
server->registerHandler(
[&](const dap::TestRequest&) -> dap::ResponseOrError<dap::TestResponse> {
return dap::Error("Oh noes!");
});
bind();
auto response = client->send(createRequest());
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, RequestResponseError) {
server->registerHandler(
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
bind();
auto response = client->send(createRequest());
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, RequestCallbackResponse) {
using ResponseCallback = std::function<void(dap::SetBreakpointsResponse)>;
server->registerHandler(
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
dap::SetBreakpointsResponse response;
dap::Breakpoint bp;
bp.line = 2;
response.breakpoints.emplace_back(std::move(bp));
callback(response);
});
bind();
auto got = client->send(dap::SetBreakpointsRequest{}).get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.breakpoints.size(), 1U);
}
TEST_F(SessionTest, RequestCallbackResponseOrError) {
using ResponseCallback =
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
server->registerHandler(
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
dap::SetBreakpointsResponse response;
dap::Breakpoint bp;
bp.line = 2;
response.breakpoints.emplace_back(std::move(bp));
callback(response);
});
bind();
auto got = client->send(dap::SetBreakpointsRequest{}).get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.breakpoints.size(), 1U);
}
TEST_F(SessionTest, RequestCallbackError) {
using ResponseCallback =
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
server->registerHandler(
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
callback(dap::Error("Oh noes!"));
});
bind();
auto got = client->send(dap::SetBreakpointsRequest{}).get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, RequestCallbackSuccessAfterReturn) {
using ResponseCallback =
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
ResponseCallback callback;
std::mutex mutex;
std::condition_variable cv;
server->registerHandler(
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
std::unique_lock<std::mutex> lock(mutex);
callback = cb;
cv.notify_all();
});
bind();
auto future = client->send(dap::SetBreakpointsRequest{});
{
dap::SetBreakpointsResponse response;
dap::Breakpoint bp;
bp.line = 2;
response.breakpoints.emplace_back(std::move(bp));
// Wait for the handler to be called.
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return static_cast<bool>(callback); });
// Issue the callback
callback(response);
}
auto got = future.get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.breakpoints.size(), 1U);
}
TEST_F(SessionTest, RequestCallbackErrorAfterReturn) {
using ResponseCallback =
std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
ResponseCallback callback;
std::mutex mutex;
std::condition_variable cv;
server->registerHandler(
[&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
std::unique_lock<std::mutex> lock(mutex);
callback = cb;
cv.notify_all();
});
bind();
auto future = client->send(dap::SetBreakpointsRequest{});
{
// Wait for the handler to be called.
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return static_cast<bool>(callback); });
// Issue the callback
callback(dap::Error("Oh noes!"));
}
auto got = future.get();
// Check response was received correctly.
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, ResponseSentHandlerSuccess) {
const auto response = createResponse();
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
server->registerHandler([&](const dap::TestRequest&) { return response; });
server->registerSentHandler(
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
bind();
client->send(createRequest());
auto got = chan.take().value();
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.b, dap::boolean(true));
ASSERT_EQ(got.response.i, dap::integer(99));
ASSERT_EQ(got.response.n, dap::number(123.456));
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
ASSERT_EQ(got.response.o.size(), 3U);
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
ASSERT_EQ(got.response.s, "ROGER");
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
ASSERT_FALSE(got.response.o2.has_value());
}
TEST_F(SessionTest, ResponseSentHandlerError) {
dap::Chan<dap::ResponseOrError<dap::TestResponse>> chan;
server->registerHandler(
[&](const dap::TestRequest&) { return dap::Error("Oh noes!"); });
server->registerSentHandler(
[&](const dap::ResponseOrError<dap::TestResponse> r) { chan.put(r); });
bind();
client->send(createRequest());
auto got = chan.take().value();
ASSERT_EQ(got.error, true);
ASSERT_EQ(got.error.message, "Oh noes!");
}
TEST_F(SessionTest, Event) {
dap::Chan<dap::TestEvent> received;
server->registerHandler([&](const dap::TestEvent& e) { received.put(e); });
bind();
auto event = createEvent();
client->send(event);
// Check event was received correctly.
auto got = received.take().value();
ASSERT_EQ(got.b, event.b);
ASSERT_EQ(got.i, event.i);
ASSERT_EQ(got.n, event.n);
ASSERT_EQ(got.a, event.a);
ASSERT_EQ(got.o.size(), 3U);
ASSERT_EQ(got.o["a"].get<dap::integer>(), event.o["a"].get<dap::integer>());
ASSERT_EQ(got.o["b"].get<dap::number>(), event.o["b"].get<dap::number>());
ASSERT_EQ(got.o["c"].get<dap::string>(), event.o["c"].get<dap::string>());
ASSERT_EQ(got.s, event.s);
ASSERT_EQ(got.o1, event.o1);
ASSERT_EQ(got.o2, event.o2);
}
TEST_F(SessionTest, RegisterHandlerFunction) {
struct S {
static dap::TestResponse requestA(const dap::TestRequest&) { return {}; }
static dap::Error requestB(const dap::TestRequest&) { return {}; }
static dap::ResponseOrError<dap::TestResponse> requestC(
const dap::TestRequest&) {
return dap::Error();
}
static void event(const dap::TestEvent&) {}
static void sent(const dap::ResponseOrError<dap::TestResponse>&) {}
};
client->registerHandler(&S::requestA);
client->registerHandler(&S::requestB);
client->registerHandler(&S::requestC);
client->registerHandler(&S::event);
client->registerSentHandler(&S::sent);
}
TEST_F(SessionTest, SendRequestNoBind) {
bool errored = false;
client->onError([&](const std::string&) { errored = true; });
auto res = client->send(createRequest()).get();
ASSERT_TRUE(errored);
ASSERT_TRUE(res.error);
}
TEST_F(SessionTest, SendEventNoBind) {
bool errored = false;
client->onError([&](const std::string&) { errored = true; });
client->send(createEvent());
ASSERT_TRUE(errored);
}
TEST_F(SessionTest, SingleThread) {
server->registerHandler(
[&](const dap::TestRequest&) { return createResponse(); });
// Explicitly connect and process request on this test thread instead of
// calling bind() which inturn starts processing messages immediately on a new
// thread.
auto client2server = dap::pipe();
auto server2client = dap::pipe();
client->connect(server2client, client2server);
server->connect(client2server, server2client);
auto request = createRequest();
auto response = client->send(request);
// Process request and response on this thread
if (auto payload = server->getPayload()) {
payload();
}
if (auto payload = client->getPayload()) {
payload();
}
auto got = response.get();
// Check response was received correctly.
ASSERT_EQ(got.error, false);
ASSERT_EQ(got.response.b, dap::boolean(true));
ASSERT_EQ(got.response.i, dap::integer(99));
ASSERT_EQ(got.response.n, dap::number(123.456));
ASSERT_EQ(got.response.a, dap::array<dap::integer>({5, 4, 3, 2, 1}));
ASSERT_EQ(got.response.o.size(), 3U);
ASSERT_EQ(got.response.o["one"].get<dap::integer>(), dap::integer(1));
ASSERT_EQ(got.response.o["two"].get<dap::number>(), dap::number(2));
ASSERT_EQ(got.response.o["three"].get<dap::string>(), dap::string("3"));
ASSERT_EQ(got.response.s, "ROGER");
ASSERT_EQ(got.response.o1, dap::optional<dap::integer>(50));
ASSERT_FALSE(got.response.o2.has_value());
}
TEST_F(SessionTest, Concurrency) {
std::atomic<int> numEventsHandled = {0};
std::atomic<bool> done = {false};
server->registerHandler(
[](const dap::TestRequest&) { return dap::TestResponse(); });
server->registerHandler([&](const dap::TestEvent&) {
if (numEventsHandled++ > 10000) {
done = true;
}
});
bind();
constexpr int numThreads = 32;
std::array<std::thread, numThreads> threads;
for (int i = 0; i < numThreads; i++) {
threads[i] = std::thread([&] {
while (!done) {
client->send(createEvent());
client->send(createRequest());
}
});
}
for (int i = 0; i < numThreads; i++) {
threads[i].join();
}
client.reset();
server.reset();
}
TEST_F(SessionTest, OnClientClosed) {
std::mutex mutex;
std::condition_variable cv;
bool clientClosed = false;
auto client2server = dap::pipe();
auto server2client = dap::pipe();
client->bind(server2client, client2server);
server->bind(client2server, server2client, [&] {
std::unique_lock<std::mutex> lock(mutex);
clientClosed = true;
cv.notify_all();
});
client.reset();
// Wait for the client closed handler to be called.
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return static_cast<bool>(clientClosed); });
}
TEST_F(SessionTest, OnServerClosed) {
std::mutex mutex;
std::condition_variable cv;
bool serverClosed = false;
auto client2server = dap::pipe();
auto server2client = dap::pipe();
client->bind(server2client, client2server, [&] {
std::unique_lock<std::mutex> lock(mutex);
serverClosed = true;
cv.notify_all();
});
server->bind(client2server, server2client);
server.reset();
// Wait for the client closed handler to be called.
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [&] { return static_cast<bool>(serverClosed); });
}

View File

@@ -0,0 +1,333 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "socket.h"
#include "rwmutex.h"
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#if defined(_WIN32)
#include <atomic>
namespace {
std::atomic<int> wsaInitCount = {0};
} // anonymous namespace
#else
#include <fcntl.h>
#include <unistd.h>
namespace {
using SOCKET = int;
} // anonymous namespace
#endif
namespace {
constexpr SOCKET InvalidSocket = static_cast<SOCKET>(-1);
void init() {
#if defined(_WIN32)
if (wsaInitCount++ == 0) {
WSADATA winsockData;
(void)WSAStartup(MAKEWORD(2, 2), &winsockData);
}
#endif
}
void term() {
#if defined(_WIN32)
if (--wsaInitCount == 0) {
WSACleanup();
}
#endif
}
bool setBlocking(SOCKET s, bool blocking) {
#if defined(_WIN32)
u_long mode = blocking ? 0 : 1;
return ioctlsocket(s, FIONBIO, &mode) == NO_ERROR;
#else
auto arg = fcntl(s, F_GETFL, nullptr);
if (arg < 0) {
return false;
}
arg = blocking ? (arg & ~O_NONBLOCK) : (arg | O_NONBLOCK);
return fcntl(s, F_SETFL, arg) >= 0;
#endif
}
bool errored(SOCKET s) {
if (s == InvalidSocket) {
return true;
}
char error = 0;
socklen_t len = sizeof(error);
getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len);
return error != 0;
}
} // anonymous namespace
class dap::Socket::Shared : public dap::ReaderWriter {
public:
static std::shared_ptr<Shared> create(const char* address, const char* port) {
init();
addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
addrinfo* info = nullptr;
getaddrinfo(address, port, &hints, &info);
if (info) {
auto socket =
::socket(info->ai_family, info->ai_socktype, info->ai_protocol);
auto out = std::make_shared<Shared>(info, socket);
out->setOptions();
return out;
}
freeaddrinfo(info);
term();
return nullptr;
}
Shared(SOCKET socket) : info(nullptr), s(socket) {}
Shared(addrinfo* info, SOCKET socket) : info(info), s(socket) {}
~Shared() {
freeaddrinfo(info);
close();
term();
}
template <typename FUNCTION>
void lock(FUNCTION&& f) {
RLock l(mutex);
f(s, info);
}
void setOptions() {
RLock l(mutex);
if (s == InvalidSocket) {
return;
}
int enable = 1;
#if !defined(_WIN32)
// Prevent sockets lingering after process termination, causing
// reconnection issues on the same port.
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable));
struct {
int l_onoff; /* linger active */
int l_linger; /* how many seconds to linger for */
} linger = {false, 0};
setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
#endif // !defined(_WIN32)
// Enable TCP_NODELAY.
// DAP usually consists of small packet requests, with small packet
// responses. When there are many frequent, blocking requests made,
// Nagle's algorithm can dramatically limit the request->response rates.
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&enable, sizeof(enable));
}
// dap::ReaderWriter compliance
bool isOpen() {
{
RLock l(mutex);
if ((s != InvalidSocket) && !errored(s)) {
return true;
}
}
WLock lock(mutex);
s = InvalidSocket;
return false;
}
void close() {
{
RLock l(mutex);
if (s != InvalidSocket) {
#if defined(_WIN32)
closesocket(s);
#elif __APPLE__
// ::shutdown() *should* be sufficient to unblock ::accept(), but
// apparently on macos it can return ENOTCONN and ::accept() continues
// to block indefinitely.
// Note: There is a race here. Calling ::close() frees the socket ID,
// which may be reused before `s` is assigned InvalidSocket.
::shutdown(s, SHUT_RDWR);
::close(s);
#else
// ::shutdown() to unblock ::accept(). We'll actually close the socket
// under lock below.
::shutdown(s, SHUT_RDWR);
#endif
}
}
WLock l(mutex);
if (s != InvalidSocket) {
#if !defined(_WIN32) && !defined(__APPLE__)
::close(s);
#endif
s = InvalidSocket;
}
}
size_t read(void* buffer, size_t bytes) {
RLock lock(mutex);
if (s == InvalidSocket) {
return 0;
}
auto len =
recv(s, reinterpret_cast<char*>(buffer), static_cast<int>(bytes), 0);
return (len < 0) ? 0 : len;
}
bool write(const void* buffer, size_t bytes) {
RLock lock(mutex);
if (s == InvalidSocket) {
return false;
}
if (bytes == 0) {
return true;
}
return ::send(s, reinterpret_cast<const char*>(buffer),
static_cast<int>(bytes), 0) > 0;
}
private:
addrinfo* const info;
SOCKET s = InvalidSocket;
RWMutex mutex;
};
namespace dap {
Socket::Socket(const char* address, const char* port)
: shared(Shared::create(address, port)) {
if (shared) {
shared->lock([&](SOCKET socket, const addrinfo* info) {
if (bind(socket, info->ai_addr, (int)info->ai_addrlen) != 0) {
shared.reset();
return;
}
if (listen(socket, 0) != 0) {
shared.reset();
return;
}
});
}
}
std::shared_ptr<ReaderWriter> Socket::accept() const {
std::shared_ptr<Shared> out;
if (shared) {
shared->lock([&](SOCKET socket, const addrinfo*) {
if (socket != InvalidSocket && !errored(socket)) {
init();
auto accepted = ::accept(socket, 0, 0);
if (accepted != InvalidSocket) {
out = std::make_shared<Shared>(accepted);
out->setOptions();
}
}
});
}
return out;
}
bool Socket::isOpen() const {
if (shared) {
return shared->isOpen();
}
return false;
}
void Socket::close() const {
if (shared) {
shared->close();
}
}
std::shared_ptr<ReaderWriter> Socket::connect(const char* address,
const char* port,
uint32_t timeoutMillis) {
auto shared = Shared::create(address, port);
if (!shared) {
return nullptr;
}
std::shared_ptr<ReaderWriter> out;
shared->lock([&](SOCKET socket, const addrinfo* info) {
if (socket == InvalidSocket) {
return;
}
if (timeoutMillis == 0) {
if (::connect(socket, info->ai_addr, (int)info->ai_addrlen) == 0) {
out = shared;
}
return;
}
if (!setBlocking(socket, false)) {
return;
}
auto res = ::connect(socket, info->ai_addr, (int)info->ai_addrlen);
if (res == 0) {
if (setBlocking(socket, true)) {
out = shared;
}
} else {
const auto microseconds = timeoutMillis * 1000;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(socket, &fdset);
timeval tv;
tv.tv_sec = microseconds / 1000000;
tv.tv_usec = microseconds - static_cast<uint32_t>(tv.tv_sec * 1000000);
res = select(static_cast<int>(socket + 1), nullptr, &fdset, nullptr, &tv);
if (res > 0 && !errored(socket) && setBlocking(socket, true)) {
out = shared;
}
}
});
if (!out) {
return nullptr;
}
return out->isOpen() ? out : nullptr;
}
} // namespace dap

View File

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

View File

@@ -0,0 +1,104 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "socket.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <chrono>
#include <thread>
#include <vector>
// Basic socket send & receive test
TEST(Socket, SendRecv) {
const char* port = "19021";
auto server = dap::Socket("localhost", port);
auto client = dap::Socket::connect("localhost", port, 0);
ASSERT_TRUE(client != nullptr);
const std::string expect = "Hello World!";
std::string read;
auto thread = std::thread([&] {
auto conn = server.accept();
ASSERT_TRUE(conn != nullptr);
char c;
while (conn->read(&c, 1) != 0) {
read += c;
}
});
ASSERT_TRUE(client->write(expect.data(), expect.size()));
client->close();
thread.join();
ASSERT_EQ(read, expect);
}
// See https://github.com/google/cppdap/issues/37
TEST(Socket, CloseOnDifferentThread) {
const char* port = "19021";
auto server = dap::Socket("localhost", port);
auto client = dap::Socket::connect("localhost", port, 0);
ASSERT_TRUE(client != nullptr);
auto conn = server.accept();
auto thread = std::thread([&] {
// Closing client on different thread should unblock client->read().
client->close();
});
char c;
while (client->read(&c, 1) != 0) {
}
thread.join();
}
TEST(Socket, ConnectTimeout) {
const char* port = "19021";
const int timeoutMillis = 200;
const int maxAttempts = 1024;
using namespace std::chrono;
auto server = dap::Socket("localhost", port);
std::vector<std::shared_ptr<dap::ReaderWriter>> connections;
for (int i = 0; i < maxAttempts; i++) {
auto start = system_clock::now();
auto connection = dap::Socket::connect("localhost", port, timeoutMillis);
auto end = system_clock::now();
if (!connection) {
auto timeTakenMillis = duration_cast<milliseconds>(end - start).count();
ASSERT_GE(timeTakenMillis + 20, // +20ms for a bit of timing wiggle room
timeoutMillis);
return;
}
// Keep hold of the connections to saturate any incoming socket buffers.
connections.emplace_back(std::move(connection));
}
FAIL() << "Failed to test timeout after " << maxAttempts << " attempts";
}

View File

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

View File

@@ -0,0 +1,387 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/traits.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
namespace traits {
namespace {
struct S {};
struct E : S {};
void F1(S) {}
void F3(int, S, float) {}
void E1(E) {}
void E3(int, E, float) {}
} // namespace
TEST(ParameterType, Function) {
F1({}); // Avoid unused method warning
F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 0>, int>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&F3), 2>, float>::value,
"");
}
TEST(ParameterType, Method) {
class C {
public:
void F1(S) {}
void F3(int, S, float) {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
}
TEST(ParameterType, ConstMethod) {
class C {
public:
void F1(S) const {}
void F3(int, S, float) const {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
}
TEST(ParameterType, StaticMethod) {
class C {
public:
static void F1(S) {}
static void F3(int, S, float) {}
};
C::F1({}); // Avoid unused method warning
C::F3(0, {}, 0); // Avoid unused method warning
static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
"");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
"");
}
TEST(ParameterType, FunctionLike) {
using F1 = std::function<void(S)>;
using F3 = std::function<void(int, S, float)>;
static_assert(std::is_same<ParameterType<F1, 0>, S>::value, "");
static_assert(std::is_same<ParameterType<F3, 0>, int>::value, "");
static_assert(std::is_same<ParameterType<F3, 1>, S>::value, "");
static_assert(std::is_same<ParameterType<F3, 2>, float>::value, "");
}
TEST(ParameterType, Lambda) {
auto l1 = [](S) {};
auto l3 = [](int, S, float) {};
static_assert(std::is_same<ParameterType<decltype(l1), 0>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 0>, int>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 1>, S>::value, "");
static_assert(std::is_same<ParameterType<decltype(l3), 2>, float>::value, "");
}
TEST(HasSignature, Function) {
F1({}); // Avoid unused method warning
F3(0, {}, 0); // Avoid unused method warning
static_assert(HasSignature<decltype(&F1), decltype(&F1)>::value, "");
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&F1), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
}
TEST(HasSignature, Method) {
class C {
public:
void F1(S) {}
void F3(int, S, float) {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
}
TEST(HasSignature, ConstMethod) {
class C {
public:
void F1(S) const {}
void F3(int, S, float) const {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
}
TEST(HasSignature, StaticMethod) {
class C {
public:
static void F1(S) {}
static void F3(int, S, float) {}
};
C::F1({}); // Avoid unused method warning
C::F3(0, {}, 0); // Avoid unused method warning
static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
}
TEST(HasSignature, FunctionLike) {
using f1 = std::function<void(S)>;
using f3 = std::function<void(int, S, float)>;
static_assert(HasSignature<f1, decltype(&F1)>::value, "");
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
static_assert(HasSignature<f3, decltype(&F3)>::value, "");
static_assert(!HasSignature<f1, decltype(&F3)>::value, "");
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
}
TEST(HasSignature, Lambda) {
auto l1 = [](S) {};
auto l3 = [](int, S, float) {};
static_assert(HasSignature<decltype(l1), decltype(&F1)>::value, "");
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(l1), decltype(&F3)>::value, "");
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
}
////
TEST(CompatibleWith, Function) {
F1({}); // Avoid unused method warning
F3(0, {}, 0); // Avoid unused method warning
E1({}); // Avoid unused method warning
E3(0, {}, 0); // Avoid unused method warning
static_assert(CompatibleWith<decltype(&F1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&F1), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&E1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&F1), decltype(&E1)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
}
TEST(CompatibleWith, Method) {
class C {
public:
void F1(S) {}
void F3(int, S, float) {}
void E1(E) {}
void E3(int, E, float) {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
C().E1({}); // Avoid unused method warning
C().E3(0, {}, 0); // Avoid unused method warning
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
}
TEST(CompatibleWith, ConstMethod) {
class C {
public:
void F1(S) const {}
void F3(int, S, float) const {}
void E1(E) const {}
void E3(int, E, float) const {}
};
C().F1({}); // Avoid unused method warning
C().F3(0, {}, 0); // Avoid unused method warning
C().E1({}); // Avoid unused method warning
C().E3(0, {}, 0); // Avoid unused method warning
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
}
TEST(CompatibleWith, StaticMethod) {
class C {
public:
static void F1(S) {}
static void F3(int, S, float) {}
static void E1(E) {}
static void E3(int, E, float) {}
};
C::F1({}); // Avoid unused method warning
C::F3(0, {}, 0); // Avoid unused method warning
C::E1({}); // Avoid unused method warning
C::E3(0, {}, 0); // Avoid unused method warning
static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
}
TEST(CompatibleWith, FunctionLike) {
using f1 = std::function<void(S)>;
using f3 = std::function<void(int, S, float)>;
using e1 = std::function<void(E)>;
using e3 = std::function<void(int, E, float)>;
static_assert(CompatibleWith<f1, decltype(&F1)>::value, "");
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
static_assert(!CompatibleWith<f1, decltype(&F3)>::value, "");
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
static_assert(CompatibleWith<e1, f1>::value, "");
static_assert(CompatibleWith<e3, f3>::value, "");
static_assert(CompatibleWith<e3, f3>::value, "");
static_assert(CompatibleWith<e3, f3>::value, "");
static_assert(!CompatibleWith<f1, e1>::value, "");
static_assert(!CompatibleWith<f3, e3>::value, "");
static_assert(!CompatibleWith<f3, e3>::value, "");
static_assert(!CompatibleWith<f3, e3>::value, "");
}
TEST(CompatibleWith, Lambda) {
auto f1 = [](S) {};
auto f3 = [](int, S, float) {};
auto e1 = [](E) {};
auto e3 = [](int, E, float) {};
static_assert(CompatibleWith<decltype(f1), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(f1), decltype(&F3)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
static_assert(CompatibleWith<decltype(e1), decltype(f1)>::value, "");
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
static_assert(!CompatibleWith<decltype(f1), decltype(e1)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
}
} // namespace traits
} // namespace dap

View File

@@ -0,0 +1,21 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/typeinfo.h"
namespace dap {
TypeInfo::~TypeInfo() = default;
} // namespace dap

View File

@@ -0,0 +1,65 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/typeof.h"
#include "dap/types.h"
#include "json_serializer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace dap {
struct BaseStruct {
dap::integer i;
dap::number n;
};
DAP_STRUCT_TYPEINFO(BaseStruct,
"BaseStruct",
DAP_FIELD(i, "i"),
DAP_FIELD(n, "n"));
struct DerivedStruct : public BaseStruct {
dap::string s;
dap::boolean b;
};
DAP_STRUCT_TYPEINFO_EXT(DerivedStruct,
BaseStruct,
"DerivedStruct",
DAP_FIELD(s, "s"),
DAP_FIELD(b, "b"));
} // namespace dap
TEST(TypeInfo, Derived) {
dap::DerivedStruct in;
in.s = "hello world";
in.b = true;
in.i = 42;
in.n = 3.14;
dap::json::Serializer s;
ASSERT_TRUE(s.serialize(in));
dap::DerivedStruct out;
dap::json::Deserializer d(s.dump());
ASSERT_TRUE(d.deserialize(&out)) << "Failed to deserialize\n" << s.dump();
ASSERT_EQ(out.s, "hello world");
ASSERT_EQ(out.b, true);
ASSERT_EQ(out.i, 42);
ASSERT_EQ(out.n, 3.14);
}

View File

@@ -0,0 +1,144 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dap/typeof.h"
#include <atomic>
#include <memory>
#include <vector>
namespace {
// TypeInfos owns all the dap::TypeInfo instances.
struct TypeInfos {
// get() returns the TypeInfos singleton pointer.
// TypeInfos is constructed with an internal reference count of 1.
static TypeInfos* get();
// reference() increments the TypeInfos reference count.
inline void reference() {
assert(refcount.load() > 0);
refcount++;
}
// release() decrements the TypeInfos reference count.
// If the reference count becomes 0, then the TypeInfos is destructed.
inline void release() {
if (--refcount == 0) {
this->~TypeInfos();
}
}
struct NullTI : public dap::TypeInfo {
using null = dap::null;
inline std::string name() const override { return "null"; }
inline size_t size() const override { return sizeof(null); }
inline size_t alignment() const override { return alignof(null); }
inline void construct(void* ptr) const override { new (ptr) null(); }
inline void copyConstruct(void* dst, const void* src) const override {
new (dst) null(*reinterpret_cast<const null*>(src));
}
inline void destruct(void* ptr) const override {
reinterpret_cast<null*>(ptr)->~null();
}
inline bool deserialize(const dap::Deserializer*, void*) const override {
return true;
}
inline bool serialize(dap::Serializer*, const void*) const override {
return true;
}
};
dap::BasicTypeInfo<dap::boolean> boolean = {"boolean"};
dap::BasicTypeInfo<dap::string> string = {"string"};
dap::BasicTypeInfo<dap::integer> integer = {"integer"};
dap::BasicTypeInfo<dap::number> number = {"number"};
dap::BasicTypeInfo<dap::object> object = {"object"};
dap::BasicTypeInfo<dap::any> any = {"any"};
NullTI null;
std::vector<std::unique_ptr<dap::TypeInfo>> types;
private:
TypeInfos() = default;
~TypeInfos() = default;
std::atomic<uint64_t> refcount = {1};
};
// aligned_storage() is a replacement for std::aligned_storage that isn't busted
// on older versions of MSVC.
template <size_t SIZE, size_t ALIGNMENT>
struct aligned_storage {
struct alignas(ALIGNMENT) type {
unsigned char data[SIZE];
};
};
TypeInfos* TypeInfos::get() {
static aligned_storage<sizeof(TypeInfos), alignof(TypeInfos)>::type memory;
struct Instance {
TypeInfos* ptr() { return reinterpret_cast<TypeInfos*>(memory.data); }
Instance() { new (ptr()) TypeInfos(); }
~Instance() { ptr()->release(); }
};
static Instance instance;
return instance.ptr();
}
} // namespace
namespace dap {
const TypeInfo* TypeOf<boolean>::type() {
return &TypeInfos::get()->boolean;
}
const TypeInfo* TypeOf<string>::type() {
return &TypeInfos::get()->string;
}
const TypeInfo* TypeOf<integer>::type() {
return &TypeInfos::get()->integer;
}
const TypeInfo* TypeOf<number>::type() {
return &TypeInfos::get()->number;
}
const TypeInfo* TypeOf<object>::type() {
return &TypeInfos::get()->object;
}
const TypeInfo* TypeOf<any>::type() {
return &TypeInfos::get()->any;
}
const TypeInfo* TypeOf<null>::type() {
return &TypeInfos::get()->null;
}
void TypeInfo::deleteOnExit(TypeInfo* ti) {
TypeInfos::get()->types.emplace_back(std::unique_ptr<TypeInfo>(ti));
}
void initialize() {
TypeInfos::get()->reference();
}
void terminate() {
TypeInfos::get()->release();
}
} // namespace dap

View File

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