move random number gen

This commit is contained in:
2025-11-29 14:22:19 -06:00
parent a9cff079d9
commit efe93b7206
5 changed files with 363 additions and 425 deletions

View File

@@ -5,70 +5,6 @@
#include "monocypher.h" #include "monocypher.h"
#include <stdint.h>
#include <stddef.h>
#if defined(_WIN32)
// ------- Windows: use BCryptGenRandom -------
#include <windows.h>
#include <bcrypt.h>
int randombytes(void *buf, size_t n) {
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return (status == 0) ? 0 : -1;
}
#elif defined(__linux__)
// ------- Linux: try getrandom, fall back to /dev/urandom -------
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <errno.h>
// If we have a new enough libc and kernel, getrandom is available.
// Otherwise, well do a /dev/urandom fallback.
#include <sys/stat.h>
static int randombytes_fallback(void *buf, size_t n) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t r = read(fd, buf, n);
close(fd);
return (r == (ssize_t)n) ? 0 : -1;
}
int randombytes(void *buf, size_t n) {
#ifdef SYS_getrandom
// Try getrandom(2) if available
ssize_t ret = syscall(SYS_getrandom, buf, n, 0);
if (ret < 0) {
// If getrandom is not supported or fails, fall back
if (errno == ENOSYS) {
return randombytes_fallback(buf, n);
}
return -1;
}
return (ret == (ssize_t)n) ? 0 : -1;
#else
// getrandom not available, just fallback
return randombytes_fallback(buf, n);
#endif
}
#else
// ------- Other Unix: read from /dev/urandom -------
#include <fcntl.h>
#include <unistd.h>
int randombytes(void *buf, size_t n) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t r = read(fd, buf, n);
close(fd);
return (r == (ssize_t)n) ? 0 : -1;
}
#endif
static inline void to_hex(const uint8_t *in, size_t in_len, char *out) static inline void to_hex(const uint8_t *in, size_t in_len, char *out)
{ {
static const char hexchars[] = "0123456789abcdef"; static const char hexchars[] = "0123456789abcdef";
@@ -134,7 +70,27 @@ JSValue js_crypto_keypair(JSContext *js, JSValue self, int argc, JSValue *argv)
uint8_t public[32]; uint8_t public[32];
uint8_t private[32]; uint8_t private[32];
randombytes(private,32); JSValue global = JS_GetGlobalObject(js);
JSValue os = JS_GetPropertyStr(js, global, "os");
JSValue random_blob = JS_GetPropertyStr(js, os, "random_blob");
JSValue size_val = JS_NewInt32(js, 32);
JSValue blob = JS_Call(js, random_blob, os, 1, &size_val);
size_t len;
uint8_t *data = js_get_blob_data(js, &len, blob);
if (!data || len != 32) {
JS_FreeValue(js, blob);
JS_FreeValue(js, size_val);
JS_FreeValue(js, random_blob);
JS_FreeValue(js, os);
JS_FreeValue(js, global);
return JS_ThrowInternalError(js, "failed to get random bytes");
}
memcpy(private, data, 32);
JS_FreeValue(js, blob);
JS_FreeValue(js, size_val);
JS_FreeValue(js, random_blob);
JS_FreeValue(js, os);
JS_FreeValue(js, global);
private[0] &= 248; private[0] &= 248;
private[31] &= 127; private[31] &= 127;
@@ -181,33 +137,6 @@ JSValue js_crypto_shared(JSContext *js, JSValue self, int argc, JSValue *argv)
return crypto2js(js, shared); return crypto2js(js, shared);
} }
JSValue js_crypto_random(JSContext *js, JSValue self, int argc, JSValue *argv)
{
// 1) Pull 64 bits of cryptographically secure randomness
uint64_t r;
if (randombytes(&r, sizeof(r)) != 0) {
// If something fails (extremely rare), throw an error
return JS_ThrowInternalError(js, "crypto.random: unable to get random bytes");
}
// 2) Convert r to a double in the range [0,1).
// We divide by (UINT64_MAX + 1.0) to ensure we never produce exactly 1.0.
double val = (double)r / ((double)UINT64_MAX + 1.0);
// 3) Return that as a JavaScript number
return JS_NewFloat64(js, val);
}
JSValue js_crypto_random_fit(JSContext *js, JSValue self, int argc, JSValue *argv)
{
int32_t r;
if (randombytes(&r, sizeof(r)) != 0) {
return JS_ThrowInternalError(js, "crypto.random: unable to get random bytes");
}
return JS_NewInt32(js, r);
}
JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv) JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv)
{ {
if (argc < 1) if (argc < 1)
@@ -245,8 +174,6 @@ JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv)
static const JSCFunctionListEntry js_crypto_funcs[] = { static const JSCFunctionListEntry js_crypto_funcs[] = {
JS_CFUNC_DEF("keypair", 0, js_crypto_keypair), JS_CFUNC_DEF("keypair", 0, js_crypto_keypair),
JS_CFUNC_DEF("shared", 1, js_crypto_shared), JS_CFUNC_DEF("shared", 1, js_crypto_shared),
JS_CFUNC_DEF("random", 0, js_crypto_random),
JS_CFUNC_DEF("random_fit", 0, js_crypto_random_fit),
JS_CFUNC_DEF("hash", 2, js_crypto_hash), JS_CFUNC_DEF("hash", 2, js_crypto_hash),
}; };

View File

@@ -7,6 +7,7 @@ var SYSYM = '__SYSTEM__'
// Get hidden modules from cell.hidden before stripping it // Get hidden modules from cell.hidden before stripping it
var hidden = cell.hidden var hidden = cell.hidden
var os = hidden.os;
var use_dyn = hidden.use_dyn var use_dyn = hidden.use_dyn
var dylib_ext = hidden.dylib_ext var dylib_ext = hidden.dylib_ext
var load_internal = hidden.load_internal var load_internal = hidden.load_internal
@@ -54,8 +55,6 @@ function get_import_dl(name) {
return null return null
} }
// tries to load a C symbol from either a dynamic library or within the executable
// an import 'debug/profile' can either be in a dynamic library debug.so or within the executable
function get_c_symbol(name) { function get_c_symbol(name) {
var dl = get_import_dl(name) var dl = get_import_dl(name)
var symname = `js_${name.replace('/', '_')}_use` var symname = `js_${name.replace('/', '_')}_use`
@@ -96,7 +95,7 @@ globalThis.log = {}
log.console = function(msg) log.console = function(msg)
{ {
var caller = caller_data(1) var caller = caller_data(1)
console_mod.print(console_rec(caller.line, caller.file, msg)) os.print(console_rec(caller.line, caller.file, msg))
} }
log.error = function(msg = new Error()) log.error = function(msg = new Error())
@@ -106,7 +105,7 @@ log.error = function(msg = new Error())
if (msg instanceof Error) if (msg instanceof Error)
msg = msg.name + ": " + msg.message + "\n" + msg.stack msg = msg.name + ": " + msg.message + "\n" + msg.stack
console_mod.print(console_rec(caller.line,caller.file,msg)) os.print(console_rec(caller.line,caller.file,msg))
} }
log.system = function(msg) { log.system = function(msg) {
@@ -121,15 +120,6 @@ if (!fd.stat('.cell').isDirectory) {
os.exit(1); os.exit(1);
} }
function is_file(path) {
try {
var st = fd.stat(path)
return !!(st && st.isFile)
} catch {
return false
}
}
function write_file(path, blob) { function write_file(path, blob) {
var fd_handle = fd.open(path, 'w') var fd_handle = fd.open(path, 'w')
fd.write(fd_handle, blob) fd.write(fd_handle, blob)
@@ -216,8 +206,13 @@ function get_package_from_path(path) {
// This will be set after shop.load_config() is called // This will be set after shop.load_config() is called
var config = null var config = null
// Unified path resolution function
// Unified path resolution function // Unified path resolution function
function resolve_path(requested, pkg_context, ext) { function resolve_path(requested, pkg_context, ext) {
if (requested.endsWith(ext)) ext = ''
var dependencies = (config && config.dependencies) ? config.dependencies : {} var dependencies = (config && config.dependencies) ? config.dependencies : {}
// Helper to check file existence and return result object // Helper to check file existence and return result object
@@ -232,14 +227,14 @@ function resolve_path(requested, pkg_context, ext) {
return null return null
} catch (e) { return null } } catch (e) { return null }
} }
if (is_file(path)) { if (fd.is_file(path)) {
// log.console("check: found file: " + path) // log.console("check: found file: " + path)
return { path: path, package_name: pkg, isCore: false } return { path: path, package_name: pkg, isCore: false }
} }
return null return null
} }
// Step 1: current package // Step 1: current package (Local)
if (pkg_context) { if (pkg_context) {
var pkg_path = '.cell/modules/' + pkg_context + '/' + requested + ext var pkg_path = '.cell/modules/' + pkg_context + '/' + requested + ext
var res = check(pkg_path, pkg_context, false) var res = check(pkg_path, pkg_context, false)
@@ -253,6 +248,7 @@ function resolve_path(requested, pkg_context, ext) {
if (res) return res if (res) return res
} }
} else { } else {
// Top-level local
var project_path = requested + ext var project_path = requested + ext
var res = check(project_path, null, false) var res = check(project_path, null, false)
if (res) return res if (res) return res
@@ -275,6 +271,12 @@ function resolve_path(requested, pkg_context, ext) {
var res = check(dep_path, pkg_alias, false) var res = check(dep_path, pkg_alias, false)
if (res) return res if (res) return res
} }
// Also check if it's just a module in .cell/modules even if not in dependencies (implicit)
var implicit_path = '.cell/modules/' + requested + ext
var res = check(implicit_path, pkg_alias, false)
if (res) return res
} else { } else {
// Check replace directives for simple names // Check replace directives for simple names
if (config && config.replace && config.replace[requested]) { if (config && config.replace && config.replace[requested]) {
@@ -285,14 +287,23 @@ function resolve_path(requested, pkg_context, ext) {
} }
// Check dependencies for simple names // Check dependencies for simple names
for (var alias in dependencies) { for (var alias in dependencies) {
var dep_simple = '.cell/modules/' + alias + '/' + requested + ext if (alias == requested) {
var res = check(dep_simple, alias, false) var dep_simple = '.cell/modules/' + alias + '/' + requested + ext
if (res) return res var res = check(dep_simple, alias, false)
if (res) return res
}
} }
// Implicit check
var implicit_path = '.cell/modules/' + requested + '/' + requested + ext
var res = check(implicit_path, requested, false)
if (res) return res
} }
// Step 3: core // Step 3: core
return check(requested + ext, null, true) var core_res = check(requested + ext, null, true)
if (core_res) log.console(`resolve_path: found core ${requested}`)
return core_res
} }
function get_compiled_path(resolved) { function get_compiled_path(resolved) {
@@ -315,197 +326,215 @@ function get_compiled_path(resolved) {
} }
} }
globalThis.use = function use(file, ...args) { var open_dl = {}
/* Package-aware module resolution:
1. Check local package (cwd, ie '.')
2. Check declared dependencies (from cell.toml [dependencies])
3. Check core_qop (standard library)
There's also the possibility of native C code; function get_c_symbol(requested, pkg_context) {
there may be, in a package, a .so/.dll/.dylib // Construct symbol name: js_x_y_z_use
that can be loaded. If that exists, as well as a .cm file, the var symname = `js_${requested.replace(/\//g, '_')}_use`
.so/.dll/.dylib is loaded and the .cm file is ran with the
loaded module as this.
for embedded modules, it's the same, but in the cell runtime, so no .so/.dll/.dylib
is loaded.
*/
var requested = file
// Check embedded modules first (these are always available) // 1. Check Local
var embed_mod = use_embed(requested) if (!pkg_context) {
if (!embed_mod) embed_mod = load_internal(requested) // Check local dylib: .cell/local/local.dylib
var local_dl_path = '.cell/local/local' + dylib_ext
// Check for dynamic library if (fd.is_file(local_dl_path)) {
var dyn_mod = null var dl = open_dl['local']
var dyn_path = resolve_path(requested, current_package, dylib_ext) if (!dl) {
if (dyn_path) { try {
try { dl = os.dylib_open(local_dl_path)
if (!dyn_path.isCore) { open_dl['local'] = dl
dyn_mod = use_dyn(dyn_path.path) } catch(e) {}
log.console("use: loaded dynamic library " + dyn_path.path) }
if (dl) {
try {
var sym = os.dylib_symbol(dl, symname)
if (sym) return sym
} catch(e) {}
} }
} catch (e) {
log.console(`use: failed to load dynamic library ${dyn_path.path}: ${e}`)
} }
} }
// If we have a dynamic module, use it as the embedded module (context) // 2. Check Modules
if (dyn_mod) { // Determine package name from requested path
embed_mod = dyn_mod var pkg_name = null
if (pkg_context) pkg_name = pkg_context
else if (requested.includes('/')) pkg_name = requested.split('/')[0]
else pkg_name = requested
if (pkg_name) {
var mod_dl_path = `.cell/modules/${pkg_name}/${pkg_name}${dylib_ext}`
if (fd.is_file(mod_dl_path)) {
var dl = open_dl[pkg_name]
if (!dl) {
try {
dl = os.dylib_open(mod_dl_path)
open_dl[pkg_name] = dl
} catch(e) {}
}
if (dl) {
try {
var sym = os.dylib_symbol(dl, symname)
if (sym) return sym
} catch(e) {}
}
}
} }
// Resolve the module path with package awareness return null
var resolved = resolve_path(requested, current_package, MOD_EXT) }
globalThis.use = function use(file, ...args) {
var requested = file
log.console(`use: ${file}`)
// Generate cache key based on resolution // 1. Check Local
// Check for C symbol locally
var c_mod = null
if (!current_package) {
c_mod = get_c_symbol(requested, null)
}
var resolved = null
// Check for local file
if (!current_package) {
resolved = resolve_path(requested, null, MOD_EXT)
// If we found a core module but we were looking for local, ignore it for now
if (resolved && resolved.isCore) resolved = null
}
// If we found something locally (C or Script), stop looking elsewhere
if (c_mod || resolved) {
// Proceed with local
} else {
// 2. Check Modules
// Check for C symbol in modules
// We need to guess the package name if not in a package context
var pkg_guess = requested.split('/')[0]
c_mod = get_c_symbol(requested, pkg_guess)
if (!c_mod) {
// Check for module file
resolved = resolve_path(requested, null, MOD_EXT) // resolve_path handles module lookup if we pass null context but path has /
if (resolved && resolved.isCore) resolved = null
}
}
// 3. Check Core
if (!c_mod && !resolved) {
var embed_mod = use_embed(requested)
if (embed_mod) c_mod = embed_mod
var res = resolve_path(requested, null, MOD_EXT)
if (res && res.isCore) resolved = res
}
// If still nothing
if (!c_mod && !resolved) {
// Try load_internal as last resort for core C
c_mod = load_internal(`js_${requested}_use`)
}
if (!c_mod && !resolved)
throw new Error(`Module ${file} could not be found (package context: ${current_package || 'none'})`)
// Generate cache key
var cache_key = resolved var cache_key = resolved
? (resolved.isCore ? 'core:' + resolved.path : resolved.path) ? (resolved.isCore ? 'core:' + resolved.path : resolved.path)
: (dyn_path ? dyn_path.path : requested) : (c_mod ? 'c:' + requested : requested)
if (use_cache[cache_key]) return use_cache[cache_key] if (use_cache[cache_key]) return use_cache[cache_key]
log.console(`cache miss: ${cache_key}`) // log.console(`cache miss: ${cache_key}`)
if (!resolved && !embed_mod) // If we have a C module, use it as context
throw new Error(`Module ${file} could not be found (package context: ${current_package || 'none'})`) var context = {}
if (c_mod) {
// If only embedded/dynamic module exists, return it context = c_mod
if (!resolved && embed_mod) { log.console("use: using c_mod as context")
use_cache[cache_key] = embed_mod
return embed_mod
} }
var path = resolved.path // If we have a script, run it
var isCore = resolved.isCore var ret = c_mod
var module_package = resolved.package_name
// If core module, load it if (resolved) {
if (isCore) { log.console("use: running script " + resolved.path)
var ret = null var path = resolved.path
try { var isCore = resolved.isCore
// Try to load compiled version first var module_package = resolved.package_name
var compiledPath = get_compiled_path(resolved)
var useCompiled = false
// Always compile from source - never use precompiled for core modules // Check for circular dependencies
// if (is_file(compiledPath)) { if (path && loadingStack.includes(path)) {
let cycleIndex = loadingStack.indexOf(path)
let cyclePath = loadingStack.slice(cycleIndex).concat(path)
throw new Error(`Circular dependency: ${cyclePath.join(" -> ")}`)
}
inProgress[path] = true
loadingStack.push(path)
var prev_package = current_package
current_package = module_package
var compiledPath = get_compiled_path(resolved)
mkdir_p(compiledPath.substring(0, compiledPath.lastIndexOf('/')))
var useCompiled = false
var srcStat = fd.stat(path)
var compiledStat = fd.stat(compiledPath)
// Always compile from source - never use precompiled for regular modules
// if (srcStat && srcStat.isFile && compiledStat && compiledStat.isFile && compiledStat.mtime > srcStat.mtime) {
// useCompiled = true // useCompiled = true
// } // }
var fn var fn
var mod_name = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'))
if (useCompiled) { if (useCompiled) {
var compiledBlob = fd.slurp(compiledPath) var compiledBlob = fd.slurp(compiledPath)
fn = js.compile_unblob(compiledBlob) fn = js.compile_unblob(compiledBlob)
fn = js.eval_compile(fn) fn = js.eval_compile(fn)
log.console("use: using compiled core version " + compiledPath) log.console("use: using compiled version " + compiledPath)
} else { } else {
var script = utf8.decode(core_qop.read(path)) if (isCore) {
var mod_script = `(function setup_${requested.replace(/[^a-zA-Z0-9_]/g, '_')}_module(arg, $_){${script};})` var script = utf8.decode(core_qop.read(path))
fn = js.compile(path, mod_script) var mod_script = `(function setup_${requested.replace(/[^a-zA-Z0-9_]/g, '_')}_module(arg, $_){${script};})`
fn = js.compile(path, mod_script)
// Save compiled version
mkdir_p(compiledPath.substring(0, compiledPath.lastIndexOf('/'))) // Save compiled version
var compiled = js.compile_blob(fn) mkdir_p(compiledPath.substring(0, compiledPath.lastIndexOf('/')))
write_file(compiledPath, compiled) var compiled = js.compile_blob(fn)
write_file(compiledPath, compiled)
fn = js.eval_compile(fn)
fn = js.eval_compile(fn)
} else {
var script = utf8.decode(fd.slurp(path))
var mod_script = `(function setup_${mod_name}_module(arg, $_){${script};})`
fn = js.compile(path, mod_script)
// Save compiled version to .cell directory
var compiled = js.compile_blob(fn)
write_file(compiledPath, compiled)
fn = js.eval_compile(fn)
}
} }
var context = embed_mod ? embed_mod : {}
ret = fn.call(context, args, $_) ret = fn.call(context, args, $_)
} catch (e) { log.console("use: script returned " + (typeof ret))
// Script component doesn't exist or failed, fall back to embedded module
// log.console("use: core module " + path + " failed to load script (using embedded if avail)") current_package = prev_package
} loadingStack.pop()
delete inProgress[path]
if (!ret && embed_mod) {
ret = embed_mod
} else if (!ret) {
throw new Error(`Use must be used with a module, but ${path} doesn't return a value`)
}
use_cache[cache_key] = ret
return ret
} }
// Check for circular dependencies using the resolved path if (!ret && c_mod) {
if (path && loadingStack.includes(path)) { log.console("use: script returned nothing, using c_mod")
let cycleIndex = loadingStack.indexOf(path) ret = c_mod
let cyclePath = loadingStack.slice(cycleIndex).concat(path)
throw new Error(
`Circular dependency detected while loading "${file}".\n` +
`Module chain: ${loadingStack.join(" -> ")}\n` +
`Cycle specifically: ${cyclePath.join(" -> ")}`
)
} }
else if (!ret) throw new Error(`Use must be used with a module, but ${file} doesn't return a value`)
log.console("use: loading file " + path + " (package: " + (module_package || 'local') + ")")
inProgress[path] = true
loadingStack.push(path)
// Save and set package context for nested use() calls
var prev_package = current_package
current_package = module_package
// Determine the compiled file path in .cell directory
var compiledPath = get_compiled_path(resolved)
mkdir_p(compiledPath.substring(0, compiledPath.lastIndexOf('/')))
// Check if compiled version exists and is newer than source
var useCompiled = false
var srcStat = fd.stat(path)
var compiledStat = fd.stat(compiledPath)
// Always compile from source - never use precompiled for regular modules
// if (srcStat && srcStat.isFile && compiledStat && compiledStat.isFile && compiledStat.mtime > srcStat.mtime) {
// useCompiled = true
// }
var fn
var mod_name = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'))
if (useCompiled) {
var compiledBlob = fd.slurp(compiledPath)
fn = js.compile_unblob(compiledBlob)
fn = js.eval_compile(fn)
log.console("use: using compiled version " + compiledPath)
} else {
var script = utf8.decode(fd.slurp(path))
var mod_script = `(function setup_${mod_name}_module(arg, $_){${script};})`
fn = js.compile(path, mod_script)
// Save compiled version to .cell directory
var compiled = js.compile_blob(fn)
write_file(compiledPath, compiled)
fn = js.eval_compile(fn)
}
// Create context - if embedded module exists, use it as 'this'
var context = embed_mod ? embed_mod : {}
// Call the script - pass embedded module as 'this' if it exists
var ret = fn.call(context, args, $_)
// Restore previous package context
current_package = prev_package
// If script doesn't return anything, check if we have embedded module
if (!ret && embed_mod) {
ret = embed_mod
} else if (!ret) {
throw new Error(`Use must be used with a module, but ${path} doesn't return a value`)
}
loadingStack.pop()
delete inProgress[path]
// Cache the result
use_cache[cache_key] = ret use_cache[cache_key] = ret
return ret return ret
} }
@@ -614,7 +643,7 @@ stone.p = function(object)
function guid(bits = 256) function guid(bits = 256)
{ {
var guid = new blob(bits, hidden.randi) var guid = new blob(bits, os.random)
stone(guid) stone(guid)
return text(guid,'h') return text(guid,'h')
} }
@@ -629,10 +658,13 @@ function create_actor(desc = {id:guid()}) {
var $_ = create_actor() var $_ = create_actor()
$_.random = hidden.rand $_.random = function() {
var n = os.random()
return n > 100000
}
$_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5." $_.random[cell.DOC] = "returns a number between 0 and 1. There is a 50% chance that the result is less than 0.5."
$_.random_fit = hidden.randi $_.random_fit = os.random
$_.clock = function(fn) { $_.clock = function(fn) {
actor_mod.clock(_ => { actor_mod.clock(_ => {
@@ -1113,7 +1145,7 @@ var useCompiled = false
// Always compile from source - never use precompiled for main program // Always compile from source - never use precompiled for main program
// if (resolved_prog.isCore) { // if (resolved_prog.isCore) {
// // For core, we check if we have a compiled version, else we compile it // // For core, we check if we have a compiled version, else we compile it
// if (is_file(compiledPath)) { // if (fd.is_file(compiledPath)) {
// useCompiled = true // useCompiled = true
// } // }
// } else { // } else {

View File

@@ -411,6 +411,20 @@ JSC_SCALL(fd_readdir,
#endif #endif
) )
JSC_SCALL(fd_is_file,
struct stat st;
if (stat(str, &st) != 0)
return JS_NewBool(js, false);
return JS_NewBool(js, S_ISREG(st.st_mode));
)
JSC_SCALL(fd_is_dir,
struct stat st;
if (stat(str, &st) != 0)
return JS_NewBool(js, false);
return JS_NewBool(js, S_ISDIR(st.st_mode));
)
static const JSCFunctionListEntry js_fd_funcs[] = { static const JSCFunctionListEntry js_fd_funcs[] = {
MIST_FUNC_DEF(fd, open, 2), MIST_FUNC_DEF(fd, open, 2),
MIST_FUNC_DEF(fd, write, 2), MIST_FUNC_DEF(fd, write, 2),
@@ -428,6 +442,8 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
MIST_FUNC_DEF(fd, stat, 1), MIST_FUNC_DEF(fd, stat, 1),
MIST_FUNC_DEF(fd, fstat, 1), MIST_FUNC_DEF(fd, fstat, 1),
MIST_FUNC_DEF(fd, readdir, 1), MIST_FUNC_DEF(fd, readdir, 1),
MIST_FUNC_DEF(fd, is_file, 1),
MIST_FUNC_DEF(fd, is_dir, 1),
}; };
JSValue js_fd_use(JSContext *js) { JSValue js_fd_use(JSContext *js) {

View File

@@ -13,6 +13,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <bcrypt.h>
#else #else
#include <unistd.h> #include <unistd.h>
#include <sys/utsname.h> #include <sys/utsname.h>
@@ -20,9 +21,14 @@
#ifdef __linux__ #ifdef __linux__
#include <sys/sysinfo.h> #include <sys/sysinfo.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <errno.h>
#endif #endif
#endif #endif
#include <fcntl.h>
static JSClassID js_dylib_class_id; static JSClassID js_dylib_class_id;
static void js_dylib_finalizer(JSRuntime *rt, JSValue val) { static void js_dylib_finalizer(JSRuntime *rt, JSValue val) {
@@ -58,6 +64,8 @@ JSC_CCALL(os_power_state,
case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break; case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break;
case SDL_POWERSTATE_CHARGING: statestr = "charging"; break; case SDL_POWERSTATE_CHARGING: statestr = "charging"; break;
case SDL_POWERSTATE_CHARGED: statestr = "charged"; break; case SDL_POWERSTATE_CHARGED: statestr = "charged"; break;
case SDL_POWERSTATE_ERROR: statestr = "error"; break;
case SDL_POWERSTATE_UNKNOWN: statestr = "unknown"; break;
} }
ret = JS_NewObject(js); ret = JS_NewObject(js);
JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr)); JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr));
@@ -383,6 +391,109 @@ static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue
return symbol(js); return symbol(js);
} }
JSC_CCALL(os_print,
size_t len;
const char *str = JS_ToCStringLen(js, &len, argv[0]);
printf("%.*s", (int)len, str);
JS_FreeCString(js, str);
)
static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValue *argv)
{
void *handle;
#ifdef _WIN32
handle = GetModuleHandle(NULL);
#else
handle = dlopen(NULL, RTLD_LAZY);
#endif
if (argc < 1) {
return JS_ThrowTypeError(js, "load_internal requires a symbol name");
}
const char *symbol_name = JS_ToCString(js, argv[0]);
if (!symbol_name) {
return JS_ThrowTypeError(js, "symbol name must be a string");
}
JSValue (*symbol)(JSContext *js);
#if defined(_WIN32)
symbol = GetProcAddress((HMODULE)handle, symbol_name);
#else
symbol = dlsym(handle, symbol_name);
#endif
JS_FreeCString(js, symbol_name);
if (!symbol) {
const char *error_msg = "Symbol not found";
#ifndef _WIN32
const char *dl_error = dlerror();
if (dl_error) {
error_msg = dl_error;
}
#endif
return JS_ThrowReferenceError(js, "Failed to get symbol: %s", error_msg);
}
// Return the symbol as a pointer value
return symbol(js);
}
#if defined(_WIN32)
// ------- Windows: use BCryptGenRandom -------
int randombytes(void *buf, size_t n) {
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return (status == 0) ? 0 : -1;
}
#elif defined(__linux__)
// ------- Linux: try getrandom, fall back to /dev/urandom -------
static int randombytes_fallback(void *buf, size_t n) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t r = read(fd, buf, n);
close(fd);
return (r == (ssize_t)n) ? 0 : -1;
}
int randombytes(void *buf, size_t n) {
#ifdef SYS_getrandom
// Try getrandom(2) if available
ssize_t ret = syscall(SYS_getrandom, buf, n, 0);
if (ret < 0) {
// If getrandom is not supported or fails, fall back
if (errno == ENOSYS) {
return randombytes_fallback(buf, n);
}
return -1;
}
return (ret == (ssize_t)n) ? 0 : -1;
#else
// getrandom not available, just fallback
return randombytes_fallback(buf, n);
#endif
}
#else
// ------- Other Unix: read from /dev/urandom -------
int randombytes(void *buf, size_t n) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
ssize_t r = read(fd, buf, n);
close(fd);
return (r == (ssize_t)n) ? 0 : -1;
}
#endif
JSC_CCALL(os_random,
double random_double;
uint8_t *buf = (uint8_t *)&random_double;
if (randombytes(buf, sizeof(double)) != 0) {
return JS_ThrowInternalError(js, "failed to generate random bytes");
}
return JS_NewFloat64(js, random_double);
)
static const JSCFunctionListEntry js_os_funcs[] = { static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, platform, 0), MIST_FUNC_DEF(os, platform, 0),
MIST_FUNC_DEF(os, arch, 0), MIST_FUNC_DEF(os, arch, 0),
@@ -400,6 +511,9 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, exit, 0), MIST_FUNC_DEF(os, exit, 0),
MIST_FUNC_DEF(os, dylib_open, 1), MIST_FUNC_DEF(os, dylib_open, 1),
MIST_FUNC_DEF(os, dylib_symbol, 2), MIST_FUNC_DEF(os, dylib_symbol, 2),
MIST_FUNC_DEF(os, load_internal, 1),
MIST_FUNC_DEF(os, print, 1),
MIST_FUNC_DEF(os, random, 0),
}; };
JSValue js_os_use(JSContext *js) { JSValue js_os_use(JSContext *js) {

View File

@@ -157,33 +157,6 @@ static uint32_t xorshift32(){
return rng_state = x; return rng_state = x;
} }
JSC_CCALL(os_guid,
uint8_t data[16];
for(int i = 0; i < 4; i++){
uint32_t v = xorshift32();
memcpy(&data[i*4], &v, 4);
}
static const char hex[] = "0123456789abcdef";
char buf[32];
for(int i = 0; i < 16; i++){
uint8_t b = data[i];
buf[i*2 ] = hex[b >> 4];
buf[i*2 + 1] = hex[b & 0x0f];
}
return JS_NewStringLen(js, buf, 32);
)
JSC_SCALL(console_print, printf("%s", str); )
static const JSCFunctionListEntry js_console_funcs[] = {
MIST_FUNC_DEF(console,print,1),
};
JSC_SCALL(os_system, ret = number2js(js,system(str)); )
JSC_CCALL(os_rand, JSC_CCALL(os_rand,
MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand; MTRand *mrand = &((cell_rt*)JS_GetContextOpaque(js))->mrand;
return number2js(js, genRand(mrand)); return number2js(js, genRand(mrand));
@@ -199,124 +172,6 @@ JSC_CCALL(os_srand,
m_seedRand(mrand, js2number(js,argv[0])); m_seedRand(mrand, js2number(js,argv[0]));
) )
static const JSCFunctionListEntry js_util_funcs[] = {
MIST_FUNC_DEF(os, guid, 0),
};
static void *get_main_module_handle() {
#if defined(_WIN32)
return GetModuleHandle(NULL);
#else
return dlopen(NULL, RTLD_LAZY);
#endif
}
static void *get_symbol(void *handle, const char *name) {
#if defined(_WIN32)
return (void*)GetProcAddress((HMODULE)handle, name);
#else
return dlsym(handle, name);
#endif
}
JSC_CCALL(os_use_dyn,
const char *path = JS_ToCString(js, argv[0]);
if (!path) return JS_ThrowTypeError(js, "path must be a string");
// 1. Load the library
#if defined(_WIN32)
HMODULE handle = LoadLibraryA(path);
#else
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
#endif
if (!handle) {
JS_FreeCString(js, path);
#if defined(_WIN32)
return JS_ThrowReferenceError(js, "Could not load library");
#else
return JS_ThrowReferenceError(js, "Could not load library: %s", dlerror());
#endif
}
// 2. Construct symbol name: js_<basename>_use
// Extract basename from path
const char *base = strrchr(path, '/');
#if defined(_WIN32)
const char *base2 = strrchr(path, '\\');
if (base2 > base) base = base2;
#endif
if (base) base++; // Skip the slash
else base = path; // No slash, use whole path
char name_buf[256];
const char *dot = strrchr(base, '.');
size_t len = dot ? (size_t)(dot - base) : strlen(base);
if (len > 100) len = 100; // Safety cap
char clean_base[128];
memcpy(clean_base, base, len);
clean_base[len] = '\0';
snprintf(name_buf, sizeof(name_buf), "js_%s_use", clean_base);
// 3. Get the symbol
typedef JSValue (*init_fn)(JSContext*);
init_fn fn = (init_fn)get_symbol(handle, name_buf);
JS_FreeCString(js, path);
if (!fn) {
// Try without stripping extension? No, standard is usually without.
// Maybe try "js_main_use"? No.
// Let's stick to the plan.
#if defined(_WIN32)
FreeLibrary(handle);
#else
dlclose(handle);
#endif
return JS_ThrowReferenceError(js, "Could not find entry point %s in library", name_buf);
}
// 4. Call the function
return fn(js);
)
JSC_SCALL(os_load_internal,
void *handle = get_main_module_handle();
if (!handle) {
return JS_ThrowReferenceError(js, "Could not get main module handle");
}
JSValue (*js_use)(JSContext*) = get_symbol(handle, str);
if (!js_use) {
// Try without "js_" prefix or other variations if needed, but standard is js_<name>_use
return JS_NULL;
}
ret = js_use(js);
)
JSValue js_util_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_util_funcs,countof(js_util_funcs));
return mod;
}
JSValue js_console_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_console_funcs,countof(js_console_funcs));
return mod;
}
JSC_CCALL(os_value_id,
JS_SetPropertyStr(js, argv[0], "id", number2js(js, (uintptr_t)JS_VALUE_GET_PTR(argv[0])));
return argv[0];
)
void ffi_load(JSContext *js) void ffi_load(JSContext *js)
{ {
cell_rt *rt = JS_GetContextOpaque(js); cell_rt *rt = JS_GetContextOpaque(js);
@@ -336,13 +191,7 @@ void ffi_load(JSContext *js)
JSValue hidden_fn = JS_NewObject(js); JSValue hidden_fn = JS_NewObject(js);
// Add functions that should only be accessible to engine.js
JS_SetPropertyStr(js, hidden_fn, "load_internal", JS_NewCFunction(js, js_os_load_internal, "load_internal", 1));
JS_SetPropertyStr(js, hidden_fn, "rand", JS_NewCFunction(js, js_os_rand, "rand", 0));
JS_SetPropertyStr(js, hidden_fn, "randi", JS_NewCFunction(js, js_os_randi, "randi", 0));
JS_SetPropertyStr(js, hidden_fn, "srand", JS_NewCFunction(js, js_os_srand, "srand", 1));
JS_SetPropertyStr(js, hidden_fn, "use_dyn", JS_NewCFunction(js, js_os_use_dyn, "use_dyn", 1));
#if defined(_WIN32) #if defined(_WIN32)
JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dll")); JS_SetPropertyStr(js, hidden_fn, "dylib_ext", JS_NewString(js, ".dll"));
#elif defined(__APPLE__) #elif defined(__APPLE__)