Template
feat(ext): expose Lua API for checks and languages
This commit is contained in:
+153
-6
@@ -54,6 +54,14 @@
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
/* The probe-spec struct (struct st_lua_probe_spec) and its static-inline
|
||||
* free live in api.h (todo 21). Including it here is deliberate and safe
|
||||
* under the frozen unit-test link lines: api.h contributes only a struct
|
||||
* definition + a static inline, so lua.c gains NO reference to any
|
||||
* api.c/abi.c symbol -- tests/unit/test_lua.c and test_discovery.c, which
|
||||
* link lua.c WITHOUT api.c/abi.c, keep linking. */
|
||||
#include "api.h"
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* libm-free math shims */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -296,7 +304,8 @@ pow(double x, double y)
|
||||
struct st_lua_reg {
|
||||
struct st_lua_reg *next;
|
||||
char *name; /* owned copy */
|
||||
void *probe_spec; /* checks only; always NULL for languages */
|
||||
void *probe_spec; /* checks: a struct st_lua_probe_spec * (owned);
|
||||
languages: always NULL (name only in v1) */
|
||||
};
|
||||
|
||||
struct st_lua_rt {
|
||||
@@ -346,6 +355,7 @@ regs_free(struct st_lua_reg *head)
|
||||
while (p != NULL) {
|
||||
struct st_lua_reg *next = p->next;
|
||||
free(p->name);
|
||||
st_lua_probe_spec_free((struct st_lua_probe_spec *)p->probe_spec);
|
||||
free(p);
|
||||
p = next;
|
||||
}
|
||||
@@ -515,15 +525,143 @@ l_math_open(lua_State *L)
|
||||
/* registration API (Lua side) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* The `st` table: st.register_check(name) / st.register_language(name).
|
||||
* Both write into the runtime's own registries through the same C API
|
||||
* todo 21 will enumerate when bridging into a struct st_ext_ctx. The
|
||||
* rt travels as a light-userdata upvalue -- there is no global state. */
|
||||
/* The `st` table: st.register_check(name[, spec]) and
|
||||
* st.register_language(name[, spec]). Both write into the runtime's own
|
||||
* registries through the same C API todo 21 enumerates when bridging into
|
||||
* a struct st_ext_ctx. The rt travels as a light-userdata upvalue -- there
|
||||
* is no global state. A present second argument (a probe-spec TABLE) is
|
||||
* parsed into a heap struct st_lua_probe_spec (api.h) and passed through
|
||||
* as the registry entry's opaque spec pointer. */
|
||||
|
||||
static void
|
||||
l_links_free(char **link)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (link == NULL) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; link[i] != NULL; i++) {
|
||||
free(link[i]);
|
||||
}
|
||||
free(link);
|
||||
}
|
||||
|
||||
/* Parses the optional probe-spec table (argument 2) into a heap
|
||||
* struct st_lua_probe_spec. Returns NULL when argument 2 is absent or
|
||||
* nil (bare registration); raises a Lua error on a malformed table.
|
||||
* All fields are strdup'd (so the borrowed lua_tostring pointers never
|
||||
* outlive their stack slot); the caller owns the result. */
|
||||
static struct st_lua_probe_spec *
|
||||
l_parse_probe_spec(lua_State *L)
|
||||
{
|
||||
struct st_lua_probe_spec *spec;
|
||||
char *kind_dup = NULL;
|
||||
char *source_dup = NULL;
|
||||
char **link = NULL;
|
||||
size_t nlink = 0;
|
||||
size_t i;
|
||||
const char *s;
|
||||
|
||||
if (lua_gettop(L) < 2 || lua_isnoneornil(L, 2)) {
|
||||
return NULL;
|
||||
}
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
|
||||
lua_getfield(L, 2, "kind");
|
||||
if (lua_type(L, -1) != LUA_TSTRING) {
|
||||
luaL_error(L, "probe spec 'kind' must be a string");
|
||||
return NULL;
|
||||
}
|
||||
s = lua_tostring(L, -1);
|
||||
if (strcmp(s, "compile") != 0 && strcmp(s, "link") != 0 &&
|
||||
strcmp(s, "run") != 0) {
|
||||
luaL_error(L, "probe spec 'kind' must be \"compile\", \"link\" or "
|
||||
"\"run\"");
|
||||
return NULL;
|
||||
}
|
||||
kind_dup = strdup(s);
|
||||
lua_pop(L, 1);
|
||||
if (kind_dup == NULL) {
|
||||
luaL_error(L, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_getfield(L, 2, "source");
|
||||
if (lua_type(L, -1) != LUA_TSTRING) {
|
||||
free(kind_dup);
|
||||
luaL_error(L, "probe spec 'source' must be a string");
|
||||
return NULL;
|
||||
}
|
||||
s = lua_tostring(L, -1);
|
||||
source_dup = strdup(s);
|
||||
lua_pop(L, 1);
|
||||
if (source_dup == NULL) {
|
||||
free(kind_dup);
|
||||
luaL_error(L, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_getfield(L, 2, "link");
|
||||
if (!lua_isnoneornil(L, -1)) {
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
nlink = lua_rawlen(L, -1);
|
||||
link = calloc(nlink + 1, sizeof *link);
|
||||
if (link == NULL) {
|
||||
free(kind_dup);
|
||||
free(source_dup);
|
||||
luaL_error(L, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < nlink; i++) {
|
||||
size_t slen;
|
||||
|
||||
lua_geti(L, -1, (lua_Integer)i + 1);
|
||||
s = lua_tostring(L, -1);
|
||||
slen = lua_rawlen(L, -1);
|
||||
if (lua_type(L, -1) != LUA_TSTRING || s == NULL ||
|
||||
strlen(s) != slen) {
|
||||
lua_pop(L, 1);
|
||||
l_links_free(link);
|
||||
free(kind_dup);
|
||||
free(source_dup);
|
||||
luaL_error(L, "probe spec 'link' entries must be strings "
|
||||
"without NUL bytes");
|
||||
return NULL;
|
||||
}
|
||||
link[i] = strdup(s);
|
||||
lua_pop(L, 1);
|
||||
if (link[i] == NULL) {
|
||||
l_links_free(link);
|
||||
free(kind_dup);
|
||||
free(source_dup);
|
||||
luaL_error(L, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
link[nlink] = NULL;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
spec = malloc(sizeof *spec);
|
||||
if (spec == NULL) {
|
||||
l_links_free(link);
|
||||
free(kind_dup);
|
||||
free(source_dup);
|
||||
luaL_error(L, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
spec->kind = kind_dup;
|
||||
spec->source = source_dup;
|
||||
spec->link = link;
|
||||
return spec;
|
||||
}
|
||||
|
||||
static int
|
||||
l_st_register_check(lua_State *L)
|
||||
{
|
||||
struct st_lua_rt *rt = lua_touserdata(L, lua_upvalueindex(1));
|
||||
struct st_lua_probe_spec *spec;
|
||||
size_t len;
|
||||
const char *name;
|
||||
|
||||
@@ -535,7 +673,9 @@ l_st_register_check(lua_State *L)
|
||||
if (name == NULL || strlen(name) != len) {
|
||||
return luaL_error(L, "check name must not contain a NUL byte");
|
||||
}
|
||||
if (st_lua_register_check(rt, name, NULL) != 0) {
|
||||
spec = l_parse_probe_spec(L);
|
||||
if (st_lua_register_check(rt, name, spec) != 0) {
|
||||
st_lua_probe_spec_free(spec); /* not stored: free it */
|
||||
return luaL_error(L, "check '%s' is already registered", name);
|
||||
}
|
||||
return 0;
|
||||
@@ -556,6 +696,13 @@ l_st_register_language(lua_State *L)
|
||||
if (name == NULL || strlen(name) != len) {
|
||||
return luaL_error(L, "language name must not contain a NUL byte");
|
||||
}
|
||||
/* Optional second arg: accept a probe-spec TABLE for forward
|
||||
* compatibility, but for v1 register the name only -- the table's
|
||||
* contents (detection info) are consumed by todo 22. A non-table
|
||||
* second arg is a clean error. */
|
||||
if (lua_gettop(L) >= 2 && !lua_isnoneornil(L, 2)) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
}
|
||||
if (st_lua_register_language(rt, name) != 0) {
|
||||
return luaL_error(L, "language '%s' is already registered", name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user