feat(udp): Udp.* - polled IPv4 datagrams on macOS and Windows

Udp.open/port/send/recv/from_ip/from_port/close/resolve/local_ip/ip/ip_text, native
BSD sockets (udp.ll) and Winsock (udp_win.ll, -lws2_32), linked only when a program
uses Udp.*; example, docs and tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-17 11:16:14 +03:00
parent 6a7f1dd393
commit ebb1a00352
24 changed files with 68630 additions and 66360 deletions

View file

@ -517,6 +517,21 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
if (meth == "free") { bare = "http_close"; push(labels, "handle") }
if (meth == "parse") { bare = "http_parse"; push(labels, "bytes"); push(labels, "len") }
}
# Udp.* — polled IPv4 datagrams (runtime/native/udp.ludic + udp.ll / udp_win.ll).
# Out-of-band like Http.*: the transport under a game's own netcode.
if (ns == "Udp") {
if (meth == "open") { bare = "udp_open"; push(labels, "port") }
if (meth == "port") { bare = "udp_port"; push(labels, "socket") }
if (meth == "send") { bare = "udp_send"; push(labels, "socket"); push(labels, "ip"); push(labels, "port"); push(labels, "bytes"); push(labels, "len") }
if (meth == "recv") { bare = "udp_recv"; push(labels, "socket"); push(labels, "bytes"); push(labels, "cap") }
if (meth == "from_ip") { bare = "udp_from_ip"; push(labels, "socket") }
if (meth == "from_port") { bare = "udp_from_port"; push(labels, "socket") }
if (meth == "close") { bare = "udp_close"; push(labels, "socket") }
if (meth == "resolve") { bare = "udp_resolve"; push(labels, "name") }
if (meth == "local_ip") { bare = "udp_local_ip" }
if (meth == "ip") { bare = "udp_ip"; push(labels, "text") }
if (meth == "ip_text") { bare = "udp_ip_text"; push(labels, "ip") }
}
# Phase 3: the bare reflection / networking / process builtins, namespaced.
# Each is a pure alias — the callee is rewritten to the bare name below.
if (ns == "World") {

View file

@ -252,6 +252,8 @@ function p_postfix() -> Node {
if e.a.kind == E_ID and e.a.s == "Audio" { g_uses_audio = true }
# Http.* (#6) — any Http method splices the HTTP client runtime.
if e.a.kind == E_ID and e.a.s == "Http" { g_uses_http = true }
# Udp.* — any Udp method splices the datagram runtime (and links udp.ll / udp_win.ll).
if e.a.kind == E_ID and e.a.s == "Udp" { g_uses_udp = true }
# Gl.* — any Gl method splices the OpenGL runtime (and links the GL backend).
if e.a.kind == E_ID and e.a.s == "Gl" { g_uses_gl = true }
# Vk.* — any Vk method splices the Vulkan runtime (and links its loader)
@ -677,6 +679,7 @@ var g_uses_tween_rt: bool = false # Tween.to/chain/delay/… (#48) -> splice tw
var g_uses_fx: bool = false # Fx.sparks/number/clear -> splice fx.ludic; fx_tick each Update, fx_draw each Render
var g_uses_audio: bool = false # Audio.* (#22) -> splice audio.ludic; a windowed build also links audio.ll + AVFoundation
var g_uses_http: bool = false # Http.* (#6) -> splice http.ludic; links http.ll + Foundation (macOS)
var g_uses_udp: bool = false # Udp.* -> splice udp.ludic; links udp.ll (macOS) / udp_win.ll + ws2_32
var g_uses_gl: bool = false # Gl.* -> splice gl.ludic (+ generated gl_api.ludic); links gl.ll + gl_thunks.ll + OpenGL
var g_uses_vk: bool = false # Vk.* -> splice vk.ludic (+ generated vk_api.ludic); links vk_thunks.ll + the platform loader
# issue #64: functions marked @System(Phase) in a prebuilt binary module — the
@ -1187,6 +1190,13 @@ function maybe_splice_runtime() -> void {
do_import("runtime/native/http.ludic")
cur_dir = saved
}
# Udp.*: splice the datagram library. The sockets are native (udp.ll / udp_win.ll, bound
# by `extern function` in udp.ludic); the address helpers are pure Ludic.
if g_uses_udp {
cur_dir = ""
do_import("runtime/native/udp.ludic")
cur_dir = saved
}
# Gl.*: splice the OpenGL surface (gl.ludic + the generated gl_api.ludic). The
# native calls are the linked GL entry points themselves; the window attach is
# is_windowed()-guarded, so a headless build renders into an offscreen context.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -273,6 +273,12 @@ entry {
if g_target_win { cmd = `{cmd} {join_path(home, "runtime/native/threads_win.ll")}` }
else { cmd = `{cmd} {join_path(home, "runtime/native/threads.ll")}` }
}
# Udp.* links the socket layer: BSD sockets from libSystem on macOS, Winsock (ws2_32) on
# Windows.
if g_uses_udp {
if g_target_win { cmd = `{cmd} {join_path(home, "runtime/native/udp_win.ll")} -lws2_32` }
else { cmd = `{cmd} {join_path(home, "runtime/native/udp.ll")}` }
}
if g_uses_http {
if g_target_win {
let httpw = join_path(home, "runtime/native/http_win.ll")