diff --git a/.cell/cell.toml b/.cell/cell.toml index 5fe113a2..e948c577 100644 --- a/.cell/cell.toml +++ b/.cell/cell.toml @@ -1,6 +1,6 @@ sdl_video = "main" [dependencies] -extramath = "https://gitea.pockle.world/john/extramath@master" +extramath = "gitea.pockle.world/john/extramath@master" [system] ar_timer = 60 actor_memory = 0 @@ -16,4 +16,4 @@ main = true [actors.prosperon] main = true [actors.accio] -main=true \ No newline at end of file +main = true \ No newline at end of file diff --git a/Makefile b/Makefile index 7e8f815c..281e56e0 100755 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ release: FORCE meson install -C build_release 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 thread: FORCE diff --git a/scripts/fd.c b/scripts/fd.c index 9c59d176..6f38c583 100644 --- a/scripts/fd.c +++ b/scripts/fd.c @@ -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 static int remove_recursive(const char *path) { struct stat st; +#ifdef _WIN32 if (stat(path, &st) != 0) +#else + if (lstat(path, &st) != 0) +#endif return -1; 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_dir, 1), MIST_FUNC_DEF(fd, enumerate, 2), + MIST_FUNC_DEF(fd, symlink, 2), }; JSValue js_fd_use(JSContext *js) { diff --git a/scripts/os.c b/scripts/os.c index 940fef8b..10a45ddd 100644 --- a/scripts/os.c +++ b/scripts/os.c @@ -383,6 +383,7 @@ static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue error_msg = dl_error; } #endif + return JS_ThrowReferenceError(js, "Failed to load symbol: %s", error_msg); return JS_NULL; } @@ -390,6 +391,34 @@ static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue 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, size_t len; 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); } +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) // ------- Windows: use BCryptGenRandom ------- 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, dylib_open, 1), 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, internal_exists, 1), MIST_FUNC_DEF(os, print, 1), MIST_FUNC_DEF(os, random, 0), }; diff --git a/scripts/replace.ce b/scripts/replace.ce index 0408f680..96330f3f 100644 --- a/scripts/replace.ce +++ b/scripts/replace.ce @@ -1,49 +1,29 @@ -// cell replace - 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') if (args.length < 2) { - log.console("Usage: cell replace ") - log.console("Examples:") - log.console(" cell replace prosperon ../prosperon") - log.console(" cell replace extramath ../my-fork-of-extramath") - $_.stop() - return + log.console("Usage: cell replace ") + log.console(" cell replace --remove") + $_.stop() } -var alias = args[0] +var pkg = args[0] var path = args[1] -// Initialize shop if needed -if (!fd.stat('.cell/cell.toml').isFile) { - log.console("No cell.toml found. Initializing...") - shop.init() +if (path == '--remove') { + if (shop.remove_replacement(pkg)) { + log.console("Replacement removed.") + } 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 -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() +$_.stop() \ No newline at end of file diff --git a/scripts/shop.cm b/scripts/shop.cm index e2587e4c..7be64e92 100644 --- a/scripts/shop.cm +++ b/scripts/shop.cm @@ -348,11 +348,6 @@ Shop.get_module_dir = function(alias) { return null } - // Check if replaced - if (config.replace && config.replace[alias]) { - return config.replace[alias] - } - var pkg = config.dependencies[alias] var parsed = Shop.parse_package(pkg) if (!parsed) return null @@ -568,19 +563,19 @@ function resolve_locator(path, ext, ctx) 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 - if (!package_ctx) { + if (!package_context) { local = `js_local_${local_sym_base}_use` } else { 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 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); if (open_dls[local_dl_name]) { - var local_addr = os.dylib_symbol(open_dls[local_dl_name], local); - if (local_addr) return {symbol: local_addr, scope: SCOPE_LOCAL}; + if (os.dylib_has_symbol(open_dls[local_dl_name], local)) + return { + symbol: function() { return os.dylib_symbol(open_dls[local_dl_name], local); }, + scope: SCOPE_LOCAL + }; } } // Try static linking fallback - var static_local_addr = os.load_internal(local); - if (static_local_addr) return {symbol: static_local_addr, scope: SCOPE_LOCAL}; + if (os.internal_exists(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 var pkg_alias = get_import_package(path) if (pkg_alias) { - var canon_pkg = get_normalized_package(path, package_ctx) + var canon_pkg = get_normalized_package(path, package_context) if (canon_pkg) { var build_dir = get_build_dir(canon_pkg) 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 (!open_dls[dl_path]) open_dls[dl_path] = os.dylib_open(dl_path) if (open_dls[dl_path]) { - var addr = os.dylib_symbol(open_dls[dl_path], sym_name) - if (addr) return {symbol: addr, scope: SCOPE_PACKAGE, package: canon_pkg} + if (os.dylib_has_symbol(open_dls[dl_path], sym_name)) + 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 (static_package_addr) - return {symbol:static_package_addr, scope: SCOPE_PACKAGE, package: canon_pkg}; + if (os.internal_exists(sym_name)) + return { + 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`); - if (core_addr) - return {symbol:core_addr, scope: SCOPE_CORE}; + var core_sym = `js_${path.replace(/\//g, '_')}_use`; + if (os.internal_exists(core_sym)) + return { + symbol: function() { return os.load_internal(core_sym); }, + scope: SCOPE_CORE + }; return null } @@ -650,11 +661,11 @@ Shop.use = function(path, package_context) { return use_cache[cache_key] 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) use_cache[cache_key] = mod_resolve.symbol.call() 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] } @@ -671,23 +682,11 @@ function get_cache_path(pkg, commit) { } function rm_recursive(path) { - var st = fd.stat(path) - if (!st) return - - if (!st.isDirectory) { - fd.unlink(path) - return + try { + fd.rm(path) + } catch (e) { + log.error("Failed to remove " + path + ": " + e) } - - 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) { @@ -745,9 +744,8 @@ function verify_zip_contents(zip, target_dir) { // Check for extra files 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 - } return true } @@ -944,100 +942,76 @@ Shop.get = function(pkg, alias) { return true } -// High-level: Update a specific package or all packages -// Like `bun update` or `bun update ` -Shop.update_all = function(alias) { +Shop.fetch = function(package) +{ + +} + +// High-level: Update a specific package +// Like `bun update ` +Shop.update = function(pkg) { var config = Shop.load_config() - if (!config || !config.dependencies) { - log.console("No dependencies to update.") - return - } - var lock = Shop.load_lock() - var queue = [] - var processed = {} - // Initialize queue - if (alias) { - if (config.dependencies[alias]) { - queue.push(config.dependencies[alias]) - } else { - log.error("Dependency not found: " + alias) - return + // Check if replaced + if (config.replace && config.replace[pkg]) { + log.console("Skipping update for replaced package " + pkg) + Shop.update(config.replace[pkg]) + return false + } + + // 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 { - for (var k in config.dependencies) { - queue.push(config.dependencies[k]) - } + log.console("Installing " + alias + "...") } - while (queue.length > 0) { - var pkg = queue.shift() - if (processed[pkg]) continue - processed[pkg] = true - - // Find existing lock info - var lock_info = lock[pkg] - var local_hash = lock_info ? lock_info.commit : null - var local_zip_hash = lock_info ? lock_info.zip_hash : 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 || 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) - } - } - } + // Install with fresh download (no zip hash to force redownload) + var result = install_from_package(pkg, target_hash, null) + + if (result) { + // Update lock + lock[pkg] = { + package: pkg, + commit: result.commit, + zip_hash: result.zip_hash, + updated: time.number() } + Shop.save_lock(lock) + log.console("Updated " + alias + ".") + return true } - Shop.save_lock(lock) - log.console("Update complete.") + log.error("Failed to update " + alias) + return false } // High-level: Remove a package and clean up @@ -1077,6 +1051,33 @@ Shop.remove = function(alias) { 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`) Shop.install_all = function() { Shop.init() diff --git a/scripts/update.ce b/scripts/update.ce index cbb4d4b6..f71ec2d8 100644 --- a/scripts/update.ce +++ b/scripts/update.ce @@ -4,14 +4,14 @@ var shop = use('shop') var alias = args.length > 0 ? args[0] : null -log.console("Checking for updates...") -shop.update_all(alias) - 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) +} $_.stop() \ No newline at end of file diff --git a/source/blob.h b/source/blob.h index 964e5e32..4fa72936 100644 --- a/source/blob.h +++ b/source/blob.h @@ -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) { if (capacity < 0) capacity = 0; blob *b = calloc(1, sizeof(blob)); - printf("allocating blob %p size %zu\n", b, capacity); if (!b) return NULL; if (blob_ensure_capacity(b, capacity) < 0) { free(b); @@ -264,7 +263,6 @@ blob *blob_new_with_fill(size_t length_bits, int logical_value) { void blob_destroy(blob *b) { if (!b) return; - printf("destroying blob %p, data %p, length %zu, capacity %zu\n", b, b->data, b->length, b->capacity); if (b->data) { free(b->data); b->data = NULL; diff --git a/source/cell.h b/source/cell.h index d4122148..97218234 100644 --- a/source/cell.h +++ b/source/cell.h @@ -102,7 +102,8 @@ JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \ #define QJSCLASS(TYPE, ...)\ JSClassID js_##TYPE##_id;\ 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);}\ static JSClassDef js_##TYPE##_class = {\ .class_name = #TYPE,\ diff --git a/source/qjs_blob.c b/source/qjs_blob.c index f8535527..8d5d5aa8 100644 --- a/source/qjs_blob.c +++ b/source/qjs_blob.c @@ -10,10 +10,7 @@ // Free function for blob void blob_free(JSRuntime *rt, blob *b) { - if (b) { - printf("destroying blob %p blob data %p, length %zu\n", b, b->data, b->length); - blob_destroy(b); - } +// blob_destroy(b); } // 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); } - JSValue ret = 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; + return blob2js(ctx, bd); } // blob.write_bit(logical) @@ -323,8 +312,6 @@ static JSValue js_blob_read_blob(JSContext *ctx, JSValueConst this_val, 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); } @@ -518,7 +505,6 @@ JSValue js_blob_use(JSContext *js) { // Explicitly set the prototype property to ensure instanceof works JS_SetPropertyStr(js, ctor, "__prototype__", JS_DupValue(js, proto)); JS_FreeValue(js, proto); - 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 blob_make_stone(b); - printf("stoned copy blob %p blob data %p, length %" PRId64 "\n", b, b->data, b->length); - 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"); 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 + 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); if (!b) { JS_ThrowReferenceError(js, "get_blob_data_bits: not called on a blob"); - return NULL; + return -1; } if (!b->is_stone) { 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; return b->data; } diff --git a/source/quickjs.c b/source/quickjs.c index 9b4eae14..f061afdf 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -240,6 +240,7 @@ struct JSRuntime { int shape_hash_count; /* number of hashed shapes */ JSShape **shape_hash; void *user_opaque; + JSContext *js; }; struct JSClass { @@ -1576,6 +1577,7 @@ void JS_FreeRuntime(JSRuntime *rt) JSContext *JS_NewContextRaw(JSRuntime *rt) { + assert(!rt->js); JSContext *ctx; int i; @@ -1599,6 +1601,7 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) ctx->regexp_ctor = JS_NULL; JS_AddIntrinsicBasicObjects(ctx); + rt->js = ctx; return ctx; } @@ -37481,3 +37484,11 @@ JSValue js_debugger_closure_variables(JSContext *ctx, JSValue fn) { done: 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; +} \ No newline at end of file diff --git a/source/quickjs.h b/source/quickjs.h index 6bba1f31..e080fea1 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -362,6 +362,7 @@ JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); JSContext *JS_NewContext(JSRuntime *rt); void JS_FreeContext(JSContext *s); JSContext *JS_DupContext(JSContext *ctx); +JSContext *JS_GetContext(JSRuntime *rt); void *JS_GetContextOpaque(JSContext *ctx); void JS_SetContextOpaque(JSContext *ctx, void *opaque); 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_fn_info(JSContext *ctx, 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_force_inline