From 5b4f0c2c1bfbefebc2617958166fcb784b9cb18d Mon Sep 17 00:00:00 2001 From: Nigro Simone Date: Sat, 19 Sep 2026 07:03:46 +0200 Subject: [PATCH] Keep onData, onDataV2 and onWritable callbacks in MoveOnlyFunction inline storage --- src/HttpResponseWrapper.h | 18 +++++++++--------- src/Utilities.h | 11 +++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/HttpResponseWrapper.h b/src/HttpResponseWrapper.h index ec326ce48..516fd95b2 100644 --- a/src/HttpResponseWrapper.h +++ b/src/HttpResponseWrapper.h @@ -94,8 +94,8 @@ struct HttpResponseWrapper { Isolate *isolate = args.GetIsolate(); auto *res = getHttpResponse(args); if (res) { - /* This thing perfectly fits in with unique_function, and will Reset on destructor */ - UniquePersistent p(isolate, Local::Cast(args[0])); + /* Noexcept move keeps the 16 byte capture in MoveOnlyFunction's inline storage */ + NoexceptPersistent p(isolate, Local::Cast(args[0])); res->onData([p = std::move(p), isolate](std::string_view data, bool last) { HandleScope hs(isolate); @@ -103,7 +103,7 @@ struct HttpResponseWrapper { Local dataArrayBuffer = ArrayBuffer_New(isolate, (void *) data.data(), data.length()); Local argv[] = {dataArrayBuffer, Boolean::New(isolate, last)}; - CallJS(isolate, Local::New(isolate, p), 2, argv); + CallJS(isolate, Local::New(isolate, p.p), 2, argv); dataArrayBuffer->Detach(); }); @@ -197,8 +197,8 @@ struct HttpResponseWrapper { Isolate *isolate = args.GetIsolate(); auto *res = getHttpResponse(args); if (res) { - /* This thing perfectly fits in with unique_function, and will Reset on destructor */ - UniquePersistent p(isolate, Local::Cast(args[0])); + /* Noexcept move keeps the 16 byte capture in MoveOnlyFunction's inline storage */ + NoexceptPersistent p(isolate, Local::Cast(args[0])); res->onDataV2([p = std::move(p), isolate](std::string_view data, uint64_t maxRemainingBodyLength) { HandleScope hs(isolate); @@ -207,7 +207,7 @@ struct HttpResponseWrapper { /* Pass maxRemainingBodyLength so user can preallocate; 0 signals the last chunk */ Local argv[] = {dataArrayBuffer, BigInt::NewFromUnsigned(isolate, maxRemainingBodyLength)}; - CallJS(isolate, Local::New(isolate, p), 2, argv); + CallJS(isolate, Local::New(isolate, p.p), 2, argv); dataArrayBuffer->Detach(); }); @@ -341,8 +341,8 @@ struct HttpResponseWrapper { Isolate *isolate = args.GetIsolate(); auto *res = getHttpResponse(args); if (res) { - /* This thing perfectly fits in with unique_function, and will Reset on destructor */ - UniquePersistent p(isolate, Local::Cast(args[0])); + /* Noexcept move keeps the 16 byte capture in MoveOnlyFunction's inline storage */ + NoexceptPersistent p(isolate, Local::Cast(args[0])); res->onWritable([p = std::move(p), isolate](size_t offset) -> bool { HandleScope hs(isolate); @@ -350,7 +350,7 @@ struct HttpResponseWrapper { Local argv[] = {Number::New(isolate, offset)}; /* We should check if this is really here! */ - MaybeLocal maybeBoolean = CallJS(isolate, Local::New(isolate, p), 1, argv); + MaybeLocal maybeBoolean = CallJS(isolate, Local::New(isolate, p.p), 1, argv); if (maybeBoolean.IsEmpty()) { std::cerr << "Warning: uWS.HttpResponse.onWritable callback should return Boolean. See documentation for uWS.HttpResponse.onWritable and consult the user manual." << std::endl; /* The default should be true, as it only adds a potential extra send, rather than erroneously avoid it */ diff --git a/src/Utilities.h b/src/Utilities.h index 78ce93845..297a9e2f0 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -112,6 +112,17 @@ static inline bool missingArguments(int length, const FunctionCallbackInfo +struct NoexceptPersistent { + UniquePersistent p; + NoexceptPersistent(Isolate *isolate, const Local &v) : p(isolate, v) {} + NoexceptPersistent(NoexceptPersistent &&other) noexcept : p(std::move(other.p)) {} +}; +static_assert(std::is_nothrow_move_constructible>::value, "NoexceptPersistent must be nothrow movable"); + struct Callback { bool invalid = false; UniquePersistent f;