Files
LNXRNT/Sources/socket_v8_bindings.cpp

312 lines
13 KiB
C++
Raw Normal View History

2026-02-20 23:40:15 -08:00
#include "socket_v8_bindings.h"
#include "socket_bridge.h"
#include <v8.h>
#include <kinc/log.h>
using namespace v8;
// V8 wrapper functions for socket bridge
void v8_runt_socket_create(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
int socket_id = runt_socket_create();
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_create returned: %d", socket_id);
#endif
args.GetReturnValue().Set(Integer::New(isolate, socket_id));
}
void v8_runt_socket_bind(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 3 || !args[0]->IsInt32() || !args[1]->IsString() || !args[2]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
String::Utf8Value address(isolate, args[1]);
int port = args[2]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool result = runt_socket_bind(socket_id, *address, port);
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_bind(%d, %s, %d) returned: %s", socket_id, *address, port, result ? "true" : "false");
#endif
args.GetReturnValue().Set(Boolean::New(isolate, result));
}
void v8_runt_socket_listen(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
int backlog = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool result = runt_socket_listen(socket_id, backlog);
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_listen(%d, %d) returned: %s", socket_id, backlog, result ? "true" : "false");
#endif
args.GetReturnValue().Set(Boolean::New(isolate, result));
}
void v8_runt_socket_accept(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
int client_id = runt_socket_accept(socket_id);
#ifdef DEBUG_NETWORK
if (client_id >= 0) {
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_accept(%d) returned: %d", socket_id, client_id);
}
#endif
args.GetReturnValue().Set(Integer::New(isolate, client_id));
}
void v8_runt_socket_connect(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 3 || !args[0]->IsInt32() || !args[1]->IsString() || !args[2]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
String::Utf8Value hostname(isolate, args[1]);
int port = args[2]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool result = runt_socket_connect(socket_id, *hostname, port);
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_connect(%d, %s, %d) returned: %s", socket_id, *hostname, port, result ? "true" : "false");
#endif
args.GetReturnValue().Set(Boolean::New(isolate, result));
}
void v8_runt_socket_send(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 2 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
int sent = 0;
// look back over how we need to handle buffers and strings as string::Utf8Value must remain
if (args[1]->IsString()) {
String::Utf8Value str_data(isolate, args[1]);
if (*str_data == nullptr) {
kinc_log(KINC_LOG_LEVEL_ERROR, "[V8] runt_socket_send: string conversion failed");
args.GetReturnValue().Set(Integer::New(isolate, -1));
return;
}
int length = str_data.length();
// send while str_data fixes dangling pointer bug
sent = runt_socket_send(socket_id, *str_data, length);
} else if (args[1]->IsArrayBuffer()) {
Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[1]);
std::shared_ptr<BackingStore> backing = buffer->GetBackingStore();
const char* data = static_cast<const char*>(backing->Data());
int length = static_cast<int>(backing->ByteLength());
sent = runt_socket_send(socket_id, data, length);
} else if (args[1]->IsArrayBufferView()) {
Local<ArrayBufferView> view = Local<ArrayBufferView>::Cast(args[1]);
Local<ArrayBuffer> buffer = view->Buffer();
std::shared_ptr<BackingStore> backing = buffer->GetBackingStore();
size_t offset = view->ByteOffset();
const char* data = static_cast<const char*>(backing->Data()) + offset;
int length = static_cast<int>(view->ByteLength());
sent = runt_socket_send(socket_id, data, length);
} else {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Data must be string, ArrayBuffer, or TypedArray").ToLocalChecked()));
return;
}
args.GetReturnValue().Set(Integer::New(isolate, sent));
}
void v8_runt_socket_recv(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
int max_length = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
char* data = nullptr;
int received = runt_socket_recv(socket_id, max_length, &data);
if (received > 0 && data) {
// ArrayBuffer with received data
std::unique_ptr<BackingStore> backing = ArrayBuffer::NewBackingStore(
data, received, [](void* data, size_t length, void* deleter_data) {
delete[] static_cast<char*>(data);
}, nullptr);
Local<ArrayBuffer> buffer = ArrayBuffer::New(isolate, std::move(backing));
args.GetReturnValue().Set(buffer);
} else if (received == -2) {
// closed by peer -> return -2 so Haxe can detect
args.GetReturnValue().Set(Integer::New(isolate, -2));
} else {
// return null for would-block (0) or error (-1)
args.GetReturnValue().Set(Null(isolate));
}
}
void v8_runt_socket_close(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
runt_socket_close(socket_id);
args.GetReturnValue().Set(Undefined(isolate));
}
void v8_runt_socket_set_blocking(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsBoolean()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool blocking = args[1]->BooleanValue(isolate);
runt_socket_set_blocking(socket_id, blocking);
args.GetReturnValue().Set(Undefined(isolate));
}
void v8_runt_socket_select(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
double timeout = 0.0;
if (args.Length() >= 2 && args[1]->IsNumber()) {
timeout = args[1]->NumberValue(isolate->GetCurrentContext()).FromJust();
}
bool hasData = runt_socket_select(socket_id, timeout);
args.GetReturnValue().Set(Boolean::New(isolate, hasData));
}
void v8_runt_socket_is_connected(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool connected = runt_socket_is_connected(socket_id);
args.GetReturnValue().Set(Boolean::New(isolate, connected));
}
#ifdef WITH_SSL
void v8_runt_socket_enable_ssl(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
if (args.Length() < 1 || !args[0]->IsInt32()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked()));
return;
}
int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust();
bool result = runt_socket_enable_ssl(socket_id);
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_enable_ssl(%d) returned: %s", socket_id, result ? "true" : "false");
#endif
args.GetReturnValue().Set(Boolean::New(isolate, result));
}
#endif
void bind_socket_bridge(Isolate* isolate, const v8::Global<v8::Context>& context) {
// V8 locking
Locker locker{isolate};
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> current_context = Local<Context>::New(isolate, context);
Context::Scope context_scope(current_context);
Local<Object> global = current_context->Global();
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "[V8] Binding socket bridge functions to global context");
#endif
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_create").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_create).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_bind").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_bind).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_listen").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_listen).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_accept").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_accept).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_connect").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_connect).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_send").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_send).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_recv").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_recv).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_close").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_close).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_set_blocking").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_set_blocking).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_select").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_select).ToLocalChecked());
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_is_connected").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_is_connected).ToLocalChecked());
#ifdef WITH_SSL
global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_enable_ssl").ToLocalChecked(),
Function::New(current_context, v8_runt_socket_enable_ssl).ToLocalChecked());
#endif
}