diff --git a/TODO.md b/TODO.md index 10c95f7..eb8b39b 100644 --- a/TODO.md +++ b/TODO.md @@ -2,21 +2,8 @@ - make `computer` component use callbacks - finish tmpfs (rework the whole thing) -- userdata support - get rid of component signals (useless) -## Userdata system - -The idea is that the computer stores userdata as structs which have a pointer (the state) and a bound-component address. -When a component is removed, all of its associated userdata is instantly dropped. -For serialization, the userdata pushes a string storing the encoded byte buffer. -For deserialization, the userdata pops a string and decodes it. - -To get component methods, requests are sent to get method info by index, and a NULL name means method is not allowed for that -particular userdata. Methods can obviously be invoked just like component data. -Serialization pushes the string on the stack, deserialization gets the buffer as ptr + len and sets the state pointer. -They are handled by components, thus if it is not mounted at deserialization time, the deserialization fails. - # To re-evaluate - Exposing the internal non-resizing hashmap implementation. diff --git a/src/luaarch.c b/src/luaarch.c index 33b9cac..f8d7498 100644 --- a/src/luaarch.c +++ b/src/luaarch.c @@ -600,6 +600,84 @@ static int luaArch_unicode_wtrunc(lua_State *L) { return 1; } +static int luaArch_userdata_free(lua_State *L) { + luaArch *arch = luaArch_from(L); + size_t user = (size_t)lua_touserdata(L, 1); + nn_freeUserdata(arch->computer, user); + return 0; +} + +static int luaArch_userdata_methods(lua_State *L) { + luaArch *arch = luaArch_from(L); + size_t user = (size_t)lua_touserdata(L, 1); + if(!nn_isUserdataValid(arch->computer, user)) { + lua_pushnil(L); + lua_pushstring(L, "no such userdata"); + return 2; + } + lua_createtable(L, 0, 0); + size_t idx = 0; + while(true) { + nn_Method m; + if(nn_getUserdataMethod(arch->computer, user, idx, &m)) break; + idx++; + if(m.name == NULL) continue; + lua_pushstring(L, m.name); + lua_createtable(L, 0, 4); + + // the method data + { + lua_pushstring(L, "doc"); + if(m.doc == NULL) lua_pushnil(L); else lua_pushstring(L, m.doc); + lua_settable(L, -3); + + lua_pushstring(L, "direct"); + lua_pushboolean(L, (m.flags & NN_DIRECT) != 0); + lua_settable(L, -3); + + lua_pushstring(L, "getter"); + lua_pushboolean(L, (m.flags & NN_GETTER) != 0); + lua_settable(L, -3); + + lua_pushstring(L, "setter"); + lua_pushboolean(L, (m.flags & NN_SETTER) != 0); + lua_settable(L, -3); + } + + lua_settable(L, -3); + } + return 1; +} + +static int luaArch_userdata_invoke(lua_State *L) { + luaArch *arch = luaArch_from(L); + size_t user = (size_t)lua_touserdata(L, 1); + if(!nn_isUserdataValid(arch->computer, user)) { + lua_pushnil(L); + lua_pushstring(L, "no such userdata"); + return 2; + } + const char *method = luaL_checkstring(L, 2); + size_t argc = lua_gettop(L); + + nn_clearstack(arch->computer); + for(size_t i = 3; i <= argc; i++) { + luaArch_luaToNN(arch, L, i); + } + nn_Exit err = nn_invokeUserdata(arch->computer, user, method); + if(err != NN_OK) { + lua_pushnil(L); + lua_pushstring(L, nn_getError(arch->computer)); + return 2; + } + size_t retc = nn_getstacksize(arch->computer); + for(size_t i = 0; i < retc; i++) { + luaArch_nnToLua(arch, L, i); + } + nn_clearstack(arch->computer); + return retc; +} + static void luaArch_loadEnv(lua_State *L) { lua_createtable(L, 0, 10); int computer = lua_gettop(L); @@ -644,6 +722,7 @@ static void luaArch_loadEnv(lua_State *L) { lua_pushcfunction(L, luaArch_computer_getDeviceInfo); lua_setfield(L, computer, "getDeviceInfo"); lua_setglobal(L, "computer"); + lua_createtable(L, 0, 10); int component = lua_gettop(L); lua_pushcfunction(L, luaArch_component_list); @@ -663,6 +742,7 @@ static void luaArch_loadEnv(lua_State *L) { lua_pushcfunction(L, luaArch_component_fields); lua_setfield(L, component, "fields"); lua_setglobal(L, "component"); + lua_createtable(L, 0, 10); int unicode = lua_gettop(L); lua_pushcfunction(L, luaArch_unicode_char); @@ -676,6 +756,16 @@ static void luaArch_loadEnv(lua_State *L) { lua_pushcfunction(L, luaArch_unicode_wtrunc); lua_setfield(L, unicode, "wtrunc"); lua_setglobal(L, "unicode"); + + lua_createtable(L, 0, 10); + int userdata = lua_gettop(L); + lua_pushcfunction(L, luaArch_userdata_free); + lua_setfield(L, userdata, "free"); + lua_pushcfunction(L, luaArch_userdata_methods); + lua_setfield(L, userdata, "methods"); + lua_pushcfunction(L, luaArch_userdata_invoke); + lua_setfield(L, userdata, "invoke"); + lua_setglobal(L, "userdata"); } static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) { diff --git a/src/machine.lua b/src/machine.lua index 381f4ab..20292e4 100644 --- a/src/machine.lua +++ b/src/machine.lua @@ -70,6 +70,44 @@ end local syncedMethodStats +local function sandboxValue(val) + if type(val) == "table" then + local nt = {} + for k, v in pairs(val) do + local sk, sv = sandboxValue(k), sandboxValue(v) + if sk ~= nil then nt[sk] = sv end + end + return nt + end + if type(val) == "userdata" then + -- Light userdata shall never escape our shi! + -- This matches how OC wraps it + -- TODO: just make our own shi with fields and stuff + -- like a component proxy, because this sucks + local userMeta = { + __gc = function() + userdata.free(val) + end, + } + + local wrapped = {type = "userdata"} + for name, m in pairs(userdata.methods(val)) do + wrapped[name] = { + name = name, + proxy = wrapped, + } + setmetatable(wrapped[name], { + __tostring = function() return m.doc or "function" end, + __call = function(_, ...) + return userdata.invoke(val, name, ...) + end, + }) + end + return setmetatable(wrapped, userMeta) + end + return val +end + local function realInvoke(address, method, ...) local t = {pcall(cinvoke, address, method, ...)} if not _SYNCED and not os.getenv("NN_FAST") then @@ -83,6 +121,8 @@ local function realInvoke(address, method, ...) print("got", table.unpack(t)) end + for i=1,#t do t[i] = sandboxValue(t[i]) end + if t[1] then return table.unpack(t, 2) end @@ -477,6 +517,9 @@ sandbox = { }, utf8 = utf8, + + -- unsafe, don't care + userdata = userdata, } sandbox._G = sandbox diff --git a/src/main.c b/src/main.c index 2cf7210..38df772 100644 --- a/src/main.c +++ b/src/main.c @@ -42,7 +42,7 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) { return NN_OK; case NN_COMP_DROP: return NN_OK; - case NN_COMP_SIGNAL: + case NN_COMP_USERDATA: return NN_OK; } return NN_OK; diff --git a/src/neonucleus.c b/src/neonucleus.c index 30c4fd7..6224c62 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -1064,6 +1064,11 @@ nn_Exit nn_resizeDeviceInfoArray(nn_Context *ctx, nn_DeviceInfoArray *arr, size_ return NN_OK; } +typedef struct nn_Userdata { + void *state; + char *compAddress; +} nn_Userdata; + typedef struct nn_Computer { nn_ComputerState state; nn_Universe *universe; @@ -1092,6 +1097,7 @@ typedef struct nn_Computer { nn_Architecture archs[NN_MAX_ARCHITECTURES]; nn_Signal signals[NN_MAX_SIGNALS]; char *users[NN_MAX_USERS]; + nn_Userdata uservals[NN_MAX_USERDATA]; } nn_Computer; nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) { @@ -1220,6 +1226,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char c->idleTimestamp = 0; // set to empty string c->errorBuffer[0] = '\0'; + for(size_t i = 0; i < NN_MAX_USERDATA; i++) c->uservals[i].state = NULL; return c; } @@ -1430,8 +1437,9 @@ void nn_destroyComputer(nn_Computer *computer) { nn_strfree(ctx, computer->users[i]); } + for(size_t i = 0; i < NN_MAX_USERDATA; i++) nn_freeUserdata(computer, i); + for(nn_ComponentEntry *c = nn_hashIterate(&computer->components, NULL); c != NULL; c = nn_hashIterate(&computer->components, c)) { - nn_signalComponent(c->comp, computer, NN_CSIGUNMOUNTED); nn_dropComponent(c->comp); } @@ -2061,7 +2069,6 @@ nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool sil }; if(!nn_hashPut(&c->components, &ent)) return NN_ELIMIT; nn_retainComponent(comp); - nn_signalComponent(comp, c, NN_CSIGMOUNTED); if(c->state == NN_RUNNING && !silent) { return nn_pushComponentAdded(c, comp->address, comp->type); } @@ -2078,7 +2085,6 @@ nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent) { if(c->state == NN_RUNNING && !silent) { e = nn_pushComponentRemoved(c, address, comp->type); } - nn_signalComponent(comp, c, NN_CSIGUNMOUNTED); nn_dropComponent(comp); return e; } @@ -2184,16 +2190,250 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const return NN_OK; } -nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal) { - nn_ComponentRequest req; - req.ctx = &component->universe->ctx; - req.computer = computer; - req.state = component->state; - req.classState = component->classState; - req.compAddress = component->address; - req.action = NN_COMP_SIGNAL; - req.signal = signal; - return component->handler(&req); +int nn_allocUserdata(nn_Computer *computer, void *state, const char *compAddress) { + for(size_t i = 0; i < NN_MAX_USERDATA; i++) { + if(nn_isUserdataValid(computer, i)) continue; + char *comp = nn_strdup(nn_getComputerContext(computer), compAddress); + if(comp == NULL) return -1; + computer->uservals[i].state = state; + computer->uservals[i].compAddress = comp; + return i; + } + return -1; +} + +// Frees a userdata index. +void nn_freeUserdata(nn_Computer *computer, size_t userdata) { + if(!nn_isUserdataValid(computer, userdata)) return; + nn_Context *ctx = nn_getComputerContext(computer); + nn_Userdata *user = computer->uservals + userdata; + + nn_UserdataRequest ureq = { + .state = user->state, + .action = NN_USER_DROP, + }; + + nn_Component *c = nn_getComponent(computer, user->compAddress); + // really, we should *panic*, as this is a BAD state + if(c == NULL) return; + + nn_ComponentRequest creq = { + .ctx = ctx, + .computer = computer, + .state = c->state, + .classState = c->classState, + .compAddress = c->address, + .action = NN_COMP_USERDATA, + .methodIdx = 0, + .user = &ureq, + }; + + // errors in here are catastrophic + c->handler(&creq); + + user->state = NULL; + nn_strfree(ctx, user->compAddress); +} + +// Returns whether the userdata index is valid +bool nn_isUserdataValid(nn_Computer *computer, size_t userdata) { + if(userdata >= NN_MAX_USERDATA) return false; + return computer->uservals[userdata].state != NULL; +} + +// If compAddress is correct and userdata is valid, returns the state pointer. +// If not, returns NULL, to prevent UB. +void *nn_unwrapUserdata(nn_Computer *computer, size_t userdata, const char *compAddress) { + if(!nn_isUserdataValid(computer, userdata)) return NULL; + nn_Userdata user = computer->uservals[userdata]; + if(nn_strcmp(user.compAddress, compAddress) != 0) return NULL; + return user.state; +} + +// gets the component address which manages this userdata +const char *nn_getUserdataComponent(nn_Computer *computer, size_t userdata) { + if(!nn_isUserdataValid(computer, userdata)) return NULL; + return computer->uservals[userdata].compAddress; +} + +// Gets information about a method of this userdata, by index. +// If idx is out of bounds, this returns true, which means to stop iteration. +// If method->name is NULL, the method should be skipped. +bool nn_getUserdataMethod(nn_Computer *computer, size_t userdata, size_t idx, nn_Method *method) { + if(!nn_isUserdataValid(computer, userdata)) return true; + nn_Context *ctx = nn_getComputerContext(computer); + nn_Userdata *user = computer->uservals + userdata; + + nn_UserdataRequest ureq = { + .state = user->state, + .action = NN_USER_GETMETHOD, + .getmethod.method = method, + .getmethod.idx = idx, + }; + + nn_Component *c = nn_getComponent(computer, user->compAddress); + // really, we should *panic*, as this is a BAD state + if(c == NULL) return true; + + nn_ComponentRequest creq = { + .ctx = ctx, + .computer = computer, + .state = c->state, + .classState = c->classState, + .compAddress = c->address, + .action = NN_COMP_USERDATA, + .methodIdx = 0, + .user = &ureq, + }; + + // errors in here are catastrophic + if(c->handler(&creq)) return true; + return ureq.getmethod.method == NULL; +} + +// Invokes a method on some userdata, same semantics as nn_invokeComponent +nn_Exit nn_invokeUserdata(nn_Computer *computer, size_t userdata, const char *method) { + if(!nn_isUserdataValid(computer, userdata)) return true; + nn_Context *ctx = nn_getComputerContext(computer); + nn_Userdata *user = computer->uservals + userdata; + + nn_UserdataRequest ureq = { + .state = user->state, + .action = NN_USER_INVOKE, + .invoke.method = method, + .invoke.returnCount = 0, + }; + + nn_Component *c = nn_getComponent(computer, user->compAddress); + // really, we should *panic*, as this is a BAD state + if(c == NULL) return true; + + nn_ComponentRequest creq = { + .ctx = ctx, + .computer = computer, + .state = c->state, + .classState = c->classState, + .compAddress = c->address, + .action = NN_COMP_USERDATA, + .methodIdx = 0, + .user = &ureq, + }; + + // prepare stack for call + while(nn_getstacksize(computer) > 0) { + if(!nn_isnull(computer, nn_getstacksize(computer) - 1)) break; + nn_pop(computer); + } + + // errors in here are catastrophic + nn_Exit e = c->handler(&creq); + if(e) { + if(e != NN_EBADCALL) nn_setErrorFromExit(computer, e); + nn_clearstack(computer); + return e; + } + + size_t endOfTrim = computer->stackSize - ureq.invoke.returnCount; + for(size_t i = 0; i < endOfTrim; i++) { + nn_dropValue(computer->callstack[i]); + } + for(size_t i = endOfTrim; i < computer->stackSize; i++) { + computer->callstack[i - endOfTrim] = computer->callstack[i]; + } + computer->stackSize = ureq.invoke.returnCount; + + if(nn_getEnergy(computer) <= 0) { + nn_setError(computer, "out of energy"); + return NN_EBADCALL; + } + + return NN_OK; +} + +// Serializes the userdata into a buffer and pushes it as a string. +// Make sure to keep track of its index and component address! +nn_Exit nn_serializeUserdata(nn_Computer *computer, size_t userdata) { + if(!nn_isUserdataValid(computer, userdata)) return true; + nn_Context *ctx = nn_getComputerContext(computer); + nn_Userdata *user = computer->uservals + userdata; + + nn_UserdataRequest ureq = { + .state = user->state, + .action = NN_USER_SERIALIZE, + }; + + nn_Component *c = nn_getComponent(computer, user->compAddress); + // really, we should *panic*, as this is a BAD state + if(c == NULL) return true; + + nn_ComponentRequest creq = { + .ctx = ctx, + .computer = computer, + .state = c->state, + .classState = c->classState, + .compAddress = c->address, + .action = NN_COMP_USERDATA, + .methodIdx = 0, + .user = &ureq, + }; + + // errors in here are catastrophic + return c->handler(&creq); +} + +// Deserializes userdata at a particular index. +// NOTE: if the component does not exist, or the userdata index is already taken, this errors. +nn_Exit nn_deserializeUserdata(nn_Computer *computer, size_t userdata, const char *compAddress, const char *buf, size_t len) { + if(userdata >= NN_MAX_USERDATA) { + nn_setError(computer, "invalid slot"); + return NN_EBADCALL; + } + + if(nn_isUserdataValid(computer, userdata)) { + nn_setError(computer, "slot taken"); + return NN_EBADCALL; + } + + nn_Component *c = nn_getComponent(computer, compAddress); + if(c == NULL) { + nn_setError(computer, "no such component"); + return NN_EBADCALL; + } + + nn_Context *ctx = nn_getComputerContext(computer); + nn_Userdata *user = computer->uservals + userdata; + + nn_UserdataRequest ureq = { + .state = user->state, + .action = NN_USER_DESERIALIZE, + .deserialize.data = buf, + .deserialize.len = len, + }; + + nn_ComponentRequest creq = { + .ctx = ctx, + .computer = computer, + .state = c->state, + .classState = c->classState, + .compAddress = c->address, + .action = NN_COMP_USERDATA, + .methodIdx = 0, + .user = &ureq, + }; + + char *compAddr = nn_strdup(ctx, compAddress); + if(compAddr == NULL) return NN_ENOMEM; + + // errors in here are catastrophic + nn_Exit e = c->handler(&creq); + if(e) { + nn_strfree(ctx, compAddr); + return e; + } + + user->state = ureq.state; + user->compAddress = compAddr; + return NN_OK; } static void nn_retainValue(nn_Value val) { @@ -3906,7 +4146,7 @@ typedef enum nn_CompNum { static nn_Exit nn_computerHandler(nn_ComponentRequest *req) { if(req->action == NN_COMP_DROP) return NN_OK; - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; nn_Computer *src = req->computer; if(src) nn_setError(src, "computer: not implemented yet"); return NN_EBADCALL; @@ -3964,7 +4204,7 @@ typedef struct nn_EEState { } nn_EEState; static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) { - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; nn_EEState *state = req->classState; nn_EEPROMRequest ereq; @@ -4230,7 +4470,7 @@ static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char } static nn_Exit nn_fsHandler(nn_ComponentRequest *req) { - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; nn_Context *ctx = req->ctx; nn_FSState *state = req->classState; @@ -4650,7 +4890,7 @@ static nn_Exit nn_drvHandler(nn_ComponentRequest *request) { dreq.drv = &state->drive; nn_Exit e; - if(request->action == NN_COMP_SIGNAL) return NN_OK; + if(request->action == NN_COMP_USERDATA) return NN_OK; if(request->action == NN_COMP_CHECKMETHOD) return NN_OK; if(request->action == NN_COMP_DROP) { @@ -4850,7 +5090,7 @@ static nn_Exit nn_flashHandler(nn_ComponentRequest *request) { freq.flash = &state->flash; nn_Exit e; - if(request->action == NN_COMP_SIGNAL) return NN_OK; + if(request->action == NN_COMP_USERDATA) return NN_OK; if(request->action == NN_COMP_CHECKMETHOD) return NN_OK; if(request->action == NN_COMP_DROP) { @@ -5069,7 +5309,7 @@ typedef struct nn_ScreenClassState { } nn_ScreenClassState; static nn_Exit nn_screenHandler(nn_ComponentRequest *req) { - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; nn_Context *ctx = req->ctx; nn_ScreenClassState *cls = req->classState; nn_Computer *C = req->computer; @@ -5335,7 +5575,7 @@ typedef struct nn_GPUClassState { static nn_Exit nn_gpuHandler(nn_ComponentRequest *req) { if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; nn_Context *ctx = req->ctx; nn_GPUClassState *cls = req->classState; nn_Computer *C = req->computer; @@ -6125,15 +6365,22 @@ nn_DataCard nn_defaultDataCards[3] = { .assymetricCost = 10.0, }, }; + +typedef struct nn_DataKey { + bool isPublic; + unsigned short bitlen; + size_t bytelen; + char bytes[]; +} nn_DataKey; + static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { - if(req->action == NN_COMP_SIGNAL) return NN_OK; nn_Context *ctx = req->ctx; nn_DataState *state = req->classState; nn_Computer *C = req->computer; nn_DataCard dataCard = state->dataCard; nn_DataNum method = req->methodIdx; - + if(req->action == NN_COMP_CHECKMETHOD) { if(method == NN_DATANUM_SHA256 || method == NN_DATANUM_MD5) { req->methodEnabled = dataCard.canHash; @@ -6149,6 +6396,84 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { } return NN_OK; } + + if(req->action == NN_COMP_USERDATA) { + nn_UserdataRequest *user = req->user; + nn_DataKey *key = user->state; + + if(user->action == NN_USER_DROP) { + nn_free(ctx, key, sizeof(*key) + key->bytelen); + return NN_OK; + } + + if(user->action == NN_USER_SERIALIZE) { + // its assumed bytelen is not ridiculous + size_t serlen = 2 + key->bytelen; + NN_VLA(unsigned char, ser, serlen); + unsigned short bitsAndPub = (key->bitlen * 2) + (key->isPublic ? 1 : 0); + ser[0] = (bitsAndPub >> 0) & 0xFF; + ser[1] = (bitsAndPub >> 8) & 0xFF; + nn_memcpy(ser + 2, key->bytes, key->bytelen); + return nn_pushlstring(C, (const char *)ser, serlen); + } + + if(user->action == NN_USER_DESERIALIZE) { + size_t serlen = user->deserialize.len; + const unsigned char *ser = (const unsigned char *)user->deserialize.data; + size_t bytelen = serlen - 2; + unsigned short bitsAndPub = ((unsigned short)ser[0]) + (((unsigned short)ser[1]) << 8); + key = nn_alloc(ctx, sizeof(*key) + bytelen); + if(key == NULL) return NN_ENOMEM; + key->isPublic = (bitsAndPub&1) != 0; + key->bitlen = bitsAndPub/2; + key->bytelen = bytelen; + nn_memcpy(key->bytes, ser + 2, key->bytelen); + user->state = key; + return NN_OK; + } + + if(user->action == NN_USER_GETMETHOD) { + size_t idx = user->getmethod.idx; + nn_Method *m = user->getmethod.method; + if(idx == 0) { + m->name = "serialize"; + m->doc = "function(): string - Serializes the key to its array of bytes"; + m->flags = NN_DIRECT; + } else if(idx == 1) { + m->name = "keyType"; + m->doc = "function(): string - Gets the type of the key (ec-public or ec-private)"; + m->flags = NN_DIRECT; + } else if(idx == 2) { + m->name = "isPublic"; + m->doc = "function(): boolean - Returns whether this key is public or not"; + m->flags = NN_DIRECT; + } else { + user->getmethod.method = NULL; + } + return NN_OK; + } + + if(user->action == NN_USER_INVOKE) { + const char *method = user->invoke.method; + if(nn_strcmp(method, "isPublic") == 0) { + user->invoke.returnCount = 1; + return nn_pushbool(C, key->isPublic); + } + if(nn_strcmp(method, "keyType") == 0) { + user->invoke.returnCount = 1; + return nn_pushstring(C, key->isPublic ? "ec-public" : "ec-private"); + } + if(nn_strcmp(method, "serialize") == 0) { + user->invoke.returnCount = 1; + return nn_pushlstring(C, key->bytes, key->bytelen); + } + + nn_setError(C, "no such method"); + return NN_EBADCALL; + } + + return NN_OK; + } nn_DataCardRequest dreq; dreq.ctx = ctx; @@ -6332,6 +6657,26 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { return NN_OK; } + if(method == NN_DATANUM_GENKEYPAIR) { + e = nn_defaultinteger(C, 0, 256); + if(e) return e; + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + size_t bitLen = nn_tointeger(C, 0); + if(bitLen != 256 && bitLen != 384) { + nn_setError(C, "invalid bit length (only 256-bit and 384-bit are supported)"); + return NN_EBADCALL; + } + // TODO: this is test code, no OOM check. Implement the correct version and make sure it handles OOMs correctly + size_t keylen = bitLen / 8; + nn_DataKey *key = nn_alloc(ctx, sizeof(*key) + keylen); + key->isPublic = true; + key->bitlen = bitLen; + key->bytelen = keylen; + nn_memset(key->bytes, 'A', key->bytelen); + req->returnCount = 1; + return nn_pushuserdata(C, nn_allocUserdata(C, key, req->compAddress)); + } + if(C) nn_setError(C, "data: not implemented yet"); return NN_EBADCALL; } @@ -6415,7 +6760,7 @@ typedef struct nn_ModemState { } nn_ModemState; static nn_Exit nn_modemHandler(nn_ComponentRequest *req) { - if(req->action == NN_COMP_SIGNAL) return NN_OK; + if(req->action == NN_COMP_USERDATA) return NN_OK; nn_Context *ctx = req->ctx; nn_ModemState *state = req->classState; nn_Computer *C = req->computer; diff --git a/src/neonucleus.h b/src/neonucleus.h index b1cf22a..a28294f 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -659,13 +659,6 @@ typedef struct nn_Method { nn_MethodFlags flags; } nn_Method; -// component signals - -// mounted -#define NN_CSIGMOUNTED "mounted" -// unmounted -#define NN_CSIGUNMOUNTED "unmounted" - typedef enum nn_ComponentAction { // component dropped NN_COMP_DROP, @@ -674,10 +667,38 @@ typedef enum nn_ComponentAction { // checking if component method is enabled // (may be locked by tier) NN_COMP_CHECKMETHOD, - // signal sent to the machine - NN_COMP_SIGNAL, + // userdata request + NN_COMP_USERDATA, } nn_ComponentAction; +typedef enum nn_UserdataAction { + NN_USER_DROP, + NN_USER_SERIALIZE, + NN_USER_DESERIALIZE, + NN_USER_GETMETHOD, + NN_USER_INVOKE, +} nn_UserdataAction; + +typedef struct nn_UserdataRequest { + void *state; + nn_UserdataAction action; + union { + struct { + const char *data; + size_t len; + } deserialize; + struct { + size_t idx; + // set to NULL if idx is out of bounds + nn_Method *method; + } getmethod; + struct { + const char *method; + size_t returnCount; + } invoke; + }; +} nn_UserdataRequest; + typedef struct nn_ComponentRequest { nn_Context *ctx; nn_Computer *computer; @@ -692,8 +713,7 @@ typedef struct nn_ComponentRequest { size_t returnCount; // method enabled bool methodEnabled; - // signal invocation - const char *signal; + nn_UserdataRequest *user; }; } nn_ComponentRequest;