This commit is contained in:
2025-12-04 13:54:00 -06:00
parent 6b8464aca4
commit 95ac4bd096
12 changed files with 280 additions and 197 deletions

View File

@@ -1,6 +1,6 @@
sdl_video = "main" sdl_video = "main"
[dependencies] [dependencies]
extramath = "https://gitea.pockle.world/john/extramath@master" extramath = "gitea.pockle.world/john/extramath@master"
[system] [system]
ar_timer = 60 ar_timer = 60
actor_memory = 0 actor_memory = 0
@@ -16,4 +16,4 @@ main = true
[actors.prosperon] [actors.prosperon]
main = true main = true
[actors.accio] [actors.accio]
main=true main = true

View File

@@ -11,7 +11,7 @@ release: FORCE
meson install -C build_release meson install -C build_release
sanitize: FORCE sanitize: FORCE
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak build_sani
meson install -C build_sani meson install -C build_sani
thread: FORCE thread: FORCE

View File

@@ -223,10 +223,36 @@ JSC_SCALL(fd_mv,
} }
) )
JSC_SCALL(fd_symlink,
if (argc < 2)
ret = JS_ThrowTypeError(js, "fd.symlink requires 2 arguments: target and link path");
else if (!JS_IsString(argv[1]))
ret = JS_ThrowTypeError(js, "second argument must be a string (link path)");
else {
const char *link_path = JS_ToCString(js, argv[1]);
#ifdef _WIN32
if (!CreateSymbolicLinkA(link_path, str, SYMBOLIC_LINK_FLAG_DIRECTORY)) {
// Try file
if (!CreateSymbolicLinkA(link_path, str, 0)) {
ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %lu", link_path, str, GetLastError());
}
}
#else
if (symlink(str, link_path) != 0)
ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %s", link_path, str, strerror(errno));
#endif
JS_FreeCString(js, link_path);
}
)
// Helper function for recursive removal // Helper function for recursive removal
static int remove_recursive(const char *path) { static int remove_recursive(const char *path) {
struct stat st; struct stat st;
#ifdef _WIN32
if (stat(path, &st) != 0) if (stat(path, &st) != 0)
#else
if (lstat(path, &st) != 0)
#endif
return -1; return -1;
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
@@ -565,6 +591,7 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
MIST_FUNC_DEF(fd, is_file, 1), MIST_FUNC_DEF(fd, is_file, 1),
MIST_FUNC_DEF(fd, is_dir, 1), MIST_FUNC_DEF(fd, is_dir, 1),
MIST_FUNC_DEF(fd, enumerate, 2), MIST_FUNC_DEF(fd, enumerate, 2),
MIST_FUNC_DEF(fd, symlink, 2),
}; };
JSValue js_fd_use(JSContext *js) { JSValue js_fd_use(JSContext *js) {

View File

@@ -383,6 +383,7 @@ static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue
error_msg = dl_error; error_msg = dl_error;
} }
#endif #endif
return JS_ThrowReferenceError(js, "Failed to load symbol: %s", error_msg);
return JS_NULL; return JS_NULL;
} }
@@ -390,6 +391,34 @@ static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue
return symbol(js); return symbol(js);
} }
static JSValue js_os_dylib_has_symbol(JSContext *js, JSValue self, int argc, JSValue *argv)
{
if (argc < 2) {
return JS_ThrowTypeError(js, "dylib_has_symbol requires dylib object and symbol name");
}
void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
if (!handle) {
return JS_ThrowTypeError(js, "First argument must be a dylib object");
}
const char *symbol_name = JS_ToCString(js, argv[1]);
if (!symbol_name) {
return JS_ThrowTypeError(js, "symbol name must be a string");
}
void *symbol;
#ifdef _WIN32
symbol = (void*)GetProcAddress((HMODULE)handle, symbol_name);
#else
symbol = dlsym(handle, symbol_name);
#endif
JS_FreeCString(js, symbol_name);
return JS_NewBool(js, symbol != NULL);
}
JSC_CCALL(os_print, JSC_CCALL(os_print,
size_t len; size_t len;
const char *str = JS_ToCStringLen(js, &len, argv[0]); const char *str = JS_ToCStringLen(js, &len, argv[0]);
@@ -427,6 +456,33 @@ static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValu
return symbol(js); return symbol(js);
} }
static JSValue js_os_internal_exists(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, "internal_exists 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");
void *symbol;
#if defined(_WIN32)
symbol = (void*)GetProcAddress((HMODULE)handle, symbol_name);
#else
symbol = dlsym(handle, symbol_name);
#endif
JS_FreeCString(js, symbol_name);
return JS_NewBool(js, symbol != NULL);
}
#if defined(_WIN32) #if defined(_WIN32)
// ------- Windows: use BCryptGenRandom ------- // ------- Windows: use BCryptGenRandom -------
int randombytes(void *buf, size_t n) { int randombytes(void *buf, size_t n) {
@@ -494,7 +550,9 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, sleep, 1), MIST_FUNC_DEF(os, sleep, 1),
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, dylib_has_symbol, 2),
MIST_FUNC_DEF(os, load_internal, 1), MIST_FUNC_DEF(os, load_internal, 1),
MIST_FUNC_DEF(os, internal_exists, 1),
MIST_FUNC_DEF(os, print, 1), MIST_FUNC_DEF(os, print, 1),
MIST_FUNC_DEF(os, random, 0), MIST_FUNC_DEF(os, random, 0),
}; };

