It would be nice if versioning could be: * safe from developer errors (preferably 100% safe) * preferably more or less fully automatic * support backports - even from a 3rd party, such as a distributor. Versioning can be done at different "levels": * Module (=library) - Can lead to faster load times * Symbol - Makes backport handling easier. - Functions can be added in any order. Version info can be generated at different "places": * By the library developers - verified either by the compiler or by the package repo * By the compiler - and of course verified by the package repo * At the package repo - i.e. implicitly or explicitly on upload - the upload CLI/API/UI could require approval of API additions, and API bumps due to incompatible changes. Versions "types": * Dotted versions, with backport support - e.g. 1.6.0, but also things backports like 1.4.9.2, or other kinds of formats such as 2024.09.30-git-12345abcde - it is easy to get these wrong, for example: - bumping the wrong component (e.g. from 1.1 to 1.2 instead of 2.0 or vice versa) - bumping to a final version too early - bumping to a "far-future" version accidentally * Simple sequence numbers - This does not support backports, so it's rather limited * Hash-based versions - Based on API contents. - Can support any number of preceeding versions (= subsets) and these can be added after-the-fact. - Could have infintely many subsets, but to be pragmatic, only actually used (uploaded or referenced) subsets should be allocated. - Handles backports flawlessly! Maybe sort all functions by name... TODO Development experience ---------------------- * It should be possible to change an interface while developing Dependencies on APIs vs "inputs" and SBOMs --------------------------------------------- It would be really nice if "interface specifications" wouldn't be "inputs" when producing an artifact (e.g. an ELF or PE binary). This can be acheived as follows: 1. Require all imports (classes, functions, etc.) to be explicitly declared in the module using them (= depending on them). - this would then also have to apply to the stdlib - and it could be a lot of things, e.g. String, File, etc. 2. Don't actually read any external source files when compiling a module, because of (1) it isn't necessary. 3. Check hashes when loading the executable. The dynamic loader can be "tricked" into doing this by appending an API hash to the symbol name, e.g. `SomeClass_somefunction__h_$lt3L3$re$e5x7ffwUyS700s$X3bW8uox0fWCkR46kQ` How to encoding API hashes in symbol names? ------------------------------------------- Where to put the API hash? * Can append it to the symbol, e.g. `SomeClass_somefunction__h_` * Can use an ELF symbol verion, e.g. `SomeClass_somefunction@SLULHASH_` * Can use an ELF symbol verion, e.g. `SomeClass_somefunction@MODNAME__H_` What hash to use? * Full BLAKE-256? * BLAKE-256 truncated to 128 bits? How to encode the hash? * base64url with `-` replaced with `$`: `$lt3L3$re$e5x7ffwUyS700s$X3bW8uox0fWCkR46kQ` = 43 characters * base32: `NTXNRBVFK6CAAENRYSTN7JSC4X6KOG6JHWSP3XBOGXAOF7IWGNTA` = 52 characters * base62: also 43 characters like base64 * base58: 44 characters * base16 (hexadecimal): 64 characters Optional parameters ------------------- Functions with optional parameters could be exported with multiple symbols / symbol-versions / hashes: func f String name ?String defval = none ?String description = none code ... end This could then be exported as 3 different symbols: * f(name) * f(name,defval) * f(name,defval,description) Also, depending on the ABI, it could be required that unused parameters are passed as zero. In that case, only one symbol would be required. For register parameters, this requires explicitly setting them to zero, and for stack it would require *clearing* the stack when no longer in use (regarding both cases, see also runtime_hardening.txt). Both cases do affect ABI however: * Requiring functions to be called with all regs and all unused stack cleared makes them incompatible with the C ABI. * Requiring the stack to be cleared makes calling C functions impossible, unless SLUL runs with it's own stack (which might have some benefits, such as separating return pointers from the normal stack, which could be better for security). There's also the problem of the API hashes themselves. Adding a new parameter should require a new API hash. So it would still require a new symbol, even if it would point to the same function.