// 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 "v8.h" #include #include // PATH_MAX #include #include #include #include #include #include // std::function #include #include #include #include #include #include #ifdef __GNUC__ #define MUST_USE_RESULT __attribute__((warn_unused_result)) #else #define MUST_USE_RESULT #endif namespace node { // Maybe remove kPathSeparator when cpp17 is ready #ifdef _WIN32 constexpr char kPathSeparator = '\\'; /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ #define PATH_MAX_BYTES (MAX_PATH * 4) #else constexpr char kPathSeparator = '/'; #define PATH_MAX_BYTES (PATH_MAX) #endif // 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 inline T* UncheckedRealloc(T* pointer, size_t n); template inline T* UncheckedMalloc(size_t n); template inline T* UncheckedCalloc(size_t n); // Same things, but aborts immediately instead of returning nullptr when // no memory is available. template inline T* Realloc(T* pointer, size_t n); template inline T* Malloc(size_t n); template 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 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); // 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() #define ERROR_AND_ABORT(expr) \ do { \ /* 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) #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))) { \ ERROR_AND_ABORT(expr); \ } \ } 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(...) \ ERROR_AND_ABORT("Unreachable code reached" __VA_OPT__(": ") __VA_ARGS__) // ECMA262 20.1.2.6 Number.MAX_SAFE_INTEGER (2^53-1) constexpr int64_t kMaxSafeJsInteger = 9007199254740991; inline bool IsSafeJsInt(v8::Local v); // TAILQ-style intrusive list node. template class ListNode; // TAILQ-style intrusive list head. template (T::*M)> class ListHead; template class ListNode { public: inline ListNode(); inline ~ListNode(); inline void Remove(); inline bool IsEmpty() const; ListNode(const ListNode&) = delete; ListNode& operator=(const ListNode&) = delete; private: template (U::*M)> friend class ListHead; friend int GenDebugSymbols(); ListNode* prev_; ListNode* next_; }; template (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* node); ListNode* 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; ListHead(const ListHead&) = delete; ListHead& operator=(const ListHead&) = delete; private: friend int GenDebugSymbols(); ListNode head_; }; // The helper is for doing safe downcasts from base types to derived types. template class ContainerOfHelper { public: inline ContainerOfHelper(Inner Outer::*field, Inner* pointer); template 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 constexpr ContainerOfHelper ContainerOf(Inner Outer::*field, Inner* pointer); // Convenience wrapper around v8::String::NewFromOneByte(). inline v8::Local OneByteString(v8::Isolate* isolate, const char* data, int length = -1); // For the people that compile with -funsigned-char. inline v8::Local OneByteString(v8::Isolate* isolate, const signed char* data, int length = -1); inline v8::Local OneByteString(v8::Isolate* isolate, const unsigned char* data, int length = -1); // Used to be a macro, hence the uppercase name. template inline v8::Local FIXED_ONE_BYTE_STRING( v8::Isolate* isolate, const char(&data)[N]) { return OneByteString(isolate, data, N - 1); } template inline v8::Local FIXED_ONE_BYTE_STRING( v8::Isolate* isolate, const std::array& 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); // toupper() is locale-sensitive. Use ToUpper() instead. inline char ToUpper(char c); inline std::string ToUpper(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 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 class ArrayBufferViewContents { public: ArrayBufferViewContents() = default; explicit inline ArrayBufferViewContents(v8::Local value); explicit inline ArrayBufferViewContents(v8::Local value); explicit inline ArrayBufferViewContents(v8::Local abv); inline void Read(v8::Local 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 { public: explicit Utf8Value(v8::Isolate* isolate, v8::Local value); }; class TwoByteValue : public MaybeStackBuffer { public: explicit TwoByteValue(v8::Isolate* isolate, v8::Local value); }; class BufferValue : public MaybeStackBuffer { public: explicit BufferValue(v8::Isolate* isolate, v8::Local value); }; #define SPREAD_BUFFER_ARG(val, name) \ CHECK((val)->IsArrayBufferView()); \ v8::Local name = (val).As(); \ std::shared_ptr name##_bs = \ name->Buffer()->GetBackingStore(); \ const size_t name##_offset = name->ByteOffset(); \ const size_t name##_length = name->ByteLength(); \ char* const name##_data = \ static_cast(name##_bs->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 inline void USE(T&&) {} template struct OnScopeLeaveImpl { Fn fn_; bool active_; explicit OnScopeLeaveImpl(Fn&& fn) : fn_(std::move(fn)), active_(true) {} ~OnScopeLeaveImpl() { if (active_) fn_(); } OnScopeLeaveImpl(const OnScopeLeaveImpl& other) = delete; OnScopeLeaveImpl& operator=(const OnScopeLeaveImpl& other) = delete; OnScopeLeaveImpl(OnScopeLeaveImpl&& other) : fn_(std::move(other.fn_)), active_(other.active_) { other.active_ = false; } OnScopeLeaveImpl& operator=(OnScopeLeaveImpl&& other) { if (this == &other) return *this; this->~OnScopeLeave(); new (this)OnScopeLeaveImpl(std::move(other)); return *this; } }; // Run a function when exiting the current scope. Used like this: // auto on_scope_leave = OnScopeLeave([&] { // // ... run some code ... // }); template inline MUST_USE_RESULT OnScopeLeaveImpl OnScopeLeave(Fn&& fn) { return OnScopeLeaveImpl{std::move(fn)}; } // Simple RAII wrapper for contiguous data that uses malloc()/free(). template 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(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 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 struct is_callable : std::is_function { }; template struct is_callable::value >::type> : std::true_type { }; template struct FunctionDeleter { void operator()(T* pointer) const { function(pointer); } typedef std::unique_ptr Pointer; }; template using DeleteFnPtr = typename FunctionDeleter::Pointer; std::vector SplitString(const std::string& in, char delim); inline v8::MaybeLocal ToV8Value(v8::Local context, const std::string& str, v8::Isolate* isolate = nullptr); template ::is_specialized, bool>::type> inline v8::MaybeLocal ToV8Value(v8::Local context, const T& number, v8::Isolate* isolate = nullptr); template inline v8::MaybeLocal ToV8Value(v8::Local context, const std::vector& vec, v8::Isolate* isolate = nullptr); template inline v8::MaybeLocal ToV8Value(v8::Local context, const std::unordered_map& map, v8::Isolate* isolate = nullptr); // These macros expects a `Isolate* isolate` and a `Local 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) \ .Check(); \ } while (0) #define READONLY_DONT_ENUM_PROPERTY(obj, name, var) \ do { \ obj->DefineOwnProperty( \ context, \ OneByteString(isolate, name), \ var, \ static_cast(v8::ReadOnly | v8::DontEnum)) \ .Check(); \ } 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 constant_name = \ v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal) \ .ToLocalChecked(); \ v8::Local constant_value = \ v8::String::NewFromUtf8(isolate, constant, v8::NewStringType::kNormal) \ .ToLocalChecked(); \ v8::PropertyAttribute constant_attributes = \ static_cast(v8::ReadOnly | v8::DontDelete); \ target \ ->DefineOwnProperty(isolate->GetCurrentContext(), \ constant_name, \ constant_value, \ constant_attributes) \ .Check(); \ } 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 constexpr size_t arraysize(const T (&)[N]) { return N; } // Round up a to the next highest multiple of b. template constexpr T RoundUp(T a, T b) { return a % b != 0 ? a + b - (a % b) : a; } class SlicedArguments : public MaybeStackBuffer> { public: inline explicit SlicedArguments( const v8::FunctionCallbackInfo& args, size_t start = 0); }; // Convert a v8::PersistentBase, e.g. v8::Global, to a Local, with an extra // optimization for strong persistent handles. class PersistentToLocal { public: // If persistent.IsWeak() == false, then do not call persistent.Reset() // while the returned Local is still in scope, it will destroy the // reference to the object. template static inline v8::Local Default( v8::Isolate* isolate, const v8::PersistentBase& persistent) { if (persistent.IsWeak()) { return PersistentToLocal::Weak(isolate, persistent); } else { return PersistentToLocal::Strong(persistent); } } // Unchecked conversion from a non-weak Persistent to Local, // use with care! // // Do not call persistent.Reset() while the returned Local is still in // scope, it will destroy the reference to the object. template static inline v8::Local Strong( const v8::PersistentBase& persistent) { return *reinterpret_cast*>( const_cast*>(&persistent)); } template static inline v8::Local Weak( v8::Isolate* isolate, const v8::PersistentBase& persistent) { return v8::Local::New(isolate, persistent); } }; } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_UTIL_H_