View File

@@ -1,49 +1,29 @@
// cell replace <alias> <path> - Add or update a replace directive for a dependency // cell replace [package] [path]
// replace a package with a local directory
var fd = use('fd')
var shop = use('shop') var shop = use('shop')
if (args.length < 2) { if (args.length < 2) {
log.console("Usage: cell replace <alias> <path>") log.console("Usage: cell replace <package> <path>")
log.console("Examples:") log.console(" cell replace <package> --remove")
log.console(" cell replace prosperon ../prosperon") $_.stop()
log.console(" cell replace extramath ../my-fork-of-extramath")
$_.stop()
return
} }
var alias = args[0] var pkg = args[0]
var path = args[1] var path = args[1]
// Initialize shop if needed if (path == '--remove') {
if (!fd.stat('.cell/cell.toml').isFile) { if (shop.remove_replacement(pkg)) {
log.console("No cell.toml found. Initializing...") log.console("Replacement removed.")
shop.init() } else {
log.console("Failed to remove replacement.")
}
} else {
if (shop.add_replacement(pkg, path)) {
log.console("Replacement added.")
} else {
log.console("Failed to add replacement.")
}
} }
// Load current config $_.stop()
var config = shop.load_config()
if (!config) {
log.error("Failed to load cell.toml")
$_.stop()
return
}
// Check if the alias exists in dependencies
if (!config.dependencies || !config.dependencies[alias]) {
log.console("Warning: '" + alias + "' is not in dependencies. Adding replace anyway.")
}
// Ensure replace section exists
if (!config.replace) {
config.replace = {}
}
// Add or update the replace directive
config.replace[alias] = path
shop.save_config(config)
log.console("Added replace directive: " + alias + " = " + path)
log.console("Run 'cell build' to apply changes")
$_.stop()

View File

