diff --git a/rewrite/main.c b/rewrite/main.c index fda553f..2bf8f2b 100644 --- a/rewrite/main.c +++ b/rewrite/main.c @@ -6,12 +6,19 @@ #include 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 diff --git a/rewrite/neonucleus.c b/rewrite/neonucleus.c index b27e538..59b1582 100644 --- a/rewrite/neonucleus.c +++ b/rewrite/neonucleus.c @@ -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); diff --git a/rewrite/neonucleus.h b/rewrite/neonucleus.h index 9916713..1644755 100644 --- a/rewrite/neonucleus.h +++ b/rewrite/neonucleus.h @@ -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.