signals and component calls

This commit is contained in:
IonutParau 2026-02-05 10:47:57 +01:00
parent 077b2eb4e2
commit e5095f4624
3 changed files with 166 additions and 6 deletions

View File

@ -6,12 +6,19 @@
#include <stdio.h>
static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
nn_Computer *c = req->computer;
switch(req->action) {
case NN_COMP_INIT:
return NN_OK;
case NN_COMP_DEINIT:
return NN_OK;
case NN_COMP_CALL:
if(nn_getstacksize(c) != 1) {
nn_setError(c, "bad argument count");
return NN_EBADCALL;
}
const char *s = nn_tostring(c, 0);
puts(s);
return NN_OK;
case NN_COMP_ENABLED:
req->methodEnabled = true; // all methods always enabled
@ -29,19 +36,21 @@ int main() {
nn_ComponentMethod sandboxMethods[] = {
{"log", "log(msg: string) - Log to stdout", true},
NULL,
{NULL},
};
nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler);
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
nn_addComponent(c, ctype, "sandbox", -1, NULL);
nn_pushstring(c, "Hello from component call");
nn_call(c, "sandbox", "log");
printf("Component added: %s\n", nn_hasComponent(c, "sandbox") ? "TRUE" : "FALSE");
printf("Method active: %s\n", nn_hasMethod(c, "sandbox", "log") ? "TRUE" : "FALSE");
printf("Incorrect method active: %s\n", nn_hasMethod(c, "sandbox", "notlog") ? "TRUE" : "FALSE");
cleanup:;
nn_destroyComputer(c);
nn_destroyComponentType(ctype);
// rip the universe

View File

@ -441,6 +441,11 @@ typedef struct nn_Table {
nn_Value vals[];
} nn_Table;
typedef struct nn_Signal {
size_t len;
nn_Value *values;
} nn_Signal;
typedef struct nn_Computer {
nn_ComputerState state;
nn_Universe *universe;
@ -461,9 +466,11 @@ typedef struct nn_Computer {
double creationTimestamp;
size_t stackSize;
size_t archCount;
size_t signalCount;
nn_Value callstack[NN_MAX_STACK];
char errorBuffer[NN_MAX_ERROR_SIZE];
nn_Architecture archs[NN_MAX_ARCHITECTURES];
nn_Signal signals[NN_MAX_SIGNALS];
} nn_Computer;
nn_Universe *nn_createUniverse(nn_Context *ctx) {
@ -494,6 +501,7 @@ nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name
const char *namecpy = nn_arstrdup(arena, name);
if(namecpy == NULL) goto fail;
ctype->name = namecpy;
size_t methodCount = 0;
while(methods[methodCount].name != NULL) methodCount++;
@ -573,6 +581,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
c->creationTimestamp = nn_currentTime(ctx);
c->stackSize = 0;
c->archCount = 0;
c->signalCount = 0;
// set to empty string
c->errorBuffer[0] = '\0';
return c;
@ -599,6 +608,11 @@ void nn_destroyComputer(nn_Computer *computer) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_dropComponent(computer, computer->components[i]);
}
for(size_t i = 0; i < computer->signalCount; i++) {
nn_Signal s = computer->signals[i];
for(size_t j = 0; j < s.len; j++) nn_dropValue(s.values[j]);
nn_free(ctx, s.values, sizeof(nn_Value) * s.len);
}
nn_free(ctx, computer->components, sizeof(nn_Component) * computer->componentCap);
nn_free(ctx, computer->deviceInfo, sizeof(nn_DeviceInfo) * computer->deviceInfoCap);
@ -812,7 +826,16 @@ nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentType *ctype, const ch
computer->components[computer->componentLen++] = c;
// TODO: send the signal
if(computer->state != NN_BOOTUP) {
err = nn_pushstring(computer, "component_added");
if(err) return err;
err = nn_pushstring(computer, address);
if(err) return err;
err = nn_pushstring(computer, ctype->name);
if(err) return err;
err = nn_pushSignal(computer, 3);
if(err) return err;
}
return NN_OK;
}
@ -890,7 +913,17 @@ nn_Exit nn_removeComponent(nn_Computer *computer, const char *address) {
if(c.address == NULL) return NN_EBADSTATE;
nn_dropComponent(computer, c);
// TODO: send the signal
if(computer->state != NN_BOOTUP) {
nn_Exit err = nn_pushstring(computer, "component_removed");
if(err) return err;
err = nn_pushstring(computer, address);
if(err) return err;
// not a UAF because c is on-stack
err = nn_pushstring(computer, c.ctype->name);
if(err) return err;
err = nn_pushSignal(computer, 3);
if(err) return err;
}
return NN_OK;
}
@ -968,7 +1001,48 @@ static void nn_dropValue(nn_Value val) {
}
}
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method) {
if(!nn_hasComponent(computer, address)) {
nn_setError(computer, "no such component");
return NN_EBADCALL;
}
if(!nn_hasMethod(computer, address, method)) {
nn_setError(computer, "no such method");
return NN_EBADCALL;
}
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component c = computer->components[i];
if(nn_strcmp(c.address, address) != 0) continue;
nn_ComponentRequest req;
req.typeUserdata = c.ctype->userdata;
req.compUserdata = c.userdata;
req.state = c.state;
req.computer = computer;
req.compAddress = address;
req.action = NN_COMP_CALL;
req.methodCalled = method;
// default is to return nothing
req.returnCount = 0;
nn_Exit err = c.ctype->handler(&req);
if(err) {
if(err != NN_EBADCALL) nn_setErrorFromExit(computer, err);
return err;
}
size_t endOfTrim = computer->stackSize - req.returnCount;
for(size_t i = 0; i < endOfTrim; i++) {
nn_dropValue(computer->callstack[i]);
}
for(size_t i = 0; i < req.returnCount; i++) {
computer->callstack[i] = computer->callstack[endOfTrim + i];
}
computer->stackSize = req.returnCount;
return NN_OK;
}
return NN_EBADSTATE;
}
bool nn_checkstack(nn_Computer *computer, size_t amount) {
return computer->stackSize + amount <= NN_MAX_STACK;
@ -1150,5 +1224,73 @@ nn_Exit nn_dumptable(nn_Computer *computer, size_t idx, size_t *len) {
return NN_OK;
}
int nn_countValueCost(nn_Computer *computer, size_t values) {
int total = 0;
for(size_t i = 0; i < values; i++) {
nn_Value val = computer->callstack[computer->stackSize - values + i];
total += 2;
switch(val.type) {
case NN_VAL_NULL:
case NN_VAL_BOOL:
total += 4;
continue;
case NN_VAL_NUM:
total += 8;
continue;
case NN_VAL_STR:
total += val.string->len;
if(val.string->len == 0) total++;
continue;
case NN_VAL_TABLE:
case NN_VAL_USERDATA:
return -1;
}
}
return total;
}
size_t nn_countSignals(nn_Computer *computer) {
return computer->signalCount;
}
nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount) {
if(computer->signalCount == NN_MAX_SIGNALS) return NN_ELIMIT;
int cost = nn_countValueCost(computer, valueCount);
if(cost == -1) return NN_EBADSTATE;
if(cost > NN_MAX_SIGNALSIZE) return NN_ELIMIT;
nn_Context ctx = computer->universe->ctx;
nn_Signal s;
s.len = valueCount;
s.values = nn_alloc(&ctx, sizeof(nn_Value) * valueCount);
if(s.values == NULL) return NN_ENOMEM;
for(size_t i = 0; i < valueCount; i++) {
s.values[i] = computer->callstack[computer->stackSize - valueCount + i];
}
computer->stackSize -= valueCount;
computer->signals[computer->signalCount++] = s;
return NN_OK;
}
nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) {
if(computer->signalCount == 0) return NN_EBADSTATE;
nn_Context ctx = computer->universe->ctx;
nn_Signal s = computer->signals[0];
if(!nn_checkstack(computer, s.len)) return NN_ENOSTACK;
if(valueCount != NULL) *valueCount = s.len;
for(size_t i = 0; i < s.len; i++) {
nn_pushvalue(computer, s.values[i]);
}
for(size_t i = 1; i < computer->signalCount; i++) {
computer->signals[i-1] = computer->signals[i];
}
computer->signalCount--;
nn_free(&ctx, s.values, sizeof(nn_Value) * s.len);
return NN_OK;
}
// todo: everything
nn_Exit nn_initComponentsLibrary(nn_Universe *universe);