@@ -348,11 +348,6 @@ Shop.get_module_dir = function(alias) {
return null return null
} }
// Check if replaced
if (config.replace && config.replace[alias]) {
return config.replace[alias]
}
var pkg = config.dependencies[alias] var pkg = config.dependencies[alias]
var parsed = Shop.parse_package(pkg) var parsed = Shop.parse_package(pkg)
if (!parsed) return null if (!parsed) return null
@@ -568,19 +563,19 @@ function resolve_locator(path, ext, ctx)
return null; return null;
} }
function resolve_c_symbol(path, package_ctx) function resolve_c_symbol(path, package_context)
{ {
var local_path = package_ctx ? package_ctx : 'local' var local_path = package_context ? package_context : 'local'
var local_sym_base = path.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_') var local_sym_base = path.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_')
var local var local
if (!package_ctx) { if (!package_context) {
local = `js_local_${local_sym_base}_use` local = `js_local_${local_sym_base}_use`
} else { } else {
local = `js_${local_path.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_')}_${local_sym_base}_use` local = `js_${local_path.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_')}_${local_sym_base}_use`
} }
var build_dir = get_build_dir(package_ctx) var build_dir = get_build_dir(package_context)
var local_dl_name = build_dir + '/cellmod' + dylib_ext var local_dl_name = build_dir + '/cellmod' + dylib_ext
if (fd.is_file(local_dl_name)) { if (fd.is_file(local_dl_name)) {
@@ -588,19 +583,25 @@ function resolve_c_symbol(path, package_ctx)
open_dls[local_dl_name] = os.dylib_open(local_dl_name); open_dls[local_dl_name] = os.dylib_open(local_dl_name);
if (open_dls[local_dl_name]) { if (open_dls[local_dl_name]) {
var local_addr = os.dylib_symbol(open_dls[local_dl_name], local); if (os.dylib_has_symbol(open_dls[local_dl_name], local))
if (local_addr) return {symbol: local_addr, scope: SCOPE_LOCAL}; return {
symbol: function() { return os.dylib_symbol(open_dls[local_dl_name], local); },
scope: SCOPE_LOCAL
};
} }
} }
// Try static linking fallback // Try static linking fallback
var static_local_addr = os.load_internal(local); if (os.internal_exists(local))
if (static_local_addr) return {symbol: static_local_addr, scope: SCOPE_LOCAL}; return {
symbol: function() { return os.load_internal(local); },
scope: SCOPE_LOCAL
};
// If 'path' has a package alias (e.g. 'prosperon/sprite'), try to resolve it // If 'path' has a package alias (e.g. 'prosperon/sprite'), try to resolve it
var pkg_alias = get_import_package(path) var pkg_alias = get_import_package(path)
if (pkg_alias) { if (pkg_alias) {
var canon_pkg = get_normalized_package(path, package_ctx) var canon_pkg = get_normalized_package(path, package_context)
if (canon_pkg) { if (canon_pkg) {
var build_dir = get_build_dir(canon_pkg) var build_dir = get_build_dir(canon_pkg)
var dl_path = build_dir + '/cellmod' + dylib_ext var dl_path = build_dir + '/cellmod' + dylib_ext
@@ -613,20 +614,30 @@ function resolve_c_symbol(path, package_ctx)
if (fd.is_file(dl_path)) { if (fd.is_file(dl_path)) {
if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path) if (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path)
if (open_dls[dl_path]) { if (open_dls[dl_path]) {
var addr = os.dylib_symbol(open_dls[dl_path], sym_name) if (os.dylib_has_symbol(open_dls[dl_path], sym_name))
if (addr) return {symbol: addr, scope: SCOPE_PACKAGE, package: canon_pkg} return {
symbol: function() { return os.dylib_symbol(open_dls[dl_path], sym_name) },
scope: SCOPE_PACKAGE,
package: canon_pkg
}
} }
} }
var static_package_addr = os.load_internal(sym_name); if (os.internal_exists(sym_name))
if (static_package_addr) return {
return {symbol:static_package_addr, scope: SCOPE_PACKAGE, package: canon_pkg}; symbol: function() { return os.load_internal(sym_name) },
scope: SCOPE_PACKAGE,
package: canon_pkg
};
} }
} }
var core_addr = os.load_internal(`js_${path.replace(/\//g, '_')}_use`); var core_sym = `js_${path.replace(/\//g, '_')}_use`;
if (core_addr) if (os.internal_exists(core_sym))
return {symbol:core_addr, scope: SCOPE_CORE}; return {
symbol: function() { return os.load_internal(core_sym); },
scope: SCOPE_CORE
};
return null return null
} }
@@ -650,11 +661,11 @@ Shop.use = function(path, package_context) {
return use_cache[cache_key] return use_cache[cache_key]
if (c_resolve.scope < mod_resolve.scope) if (c_resolve.scope < mod_resolve.scope)
use_cache[cache_key] = c_resolve.symbol use_cache[cache_key] = c_resolve.symbol()
else if (mod_resolve.scope < c_resolve.scope) else if (mod_resolve.scope < c_resolve.scope)
use_cache[cache_key] = mod_resolve.symbol.call() use_cache[cache_key] = mod_resolve.symbol.call()
else else
use_cache[cache_key] = mod_resolve.symbol.call(c_resolve.symbol) use_cache[cache_key] = mod_resolve.symbol.call(c_resolve.symbol())
return use_cache[cache_key] return use_cache[cache_key]
} }
@@ -671,23 +682,11 @@ function get_cache_path(pkg, commit) {
} }
function rm_recursive(path) { function rm_recursive(path) {
var st = fd.stat(path) try {
if (!st) return fd.rm(path)
} catch (e) {
if (!st.isDirectory) { log.error("Failed to remove " + path + ": " + e)
fd.unlink(path)
return
} }
var list = fd.readdir(path)
if (list) {
for (var i = 0; i < list.length; i++) {
var item = list[i]
if (item == '.' || item == '..') continue
rm_recursive(path + "/" + item)
}
}
fd.rmdir(path)
} }
function get_all_files(dir, prefix, results) { function get_all_files(dir, prefix, results) {
@@ -745,9 +744,8 @@ function verify_zip_contents(zip, target_dir) {
// Check for extra files // Check for extra files
var existing_files = get_all_files(target_dir) var existing_files = get_all_files(target_dir)
for (var i = 0; i < existing_files.length; i++) { for (var i = 0; i < existing_files.length; i++)
if (!expected_files[existing_files[i]]) return false if (!expected_files[existing_files[i]]) return false
}
return true return true
} }
@@ -944,100 +942,76 @@ Shop.get = function(pkg, alias) {
return true return true
} }
// High-level: Update a specific package or all packages Shop.fetch = function(package)
// Like `bun update` or `bun update <pkg>` {
Shop.update_all = function(alias) {
}
// High-level: Update a specific package
// Like `bun update <pkg>`
Shop.update = function(pkg) {
var config = Shop.load_config() var config = Shop.load_config()
if (!config || !config.dependencies) {
log.console("No dependencies to update.")
return
}
var lock = Shop.load_lock() var lock = Shop.load_lock()
var queue = []
var processed = {}
// Initialize queue // Check if replaced
if (alias) { if (config.replace && config.replace[pkg]) {
if (config.dependencies[alias]) { log.console("Skipping update for replaced package " + pkg)
queue.push(config.dependencies[alias]) Shop.update(config.replace[pkg])
} else { return false
log.error("Dependency not found: " + alias) }
return
// Find existing lock info
var lock_info = lock[pkg]
var local_hash = lock_info ? lock_info.commit : null
var api_url = Shop.get_api_url(pkg)
var remote_hash = null
// Check for updates if possible
if (api_url) {
try {
var resp = http.fetch(api_url)
remote_hash = Shop.extract_commit_hash(pkg, text(resp))
} catch (e) {
log.console("Warning: Could not check for updates for " + pkg)
} }
}
var target_hash = remote_hash
if (!target_hash) {
log.error("Could not resolve remote commit for " + pkg)
return false
}
if (local_hash && remote_hash == local_hash) {
log.console(alias + " is already up to date.")
return true
}
if (local_hash) {
log.console("Updating " + alias + " " + local_hash.substring(0,8) + " -> " + remote_hash.substring(0,8))
} else { } else {
for (var k in config.dependencies) { log.console("Installing " + alias + "...")
queue.push(config.dependencies[k])
}
} }
while (queue.length > 0) { // Install with fresh download (no zip hash to force redownload)
var pkg = queue.shift() var result = install_from_package(pkg, target_hash, null)
if (processed[pkg]) continue
processed[pkg] = true if (result) {
// Update lock
// Find existing lock info lock[pkg] = {
var lock_info = lock[pkg] package: pkg,
var local_hash = lock_info ? lock_info.commit : null commit: result.commit,
var local_zip_hash = lock_info ? lock_info.zip_hash : null zip_hash: result.zip_hash,
updated: time.number()
var api_url = Shop.get_api_url(pkg)
var remote_hash = null
// Check for updates if possible
if (api_url) {
try {
var resp = http.fetch(api_url)
remote_hash = Shop.extract_commit_hash(pkg, text(resp))
} catch (e) {
log.console("Warning: Could not check for updates for " + pkg)
}
}
var target_hash = remote_hash || local_hash
if (!target_hash) {
log.error("Could not resolve commit for " + pkg)
continue
}
var is_update = remote_hash && local_hash && (remote_hash != local_hash)
if (is_update) {
log.console("Updating " + pkg + " " + local_hash.substring(0,8) + " -> " + remote_hash.substring(0,8))
} else {
log.console("Checking " + pkg + "...")
}
// Install/Verify
// If updating, we pass null as local_zip_hash to force fresh download/check
// If verifying, we pass local_zip_hash
var result = install_from_package(pkg, target_hash, is_update ? null : local_zip_hash)
if (result) {
// Update lock
lock[pkg] = {
package: pkg,
commit: result.commit,
zip_hash: result.zip_hash,
updated: time.number()
}
// Read package config to find dependencies
var parsed = Shop.parse_package(pkg)
var pkg_config = Shop.load_config(parsed.path)
if (pkg_config && pkg_config.dependencies) {
for (var k in pkg_config.dependencies) {
var dep_pkg = pkg_config.dependencies[k]
if (!processed[dep_pkg]) {
queue.push(dep_pkg)
}
}
}
} }
Shop.save_lock(lock)
log.console("Updated " + alias + ".")
return true
} }
Shop.save_lock(lock) log.error("Failed to update " + alias)
log.console("Update complete.") return false
} }
// High-level: Remove a package and clean up // High-level: Remove a package and clean up
@@ -1077,6 +1051,33 @@ Shop.remove = function(alias) {
return true return true
} }
Shop.add_replacement = function(alias, replacement) {
var config = Shop.load_config()
if (!config) config = {}
if (!config.replace) config.replace = {}
config.replace[alias] = replacement
Shop.save_config(config)
log.console("Added replacement: " + alias + " = " + replacement)
return true
}
Shop.remove_replacement = function(alias) {
var config = Shop.load_config()
if (!config || !config.replace || !config.replace[alias]) {
log.error("No replacement found for " + alias)
return false
}
delete config.replace[alias]
Shop.save_config(config)
log.console("Removed replacement for " + alias)
log.console("Run 'cell update " + alias + "' to restore the package.")
return true
}
// Install all dependencies from config (like `bun install`) // Install all dependencies from config (like `bun install`)
Shop.install_all = function() { Shop.install_all = function() {
Shop.init() Shop.init()

View File

@@ -4,14 +4,14 @@ var shop = use('shop')
var alias = args.length > 0 ? args[0] : null var alias = args.length > 0 ? args[0] : null
log.console("Checking for updates...")
shop.update_all(alias)
var packages = shop.list_packages() var packages = shop.list_packages()
log.console(packages) log.console("Checking for updates...")
for (var pack of packages) for (var pack of packages) {
log.console("Updating " + pack)
shop.update(pack)
shop.build_package(pack) shop.build_package(pack)
}
$_.stop() $_.stop()

View File

@@ -221,7 +221,6 @@ static int blob_copy_bits(blob *dest, const void *src, size_t from, size_t to) {
blob *blob_new(size_t capacity) { blob *blob_new(size_t capacity) {
if (capacity < 0) capacity = 0; if (capacity < 0) capacity = 0;
blob *b = calloc(1, sizeof(blob)); blob *b = calloc(1, sizeof(blob));
printf("allocating blob %p size %zu\n", b, capacity);
if (!b) return NULL; if (!b) return NULL;
if (blob_ensure_capacity(b, capacity) < 0) { if (blob_ensure_capacity(b, capacity) < 0) {
free(b); free(b);
@@ -264,7 +263,6 @@ blob *blob_new_with_fill(size_t length_bits, int logical_value) {
void blob_destroy(blob *b) { void blob_destroy(blob *b) {
if (!b) return; if (!b) return;
printf("destroying blob %p, data %p, length %zu, capacity %zu\n", b, b->data, b->length, b->capacity);
if (b->data) { if (b->data) {
free(b->data); free(b->data);
b->data = NULL; b->data = NULL;

View File

@@ -102,7 +102,8 @@ JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
#define QJSCLASS(TYPE, ...)\ #define QJSCLASS(TYPE, ...)\
JSClassID js_##TYPE##_id;\ JSClassID js_##TYPE##_id;\
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\ JSContext *js = JS_GetContext(rt);\
TYPE *n = JS_GetOpaque2(js, val, js_##TYPE##_id); \
TYPE##_free(rt,n);}\ TYPE##_free(rt,n);}\
static JSClassDef js_##TYPE##_class = {\ static JSClassDef js_##TYPE##_class = {\
.class_name = #TYPE,\ .class_name = #TYPE,\

View File

@@ -10,10 +10,7 @@
// Free function for blob // Free function for blob
void blob_free(JSRuntime *rt, blob *b) void blob_free(JSRuntime *rt, blob *b)
{ {
if (b) { // blob_destroy(b);
printf("destroying blob %p blob data %p, length %zu\n", b, b->data, b->length);
blob_destroy(b);
}
} }
// Use QJSCLASS macro to generate class boilerplate // Use QJSCLASS macro to generate class boilerplate
@@ -122,15 +119,7 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
return JS_ThrowOutOfMemory(ctx); return JS_ThrowOutOfMemory(ctx);
} }
JSValue ret = blob2js(ctx, bd); return blob2js(ctx, bd);
printf("created blob %p blob data %p, length %zu\n", bd, bd->data, bd->length);
// Ensure the returned object's prototype is set correctly for instanceof
JSValue ctor_proto = JS_GetPropertyStr(ctx, new_target, "prototype");
if (!JS_IsException(ctor_proto)) {
JS_SetPrototype(ctx, ret, ctor_proto);
}
JS_FreeValue(ctx, ctor_proto);
return ret;
} }
// blob.write_bit(logical) // blob.write_bit(logical)
@@ -323,8 +312,6 @@ static JSValue js_blob_read_blob(JSContext *ctx, JSValueConst this_val,
return JS_ThrowOutOfMemory(ctx); return JS_ThrowOutOfMemory(ctx);
} }
printf("stoned copy blob %p blob data %p, length %" PRId64 "\n", new_bd, new_bd->data, new_bd->length);
return blob2js(ctx, new_bd); return blob2js(ctx, new_bd);
} }
@@ -518,7 +505,6 @@ JSValue js_blob_use(JSContext *js) {
// Explicitly set the prototype property to ensure instanceof works // Explicitly set the prototype property to ensure instanceof works
JS_SetPropertyStr(js, ctor, "__prototype__", JS_DupValue(js, proto)); JS_SetPropertyStr(js, ctor, "__prototype__", JS_DupValue(js, proto));
JS_FreeValue(js, proto); JS_FreeValue(js, proto);
return ctor; return ctor;
} }
@@ -529,8 +515,6 @@ JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes)
b->length = bytes * 8; // Set the actual length in bits b->length = bytes * 8; // Set the actual length in bits
blob_make_stone(b); blob_make_stone(b);
printf("stoned copy blob %p blob data %p, length %" PRId64 "\n", b, b->data, b->length);
return blob2js(js, b); return blob2js(js, b);
} }
@@ -546,8 +530,14 @@ void *js_get_blob_data(JSContext *js, size_t *size, JSValue v)
JS_ThrowReferenceError(js, "attempted to read data from a non-stone blob"); JS_ThrowReferenceError(js, "attempted to read data from a non-stone blob");
return -1; return -1;
} }
if (b->length % 8 != 0) {
JS_ThrowReferenceError(js, "attempted to read data from a non-byte aligned blob");
return -1;
}
*size = (b->length + 7) / 8; // Return actual byte size based on bit length *size = (b->length + 7) / 8; // Return actual byte size based on bit length
return b->data; return b->data;
} }
@@ -556,13 +546,28 @@ void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v)
blob *b = js2blob(js, v); blob *b = js2blob(js, v);
if (!b) { if (!b) {
JS_ThrowReferenceError(js, "get_blob_data_bits: not called on a blob"); JS_ThrowReferenceError(js, "get_blob_data_bits: not called on a blob");
return NULL; return -1;
} }
if (!b->is_stone) { if (!b->is_stone) {
JS_ThrowReferenceError(js, "attempted to read data from a non-stone blob"); JS_ThrowReferenceError(js, "attempted to read data from a non-stone blob");
return NULL; return -1;
} }
if (!b->data) {
JS_ThrowReferenceError(js, "attempted to read data from an empty blob");
return -1;
}
if (b->length % 8 != 0) {
JS_ThrowReferenceError(js, "attempted to read data from a non-byte aligned blob");
return -1;
}
if (b->length == 0) {
JS_ThrowReferenceError(js, "attempted to read data from an empty blob");
return -1;
}
*bits = b->length; *bits = b->length;
return b->data; return b->data;
} }

