mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-26 09:13:46 -05:00
179 lines
4.9 KiB
C++
179 lines
4.9 KiB
C++
#ifndef SUPPORT_H_
|
|
#define SUPPORT_H_
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <new>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "v8/include/v8.h"
|
|
|
|
// Work around a bug in the V8 headers.
|
|
//
|
|
// The following template is defined in v8-internal.h. It has a subtle bug that
|
|
// indirectly makes it impossible to convert `v8::Data` handles to themselves.
|
|
// Some methods do that impliclity so they don't compile without this hack; one
|
|
// example is `Local<Data> MaybeLocal::FromMaybe(Local<Data> default_value)`.
|
|
//
|
|
// Spot the bug :)
|
|
//
|
|
// ```
|
|
// template <class T>
|
|
// V8_INLINE void PerformCastCheck(T* data) {
|
|
// CastCheck<std::is_base_of<Data, T>::value &&
|
|
// !std::is_same<Data, std::remove_cv<T>>::value>::Perform(data);
|
|
// }
|
|
// ```
|
|
template <>
|
|
template <>
|
|
inline void v8::internal::CastCheck<true>::Perform<v8::Data>(v8::Data* data) {}
|
|
|
|
// Check assumptions made in binding code.
|
|
static_assert(sizeof(bool) == sizeof(uint8_t), "");
|
|
static_assert(sizeof(std::unique_ptr<void>) == sizeof(void*), "");
|
|
|
|
namespace support {
|
|
template <class T>
|
|
using uninit_t = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
|
|
|
|
template <class T, class... Args>
|
|
class construct_in_place_helper {
|
|
public:
|
|
construct_in_place_helper(uninit_t<T>* buf, Args... args)
|
|
: inner_(std::forward<Args>(args)...) {}
|
|
|
|
private:
|
|
T inner_;
|
|
};
|
|
|
|
template <class T, class... Args>
|
|
void construct_in_place(uninit_t<T>* buf, Args... args) {
|
|
new (buf)
|
|
construct_in_place_helper<T, Args...>(buf, std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Rust's FFI only supports returning normal C data structures (AKA plain old
|
|
// data). There are some situations in the V8 API where functions return non-POD
|
|
// data. We use make_pod to carefully adjust the return values so they can be
|
|
// passed into Rust.
|
|
//
|
|
// The destructor of V is never called.
|
|
// P is not allowed to have a destructor.
|
|
template <class P>
|
|
struct make_pod {
|
|
template <class V>
|
|
inline make_pod(V&& value) : pod_(helper<V>(std::move(value))) {}
|
|
template <class V>
|
|
inline make_pod(const V& value) : pod_(helper<V>(value)) {}
|
|
inline operator P() { return pod_; }
|
|
|
|
private:
|
|
P pod_;
|
|
|
|
// This helper exists to avoid calling the destructor.
|
|
// Using a union is a C++ trick to achieve this.
|
|
template <class V>
|
|
union helper {
|
|
static_assert(std::is_pod<P>::value, "type P must a pod type");
|
|
static_assert(sizeof(V) == sizeof(P), "type P must be same size as type V");
|
|
static_assert(alignof(V) == alignof(P),
|
|
"alignment of type P must be compatible with that of type V");
|
|
|
|
inline helper(V&& value) : value_(std::move(value)) {}
|
|
inline helper(const V& value) : value_(value) {}
|
|
inline ~helper() {}
|
|
|
|
inline operator P() {
|
|
// Do a memcpy here avoid undefined behavior.
|
|
P result;
|
|
memcpy(&result, this, sizeof result);
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
V value_;
|
|
};
|
|
};
|
|
|
|
// The C-ABI compatible equivalent of V8's Maybe<bool>.
|
|
enum class MaybeBool { JustFalse = 0, JustTrue = 1, Nothing = 2 };
|
|
|
|
inline static MaybeBool maybe_to_maybe_bool(v8::Maybe<bool> maybe) {
|
|
if (maybe.IsNothing()) {
|
|
return MaybeBool::Nothing;
|
|
} else if (maybe.FromJust()) {
|
|
return MaybeBool::JustTrue;
|
|
} else {
|
|
return MaybeBool::JustFalse;
|
|
}
|
|
}
|
|
|
|
inline static v8::Maybe<bool> maybe_bool_to_maybe(MaybeBool maybe) {
|
|
switch (maybe) {
|
|
case MaybeBool::JustTrue:
|
|
case MaybeBool::JustFalse:
|
|
return v8::Just<bool>(maybe == MaybeBool::JustTrue);
|
|
default:
|
|
return v8::Nothing<bool>();
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
inline static T* local_to_ptr(v8::Local<T> local) {
|
|
return *local;
|
|
}
|
|
|
|
template <class T>
|
|
inline static const v8::Local<T> ptr_to_local(const T* ptr) {
|
|
static_assert(sizeof(v8::Local<T>) == sizeof(T*), "");
|
|
auto local = *reinterpret_cast<const v8::Local<T>*>(&ptr);
|
|
assert(*local == ptr);
|
|
return local;
|
|
}
|
|
|
|
template <class T>
|
|
inline static v8::Local<T>* const_ptr_array_to_local_array(
|
|
const T* const ptr_array[]) {
|
|
static_assert(sizeof(v8::Local<T>[42]) == sizeof(T* [42]), "");
|
|
auto mut_ptr_array = const_cast<T**>(ptr_array);
|
|
auto mut_local_array = reinterpret_cast<v8::Local<T>*>(mut_ptr_array);
|
|
return mut_local_array;
|
|
}
|
|
|
|
template <class T>
|
|
inline static T* maybe_local_to_ptr(v8::MaybeLocal<T> local) {
|
|
return *local.FromMaybe(v8::Local<T>());
|
|
}
|
|
|
|
template <class T>
|
|
inline static const v8::MaybeLocal<T> ptr_to_maybe_local(const T* ptr) {
|
|
static_assert(sizeof(v8::MaybeLocal<T>) == sizeof(T*), "");
|
|
return *reinterpret_cast<const v8::MaybeLocal<T>*>(&ptr);
|
|
}
|
|
|
|
template <class T>
|
|
inline static v8::Global<T> ptr_to_global(const T* ptr) {
|
|
v8::Global<T> global;
|
|
std::swap(ptr, *reinterpret_cast<const T**>(&global));
|
|
return global;
|
|
}
|
|
|
|
// Because, for some reason, Clang complains that `std::array<void*, 2>` is an
|
|
// incomplete type, incompatible with C linkage.
|
|
struct two_pointers_t {
|
|
void* a;
|
|
void* b;
|
|
};
|
|
|
|
struct three_pointers_t {
|
|
void* a;
|
|
void* b;
|
|
void* c;
|
|
};
|
|
|
|
} // namespace support
|
|
|
|
#endif // SUPPORT_H_
|