more native without fallback

This commit is contained in:
2026-02-23 10:20:18 -06:00
parent a18584afd3
commit 94c28f0e17
12 changed files with 422 additions and 342 deletions

View File

@@ -523,6 +523,11 @@ function try_native_mod_dylib(pkg, stem) {
var handle = open_dylib_cached(build_path)
if (!handle) return null
var sym = Shop.c_symbol_for_file(pkg, stem)
// Verify the symbol actually exists in the dylib before returning native descriptor
if (sym && !os.dylib_has_symbol(handle, sym) && !os.dylib_has_symbol(handle, 'cell_main')) {
log.shop('native dylib for ' + stem + ' missing symbol ' + sym + ' and cell_main, falling back to bytecode')
return null
}
return {_native: true, _handle: handle, _sym: sym}
}
@@ -774,8 +779,8 @@ function resolve_mod_fn(path, pkg) {
}
}
// Check for native .cm dylib at deterministic path first
if (policy.allow_dylib && pkg && _stem) {
// Check for native .cm dylib at deterministic path first (only in native mode)
if (policy.native && policy.allow_dylib && pkg && _stem) {
native_result = try_native_mod_dylib(pkg, _stem)
if (native_result != null) return native_result
}
@@ -846,6 +851,50 @@ function resolve_mod_fn(path, pkg) {
disrupt
}
// Resolve a module's bytecode only (skip native dylib check).
// Used as fallback when a cached native dylib fails to load.
function resolve_mod_fn_bytecode(path, pkg) {
if (!fd.is_file(path)) return null
var content = text(fd.slurp(path))
if (length(content) == 0) return null
var content_key = stone(blob(content))
var cached = null
var cached_mcode_path = null
var mcode_json = null
var compiled = null
// Check cache for pre-compiled .mach blob
cached = pull_from_cache(content_key)
if (cached) return cached
// Check for cached mcode
cached_mcode_path = hash_path(content_key, 'mcode')
if (fd.is_file(cached_mcode_path)) {
mcode_json = text(fd.slurp(cached_mcode_path))
compiled = mach_compile_mcode_bin(path, mcode_json)
put_into_cache(content_key, compiled)
return compiled
}
// Compile from source
if (!_mcode_mod) _mcode_mod = use_cache['core/mcode'] || use_cache['mcode']
if (!_streamline_mod) _streamline_mod = use_cache['core/streamline'] || use_cache['streamline']
if (!_mcode_mod || !_streamline_mod) return null
var ast = analyze(content, path)
var ir = _mcode_mod(ast)
var optimized = _streamline_mod(ir)
mcode_json = shop_json.encode(optimized)
fd.ensure_dir(global_shop_path + '/build')
fd.slurpwrite(hash_path(content_key, 'mcode'), stone(blob(mcode_json)))
compiled = mach_compile_mcode_bin(path, mcode_json)
put_into_cache(content_key, compiled)
return compiled
}
// given a path and a package context
// return module info about where it was found
// Resolve a module path to {path, scope, pkg} without compiling.
@@ -1282,7 +1331,6 @@ Shop.is_loaded = function is_loaded(path, package_context) {
// Create a use function bound to a specific package context
function make_use_fn(pkg, force_native) {
return function(path) {
if (path == 'prosperon/compositor') log.shop('DEBUG make_use_fn: pkg=' + (is_text(pkg) ? pkg : '(non-text)') + ' force_native=' + (force_native ? 'true' : 'false'))
var _native = null
if (force_native && !native_mode) {
_native = function() {
@@ -1314,6 +1362,8 @@ function execute_module(info)
var inject = null
var env = null
var pkg = null
var _native_load = null
var _bc = null
if (mod_resolve.scope < 900) {
// Check if native dylib was resolved (descriptor with _handle and _sym)
@@ -1324,9 +1374,27 @@ function execute_module(info)
pkg = file_info.package
env.use = make_use_fn(pkg, true)
env = stone(env)
used = os.native_module_load_named(
mod_resolve.symbol._handle, mod_resolve.symbol._sym, env)
log.shop('loaded ' + info.cache_key + ' [native]')
_native_load = function() {
used = os.native_module_load_named(
mod_resolve.symbol._handle, mod_resolve.symbol._sym, env)
log.shop('loaded ' + info.cache_key + ' [native]')
} disruption {
// Native load failed — fall back to bytecode
log.shop('native load failed for ' + info.cache_key + ' (sym=' + text(mod_resolve.symbol._sym || '') + '), falling back to bytecode')
_bc = resolve_mod_fn_bytecode(mod_resolve.path, file_info.package)
if (_bc) {
// Build a fresh env for bytecode (env is stoned, can't modify)
env = inject_env(inject)
env.use = make_use_fn(pkg)
env = stone(env)
used = mach_load(_bc, env)
log.shop('loaded ' + info.cache_key + ' [bytecode fallback]')
} else {
log.error('native load failed and bytecode fallback also failed for ' + info.cache_key)
disrupt
}
}
_native_load()
} else {
// Build env with runtime fns, capabilities, and use function
file_info = Shop.file_info(mod_resolve.path)
@@ -1347,7 +1415,7 @@ function execute_module(info)
log.shop(`Module could not be found (c_resolve scope=${info.c_resolve.scope}, mod_resolve scope=${info.mod_resolve.scope}, cache_key=${info.cache_key})`); disrupt
}
if (!used) { log.error(`Module ${info} returned null`); disrupt }
if (!used) { log.error('Module ' + text(info.cache_key || info) + ' returned null'); disrupt }
return used
}
@@ -1378,11 +1446,6 @@ Shop.use = function use(path, _pkg_ctx) {
log.error("use() expects a text module path, but received a non-text value")
disrupt
}
if (path == 'prosperon/compositor') {
if (is_text(_pkg_ctx)) log.shop('DEBUG use(): _pkg_ctx=' + _pkg_ctx)
else if (_pkg_ctx == null) log.shop('DEBUG use(): _pkg_ctx=NULL')
else log.shop('DEBUG use(): _pkg_ctx is non-text non-null')
}
var package_context = is_core_dir(_pkg_ctx) ? 'core' : safe_canonicalize(_pkg_ctx)
// Check for embedded module (static builds)
var embed_key = 'embedded:' + path
@@ -2288,7 +2351,7 @@ Shop.use_native = function(path, package_context) {
if (!starts_with(path, '/') && !fd.is_file(path)) {
lookup = ends_with(path, '.cm') ? path : path + '.cm'
locator = resolve_locator(lookup, package_context)
if (!locator) { print('Module not found: ' + path); disrupt }
if (!locator) { log.error('use_native: module not found: ' + path + ' (package: ' + text(package_context || '') + ')'); disrupt }
src_path = locator.path
} else if (!starts_with(path, '/')) {
src_path = fd.realpath(path)
@@ -2304,7 +2367,7 @@ Shop.use_native = function(path, package_context) {
var sym_name = null
if (pkg) {
pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
pkg_dir = get_packages_dir() + '/' + fd.safe_package_path(pkg)
if (starts_with(src_path, pkg_dir + '/')) {
sym_stem = text(src_path, length(pkg_dir) + 1)
}