From e53d87a57c0698fee8ecbbe92e80a7b01184a394 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 11 Aug 2026 12:56:18 +0200 Subject: [PATCH 1/3] tools: remove skip logic in `commit-queue.sh` Signed-off-by: Antoine du Hamel PR-URL: https://github.com/nodejs/node/pull/65162 Reviewed-By: Jithil P Ponnan Reviewed-By: Filip Skokan Reviewed-By: Chemi Atlow Reviewed-By: Luigi Pinca Reviewed-By: Aviv Keller Reviewed-By: Trivikram Kamat Reviewed-By: Colin Ihrig --- tools/actions/commit-queue.sh | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tools/actions/commit-queue.sh b/tools/actions/commit-queue.sh index b6e62139d626..9828f2d6b3e8 100755 --- a/tools/actions/commit-queue.sh +++ b/tools/actions/commit-queue.sh @@ -35,18 +35,7 @@ SHOULD_ABORT= for pr in "$@"; do gh pr view "$pr" --json labels --jq ".labels" > labels.json - # Skip PR if CI was requested - if jq -e 'map(.name) | index("request-ci")' < labels.json; then - echo "pr ${pr} skipped, waiting for CI to start" - continue - fi - - # Skip PR if CI is still running - if gh pr checks "$pr" | grep -q "\spending\s"; then - echo "pr ${pr} skipped, CI still running" - continue - fi - + if jq -e 'map(.name) | index("commit-queue-squash")' < labels.json; then MULTIPLE_COMMIT_POLICY="--fixupAll" elif jq -e 'map(.name) | index("commit-queue-rebase")' < labels.json; then From 2fa068af8c3a2ce038d704b28c9ea1d6cb063a7c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 11 Aug 2026 07:08:57 -0700 Subject: [PATCH 2/3] src: use concepts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some general modernizations of templates Signed-off-by: James M Snell PR-URL: https://github.com/nodejs/node/pull/65148 Reviewed-By: Aviv Keller Reviewed-By: Tobias Nießen --- node.gyp | 1 + src/aliased_buffer-inl.h | 20 +++++++++++++ src/aliased_buffer.h | 3 +- src/blob_serializer_deserializer-inl.h | 20 ++++++------- src/blob_serializer_deserializer.h | 10 +++++++ src/crypto/crypto_aes.cc | 2 +- src/crypto/crypto_util.h | 2 +- src/debug_utils-inl.h | 14 +++------ src/json_utils.h | 6 ++-- src/memory_tracker-inl.h | 2 +- src/memory_tracker.h | 6 ++-- src/node_concepts.h | 39 ++++++++++++++++++++++++++ src/node_i18n.cc | 4 +-- src/node_platform.h | 9 ++---- src/node_realm-inl.h | 4 +-- src/node_realm.h | 2 +- src/node_report.h | 2 +- src/node_sea.cc | 10 +++---- src/node_snapshotable.cc | 12 ++++---- src/node_snapshotable.h | 13 ++------- src/node_wasi.cc | 8 +++--- src/req_wrap-inl.h | 3 +- src/stream_base-inl.h | 31 +++++++++----------- src/stream_base.h | 7 ++--- src/stream_wrap.cc | 6 ++-- src/tracing/trace_event_legacy_inl.h | 7 +++-- src/util-inl.h | 26 ++++++++--------- src/util.h | 33 ++++++++-------------- 28 files changed, 161 insertions(+), 141 deletions(-) create mode 100644 src/node_concepts.h diff --git a/node.gyp b/node.gyp index 9f99f3d64b7c..7d888e84eec4 100644 --- a/node.gyp +++ b/node.gyp @@ -255,6 +255,7 @@ 'src/node_blob.h', 'src/node_buffer.h', 'src/node_builtins.h', + 'src/node_concepts.h', 'src/node_config_file.h', 'src/node_constants.h', 'src/node_context_data.h', diff --git a/src/aliased_buffer-inl.h b/src/aliased_buffer-inl.h index da2c45321f6d..5cb36e707ce2 100644 --- a/src/aliased_buffer-inl.h +++ b/src/aliased_buffer-inl.h @@ -12,6 +12,7 @@ namespace node { typedef size_t AliasedBufferIndex; template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( v8::Isolate* isolate, const size_t count, const AliasedBufferIndex* index) : isolate_(isolate), count_(count), byte_offset_(0), index_(index) { @@ -34,6 +35,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( v8::Isolate* isolate, const size_t byte_offset, @@ -65,6 +67,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferBase::AliasedBufferBase( const AliasedBufferBase& that) : isolate_(that.isolate_), @@ -76,6 +79,7 @@ AliasedBufferBase::AliasedBufferBase( } template + requires std::is_scalar_v AliasedBufferIndex AliasedBufferBase::Serialize( v8::Local context, v8::SnapshotCreator* creator) { DCHECK(is_valid()); @@ -83,6 +87,7 @@ AliasedBufferIndex AliasedBufferBase::Serialize( } template + requires std::is_scalar_v inline void AliasedBufferBase::Deserialize( v8::Local context) { DCHECK_NOT_NULL(index_); @@ -99,6 +104,7 @@ inline void AliasedBufferBase::Deserialize( } template + requires std::is_scalar_v AliasedBufferBase& AliasedBufferBase::operator=( AliasedBufferBase&& that) noexcept { DCHECK(is_valid()); @@ -116,41 +122,48 @@ AliasedBufferBase& AliasedBufferBase::operator=( } template + requires std::is_scalar_v v8::Local AliasedBufferBase::GetJSArray() const { DCHECK(is_valid()); return js_array_.Get(isolate_); } template + requires std::is_scalar_v void AliasedBufferBase::Release() { DCHECK_NULL(index_); js_array_.Reset(); } template + requires std::is_scalar_v inline void AliasedBufferBase::MakeWeak() { DCHECK(is_valid()); js_array_.SetWeak(); } template + requires std::is_scalar_v v8::Local AliasedBufferBase::GetArrayBuffer() const { return GetJSArray()->Buffer(); } template + requires std::is_scalar_v inline const NativeT* AliasedBufferBase::GetNativeBuffer() const { DCHECK(is_valid()); return buffer_; } template + requires std::is_scalar_v inline const NativeT* AliasedBufferBase::operator*() const { return GetNativeBuffer(); } template + requires std::is_scalar_v inline void AliasedBufferBase::SetValue(const size_t index, NativeT value) { DCHECK_LT(index, count_); @@ -159,6 +172,7 @@ inline void AliasedBufferBase::SetValue(const size_t index, } template + requires std::is_scalar_v inline const NativeT AliasedBufferBase::GetValue( const size_t index) const { DCHECK(is_valid()); @@ -167,6 +181,7 @@ inline const NativeT AliasedBufferBase::GetValue( } template + requires std::is_scalar_v typename AliasedBufferBase::Reference AliasedBufferBase::operator[](size_t index) { DCHECK(is_valid()); @@ -174,16 +189,19 @@ AliasedBufferBase::operator[](size_t index) { } template + requires std::is_scalar_v NativeT AliasedBufferBase::operator[](size_t index) const { return GetValue(index); } template + requires std::is_scalar_v size_t AliasedBufferBase::Length() const { return count_; } template + requires std::is_scalar_v void AliasedBufferBase::reserve(size_t new_capacity) { DCHECK(is_valid()); DCHECK_GE(new_capacity, count_); @@ -214,11 +232,13 @@ void AliasedBufferBase::reserve(size_t new_capacity) { } template + requires std::is_scalar_v inline bool AliasedBufferBase::is_valid() const { return index_ == nullptr && !js_array_.IsEmpty(); } template + requires std::is_scalar_v inline size_t AliasedBufferBase::SelfSize() const { return sizeof(*this); } diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h index 8e988bcb010d..ff2b961724d6 100644 --- a/src/aliased_buffer.h +++ b/src/aliased_buffer.h @@ -29,10 +29,9 @@ typedef size_t AliasedBufferIndex; * observed. Any notification APIs will be left as a future exercise. */ template + requires std::is_scalar_v class AliasedBufferBase final : public MemoryRetainer { public: - static_assert(std::is_scalar_v); - AliasedBufferBase(v8::Isolate* isolate, size_t count, const AliasedBufferIndex* index = nullptr); diff --git a/src/blob_serializer_deserializer-inl.h b/src/blob_serializer_deserializer-inl.h index fa258dbe8144..2a2aed1171f7 100644 --- a/src/blob_serializer_deserializer-inl.h +++ b/src/blob_serializer_deserializer-inl.h @@ -90,8 +90,8 @@ std::string BlobSerializerDeserializer::GetName() const { // Helper for reading numeric types. template template + requires std::is_arithmetic_v T BlobDeserializer::ReadArithmetic() { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); T result; ReadArithmetic(&result, 1); return result; @@ -158,8 +158,8 @@ std::string_view BlobDeserializer::ReadStringView(StringLogMode mode) { // Helper for reading an array of numeric types. template template + requires std::is_arithmetic_v void BlobDeserializer::ReadArithmetic(T* out, size_t count) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. if (is_debug) { std::string name = GetName(); @@ -180,8 +180,8 @@ void BlobDeserializer::ReadArithmetic(T* out, size_t count) { // Helper for reading numeric vectors. template template + requires std::is_arithmetic_v std::vector BlobDeserializer::ReadArithmeticVector(size_t count) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result(count); ReadArithmetic(result.data(), count); @@ -191,8 +191,8 @@ std::vector BlobDeserializer::ReadArithmeticVector(size_t count) { // Helper for reading non-numeric vectors. template template + requires(!std::is_arithmetic_v) std::vector BlobDeserializer::ReadNonArithmeticVector(size_t count) { - static_assert(!std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not read contents for vectors of size 0. std::vector result; result.reserve(count); @@ -224,8 +224,8 @@ T BlobDeserializer::ReadElement() { // Helper for writing numeric types. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmetic(const T& data) { - static_assert(std::is_arithmetic_v, "Not an arithmetic type"); return WriteArithmetic(&data, 1); } @@ -303,8 +303,8 @@ static size_t kPreviewCount = 16; // Helper for writing an array of numeric types. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmetic(const T* data, size_t count) { - static_assert(std::is_arithmetic_v, "Arithmetic type"); DCHECK_GT(count, 0); // Should not write contents for vectors of size 0. if (is_debug) { size_t preview_count = count < kPreviewCount ? count : kPreviewCount; @@ -338,18 +338,18 @@ size_t BlobSerializer::WriteArithmetic(const T* data, size_t count) { // Helper for writing numeric vectors. template template + requires std::is_arithmetic_v size_t BlobSerializer::WriteArithmeticVector( const std::vector& data) { - static_assert(std::is_arithmetic_v, "Arithmetic type"); return WriteArithmetic(data.data(), data.size()); } // Helper for writing non-numeric vectors. template template -size_t BlobSerializer::WriteNonArithmeticVector( - const std::vector& data) { - static_assert(!std::is_arithmetic_v, "Arithmetic type"); + requires(!std::is_arithmetic_v) +size_t + BlobSerializer::WriteNonArithmeticVector(const std::vector& data) { DCHECK_GT(data.size(), 0); // Should not write contents for vectors of size 0. size_t written_total = 0; diff --git a/src/blob_serializer_deserializer.h b/src/blob_serializer_deserializer.h index fe7989e22a35..cd63d0477675 100644 --- a/src/blob_serializer_deserializer.h +++ b/src/blob_serializer_deserializer.h @@ -1,7 +1,9 @@ #ifndef SRC_BLOB_SERIALIZER_DESERIALIZER_H_ #define SRC_BLOB_SERIALIZER_DESERIALIZER_H_ +#include #include +#include #include #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS @@ -49,6 +51,7 @@ class BlobDeserializer : public BlobSerializerDeserializer { // Helper for reading numeric types. template + requires std::is_arithmetic_v T ReadArithmetic(); // Layout of vectors: @@ -63,15 +66,18 @@ class BlobDeserializer : public BlobSerializerDeserializer { // Helper for reading an array of numeric types. template + requires std::is_arithmetic_v void ReadArithmetic(T* out, size_t count); // Helper for reading numeric vectors. template + requires std::is_arithmetic_v std::vector ReadArithmeticVector(size_t count); private: // Helper for reading non-numeric vectors. template + requires(!std::is_arithmetic_v) std::vector ReadNonArithmeticVector(size_t count); template @@ -94,6 +100,7 @@ class BlobSerializer : public BlobSerializerDeserializer { // Helper for writing numeric types. template + requires std::is_arithmetic_v size_t WriteArithmetic(const T& data); // Layout of vectors: @@ -110,15 +117,18 @@ class BlobSerializer : public BlobSerializerDeserializer { // Helper for writing an array of numeric types. template + requires std::is_arithmetic_v size_t WriteArithmetic(const T* data, size_t count); // Helper for writing numeric vectors. template + requires std::is_arithmetic_v size_t WriteArithmeticVector(const std::vector& data); private: // Helper for writing non-numeric vectors. template + requires(!std::is_arithmetic_v) size_t WriteNonArithmeticVector(const std::vector& data); template diff --git a/src/crypto/crypto_aes.cc b/src/crypto/crypto_aes.cc index 171688b92926..bea8f5b24be5 100644 --- a/src/crypto/crypto_aes.cc +++ b/src/crypto/crypto_aes.cc @@ -259,7 +259,7 @@ WebCryptoCipherStatus AES_KW_Cipher(Environment* env, // implementation here: // https://github.com/chromium/chromium/blob/7af6cfd/components/webcrypto/algorithms/aes_ctr.cc -template +template T CeilDiv(T a, T b) { return a == 0 ? 0 : 1 + (a - 1) / b; } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 5c9dad13196b..dd7e0842a29b 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -732,8 +732,8 @@ class ArrayBufferOrViewContents final { } template + requires(sizeof(M) == 1) void CopyTo(M* dest, size_t len) const { - static_assert(sizeof(M) == 1, "sizeof(M) must equal 1"); len = std::min(len, size()); if (len > 0 && data() != nullptr) { memcpy(dest, data(), len); diff --git a/src/debug_utils-inl.h b/src/debug_utils-inl.h index 3afab8629968..5f739246825a 100644 --- a/src/debug_utils-inl.h +++ b/src/debug_utils-inl.h @@ -49,10 +49,7 @@ struct ToStringHelper { return value.ToStringView(); } - template || std::is_enum_v, bool>, - typename dummy = bool> + template static std::string Convert(const T& value) { return std::to_string(value); } @@ -81,9 +78,7 @@ struct ToStringHelper { return utf8_value.ToString(); } - template >> + template static std::string BaseConvert(const T& value) { auto v = static_cast(value); char ret[3 * sizeof(T)]; @@ -96,9 +91,8 @@ struct ToStringHelper { } while ((v >>= BASE_BITS) != 0); return ptr; } - template >> + template + requires(!std::integral) static auto BaseConvert(T&& value) { return Convert(std::forward(value)); } diff --git a/src/json_utils.h b/src/json_utils.h index 06d4a7ac0905..e75556578522 100644 --- a/src/json_utils.h +++ b/src/json_utils.h @@ -3,6 +3,8 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_concepts.h" + #include #include #include @@ -132,9 +134,7 @@ class JSONWriter { }; private: - template ::is_specialized, bool>::type> + template inline void write_value(T number) { if constexpr (std::is_same::value) out_ << (number ? "true" : "false"); diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index a59452fd994c..3c82983ce01e 100644 --- a/src/memory_tracker-inl.h +++ b/src/memory_tracker-inl.h @@ -203,7 +203,7 @@ void MemoryTracker::TrackField(const char* edge_name, TrackField(edge_name, container, node_name, element_name); } -template +template void MemoryTracker::TrackField(const char* edge_name, const T& value, const char* node_name) { diff --git a/src/memory_tracker.h b/src/memory_tracker.h index 88987c954f4d..d7893f10b3af 100644 --- a/src/memory_tracker.h +++ b/src/memory_tracker.h @@ -2,6 +2,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "node_concepts.h" #include "v8-profiler.h" #include @@ -229,10 +230,7 @@ class MemoryTracker { inline void TrackField(const char* edge_name, const std::basic_string& value, const char* node_name = nullptr); - template ::is_specialized, bool>::type, - typename dummy = bool> + template inline void TrackField(const char* edge_name, const T& value, const char* node_name = nullptr); diff --git a/src/node_concepts.h b/src/node_concepts.h new file mode 100644 index 000000000000..5dbce5c6f88a --- /dev/null +++ b/src/node_concepts.h @@ -0,0 +1,39 @@ +#ifndef SRC_NODE_CONCEPTS_H_ +#define SRC_NODE_CONCEPTS_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include + +namespace node { + +// A numeric type recognized by std::numeric_limits. +// The is_array guard prevents a hard error from instantiating +// numeric_limits, whose member functions would return array types. +template +concept NumericValue = ! +std::is_array_v&& std::numeric_limits::is_specialized; + +// A numeric type or an enum (which has an underlying numeric type). +template +concept NumericOrEnum = NumericValue || std::is_enum_v; + +// A type that has a valid std::char_traits specialization, as required by +// std::basic_string and std::basic_string_view. +template +concept StandardCharType = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v; + +// Test whether some value can be called with (). +template +concept IsCallable = std::is_function::value || requires { &T::operator(); }; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_CONCEPTS_H_ diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 3c4f419aa294..96ec2f6ee5ab 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -103,14 +103,12 @@ namespace i18n { namespace { template + requires(sizeof(T) == 1 || sizeof(T) == 2) MaybeLocal ToBufferEndian(Environment* env, MaybeStackBuffer* buf) { Local ret; if (!Buffer::New(env, buf).ToLocal(&ret)) { return {}; } - - static_assert(sizeof(T) == 1 || sizeof(T) == 2, - "Currently only one- or two-byte buffers are supported"); if constexpr (sizeof(T) > 1 && IsBigEndian()) { SPREAD_BUFFER_ARG(ret, retbuf); CHECK(nbytes::SwapBytes16(retbuf_data, retbuf_length)); diff --git a/src/node_platform.h b/src/node_platform.h index f47e2a46b66b..e98ecf322802 100644 --- a/src/node_platform.h +++ b/src/node_platform.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include #include #include @@ -20,12 +21,8 @@ class NodePlatform; class IsolateData; class PerIsolatePlatformData; -template -struct has_priority : std::false_type {}; - template -struct has_priority().priority)>> - : std::true_type {}; +concept has_priority = requires(T t) { t.priority; }; template class TaskQueue { @@ -35,7 +32,7 @@ class TaskQueue { struct EntryCompare { bool operator()(const std::unique_ptr& a, const std::unique_ptr& b) const { - if constexpr (has_priority::value) { + if constexpr (has_priority) { return a->priority < b->priority; } else { return false; diff --git a/src/node_realm-inl.h b/src/node_realm-inl.h index b004bd1e1500..394ece5a8ace 100644 --- a/src/node_realm-inl.h +++ b/src/node_realm-inl.h @@ -92,10 +92,8 @@ inline T* Realm::GetBindingData() { return result; } -template +template T, typename... Args> inline T* Realm::AddBindingData(v8::Local target, Args&&... args) { - // This won't compile if T is not a BaseObject subclass. - static_assert(std::is_base_of_v); // The binding data must be weak so that it won't keep the realm reachable // from strong GC roots indefinitely. The wrapper object of binding data // should be referenced from JavaScript, thus the binding data should be diff --git a/src/node_realm.h b/src/node_realm.h index 2ed04aa271b4..690beaf1a1aa 100644 --- a/src/node_realm.h +++ b/src/node_realm.h @@ -127,7 +127,7 @@ class Realm : public MemoryRetainer { // Methods created using SetMethod(), SetPrototypeMethod(), etc. inside // this scope can access the created T* object using // GetBindingData(args) later. - template + template T, typename... Args> T* AddBindingData(v8::Local target, Args&&... args); template static inline T* GetBindingData(const v8::PropertyCallbackInfo& info); diff --git a/src/node_report.h b/src/node_report.h index 98be339ae90d..ae1e03df625b 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -22,7 +22,7 @@ namespace report { void WalkHandleNetwork(uv_handle_t* h, void* arg); void WalkHandleNoNetwork(uv_handle_t* h, void* arg); -template +template std::string ValueToHexString(T value) { std::stringstream hex; diff --git a/src/node_sea.cc b/src/node_sea.cc index 81d47c11ffab..03d487f1dba7 100644 --- a/src/node_sea.cc +++ b/src/node_sea.cc @@ -66,9 +66,8 @@ class SeaSerializer : public BlobSerializer { : BlobSerializer( per_process::enabled_debug_list.enabled(DebugCategory::SEA)) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) size_t Write(const T& data); }; @@ -150,9 +149,8 @@ class SeaDeserializer : public BlobDeserializer { : BlobDeserializer( per_process::enabled_debug_list.enabled(DebugCategory::SEA), v) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) T Read(); }; diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index f90e5f28ac28..7f5d9b9e1821 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -155,9 +155,8 @@ class SnapshotDeserializer : public BlobDeserializer { DebugCategory::SNAPSHOT_SERDES), v) {} - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) T Read(); }; @@ -172,9 +171,8 @@ class SnapshotSerializer : public BlobSerializer { sink.reserve(4 * 1024 * 1024); } - template ::value>* = nullptr, - std::enable_if_t::value>* = nullptr> + template + requires(!std::is_arithmetic_v && !std::same_as) size_t Write(const T& data); }; @@ -733,13 +731,13 @@ static std::string FormatSize(size_t size) { } template + requires(std::same_as || std::same_as) void WriteByteVectorLiteral(std::ostream* ss, const T* vec, size_t size, const char* var_name, bool use_array_literals) { constexpr bool is_uint8_t = std::is_same_v; - static_assert(is_uint8_t || std::is_same_v); constexpr const char* type_name = is_uint8_t ? "uint8_t" : "char"; if (!use_array_literals) { const uint8_t* data = reinterpret_cast(vec); diff --git a/src/node_snapshotable.h b/src/node_snapshotable.h index 31be74bcfd56..18ef5fbd43fd 100644 --- a/src/node_snapshotable.h +++ b/src/node_snapshotable.h @@ -41,11 +41,8 @@ struct InternalFieldInfoBase { EmbedderObjectType type; size_t length; - template + template T> static T* New(EmbedderObjectType type) { - static_assert(std::is_base_of_v || - std::is_same_v, - "Can only accept InternalFieldInfoBase subclasses"); void* buf = ::operator new[](sizeof(T)); memset(buf, 0, sizeof(T)); // Make the padding reproducible. T* result = new (buf) T; @@ -54,13 +51,9 @@ struct InternalFieldInfoBase { return result; } - template + template T> + requires std::is_trivially_copyable_v T* Copy() const { - static_assert(std::is_base_of_v || - std::is_same_v, - "Can only accept InternalFieldInfoBase subclasses"); - static_assert(std::is_trivially_copyable_v, - "Can only memcpy trivially copyable class"); void* buf = ::operator new[](sizeof(T)); T* result = new (buf) T; memcpy(result, this, sizeof(T)); diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 5cd9115611f1..6c2adc826571 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -358,8 +358,8 @@ template ::value, bool> = true> + std::size_t... Indices> + requires(!std::is_void_v) inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, @@ -372,8 +372,8 @@ template ::value, bool> = true> + std::size_t... Indices> + requires std::is_void_v inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index ac0ca8921a1e..61d6d79116ad 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -111,8 +111,7 @@ struct CallLibuvFunction { template struct MakeLibuvRequestCallback { static T For(ReqWrap* req_wrap, T v) { - static_assert(!is_callable, - "MakeLibuvRequestCallback missed a callback"); + static_assert(!IsCallable, "MakeLibuvRequestCallback missed a callback"); return v; } }; diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 4418cdad504f..cc468ee566a7 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -98,25 +98,20 @@ StreamBase::StreamBase(Environment* env) : env_(env) { PushStreamListener(&default_listener_); } -template +template OtherBase> SimpleShutdownWrap::SimpleShutdownWrap( - StreamBase* stream, - v8::Local req_wrap_obj) - : ShutdownWrap(stream, req_wrap_obj), - OtherBase(stream->stream_env(), - req_wrap_obj, - AsyncWrap::PROVIDER_SHUTDOWNWRAP) { -} - -template -SimpleWriteWrap::SimpleWriteWrap( - StreamBase* stream, - v8::Local req_wrap_obj) - : WriteWrap(stream, req_wrap_obj), - OtherBase(stream->stream_env(), - req_wrap_obj, - AsyncWrap::PROVIDER_WRITEWRAP) { -} + StreamBase* stream, v8::Local req_wrap_obj) + : ShutdownWrap(stream, req_wrap_obj), + OtherBase(stream->stream_env(), + req_wrap_obj, + AsyncWrap::PROVIDER_SHUTDOWNWRAP) {} + +template OtherBase> +SimpleWriteWrap::SimpleWriteWrap(StreamBase* stream, + v8::Local req_wrap_obj) + : WriteWrap(stream, req_wrap_obj), + OtherBase( + stream->stream_env(), req_wrap_obj, AsyncWrap::PROVIDER_WRITEWRAP) {} void StreamBase::AttachToObject(v8::Local obj) { obj->SetAlignedPointerInInternalField( diff --git a/src/stream_base.h b/src/stream_base.h index be00134eb1fc..cb795a541297 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -432,12 +432,11 @@ class StreamBase : public StreamResource { friend class Environment; // For kNumStreamBaseStateFields. }; - // These are helpers for creating `ShutdownWrap`/`WriteWrap` instances. // `OtherBase` must have a constructor that matches the `AsyncWrap` -// constructors’s (Environment*, Local, AsyncWrap::Provider) signature +// constructors's (Environment*, Local, AsyncWrap::Provider) signature // and be a subclass of `AsyncWrap`. -template +template OtherBase> class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { public: enum InternalFields { @@ -459,7 +458,7 @@ class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { } }; -template +template OtherBase> class SimpleWriteWrap : public WriteWrap, public OtherBase { public: enum InternalFields { diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index b41f6ac74947..6b85d6533879 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -227,12 +227,10 @@ void LibuvStreamWrap::OnUvAlloc(size_t suggested_size, uv_buf_t* buf) { } template + requires(std::derived_from || + std::derived_from) static MaybeLocal AcceptHandle(Environment* env, LibuvStreamWrap* parent) { - static_assert(std::is_base_of::value || - std::is_base_of::value, - "Can only accept stream handles"); - EscapableHandleScope scope(env->isolate()); Local wrap_obj; diff --git a/src/tracing/trace_event_legacy_inl.h b/src/tracing/trace_event_legacy_inl.h index 4fadb6d8e38f..9030473afe96 100644 --- a/src/tracing/trace_event_legacy_inl.h +++ b/src/tracing/trace_event_legacy_inl.h @@ -9,6 +9,8 @@ #error Perfetto is enabled. #endif +#include + #include "v8-platform.h" #include "tracing/agent_legacy.h" #include "tracing/trace_event_helper.h" @@ -558,9 +560,8 @@ static inline void SetTraceValue(v8::ConvertableToTraceFormat* convertable_value *value = static_cast(reinterpret_cast(convertable_value)); } -template -static inline typename std::enable_if< - std::is_convertible::value>::type +template T> +static inline void SetTraceValue(std::unique_ptr ptr, unsigned char* type, uint64_t* value) { SetTraceValue(ptr.release(), type, value); } diff --git a/src/util-inl.h b/src/util-inl.h index e357d15a1449..4ac8726aa43f 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -195,7 +195,7 @@ char ToLower(char c) { return std::tolower(c, std::locale::classic()); } -template +template std::string ToLower(const T& in) { auto it = std::cbegin(in); auto end = std::cend(in); @@ -210,7 +210,7 @@ char ToUpper(char c) { return std::toupper(c, std::locale::classic()); } -template +template std::string ToUpper(const T& in) { auto it = std::cbegin(in); auto end = std::cend(in); @@ -239,7 +239,7 @@ bool StringEqualNoCaseN(const char* a, const char* b, size_t length) { return true; } -template +template inline T MultiplyWithOverflowCheck(T a, T b) { auto ret = a * b; if (a != 0) @@ -485,7 +485,7 @@ v8::Local ConvertNumberToV8Value(v8::Isolate* isolate, return v8::Number::New(isolate, static_cast(number)); } -template +template v8::MaybeLocal ToV8Value(v8::Local context, const T& number, v8::Isolate* isolate) { @@ -494,14 +494,10 @@ v8::MaybeLocal ToV8Value(v8::Local context, } template + requires std::is_arithmetic_v v8::Local ToV8ValuePrimitiveArray(v8::Local context, const std::vector& vec, v8::Isolate* isolate) { - static_assert( - std::is_same_v || std::is_integral_v || - std::is_floating_point_v, - "Only primitive types (bool, integral, floating-point) are supported."); - if (isolate == nullptr) isolate = v8::Isolate::GetCurrent(); v8::EscapableHandleScope handle_scope(isolate); @@ -550,6 +546,7 @@ void MaybeStackBuffer::AllocateSufficientStorage( } template + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { DCHECK(value->IsArrayBufferView() || value->IsSharedArrayBuffer() || @@ -558,6 +555,7 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local value) { CHECK(value->IsArrayBufferView()); @@ -565,14 +563,15 @@ ArrayBufferViewContents::ArrayBufferViewContents( } template + requires(sizeof(T) == 1) ArrayBufferViewContents::ArrayBufferViewContents( v8::Local abv) { Read(abv); } template + requires(sizeof(T) == 1) void ArrayBufferViewContents::Read(v8::Local abv) { - static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); length_ = abv->ByteLength(); if (length_ > sizeof(stack_storage_) || abv->HasBuffer()) { auto buf_data = abv->Buffer()->Data(); @@ -585,8 +584,8 @@ void ArrayBufferViewContents::Read(v8::Local abv) { } template + requires(sizeof(T) == 1) void ArrayBufferViewContents::ReadValue(v8::Local buf) { - static_assert(sizeof(T) == 1, "Only supports one-byte data at the moment"); DCHECK(buf->IsArrayBufferView() || buf->IsSharedArrayBuffer() || buf->IsArrayBuffer()); @@ -649,10 +648,7 @@ constexpr std::string_view FastStringKey::as_string_view() const { } // Converts a V8 numeric value to a corresponding C++ primitive or enum type. -template ::is_specialized || - std::is_enum_v>> +template T FromV8Value(v8::Local value) { if constexpr (std::is_enum_v) { using Underlying = std::underlying_type_t; diff --git a/src/util.h b/src/util.h index 48305bfdc131..a979f626bdb4 100644 --- a/src/util.h +++ b/src/util.h @@ -30,6 +30,7 @@ #include "v8.h" #include "node.h" +#include "node_concepts.h" #include "node_exit_code.h" #include @@ -40,6 +41,7 @@ #include #include +#include #include #include #include @@ -99,7 +101,7 @@ inline char* Calloc(size_t n); inline char* UncheckedMalloc(size_t n); inline char* UncheckedCalloc(size_t n); -template +template inline T MultiplyWithOverflowCheck(T a, T b); namespace per_process { @@ -366,12 +368,12 @@ inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, // tolower() is locale-sensitive. Use ToLower() instead. inline char ToLower(char c); -template +template inline std::string ToLower(const T& in); // toupper() is locale-sensitive. Use ToUpper() instead. inline char ToUpper(char c); -template +template inline std::string ToUpper(const T& in); // strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead. @@ -390,14 +392,6 @@ constexpr size_t strsize(const T (&)[N]) { return N - 1; } -// A type that has a valid std::char_traits specialization, as required by -// std::basic_string and std::basic_string_view. -template -concept standard_char_type = - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v; - // Allocates an array of member type T. For up to kStackStorageSize items, // the stack is used, otherwise malloc(). template @@ -511,11 +505,11 @@ class MaybeStackBuffer { free(buf_); } - template + template inline std::basic_string ToString() const { return {out(), length()}; } - template + template inline std::basic_string_view ToStringView() const { return {out(), length()}; } @@ -534,6 +528,7 @@ class MaybeStackBuffer { // or for small data, a copy of it. This object's lifetime is bound to the // original ArrayBufferView's lifetime. template + requires(sizeof(T) == 1) class ArrayBufferViewContents { public: ArrayBufferViewContents() = default; @@ -610,7 +605,7 @@ class BufferValue : public MaybeStackBuffer { // silence a compiler warning about that. template inline void USE(T&&) {} -template +template struct OnScopeLeaveImpl { Fn fn_; bool active_; @@ -630,7 +625,7 @@ struct OnScopeLeaveImpl { // auto on_scope_leave = OnScopeLeave([&] { // // ... run some code ... // }); -template +template inline MUST_USE_RESULT OnScopeLeaveImpl OnScopeLeave(Fn&& fn) { return OnScopeLeaveImpl{std::move(fn)}; } @@ -676,11 +671,6 @@ struct MallocedBuffer { MallocedBuffer& operator=(const MallocedBuffer&) = delete; }; -// Test whether some value can be called with (). -template -concept is_callable = - std::is_function::value || requires { &T::operator(); }; - template struct FunctionDeleter { void operator()(T* pointer) const { function(pointer); } @@ -710,8 +700,7 @@ inline v8::MaybeLocal ToV8Value(v8::Local context, inline v8::MaybeLocal ToV8Value(v8::Local context, v8_inspector::StringView str, v8::Isolate* isolate); -template ::is_specialized, bool>::type> +template inline v8::MaybeLocal ToV8Value(v8::Local context, const T& number, v8::Isolate* isolate = nullptr); From 175cd52c3b78c18d8ce816ffd1b5b282dbdf3d0d Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 11 Aug 2026 10:09:16 -0400 Subject: [PATCH 3/3] deps: enable AVX-512 OpenSSL asm with clang Node.js ships two pre-generated sets of OpenSSL assembly: `asm`, which contains the AVX-512 routines, and `asm_avx2`, which does not. The set is picked in deps/openssl/openssl.gyp based on `gas_version` or `nasm_version`, but configure.py only reports `gas_version` when the compiler is not clang, because clang uses its own integrated assembler and has no GNU assembler version to report. Consequently every clang build silently falls back to the AVX-512-less `asm_avx2` set, with no warning. The result is that `ossl_vaes_vpclmulqdq_capable()` is assembled as a stub that always returns 0, so OpenSSL never selects `ossl_aes_gcm_encrypt_avx512()` and uses the older AES-NI path instead. On an Intel Xeon Gold 6548N this costs roughly 1.6x on AES-256-GCM and 1.8x on both ChaCha20-Poly1305 and RSA-2048 signing. This is not limited to custom builds: BUILDING.md documents that the official linux-x64 binaries are produced with clang, and the shipped v25.x and v26.x binaries contain the stub. Accept `llvm_version` in the condition, the way deps/openssl/openssl.gypi already does for the AVX2 set. clang's integrated assembler has handled AVX512IFMA since 3.9 and VAES / VPCLMULQDQ since 6.0; 8.0 is used as a conservative floor, well below the clang 19.1 that Node.js already requires. PR-URL: https://github.com/nodejs/node/pull/65136 Reviewed-By: Aviv Keller Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- deps/openssl/openssl.gyp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 4e16412a0283..144085fd33df 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -36,7 +36,8 @@ # VC-WIN64-ARM inherits from VC-noCE-common that has no asms. 'includes': ['./openssl_no_asm.gypi'], }, 'gas_version and v(gas_version) >= v("2.26") or ' - 'nasm_version and v(nasm_version) >= v("2.11.8")', { + 'nasm_version and v(nasm_version) >= v("2.11.8") or ' + 'llvm_version and v(llvm_version) >= v("8.0")', { # Require AVX512IFMA supported. See # https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_ia32cap.html # Currently crypto/poly1305/asm/poly1305-x86_64.pl requires AVX512IFMA. @@ -114,7 +115,8 @@ # VC-WIN64-ARM inherits from VC-noCE-common that has no asms. 'includes': ['./openssl-fips_no_asm.gypi'], }, 'gas_version and v(gas_version) >= v("2.26") or ' - 'nasm_version and v(nasm_version) >= v("2.11.8")', { + 'nasm_version and v(nasm_version) >= v("2.11.8") or ' + 'llvm_version and v(llvm_version) >= v("8.0")', { # Require AVX512IFMA supported. See # https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_ia32cap.html # Currently crypto/poly1305/asm/poly1305-x86_64.pl requires AVX512IFMA.