View File

@@ -240,6 +240,7 @@ struct JSRuntime {
int shape_hash_count; /* number of hashed shapes */ int shape_hash_count; /* number of hashed shapes */
JSShape **shape_hash; JSShape **shape_hash;
void *user_opaque; void *user_opaque;
JSContext *js;
}; };
struct JSClass { struct JSClass {
@@ -1576,6 +1577,7 @@ void JS_FreeRuntime(JSRuntime *rt)
JSContext *JS_NewContextRaw(JSRuntime *rt) JSContext *JS_NewContextRaw(JSRuntime *rt)
{ {
assert(!rt->js);
JSContext *ctx; JSContext *ctx;
int i; int i;
@@ -1599,6 +1601,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
ctx->regexp_ctor = JS_NULL; ctx->regexp_ctor = JS_NULL;
JS_AddIntrinsicBasicObjects(ctx); JS_AddIntrinsicBasicObjects(ctx);
rt->js = ctx;
return ctx; return ctx;
} }
@@ -37481,3 +37484,11 @@ JSValue js_debugger_closure_variables(JSContext *ctx, JSValue fn) {
done: done:
return ret; return ret;
} }
void *js_debugger_val_address(JSContext *ctx, JSValue val) {
return JS_VALUE_GET_PTR(val);
}
JSContext *JS_GetContext(JSRuntime *rt) {
return rt->js;
}

View File

@@ -362,6 +362,7 @@ JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);
JSContext *JS_NewContext(JSRuntime *rt); JSContext *JS_NewContext(JSRuntime *rt);
void JS_FreeContext(JSContext *s); void JS_FreeContext(JSContext *s);
JSContext *JS_DupContext(JSContext *ctx); JSContext *JS_DupContext(JSContext *ctx);
JSContext *JS_GetContext(JSRuntime *rt);
void *JS_GetContextOpaque(JSContext *ctx); void *JS_GetContextOpaque(JSContext *ctx);
void JS_SetContextOpaque(JSContext *ctx, void *opaque); void JS_SetContextOpaque(JSContext *ctx, void *opaque);
JSRuntime *JS_GetRuntime(JSContext *ctx); JSRuntime *JS_GetRuntime(JSContext *ctx);
@@ -990,6 +991,7 @@ JSValue js_debugger_local_variables(JSContext *ctx, int stack_index);
JSValue js_debugger_build_backtrace(JSContext *ctx, const uint8_t *cur_pc); JSValue js_debugger_build_backtrace(JSContext *ctx, const uint8_t *cur_pc);
JSValue js_debugger_fn_info(JSContext *ctx, JSValue fn); JSValue js_debugger_fn_info(JSContext *ctx, JSValue fn);
JSValue js_debugger_fn_bytecode(JSContext *js, JSValue fn); JSValue js_debugger_fn_bytecode(JSContext *js, JSValue fn);
void *js_debugger_val_address(JSContext *js, JSValue val);
#undef js_unlikely #undef js_unlikely
#undef js_force_inline #undef js_force_inline