From 871ae4e3dd66ea685a37476d894c2367b8a643eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:50:55 +0000 Subject: [PATCH] Pare back V8 fast call to WebSocketWrapper.send only Co-authored-by: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com> --- src/HttpResponseWrapper.h | 57 +++------------------------- src/WebSocketWrapper.h | 78 +++++---------------------------------- 2 files changed, 14 insertions(+), 121 deletions(-) diff --git a/src/HttpResponseWrapper.h b/src/HttpResponseWrapper.h index f98c86d3..ec71920c 100644 --- a/src/HttpResponseWrapper.h +++ b/src/HttpResponseWrapper.h @@ -19,7 +19,6 @@ #include "Utilities.h" #include -#include "v8-fast-api-calls.h" using namespace v8; thread_local int insideCorkCallback = 0; @@ -566,46 +565,6 @@ struct HttpResponseWrapper { } } - /* V8 fast call fast paths - called directly from JIT-optimised code. - * Requirements: no JS heap allocation, no JS execution, POD or ArrayBuffer args only. - * A null internal-field pointer (closed/invalid response) sets options.fallback = true so V8 - * re-invokes the slow path which throws the proper exception. */ - - template - static uint32_t res_getRemotePort_fast(v8::Local receiver, v8::FastApiCallbackOptions& options) { - void *ptr = receiver->GetAlignedPointerFromInternalField(0); - if (!ptr) { options.fallback = true; return 0; } - return ((uWS::HttpResponse *) ptr)->getRemotePort(); - } - - template - static uint32_t res_getProxiedRemotePort_fast(v8::Local receiver, v8::FastApiCallbackOptions& options) { - void *ptr = receiver->GetAlignedPointerFromInternalField(0); - if (!ptr) { options.fallback = true; return 0; } - return ((uWS::HttpResponse *) ptr)->getProxiedRemotePort(); - } - - template - static double res_getWriteOffset_fast(v8::Local receiver, v8::FastApiCallbackOptions& options) { - void *ptr = receiver->GetAlignedPointerFromInternalField(0); - if (!ptr) { options.fallback = true; return 0.0; } - return (double) ((uWS::HttpResponse *) ptr)->getWriteOffset(); - } - - /* write() returns bool (true = success, false = backpressure). Handles TCP, TLS, and QUIC. */ - template - static bool res_write_fast(v8::Local receiver, const v8::FastApiTypedArray& data, v8::FastApiCallbackOptions& options) { - void *ptr = receiver->GetAlignedPointerFromInternalField(0); - if (!ptr) { options.fallback = true; return false; } - uint8_t *buf = nullptr; - if (!data.getStorageIfAligned(&buf)) { options.fallback = true; return false; } - if constexpr (PROTOCOL == 2) { - return ((uWS::Http3Response *) ptr)->write(std::string_view((char *) buf, data.length())); - } else { - return ((uWS::HttpResponse *) ptr)->write(std::string_view((char *) buf, data.length())); - } - } - /* 0 = TCP, 1 = TLS, 2 = QUIC, 3 = CACHE */ template static Local init(Isolate *isolate) { @@ -621,9 +580,7 @@ struct HttpResponseWrapper { } resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - /* Register our functions. - * Static CFunction descriptors are per template specialisation (SSL 0/1/2/3) - * and are initialised once because the addon is a single translation unit. */ + /* Register our functions */ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_end)); /* Cache has almost nothing wrapped yet */ @@ -631,8 +588,7 @@ struct HttpResponseWrapper { resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeStatus", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeStatus)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "endWithoutBody", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_endWithoutBody)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "tryEnd", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_tryEnd)); - static v8::CFunction fast_write = v8::CFunction::Make(res_write_fast); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_write)); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeHeader)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_close)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onWritable", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onWritable)); @@ -642,20 +598,17 @@ struct HttpResponseWrapper { /* QUIC has a lot of functions unimplemented */ if constexpr (SSL != 2) { resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onFullData", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onFullData)); - static v8::CFunction fast_getWriteOffset = v8::CFunction::Make(res_getWriteOffset_fast); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getWriteOffset)); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "maxRemainingBodyLength", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_maxRemainingBodyLength)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddress)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "collect", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "upgrade", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_upgrade)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddressAsText)); - static v8::CFunction fast_getRemotePort = v8::CFunction::Make(res_getRemotePort_fast); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemotePort, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getRemotePort)); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemotePort)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddress)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddressAsText)); - static v8::CFunction fast_getProxiedRemotePort = v8::CFunction::Make(res_getProxiedRemotePort_fast); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemotePort, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getProxiedRemotePort)); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemotePort)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "pause", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_pause)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_resume)); } diff --git a/src/WebSocketWrapper.h b/src/WebSocketWrapper.h index c32c322e..655b944c 100644 --- a/src/WebSocketWrapper.h +++ b/src/WebSocketWrapper.h @@ -320,54 +320,11 @@ struct WebSocketWrapper { } } - /* V8 fast call fast paths - called directly from JIT-optimised code. - * Requirements: no JS heap allocation, no JS execution, POD or ArrayBuffer args only. + /* V8 fast call fast path for send - called directly from JIT-optimised code. + * Requirements: no JS heap allocation, no JS execution, Uint8Array arg only. * A null internal-field pointer (closed socket) sets options.fallback = true so V8 * re-invokes the slow path which throws the proper exception. */ - template - static uint32_t uWS_WebSocket_getBufferedAmount_fast(v8::Local receiver, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - return ws->getBufferedAmount(); - } - - template - static uint32_t uWS_WebSocket_getRemotePort_fast(v8::Local receiver, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - return ws->getRemotePort(); - } - - /* The three send-fragment helpers share the same ArrayBuffer + bool(s) pattern. */ - template - static uint32_t uWS_WebSocket_sendFirstFragment_fast(v8::Local receiver, const v8::FastApiTypedArray& message, bool isBinary, bool compress, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - uint8_t *data = nullptr; - if (!message.getStorageIfAligned(&data)) { options.fallback = true; return 0; } - return ws->sendFirstFragment(std::string_view((char *) data, message.length()), - isBinary ? uWS::OpCode::BINARY : uWS::OpCode::TEXT, compress); - } - - template - static uint32_t uWS_WebSocket_sendFragment_fast(v8::Local receiver, const v8::FastApiTypedArray& message, bool compress, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - uint8_t *data = nullptr; - if (!message.getStorageIfAligned(&data)) { options.fallback = true; return 0; } - return ws->sendFragment(std::string_view((char *) data, message.length()), compress); - } - - template - static uint32_t uWS_WebSocket_sendLastFragment_fast(v8::Local receiver, const v8::FastApiTypedArray& message, bool compress, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - uint8_t *data = nullptr; - if (!message.getStorageIfAligned(&data)) { options.fallback = true; return 0; } - return ws->sendLastFragment(std::string_view((char *) data, message.length()), compress); - } - template static uint32_t uWS_WebSocket_send_fast(v8::Local receiver, const v8::FastApiTypedArray& message, bool isBinary, bool compress, v8::FastApiCallbackOptions& options) { auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); @@ -378,15 +335,6 @@ struct WebSocketWrapper { isBinary ? uWS::OpCode::BINARY : uWS::OpCode::TEXT, compress); } - template - static uint32_t uWS_WebSocket_ping_fast(v8::Local receiver, const v8::FastApiTypedArray& message, v8::FastApiCallbackOptions& options) { - auto *ws = (uWS::WebSocket *) receiver->GetAlignedPointerFromInternalField(0); - if (!ws) { options.fallback = true; return 0; } - uint8_t *data = nullptr; - if (!message.getStorageIfAligned(&data)) { options.fallback = true; return 0; } - return ws->send(std::string_view((char *) data, message.length()), uWS::OpCode::PING); - } - template static Local init(Isolate *isolate) { Local wsTemplateLocal = FunctionTemplate::New(isolate); @@ -397,33 +345,25 @@ struct WebSocketWrapper { } wsTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - /* Register our functions. - * Static CFunction descriptors are per template specialisation (SSL=true vs SSL=false) - * and are initialised once because the addon is a single translation unit. */ - static v8::CFunction fast_sendFirstFragment = v8::CFunction::Make(uWS_WebSocket_sendFirstFragment_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFirstFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFirstFragment, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_sendFirstFragment)); - static v8::CFunction fast_sendFragment = v8::CFunction::Make(uWS_WebSocket_sendFragment_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFragment, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_sendFragment)); - static v8::CFunction fast_sendLastFragment = v8::CFunction::Make(uWS_WebSocket_sendLastFragment_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendLastFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendLastFragment, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_sendLastFragment)); + /* Register our functions */ + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFirstFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFirstFragment)); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFragment)); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendLastFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendLastFragment)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getUserData", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getUserData)); static v8::CFunction fast_send = v8::CFunction::Make(uWS_WebSocket_send_fast); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_send, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_send)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_end)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_close)); - static v8::CFunction fast_getBufferedAmount = v8::CFunction::Make(uWS_WebSocket_getBufferedAmount_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getBufferedAmount", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getBufferedAmount, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getBufferedAmount)); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getBufferedAmount", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getBufferedAmount)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddress)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "subscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_subscribe)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unsubscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_unsubscribe)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "publish", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_publish)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_cork)); - static v8::CFunction fast_ping = v8::CFunction::Make(uWS_WebSocket_ping_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_ping)); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddressAsText)); - static v8::CFunction fast_getRemotePort = v8::CFunction::Make(uWS_WebSocket_getRemotePort_fast); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemotePort, Local(), Local(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getRemotePort)); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemotePort)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "isSubscribed", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_isSubscribed)); /* This one does not exist in C++ */