// 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 #include #include #include #include #include // std::remove_reference 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 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); // Used by the allocation functions when allocation fails. // Thin wrapper around v8::Isolate::LowMemoryNotification() that checks // whether V8 is initialized. void LowMemoryNotification(); #ifdef __GNUC__ #define NO_RETURN __attribute__((noreturn)) #else #define NO_RETURN #endif // The slightly odd function signature for Assert() is to ease // instruction cache pressure in calls from CHECK. NO_RETURN void Abort(); NO_RETURN void Assert(const char* const (*args)[4]); void DumpBacktrace(FILE* fp); template using remove_reference = std::remove_reference; #define FIXED_ONE_BYTE_STRING(isolate, string) \ (node::OneByteString((isolate), (string), sizeof(string) - 1)) #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ void operator=(const TypeName&) = delete; \ void operator=(TypeName&&) = delete; \ TypeName(const TypeName&) = delete; \ TypeName(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))) { \ static const char* const 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 UNREACHABLE() ABORT() #define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \ do { \ *ptr = \ Unwrap::type>(obj); \ if (*ptr == nullptr) \ return __VA_ARGS__; \ } while (0) // 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; private: template (U::*M)> friend class ListHead; ListNode* prev_; ListNode* next_; DISALLOW_COPY_AND_ASSIGN(ListNode); }; 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 MoveBack(ListHead* that); 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: ListNode head_; DISALLOW_COPY_AND_ASSIGN(ListHead); }; // 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 inline ContainerOfHelper ContainerOf(Inner Outer::*field, Inner* pointer); // 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 inline v8::Local PersistentToLocal( v8::Isolate* isolate, const v8::Persistent& 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 inline v8::Local StrongPersistentToLocal( const v8::Persistent& persistent); template inline v8::Local WeakPersistentToLocal( v8::Isolate* isolate, const v8::Persistent& persistent); // 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); inline void Wrap(v8::Local object, void* pointer); inline void ClearWrap(v8::Local object); template inline TypeName* Unwrap(v8::Local object); // 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); // 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]; }; 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 THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \ do { \ if (!Buffer::HasInstance(obj)) \ return env->ThrowTypeError("argument should be a Buffer"); \ } while (0) #define SPREAD_BUFFER_ARG(val, name) \ CHECK((val)->IsArrayBufferView()); \ v8::Local name = (val).As(); \ 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(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 inline void USE(T&&) {} } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_UTIL_H_