move random number gen
This commit is contained in:
115
scripts/crypto.c
115
scripts/crypto.c
@@ -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, we’ll 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),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
16
scripts/fd.c
16
scripts/fd.c
@@ -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) {
|
||||||
|
|||||||
114
scripts/os.c
114
scripts/os.c
@@ -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) {
|
||||||
|
|||||||
153
source/jsffi.c
153
source/jsffi.c
@@ -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__)
|
||||||
|
|||||||
Reference in New Issue
Block a user