View File

@ -414,6 +414,9 @@ nn_Exit nn_dupen(nn_Computer *computer, size_t n);
// For component calls, calling this at the start effectively gives you the argument count.
size_t nn_getstacksize(nn_Computer *computer);
// Removes all values from the stack.
// It is recommended to do this when initiating a component call or
// after returning from errors, as the call stack may have
// random junk on it.
void nn_clearstack(nn_Computer *computer);
// type check! The API may misbehave if types do not match, so always type-check!
@ -453,11 +456,17 @@ size_t nn_touserdata(nn_Computer *computer, size_t idx);
// It pushes them as K1,V1,K2,V2,K3,V3,etc., just like went in to pushtable. And yes, the keys are not de-duplicated.
nn_Exit nn_dumptable(nn_Computer *computer, size_t idx, size_t *len);
// computes the cost of the top [values] values using the same algorithm as
// the modem.
// It will return -1 if the values are invalid.
int nn_countValueCost(nn_Computer *computer, size_t values);
// Returns the amount of signals stored
size_t nn_countSignals(nn_Computer *computer);
// Pops [valueCount] values from the call stack and pushes them as a signal.
nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount);
// Removes the first signal and pushes the values onto the call stack, while setting valueCount to the amount of values in the signal.
// If there is no signal, it returns EBADSTATE
nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
// The high-level API of the built-in components.