Pare back V8 fast call to WebSocketWrapper.send only

Co-authored-by: uNetworkingAB <110806833+uNetworkingAB@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-14 21:50:55 +00:00
parent 15b080cd98
commit 871ae4e3dd
2 changed files with 14 additions and 121 deletions

View File

@@ -19,7 +19,6 @@
#include "Utilities.h"
#include <v8.h>
#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 <int PROTOCOL>
static uint32_t res_getRemotePort_fast(v8::Local<v8::Object> receiver, v8::FastApiCallbackOptions& options) {
void *ptr = receiver->GetAlignedPointerFromInternalField(0);
if (!ptr) { options.fallback = true; return 0; }
return ((uWS::HttpResponse<PROTOCOL != 0> *) ptr)->getRemotePort();
}
template <int PROTOCOL>
static uint32_t res_getProxiedRemotePort_fast(v8::Local<v8::Object> receiver, v8::FastApiCallbackOptions& options) {
void *ptr = receiver->GetAlignedPointerFromInternalField(0);
if (!ptr) { options.fallback = true; return 0; }
return ((uWS::HttpResponse<PROTOCOL != 0> *) ptr)->getProxiedRemotePort();
}
template <int PROTOCOL>
static double res_getWriteOffset_fast(v8::Local<v8::Object> receiver, v8::FastApiCallbackOptions& options) {
void *ptr = receiver->GetAlignedPointerFromInternalField(0);
if (!ptr) { options.fallback = true; return 0.0; }
return (double) ((uWS::HttpResponse<PROTOCOL != 0> *) ptr)->getWriteOffset();
}
/* write() returns bool (true = success, false = backpressure). Handles TCP, TLS, and QUIC. */
template <int PROTOCOL>
static bool res_write_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& 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<PROTOCOL != 0> *) ptr)->write(std::string_view((char *) buf, data.length()));
}
}
/* 0 = TCP, 1 = TLS, 2 = QUIC, 3 = CACHE */
template <int SSL>
static Local<Object> 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<SSL>));
/* 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<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "endWithoutBody", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_endWithoutBody<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "tryEnd", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_tryEnd<SSL>));
static v8::CFunction fast_write = v8::CFunction::Make(res_write_fast<SSL>);
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_write));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeHeader<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_close<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onWritable", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onWritable<SSL>));
@@ -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<SSL>));
static v8::CFunction fast_getWriteOffset = v8::CFunction::Make(res_getWriteOffset_fast<SSL>);
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getWriteOffset));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "maxRemainingBodyLength", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_maxRemainingBodyLength<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddress<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "collect", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "upgrade", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_upgrade<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddressAsText<SSL>));
static v8::CFunction fast_getRemotePort = v8::CFunction::Make(res_getRemotePort_fast<SSL>);
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemotePort<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getRemotePort));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemotePort<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddress<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemoteAddressAsText<SSL>));
static v8::CFunction fast_getProxiedRemotePort = v8::CFunction::Make(res_getProxiedRemotePort_fast<SSL>);
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemotePort<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getProxiedRemotePort));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getProxiedRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getProxiedRemotePort<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "pause", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_pause<SSL>));
resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_resume<SSL>));
}

View File

@@ -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 <bool SSL>
static uint32_t uWS_WebSocket_getBufferedAmount_fast(v8::Local<v8::Object> receiver, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) receiver->GetAlignedPointerFromInternalField(0);
if (!ws) { options.fallback = true; return 0; }
return ws->getBufferedAmount();
}
template <bool SSL>
static uint32_t uWS_WebSocket_getRemotePort_fast(v8::Local<v8::Object> receiver, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) 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 <bool SSL>
static uint32_t uWS_WebSocket_sendFirstFragment_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& message, bool isBinary, bool compress, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) 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 <bool SSL>
static uint32_t uWS_WebSocket_sendFragment_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& message, bool compress, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) 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 <bool SSL>
static uint32_t uWS_WebSocket_sendLastFragment_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& message, bool compress, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) 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 <bool SSL>
static uint32_t uWS_WebSocket_send_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& message, bool isBinary, bool compress, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) receiver->GetAlignedPointerFromInternalField(0);
@@ -378,15 +335,6 @@ struct WebSocketWrapper {
isBinary ? uWS::OpCode::BINARY : uWS::OpCode::TEXT, compress);
}
template <bool SSL>
static uint32_t uWS_WebSocket_ping_fast(v8::Local<v8::Object> receiver, const v8::FastApiTypedArray<uint8_t>& message, v8::FastApiCallbackOptions& options) {
auto *ws = (uWS::WebSocket<SSL, true, PerSocketData> *) 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 <bool SSL>
static Local<Object> init(Isolate *isolate) {
Local<FunctionTemplate> 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<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFirstFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFirstFragment<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_sendFirstFragment));
static v8::CFunction fast_sendFragment = v8::CFunction::Make(uWS_WebSocket_sendFragment_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFragment<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_sendFragment));
static v8::CFunction fast_sendLastFragment = v8::CFunction::Make(uWS_WebSocket_sendLastFragment_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendLastFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendLastFragment<SSL>, Local<Value>(), Local<Signature>(), 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<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendFragment<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "sendLastFragment", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_sendLastFragment<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getUserData", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getUserData<SSL>));
static v8::CFunction fast_send = v8::CFunction::Make(uWS_WebSocket_send_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_send<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_send));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_end<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_close<SSL>));
static v8::CFunction fast_getBufferedAmount = v8::CFunction::Make(uWS_WebSocket_getBufferedAmount_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getBufferedAmount", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getBufferedAmount<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getBufferedAmount));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getBufferedAmount", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getBufferedAmount<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddress<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "subscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_subscribe<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unsubscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_unsubscribe<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "publish", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_publish<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_cork<SSL>));
static v8::CFunction fast_ping = v8::CFunction::Make(uWS_WebSocket_ping_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasSideEffect, &fast_ping));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddressAsText<SSL>));
static v8::CFunction fast_getRemotePort = v8::CFunction::Make(uWS_WebSocket_getRemotePort_fast<SSL>);
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemotePort<SSL>, Local<Value>(), Local<Signature>(), 0, ConstructorBehavior::kAllow, SideEffectType::kHasNoSideEffect, &fast_getRemotePort));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemotePort", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemotePort<SSL>));
wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "isSubscribed", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_isSubscribed<SSL>));
/* This one does not exist in C++ */