mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Where appropriate, use a helper that wraps around `ArrayBufferView::Buffer()` or `ArrayBufferView::CopyContents()` rather than `Buffer::Data()`, as that may help to avoid materializing the underlying `ArrayBuffer` when reading small typed arrays from C++. This allows keeping the performance benefits of the faster creation of heap-allocated small typed arrays in many cases. PR-URL: https://github.com/nodejs/node/pull/26301 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
673 lines
22 KiB
C++
673 lines
22 KiB
C++
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
#ifndef SRC_UTIL_H_
|
|
#define SRC_UTIL_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include "node_persistent.h"
|
|
#include "v8.h"
|
|
|
|
#include <cassert>
|
|
#include <csignal>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include <functional> // std::function
|
|
#include <limits>
|
|
#include <set>
|
|
#include <string>
|
|
#include <array>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace node {
|
|
|
|
// These should be used in our code as opposed to the native
|
|
// versions as they abstract out some platform and or
|
|
// compiler version specific functionality
|
|
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
|
|
// that the standard allows them to either return a unique pointer or a
|
|
// nullptr for zero-sized allocation requests. Normalize by always using
|
|
// a nullptr.
|
|
template <typename T>
|
|
inline T* UncheckedRealloc(T* pointer, size_t n);
|
|
template <typename T>
|
|
inline T* UncheckedMalloc(size_t n);
|
|
template <typename T>
|
|
inline T* UncheckedCalloc(size_t n);
|
|
|
|
// Same things, but aborts immediately instead of returning nullptr when
|
|
// no memory is available.
|
|
template <typename T>
|
|
inline T* Realloc(T* pointer, size_t n);
|
|
template <typename T>
|
|
inline T* Malloc(size_t n);
|
|
template <typename T>
|
|
inline T* Calloc(size_t n);
|
|
|
|
inline char* Malloc(size_t n);
|
|
inline char* Calloc(size_t n);
|
|
inline char* UncheckedMalloc(size_t n);
|
|
inline char* UncheckedCalloc(size_t n);
|
|
|
|
template <typename T>
|
|
inline T MultiplyWithOverflowCheck(T a, T b);
|
|
|
|
namespace per_process {
|
|
// Tells whether the per-process V8::Initialize() is called and
|
|
// if it is safe to call v8::Isolate::GetCurrent().
|
|
extern bool v8_initialized;
|
|
} // namespace per_process
|
|
|
|
// Used by the allocation functions when allocation fails.
|
|
// Thin wrapper around v8::Isolate::LowMemoryNotification() that checks
|
|
// whether V8 is initialized.
|
|
void LowMemoryNotification();
|
|
|
|
// The reason that Assert() takes a struct argument instead of individual
|
|
// const char*s is to ease instruction cache pressure in calls from CHECK.
|
|
struct AssertionInfo {
|
|
const char* file_line; // filename:line
|
|
const char* message;
|
|
const char* function;
|
|
};
|
|
[[noreturn]] void Assert(const AssertionInfo& info);
|
|
[[noreturn]] void Abort();
|
|
void DumpBacktrace(FILE* fp);
|
|
|
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
|
TypeName(const TypeName&) = delete; \
|
|
TypeName& operator=(const TypeName&) = delete
|
|
|
|
// Windows 8+ does not like abort() in Release mode
|
|
#ifdef _WIN32
|
|
#define ABORT_NO_BACKTRACE() _exit(134)
|
|
#else
|
|
#define ABORT_NO_BACKTRACE() abort()
|
|
#endif
|
|
|
|
#define ABORT() node::Abort()
|
|
|
|
#ifdef __GNUC__
|
|
#define LIKELY(expr) __builtin_expect(!!(expr), 1)
|
|
#define UNLIKELY(expr) __builtin_expect(!!(expr), 0)
|
|
#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__
|
|
#else
|
|
#define LIKELY(expr) expr
|
|
#define UNLIKELY(expr) expr
|
|
#define PRETTY_FUNCTION_NAME ""
|
|
#endif
|
|
|
|
#define STRINGIFY_(x) #x
|
|
#define STRINGIFY(x) STRINGIFY_(x)
|
|
|
|
#define CHECK(expr) \
|
|
do { \
|
|
if (UNLIKELY(!(expr))) { \
|
|
/* Make sure that this struct does not end up in inline code, but */ \
|
|
/* rather in a read-only data section when modifying this code. */ \
|
|
static const node::AssertionInfo args = { \
|
|
__FILE__ ":" STRINGIFY(__LINE__), #expr, PRETTY_FUNCTION_NAME \
|
|
}; \
|
|
node::Assert(args); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHECK_EQ(a, b) CHECK((a) == (b))
|
|
#define CHECK_GE(a, b) CHECK((a) >= (b))
|
|
#define CHECK_GT(a, b) CHECK((a) > (b))
|
|
#define CHECK_LE(a, b) CHECK((a) <= (b))
|
|
#define CHECK_LT(a, b) CHECK((a) < (b))
|
|
#define CHECK_NE(a, b) CHECK((a) != (b))
|
|
#define CHECK_NULL(val) CHECK((val) == nullptr)
|
|
#define CHECK_NOT_NULL(val) CHECK((val) != nullptr)
|
|
#define CHECK_IMPLIES(a, b) CHECK(!(a) || (b))
|
|
|
|
#ifdef DEBUG
|
|
#define DCHECK(expr) CHECK(expr)
|
|
#define DCHECK_EQ(a, b) CHECK((a) == (b))
|
|
#define DCHECK_GE(a, b) CHECK((a) >= (b))
|
|
#define DCHECK_GT(a, b) CHECK((a) > (b))
|
|
#define DCHECK_LE(a, b) CHECK((a) <= (b))
|
|
#define DCHECK_LT(a, b) CHECK((a) < (b))
|
|
#define DCHECK_NE(a, b) CHECK((a) != (b))
|
|
#define DCHECK_NULL(val) CHECK((val) == nullptr)
|
|
#define DCHECK_NOT_NULL(val) CHECK((val) != nullptr)
|
|
#define DCHECK_IMPLIES(a, b) CHECK(!(a) || (b))
|
|
#else
|
|
#define DCHECK(expr)
|
|
#define DCHECK_EQ(a, b)
|
|
#define DCHECK_GE(a, b)
|
|
#define DCHECK_GT(a, b)
|
|
#define DCHECK_LE(a, b)
|
|
#define DCHECK_LT(a, b)
|
|
#define DCHECK_NE(a, b)
|
|
#define DCHECK_NULL(val)
|
|
#define DCHECK_NOT_NULL(val)
|
|
#define DCHECK_IMPLIES(a, b)
|
|
#endif
|
|
|
|
|
|
#define UNREACHABLE() ABORT()
|
|
|
|
// TAILQ-style intrusive list node.
|
|
template <typename T>
|
|
class ListNode;
|
|
|
|
// TAILQ-style intrusive list head.
|
|
template <typename T, ListNode<T> (T::*M)>
|
|
class ListHead;
|
|
|
|
template <typename T>
|
|
class ListNode {
|
|
public:
|
|
inline ListNode();
|
|
inline ~ListNode();
|
|
inline void Remove();
|
|
inline bool IsEmpty() const;
|
|
|
|
private:
|
|
template <typename U, ListNode<U> (U::*M)> friend class ListHead;
|
|
friend int GenDebugSymbols();
|
|
ListNode* prev_;
|
|
ListNode* next_;
|
|
DISALLOW_COPY_AND_ASSIGN(ListNode);
|
|
};
|
|
|
|
template <typename T, ListNode<T> (T::*M)>
|
|
class ListHead {
|
|
public:
|
|
class Iterator {
|
|
public:
|
|
inline T* operator*() const;
|
|
inline const Iterator& operator++();
|
|
inline bool operator!=(const Iterator& that) const;
|
|
|
|
private:
|
|
friend class ListHead;
|
|
inline explicit Iterator(ListNode<T>* node);
|
|
ListNode<T>* node_;
|
|
};
|
|
|
|
inline ListHead() = default;
|
|
inline ~ListHead();
|
|
inline void PushBack(T* element);
|
|
inline void PushFront(T* element);
|
|
inline bool IsEmpty() const;
|
|
inline T* PopFront();
|
|
inline Iterator begin() const;
|
|
inline Iterator end() const;
|
|
|
|
private:
|
|
friend int GenDebugSymbols();
|
|
ListNode<T> head_;
|
|
DISALLOW_COPY_AND_ASSIGN(ListHead);
|
|
};
|
|
|
|
// The helper is for doing safe downcasts from base types to derived types.
|
|
template <typename Inner, typename Outer>
|
|
class ContainerOfHelper {
|
|
public:
|
|
inline ContainerOfHelper(Inner Outer::*field, Inner* pointer);
|
|
template <typename TypeName>
|
|
inline operator TypeName*() const;
|
|
private:
|
|
Outer* const pointer_;
|
|
};
|
|
|
|
// Calculate the address of the outer (i.e. embedding) struct from
|
|
// the interior pointer to a data member.
|
|
template <typename Inner, typename Outer>
|
|
constexpr ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field,
|
|
Inner* pointer);
|
|
|
|
// Convenience wrapper around v8::String::NewFromOneByte().
|
|
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
|
const char* data,
|
|
int length = -1);
|
|
|
|
// For the people that compile with -funsigned-char.
|
|
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
|
const signed char* data,
|
|
int length = -1);
|
|
|
|
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
|
const unsigned char* data,
|
|
int length = -1);
|
|
|
|
// Used to be a macro, hence the uppercase name.
|
|
template <int N>
|
|
inline v8::Local<v8::String> FIXED_ONE_BYTE_STRING(
|
|
v8::Isolate* isolate,
|
|
const char(&data)[N]) {
|
|
return OneByteString(isolate, data, N - 1);
|
|
}
|
|
|
|
template <std::size_t N>
|
|
inline v8::Local<v8::String> FIXED_ONE_BYTE_STRING(
|
|
v8::Isolate* isolate,
|
|
const std::array<char, N>& arr) {
|
|
return OneByteString(isolate, arr.data(), N - 1);
|
|
}
|
|
|
|
|
|
|
|
// Swaps bytes in place. nbytes is the number of bytes to swap and must be a
|
|
// multiple of the word size (checked by function).
|
|
inline void SwapBytes16(char* data, size_t nbytes);
|
|
inline void SwapBytes32(char* data, size_t nbytes);
|
|
inline void SwapBytes64(char* data, size_t nbytes);
|
|
|
|
// tolower() is locale-sensitive. Use ToLower() instead.
|
|
inline char ToLower(char c);
|
|
inline std::string ToLower(const std::string& in);
|
|
|
|
// strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead.
|
|
inline bool StringEqualNoCase(const char* a, const char* b);
|
|
|
|
// strncasecmp() is locale-sensitive. Use StringEqualNoCaseN() instead.
|
|
inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length);
|
|
|
|
// Allocates an array of member type T. For up to kStackStorageSize items,
|
|
// the stack is used, otherwise malloc().
|
|
template <typename T, size_t kStackStorageSize = 1024>
|
|
class MaybeStackBuffer {
|
|
public:
|
|
const T* out() const {
|
|
return buf_;
|
|
}
|
|
|
|
T* out() {
|
|
return buf_;
|
|
}
|
|
|
|
// operator* for compatibility with `v8::String::(Utf8)Value`
|
|
T* operator*() {
|
|
return buf_;
|
|
}
|
|
|
|
const T* operator*() const {
|
|
return buf_;
|
|
}
|
|
|
|
T& operator[](size_t index) {
|
|
CHECK_LT(index, length());
|
|
return buf_[index];
|
|
}
|
|
|
|
const T& operator[](size_t index) const {
|
|
CHECK_LT(index, length());
|
|
return buf_[index];
|
|
}
|
|
|
|
size_t length() const {
|
|
return length_;
|
|
}
|
|
|
|
// Current maximum capacity of the buffer with which SetLength() can be used
|
|
// without first calling AllocateSufficientStorage().
|
|
size_t capacity() const {
|
|
return IsAllocated() ? capacity_ :
|
|
IsInvalidated() ? 0 : kStackStorageSize;
|
|
}
|
|
|
|
// Make sure enough space for `storage` entries is available.
|
|
// This method can be called multiple times throughout the lifetime of the
|
|
// buffer, but once this has been called Invalidate() cannot be used.
|
|
// Content of the buffer in the range [0, length()) is preserved.
|
|
void AllocateSufficientStorage(size_t storage) {
|
|
CHECK(!IsInvalidated());
|
|
if (storage > capacity()) {
|
|
bool was_allocated = IsAllocated();
|
|
T* allocated_ptr = was_allocated ? buf_ : nullptr;
|
|
buf_ = Realloc(allocated_ptr, storage);
|
|
capacity_ = storage;
|
|
if (!was_allocated && length_ > 0)
|
|
memcpy(buf_, buf_st_, length_ * sizeof(buf_[0]));
|
|
}
|
|
|
|
length_ = storage;
|
|
}
|
|
|
|
void SetLength(size_t length) {
|
|
// capacity() returns how much memory is actually available.
|
|
CHECK_LE(length, capacity());
|
|
length_ = length;
|
|
}
|
|
|
|
void SetLengthAndZeroTerminate(size_t length) {
|
|
// capacity() returns how much memory is actually available.
|
|
CHECK_LE(length + 1, capacity());
|
|
SetLength(length);
|
|
|
|
// T() is 0 for integer types, nullptr for pointers, etc.
|
|
buf_[length] = T();
|
|
}
|
|
|
|
// Make derefencing this object return nullptr.
|
|
// This method can be called multiple times throughout the lifetime of the
|
|
// buffer, but once this has been called AllocateSufficientStorage() cannot
|
|
// be used.
|
|
void Invalidate() {
|
|
CHECK(!IsAllocated());
|
|
length_ = 0;
|
|
buf_ = nullptr;
|
|
}
|
|
|
|
// If the buffer is stored in the heap rather than on the stack.
|
|
bool IsAllocated() const {
|
|
return !IsInvalidated() && buf_ != buf_st_;
|
|
}
|
|
|
|
// If Invalidate() has been called.
|
|
bool IsInvalidated() const {
|
|
return buf_ == nullptr;
|
|
}
|
|
|
|
// Release ownership of the malloc'd buffer.
|
|
// Note: This does not free the buffer.
|
|
void Release() {
|
|
CHECK(IsAllocated());
|
|
buf_ = buf_st_;
|
|
length_ = 0;
|
|
capacity_ = 0;
|
|
}
|
|
|
|
MaybeStackBuffer() : length_(0), capacity_(0), buf_(buf_st_) {
|
|
// Default to a zero-length, null-terminated buffer.
|
|
buf_[0] = T();
|
|
}
|
|
|
|
explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() {
|
|
AllocateSufficientStorage(storage);
|
|
}
|
|
|
|
~MaybeStackBuffer() {
|
|
if (IsAllocated())
|
|
free(buf_);
|
|
}
|
|
|
|
private:
|
|
size_t length_;
|
|
// capacity of the malloc'ed buf_
|
|
size_t capacity_;
|
|
T* buf_;
|
|
T buf_st_[kStackStorageSize];
|
|
};
|
|
|
|
// Provides access to an ArrayBufferView's storage, either the original,
|
|
// or for small data, a copy of it. This object's lifetime is bound to the
|
|
// original ArrayBufferView's lifetime.
|
|
template <typename T, size_t kStackStorageSize = 64>
|
|
class ArrayBufferViewContents {
|
|
public:
|
|
ArrayBufferViewContents() {}
|
|
|
|
explicit inline ArrayBufferViewContents(v8::Local<v8::Value> value);
|
|
explicit inline ArrayBufferViewContents(v8::Local<v8::ArrayBufferView> abv);
|
|
inline void Read(v8::Local<v8::ArrayBufferView> abv);
|
|
|
|
inline const T* data() const { return data_; }
|
|
inline size_t length() const { return length_; }
|
|
|
|
private:
|
|
T stack_storage_[kStackStorageSize];
|
|
T* data_ = nullptr;
|
|
size_t length_ = 0;
|
|
};
|
|
|
|
class Utf8Value : public MaybeStackBuffer<char> {
|
|
public:
|
|
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
|
};
|
|
|
|
class TwoByteValue : public MaybeStackBuffer<uint16_t> {
|
|
public:
|
|
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
|
};
|
|
|
|
class BufferValue : public MaybeStackBuffer<char> {
|
|
public:
|
|
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
|
};
|
|
|
|
#define SPREAD_BUFFER_ARG(val, name) \
|
|
CHECK((val)->IsArrayBufferView()); \
|
|
v8::Local<v8::ArrayBufferView> name = (val).As<v8::ArrayBufferView>(); \
|
|
v8::ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \
|
|
const size_t name##_offset = name->ByteOffset(); \
|
|
const size_t name##_length = name->ByteLength(); \
|
|
char* const name##_data = \
|
|
static_cast<char*>(name##_c.Data()) + name##_offset; \
|
|
if (name##_length > 0) \
|
|
CHECK_NE(name##_data, nullptr);
|
|
|
|
// Use this when a variable or parameter is unused in order to explicitly
|
|
// silence a compiler warning about that.
|
|
template <typename T> inline void USE(T&&) {}
|
|
|
|
// Run a function when exiting the current scope.
|
|
struct OnScopeLeave {
|
|
std::function<void()> fn_;
|
|
|
|
explicit OnScopeLeave(std::function<void()> fn) : fn_(std::move(fn)) {}
|
|
~OnScopeLeave() { fn_(); }
|
|
};
|
|
|
|
// Simple RAII wrapper for contiguous data that uses malloc()/free().
|
|
template <typename T>
|
|
struct MallocedBuffer {
|
|
T* data;
|
|
size_t size;
|
|
|
|
T* release() {
|
|
T* ret = data;
|
|
data = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
void Truncate(size_t new_size) {
|
|
CHECK(new_size <= size);
|
|
size = new_size;
|
|
}
|
|
|
|
inline bool is_empty() const { return data == nullptr; }
|
|
|
|
MallocedBuffer() : data(nullptr), size(0) {}
|
|
explicit MallocedBuffer(size_t size) : data(Malloc<T>(size)), size(size) {}
|
|
MallocedBuffer(T* data, size_t size) : data(data), size(size) {}
|
|
MallocedBuffer(MallocedBuffer&& other) : data(other.data), size(other.size) {
|
|
other.data = nullptr;
|
|
}
|
|
MallocedBuffer& operator=(MallocedBuffer&& other) {
|
|
this->~MallocedBuffer();
|
|
return *new(this) MallocedBuffer(std::move(other));
|
|
}
|
|
~MallocedBuffer() {
|
|
free(data);
|
|
}
|
|
MallocedBuffer(const MallocedBuffer&) = delete;
|
|
MallocedBuffer& operator=(const MallocedBuffer&) = delete;
|
|
};
|
|
|
|
template <typename T>
|
|
class NonCopyableMaybe {
|
|
public:
|
|
NonCopyableMaybe() : empty_(true) {}
|
|
explicit NonCopyableMaybe(T&& value)
|
|
: empty_(false),
|
|
value_(std::move(value)) {}
|
|
|
|
bool IsEmpty() const {
|
|
return empty_;
|
|
}
|
|
|
|
T&& Release() {
|
|
CHECK_EQ(empty_, false);
|
|
empty_ = true;
|
|
return std::move(value_);
|
|
}
|
|
|
|
private:
|
|
bool empty_;
|
|
T value_;
|
|
};
|
|
|
|
// Test whether some value can be called with ().
|
|
template <typename T, typename = void>
|
|
struct is_callable : std::is_function<T> { };
|
|
|
|
template <typename T>
|
|
struct is_callable<T, typename std::enable_if<
|
|
std::is_same<decltype(void(&T::operator())), void>::value
|
|
>::type> : std::true_type { };
|
|
|
|
template <typename T, void (*function)(T*)>
|
|
struct FunctionDeleter {
|
|
void operator()(T* pointer) const { function(pointer); }
|
|
typedef std::unique_ptr<T, FunctionDeleter> Pointer;
|
|
};
|
|
|
|
template <typename T, void (*function)(T*)>
|
|
using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
|
|
|
|
std::vector<std::string> SplitString(const std::string& in, char delim);
|
|
|
|
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
|
|
const std::string& str,
|
|
v8::Isolate* isolate = nullptr);
|
|
template <typename T, typename test_for_number =
|
|
typename std::enable_if<std::numeric_limits<T>::is_specialized, bool>::type>
|
|
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
|
|
const T& number,
|
|
v8::Isolate* isolate = nullptr);
|
|
template <typename T>
|
|
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
|
|
const std::vector<T>& vec,
|
|
v8::Isolate* isolate = nullptr);
|
|
template <typename T, typename U>
|
|
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
|
|
const std::unordered_map<T, U>& map,
|
|
v8::Isolate* isolate = nullptr);
|
|
|
|
// These macros expects a `Isolate* isolate` and a `Local<Context> context`
|
|
// to be in the scope.
|
|
#define READONLY_PROPERTY(obj, name, value) \
|
|
do { \
|
|
obj->DefineOwnProperty( \
|
|
context, FIXED_ONE_BYTE_STRING(isolate, name), value, v8::ReadOnly) \
|
|
.FromJust(); \
|
|
} while (0)
|
|
|
|
#define READONLY_DONT_ENUM_PROPERTY(obj, name, var) \
|
|
do { \
|
|
obj->DefineOwnProperty( \
|
|
context, \
|
|
OneByteString(isolate, name), \
|
|
var, \
|
|
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)) \
|
|
.FromJust(); \
|
|
} while (0)
|
|
|
|
#define READONLY_FALSE_PROPERTY(obj, name) \
|
|
READONLY_PROPERTY(obj, name, v8::False(isolate))
|
|
|
|
#define READONLY_TRUE_PROPERTY(obj, name) \
|
|
READONLY_PROPERTY(obj, name, v8::True(isolate))
|
|
|
|
#define READONLY_STRING_PROPERTY(obj, name, str) \
|
|
READONLY_PROPERTY(obj, name, ToV8Value(context, str).ToLocalChecked())
|
|
|
|
// Variation on NODE_DEFINE_CONSTANT that sets a String value.
|
|
#define NODE_DEFINE_STRING_CONSTANT(target, name, constant) \
|
|
do { \
|
|
v8::Isolate* isolate = target->GetIsolate(); \
|
|
v8::Local<v8::String> constant_name = \
|
|
v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal) \
|
|
.ToLocalChecked(); \
|
|
v8::Local<v8::String> constant_value = \
|
|
v8::String::NewFromUtf8(isolate, constant, v8::NewStringType::kNormal) \
|
|
.ToLocalChecked(); \
|
|
v8::PropertyAttribute constant_attributes = \
|
|
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); \
|
|
target \
|
|
->DefineOwnProperty(isolate->GetCurrentContext(), \
|
|
constant_name, \
|
|
constant_value, \
|
|
constant_attributes) \
|
|
.FromJust(); \
|
|
} while (0)
|
|
|
|
enum Endianness {
|
|
kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h.
|
|
kBigEndian
|
|
};
|
|
|
|
inline enum Endianness GetEndianness() {
|
|
// Constant-folded by the compiler.
|
|
const union {
|
|
uint8_t u8[2];
|
|
uint16_t u16;
|
|
} u = {{1, 0}};
|
|
return u.u16 == 1 ? kLittleEndian : kBigEndian;
|
|
}
|
|
|
|
inline bool IsLittleEndian() {
|
|
return GetEndianness() == kLittleEndian;
|
|
}
|
|
|
|
inline bool IsBigEndian() {
|
|
return GetEndianness() == kBigEndian;
|
|
}
|
|
|
|
template <typename T, size_t N>
|
|
constexpr size_t arraysize(const T (&)[N]) {
|
|
return N;
|
|
}
|
|
|
|
// Round up a to the next highest multiple of b.
|
|
template <typename T>
|
|
constexpr T RoundUp(T a, T b) {
|
|
return a % b != 0 ? a + b - (a % b) : a;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
#define MUST_USE_RESULT __attribute__((warn_unused_result))
|
|
#else
|
|
#define MUST_USE_RESULT
|
|
#endif
|
|
|
|
class SlicedArguments : public MaybeStackBuffer<v8::Local<v8::Value>> {
|
|
public:
|
|
inline explicit SlicedArguments(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args, size_t start = 0);
|
|
};
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_UTIL_H_
|