reduce shop

This commit is contained in:
2025-12-08 19:50:09 -06:00
parent 38d6b4d4e8
commit 5c5427fdd9
9 changed files with 229 additions and 297 deletions

View File

@@ -35,6 +35,7 @@ all source for every single part of a cell program are located in the cell shop.
the cell shop looks like this: the cell shop looks like this:
.cell .cell
shop.toml <---- shop configuration
packages packages
gitea.pockle.world/john/cell <--- this is the root cell gitea.pockle.world/john/cell <--- this is the root cell
gitea.pockle.world/john/prosperon gitea.pockle.world/john/prosperon
@@ -62,3 +63,35 @@ When a module <name/mod> is requested, from a within package <a> ..
1) cell looks within package <a> for a folder <name> and loads <name/mod>, if present 1) cell looks within package <a> for a folder <name> and loads <name/mod>, if present
2) cell looks for a package <name> with a top level <mod> 2) cell looks for a package <name> with a top level <mod>
3) cell looks in the core for <name/mod> 3) cell looks in the core for <name/mod>
The core can be set and have something else linked into it, if you want to break your cell build
## cell build
cell build compiles all c into the appropriate binaries. All object files are stored in the .cell/cache
there are two ways to build a cell program: as a shared library, or as a static binary.
Cell script files are simply compiled into built objects
Then, all C files in the package are compiled.
If you have a file like fd.c and fd_playdate.c, that is a signal to cell to compile fd.c usually, but then for the playdate target, compile fd_playdate.c instead.
files name "main.c" are not compiled.
each cell.toml in a package
### shared library
this is more suitable for development. firstly, the cell core must build a shared library for the platform.
Then, each package compiles, linking to the cell core shared library. Modules are written to include the cell core headers.
Shared libraries are stored
### bootstrapping cell
after the cell shared libraries are all compiled, the main.c of the cell core is compiled against the cell core shared library, creating a thin cell runner. However, even on subsequent updates to cell, this runner is not updated; only for bootstrapping.
### static binary
For static binaries, rather than going through and compiling each dynamic library for a package, a static binary is created for a particular package, based on its dependencies. All C symbols, plus the main.c runner from the cell core, are packaged into one binary. It works just like the basic cell program.
However, you can also specify an entry point for this binary, for example, a particular actor. When the binary is run, that particular actor runs.

View File

@@ -9,6 +9,8 @@ cell.os = null
var dylib_ext var dylib_ext
cell.id ??= "newguy"
switch(os.platform()) { switch(os.platform()) {
case 'Windows': dylib_ext = '.dll'; break; case 'Windows': dylib_ext = '.dll'; break;
case 'macOS': dylib_ext = '.dylib'; break; case 'macOS': dylib_ext = '.dylib'; break;
@@ -55,19 +57,15 @@ function use_core(path) {
if (use_cache[cache_path]) if (use_cache[cache_path])
return use_cache[cache_path]; return use_cache[cache_path];
var sym = use_embed(path) var sym = use_embed(path.replace('/','_'))
// Core scripts are now in .cell/core/scripts
var file_path = core_path + '/scripts/' + path + MOD_EXT
// Fallback check for root (optional, for backward compat if needed, but not strictly necessary if we are consistent) // Core scripts are now in .cell/core/scripts
if (!fd.is_file(file_path)) { var file_path = core_path + '/' + path + MOD_EXT
file_path = core_path + '/' + path + MOD_EXT
}
if (fd.is_file(file_path)) { if (fd.is_file(file_path)) {
var script_blob = fd.slurp(file_path) var script_blob = fd.slurp(file_path)
var script = utf8.decode(script_blob) var script = utf8.decode(script_blob)
var mod = `(function setup_${path}_module($_){${script}})` var mod = `(function setup_module($_){${script}})`
var fn = js.eval(path, mod) var fn = js.eval(path, mod)
var result = fn.call(sym); var result = fn.call(sym);
use_cache[cache_path] = result; use_cache[cache_path] = result;
@@ -114,7 +112,7 @@ stone.p = function(object)
var actor_mod = use('actor') var actor_mod = use('actor')
var wota = use('wota') var wota = use('wota')
var nota = use('nota') var nota = use('nota')
globalThis.text = use('text') globalThis.text = use('internal/text')
var ENETSERVICE = 0.1 var ENETSERVICE = 0.1
var REPLYTIMEOUT = 60 // seconds before replies are ignored var REPLYTIMEOUT = 60 // seconds before replies are ignored
@@ -231,7 +229,6 @@ function get_package_from_path(path) {
globalThis.json = use('json') globalThis.json = use('json')
var time = use('time') var time = use('time')
var st_now = time.number()
var default_config = { var default_config = {
ar_timer: 60, ar_timer: 60,
@@ -653,16 +650,18 @@ if (!program) {
// Find the package containing the program // Find the package containing the program
// The program path is resolved relative to current directory // The program path is resolved relative to current directory
var package_dir = shop.set_current_package(program) // Find the package containing the program
if (package_dir) { // The program path is resolved relative to current directory
// Reload config from the package // var package_dir = shop.set_current_package(program)
config = shop.load_config() // if (package_dir) {
if (config) { // // Reload config from the package
config.system = config.system || {} // config = shop.load_config()
config.system.__proto__ = default_config // if (config) {
cell.config = config // config.system = config.system || {}
} // config.system.__proto__ = default_config
} // cell.config = config
// }
// }
function handle_actor_disconnect(id) { function handle_actor_disconnect(id) {
var greeter = greeters[id] var greeter = greeters[id]

View File

@@ -298,7 +298,7 @@ static const JSCFunctionListEntry js_text_funcs[] = {
MIST_FUNC_DEF(text, base64url_to_blob, 1), MIST_FUNC_DEF(text, base64url_to_blob, 1),
}; };
JSValue js_text_use(JSContext *js) JSValue js_internal_text_use(JSContext *js)
{ {
JSValue mod = JS_NewObject(js); JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs)); JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs));

View File

@@ -52,34 +52,33 @@ src += [ # core
src += ['scheduler.c'] src += ['scheduler.c']
scripts = [ scripts = [
'nota.c', 'internal/nota.c',
'js.c', 'debug/js.c',
'qop.c', 'qop.c',
'wildstar.c', 'wildstar.c',
'fit.c', 'fit.c',
'crypto.c', 'crypto.c',
'text.c', 'internal/text.c',
'utf8.c', 'utf8.c',
'kim.c', 'internal/kim.c',
'time.c', 'time.c',
'nota.c', 'internal/nota.c',
'debug.c', 'debug/debug.c',
'os.c', 'internal/os.c',
'fd.c', 'fd.c',
'http.c', 'net/http.c',
'enet.c', 'net/enet.c',
'wildstar.c', 'wildstar.c',
'miniz.c', 'archive/miniz.c',
'json.c' 'internal/json.c'
] ]
foreach file: scripts foreach file: scripts
full_path = join_paths('scripts', file) sources += files(file)
sources += files(full_path)
endforeach endforeach
srceng = 'source' srceng = 'source'
includes = [srceng] includes = [srceng, 'internal', 'debug', 'net', 'archive']
foreach file : src foreach file : src
full_path = join_paths(srceng, file) full_path = join_paths(srceng, file)
@@ -99,7 +98,6 @@ else
link += '-Wl,-export_dynamic' link += '-Wl,-export_dynamic'
endif endif
cell_so = shared_library( cell_so = shared_library(
'cell_runtime', 'cell_runtime',
sources, sources,
@@ -108,14 +106,6 @@ cell_so = shared_library(
install : true, install : true,
) )
# Create core.zip from scripts folder
qop_target = custom_target('core.qop',
output: 'core.qop',
command: ['sh', '-c', ' qopconv -d ' + meson.project_source_root() / 'scripts . core.qop'],
build_by_default: true,
build_always_stale: true
)
# Determine RPATH based on OS # Determine RPATH based on OS
# MacOS uses @loader_path, Linux uses $ORIGIN # MacOS uses @loader_path, Linux uses $ORIGIN
# We include both the current directory (for build) and ../lib (for install) # We include both the current directory (for build) and ../lib (for install)
@@ -127,28 +117,14 @@ elif host_machine.system() == 'linux'
endif endif
# Create main executable linked against the cell library # Create main executable linked against the cell library
cell_exe = executable('cell_exe', cell_exe = executable('cell',
'source/main.c', 'source/main.c',
dependencies: deps, dependencies: deps,
include_directories: includers, include_directories: includers,
link_with: cell_so, link_with: cell_so,
link_args: link, link_args: link,
build_rpath: ':'.join(rpath_dirs), build_rpath: ':'.join(rpath_dirs),
install: false install: true
)
# Create final cell executable by appending core.zip to cell_exe
cell = custom_target('cell',
input: [cell_exe, qop_target],
output: 'cell' + exe_ext,
command: [
'sh', '-c',
'cp "$1" "$3" && cat "$2" >> "$3" && chmod +x "$3"',
'cell-cat', '@INPUT0@', '@INPUT1@', '@OUTPUT@'
],
build_by_default: true,
install: true,
install_dir: get_option('bindir')
) )
# Install headers for building dynamic libraries using Cell # Install headers for building dynamic libraries using Cell

326
shop.cm
View File

@@ -10,13 +10,13 @@ var utf8 = use('utf8')
var blob = use('blob') var blob = use('blob')
var build_utils = use('build') var build_utils = use('build')
var core = "gitea.pockle.world/john/cell"
function content_hash(content) function content_hash(content)
{ {
return text(crypto.blake2(utf8.encode(content)), 'h') return text(crypto.blake2(utf8.encode(content)), 'h')
} }
log.console(content_hash("hello"))
// a package string is what is used to import a module, like prosperon/sprite // a package string is what is used to import a module, like prosperon/sprite
// in prosperon/sprite, sprite is the module, and prosperon is the package (usually, an alias) // in prosperon/sprite, sprite is the module, and prosperon is the package (usually, an alias)
// a canonical package name relates prosperon to its source, like gitea.pockle.world/john/prosperon // a canonical package name relates prosperon to its source, like gitea.pockle.world/john/prosperon
@@ -27,9 +27,6 @@ var Shop = {}
// Located at ~/.cell on the user's machine // Located at ~/.cell on the user's machine
var global_shop_path = null var global_shop_path = null
// Current package context - the package we're running from (resolved from program path)
var current_package_path = null
var SCOPE_LOCAL = 0 var SCOPE_LOCAL = 0
var SCOPE_PACKAGE = 1 var SCOPE_PACKAGE = 1
var SCOPE_CORE = 2 var SCOPE_CORE = 2
@@ -165,21 +162,7 @@ function update_local_lock(abs_path) {
Shop.ensure_package_link = ensure_package_link Shop.ensure_package_link = ensure_package_link
// Set the current package context from a program path // Set the current package context from a program path
Shop.set_current_package = function(program_path) {
current_package_path = Shop.find_package_dir(program_path)
if (current_package_path && current_package_path.startsWith('/')) {
// It's a local package, ensure it is linked in the shop
ensure_package_link(current_package_path)
}
return current_package_path
}
// Get the current package path
Shop.get_current_package = function() {
return current_package_path
}
Shop.set_os = function(o, $guy) Shop.set_os = function(o, $guy)
{ {
@@ -203,9 +186,6 @@ function get_config_path(package_path) {
if (package_path) { if (package_path) {
return package_path + '/cell.toml' return package_path + '/cell.toml'
} }
if (current_package_path) {
return current_package_path + '/cell.toml'
}
// Fallback to current directory // Fallback to current directory
return 'cell.toml' return 'cell.toml'
} }
@@ -351,6 +331,10 @@ function get_path_in_package(path, ctx)
return canon_pkg + "/" + mod_name return canon_pkg + "/" + mod_name
} }
// Given a path like 'prosperon/sprite', extract the package alias ('prosperon')
// and resolve it to its canonical path using the dependencies in ctx's config.
// Returns the canonical package path (e.g., 'gitea.pockle.world/john/prosperon')
// or null if the path has no package prefix or the package is not found.
function get_normalized_package(path, ctx) function get_normalized_package(path, ctx)
{ {
var pkg = get_import_package(path) var pkg = get_import_package(path)
@@ -773,35 +757,13 @@ Shop.check_cache = function(pkg) {
var open_dls = {} var open_dls = {}
// for script forms, path is the canonical path of the module // for script forms, path is the canonical path of the module
var script_forms = [] var script_form = function(path, script, pkg) {
// Construct a descriptive compile name with extension preserved.
// Formats:
// core:<module path>
// <package>:<module path> (package is the full canonical package name)
// local:<module path>
// package:<module path> (fallback when pkg isn't provided but path is under modules)
function make_compile_name(path, rel_path, pkg, scope) {
if (scope == SCOPE_CORE) return 'core:' + rel_path
if (pkg) return pkg + ':' + rel_path
var modules_dir = get_modules_dir()
if (path && path.startsWith(modules_dir + '/')) return 'package:' + rel_path
return 'local:' + rel_path
}
script_forms['.cm'] = function(path, script, pkg) {
var pkg_arg = pkg ? `'${pkg}'` : 'null' var pkg_arg = pkg ? `'${pkg}'` : 'null'
var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}` var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}`
var fn = `(function setup_module($_){ ${relative_use_fn}; ${script}})` var fn = `(function setup_module($_){ ${relative_use_fn}; ${script}})`
return fn return fn
} }
script_forms['.ce'] = function(path, script, pkg) {
var pkg_arg = pkg ? `'${pkg}'` : 'null'
var relative_use_fn = `def use = function(path) { return globalThis.use(path, ${pkg_arg});}`
return `(function start($_, arg) { ${relative_use_fn}; var args = arg; ${script} ; })`
}
// Get flags from config // Get flags from config
function get_flags(config, platform, key) { function get_flags(config, platform, key) {
var flags = '' var flags = ''
@@ -817,136 +779,81 @@ function get_flags(config, platform, key) {
Shop.get_flags = get_flags Shop.get_flags = get_flags
function get_build_dir(pkg) {
if (!pkg) pkg = current_package_path
if (!pkg) return get_global_build_dir() + '/local' // Fallback for non-package scripts
// If pkg is absolute path, mirror it
if (pkg.startsWith('/')) {
// Accio folder should be .cell/packages/Users/...
// But build should be .cell/build/packages/Users/...
return get_global_build_dir() + '/packages' + pkg
}
// Otherwise it's a relative package path (from packages dir) // Resolve module function
return get_global_build_dir() + '/packages/' + pkg function resolve_mod_fn(path, pkg) {
}
Shop.get_build_dir = get_build_dir
Shop.get_global_build_dir = get_global_build_dir
function get_rel_path(path, pkg) {
if (!pkg) {
// For local files, strip the current package path prefix if present
if (current_package_path && path.startsWith(current_package_path + '/')) {
return path.substring(current_package_path.length + 1)
}
return path
}
var prefix
if (pkg.startsWith('/')) {
prefix = pkg + '/'
} else {
prefix = get_modules_dir() + '/' + pkg + '/'
}
if (path.startsWith(prefix)) {
return path.substring(prefix.length)
}
return path
}
function resolve_mod_fn(path, pkg)
{
if (!fd.is_file(path)) throw new Error(`path ${path} is not a file`) if (!fd.is_file(path)) throw new Error(`path ${path} is not a file`)
var rel_path = get_rel_path(path, pkg)
// Determine build directory based on context var content = fd.slurp(path)
var build_dir var hash = content_hash(content)
if (pkg) { var hash_path = get_global_build_dir() + '/' + hash
build_dir = get_build_dir(pkg)
} else if (current_package_path) {
// Local file in current package - use package path (strip leading /)
var pkg_id = current_package_path.substring(1) // Remove leading /
build_dir = get_global_build_dir() + '/' + pkg_id
} else {
build_dir = get_build_dir('local')
}
var cache_path = build_dir + '/' + rel_path + '.o' if (fd.is_file(hash_path)) {
var obj = fd.slurp(hash_path)
if (fd.is_file(cache_path) && fd.stat(path).mtime <= fd.stat(cache_path).mtime) {
var obj = fd.slurp(cache_path)
var fn = js.compile_unblob(obj) var fn = js.compile_unblob(obj)
return js.eval_compile(fn) return js.eval_compile(fn)
} }
var ext = path.substring(path.lastIndexOf('.')) var form = script_form
var script_form = script_forms[ext]
if (!script_form) throw new Error(`No script form for extension ${ext}`)
var compile_name = make_compile_name(path, rel_path, pkg, pkg ? SCOPE_PACKAGE : SCOPE_LOCAL) // We don't really use pkg scope for compilation anymore since it's just hash based cache
// But we need to pass a package context for the 'use' function inside the module
var script = form(path, text(content), pkg);
// Compile name is just for debug/stack traces
var compile_name = pkg ? pkg + ':' + path : 'local:' + path
var script = script_form(path, text(fd.slurp(path)), pkg)
var fn = js.compile(compile_name, script) var fn = js.compile(compile_name, script)
ensure_dir(cache_path.substring(0, cache_path.lastIndexOf('/')))
fd.slurpwrite(cache_path, js.compile_blob(fn)) // Ensure build dir exists
ensure_dir(get_global_build_dir())
fd.slurpwrite(hash_path, js.compile_blob(fn))
return js.eval_compile(fn) return js.eval_compile(fn)
} }
// Resolve and cache a core module // resolve_core_mod_fn is no longer needed as core modules are just modules in a package (or local)
function resolve_core_mod_fn(core_path, rel_path) {
var build_dir = get_global_build_dir() + '/core'
var cache_path = build_dir + '/' + rel_path + '.o'
if (fd.is_file(cache_path) && fd.stat(core_path).mtime <= fd.stat(cache_path).mtime) {
var obj = fd.slurp(cache_path)
var fn = js.compile_unblob(obj)
return js.eval_compile(fn)
}
var ext = core_path.substring(core_path.lastIndexOf('.'))
var form = script_forms[ext]
if (!form) throw new Error(`No script form for extension ${ext}`)
var compile_name = make_compile_name(core_path, rel_path, null, SCOPE_CORE)
var script = form(null, text(fd.slurp(core_path)))
var fn = js.compile(compile_name, script)
ensure_dir(cache_path.substring(0, cache_path.lastIndexOf('/')))
fd.slurpwrite(cache_path, js.compile_blob(fn))
return js.eval_compile(fn)
}
function resolve_locator(path, ext, ctx) function resolve_locator(path, ext, ctx)
{ {
// In static_only mode, only look in the embedded pack if (path.endsWith(ext)) ext = ''
var static_only = cell.static_only
if (!static_only) { // 1. Check local file (relative to current directory if no context, or relative to package 'ctx' if provided)
// First, check if file exists in current package directory // If ctx is provided, it's a package alias or path.
if (current_package_path) {
var pkg_local_path = current_package_path + '/' + path + ext
if (fd.is_file(pkg_local_path)) {
var fn = resolve_mod_fn(pkg_local_path, null)
return {path: pkg_local_path, scope: SCOPE_LOCAL, symbol:fn}
}
}
// Check CWD for local file
var local_path var local_path
if (ctx) if (ctx) {
local_path = get_modules_dir() + '/' + ctx + '/' + path + ext var mod_dir = get_modules_dir()
else // Check if ctx is an absolute path (local package)
if (ctx.startsWith('/')) {
local_path = ctx + '/' + path + ext
} else {
local_path = mod_dir + '/' + ctx + '/' + path + ext
}
} else {
// No context, just check simple local path
local_path = path + ext local_path = path + ext
}
if (fd.is_file(local_path)) { if (fd.is_file(local_path)) {
var fn = resolve_mod_fn(local_path, ctx) var fn = resolve_mod_fn(local_path, ctx)
return {path: local_path, scope: SCOPE_LOCAL, symbol:fn} return {path: local_path, scope: SCOPE_LOCAL, symbol:fn}
} else {
// Fallback: check if the path itself is a file (ignoring required extension)
// This allows running scripts like 'test.cm' even if engine asks for '.ce'
if (ext && path != local_path && fd.is_file(path)) {
var fn = resolve_mod_fn(path, ctx)
return {path: path, scope: SCOPE_LOCAL, symbol:fn}
}
} }
// Check installed packages
// 2. Check installed packages (if path suggests a package import)
// This handles imports like 'prosperon/sprite'
var canonical_pkg = get_normalized_package(path, ctx) var canonical_pkg = get_normalized_package(path, ctx)
if (canonical_pkg) {
var pkg_path = get_path_in_package(path, ctx) var pkg_path = get_path_in_package(path, ctx)
var mod_path = get_modules_dir() + '/' + pkg_path + ext var mod_path = get_modules_dir() + '/' + pkg_path + ext
if (fd.is_file(mod_path)) { if (fd.is_file(mod_path)) {
@@ -955,32 +862,25 @@ function resolve_locator(path, ext, ctx)
} }
} }
// Check core directory for core modules // 3. Check core (as a fallback)
// "core" is now just another package, potentially.
// But if the user really wants to load "time", "js", etc, which are in core.
// We can try to resolve them in the core package.
// Ideally 'core' is defined in dependencies if needed, or we hardcode a fallback.
// Hardcoded fallback for now to match behavior:
var core_dir = Shop.get_core_dir() var core_dir = Shop.get_core_dir()
var core_file_path = core_dir + '/' + path + ext
// For packages, try the full package path first in core
if (ctx) {
var pkg_rel = ctx + '/' + path + ext
var pkg_core_path = core_dir + '/' + pkg_rel
if (fd.is_file(pkg_core_path)) {
var fn = resolve_core_mod_fn(pkg_core_path, pkg_rel)
return {path: pkg_rel, scope: SCOPE_CORE, symbol:fn};
}
}
// Check core directory for the module
// Core scripts are now in .cell/core/scripts
var core_dir = Shop.get_core_dir()
var core_file_path = core_dir + '/scripts/' + path + ext
if (path == 'text') log.console("Checking core mod: " + core_file_path + " exists: " + fd.is_file(core_file_path))
if (fd.is_file(core_file_path)) { if (fd.is_file(core_file_path)) {
var fn = resolve_core_mod_fn(core_file_path, path + ext) // Core is treated as a package now, essentially
var fn = resolve_mod_fn(core_file_path, 'core') // using 'core' string as package for now
return {path: path + ext, scope: SCOPE_CORE, symbol:fn}; return {path: path + ext, scope: SCOPE_CORE, symbol:fn};
} }
return null; return null;
} }
function c_sym_path(path) function c_sym_path(path)
{ {
return path.replace(/\//g, '_').replace(/\\/g, '_').replace(/\./g, '_').replace(/-/g, '_') return path.replace(/\//g, '_').replace(/\\/g, '_').replace(/\./g, '_').replace(/-/g, '_')
@@ -989,14 +889,16 @@ function c_sym_path(path)
function resolve_c_symbol(path, package_context) function resolve_c_symbol(path, package_context)
{ {
var static_only = cell.static_only var static_only = cell.static_only
var local_path = package_context ? package_context : 'local'
var local_sym_base = c_sym_path(path) var local_sym_base = c_sym_path(path)
var local
// Candidates for symbol names
function symbol_candidates(pkg_path, mod_sym) { function symbol_candidates(pkg_path, mod_sym) {
var variants = [] var variants = []
// if pkg_path is 'gitea.pockle.world/john/prosperon', we want:
// js_gitea_pockle_world_john_prosperon_mod_use
// and maybe with leading slash?
var paths = [pkg_path] var paths = [pkg_path]
if (!pkg_path.startsWith('/')) paths.push('/' + pkg_path) // if (!pkg_path.startsWith('/')) paths.push('/' + pkg_path) // unlikely to need slash variant for standard pkgs
for (var i = 0; i < paths.length; i++) { for (var i = 0; i < paths.length; i++) {
var candidate = `js_${c_sym_path(paths[i])}_${mod_sym}_use` var candidate = `js_${c_sym_path(paths[i])}_${mod_sym}_use`
if (variants.indexOf(candidate) < 0) if (variants.indexOf(candidate) < 0)
@@ -1005,36 +907,39 @@ function resolve_c_symbol(path, package_context)
return variants return variants
} }
if (!package_context) { // 1. Check internal symbols (statically linked)
local = `js_local_${local_sym_base}_use` // If we have a package context, we check package-prefixed symbols
} else { // If not, we look for 'js_local_...' or 'js_path_use' directly?
local = null // handled via candidates below
}
// First check for statically linked/internal symbols if (package_context) {
var local_candidates = package_context ? symbol_candidates(local_path, local_sym_base) : [local] // Check package internal symbols
for (var li = 0; li < local_candidates.length; li++) { var variants = symbol_candidates(package_context, local_sym_base)
var lc = local_candidates[li] for (var i = 0; i < variants.length; i++) {
if (os.internal_exists(lc)) if (os.internal_exists(variants[i])) {
return { return {
symbol: function() { return os.load_internal(lc); }, symbol: function() { return os.load_internal(variants[i]); },
scope: SCOPE_LOCAL, scope: SCOPE_PACKAGE,
path: lc path: variants[i]
}; }
}
} }
// In static_only mode, skip dynamic library lookups // In static_only mode, skip dynamic library lookups
if (!static_only) { if (!static_only) {
// Then try dynamic library // Then try dynamic library
var build_dir = get_build_dir(package_context) // Use ./libcellmod for local?
var local_dl_name = build_dir + '/libcellmod' + dylib_ext var local_dl_name = './libcellmod' + dylib_ext
// Or check package context dir if it's a local package path?
if (package_context && package_context.startsWith('/')) {
local_dl_name = package_context + '/libcellmod' + dylib_ext
}
if (fd.is_file(local_dl_name)) { if (fd.is_file(local_dl_name)) {
if (!open_dls[local_dl_name]) if (!open_dls[local_dl_name])
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 locals = package_context ? symbol_candidates(local_path, local_sym_base) : [local] var locals = package_context ? symbol_candidates(package_context, local_sym_base) : [`js_local_${local_sym_base}_use`]
for (var i = 0; i < locals.length; i++) { for (var i = 0; i < locals.length; i++) {
var candidate = locals[i] var candidate = locals[i]
if (os.dylib_has_symbol(open_dls[local_dl_name], candidate)) if (os.dylib_has_symbol(open_dls[local_dl_name], candidate))
@@ -1047,8 +952,19 @@ function resolve_c_symbol(path, package_context)
} }
} }
} }
} else {
// Local context
var local_sym = `js_local_${local_sym_base}_use`
if (os.internal_exists(local_sym)) {
return {
symbol: function() { return os.load_internal(local_sym); },
scope: SCOPE_LOCAL,
path: local_sym
}
}
}
// If 'path' has a package alias (e.g. 'prosperon/sprite'), try to resolve it // 2. Check if valid package import (e.g. 'prosperon/sprite')
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_context) var canon_pkg = get_normalized_package(path, package_context)
@@ -1057,21 +973,20 @@ function resolve_c_symbol(path, package_context)
var mod_sym = mod_name.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_') var mod_sym = mod_name.replace(/\//g, '_').replace(/-/g, '_').replace(/\./g, '_')
var sym_names = symbol_candidates(canon_pkg, mod_sym) var sym_names = symbol_candidates(canon_pkg, mod_sym)
// First check internal/static symbols for package for (var i = 0; i < sym_names.length; i++) {
for (var sii = 0; sii < sym_names.length; sii++) { if (os.internal_exists(sym_names[i])) {
var sym_name = sym_names[sii]
if (os.internal_exists(sym_name))
return { return {
symbol: function() { return os.load_internal(sym_name) }, symbol: function() { return os.load_internal(sym_names[i]); },
scope: SCOPE_PACKAGE, scope: SCOPE_PACKAGE,
package: canon_pkg, package: canon_pkg,
path: sym_name path: sym_names[i]
}; }
}
} }
// Then try dynamic library for package (skip in static_only mode) // Then try dynamic library for package (skip in static_only mode)
if (!static_only) { if (!static_only) {
var pkg_build_dir = get_build_dir(canon_pkg) // Check package dir for libcellmod
var pkg_build_dir = get_modules_dir() + '/' + canon_pkg
var dl_path = pkg_build_dir + '/libcellmod' + dylib_ext var dl_path = pkg_build_dir + '/libcellmod' + dylib_ext
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)
@@ -1092,6 +1007,7 @@ function resolve_c_symbol(path, package_context)
} }
} }
// 3. Check Core/General fallback (js_path_use)
var core_sym = `js_${path.replace(/\//g, '_')}_use`; var core_sym = `js_${path.replace(/\//g, '_')}_use`;
if (os.internal_exists(core_sym)) if (os.internal_exists(core_sym))
return { return {
@@ -1150,14 +1066,20 @@ function execute_module(info)
var used var used
if (c_resolve.scope < mod_resolve.scope) // If we have a script, it always takes precedence (containing the C symbol if available)
// This supports "Hybrid" modules where JS wraps C.
if (mod_resolve.scope < 900) {
var context = null
if (c_resolve.scope < 900) {
context = c_resolve.symbol(null, $_)
}
used = mod_resolve.symbol.call(context, $_)
} else if (c_resolve.scope < 900) {
// C only
used = c_resolve.symbol(null, $_) used = c_resolve.symbol(null, $_)
else if (mod_resolve.scope < c_resolve.scope) } else {
used = mod_resolve.symbol.call(null, $_) throw new Error(`Module ${info.path} could not be found`)
else } if (!used)
used = mod_resolve.symbol.call(c_resolve.symbol(), $_)
if (!used)
throw new Error(`Module ${json.encode(info)} returned null`) throw new Error(`Module ${json.encode(info)} returned null`)
return used return used
@@ -1172,10 +1094,6 @@ function get_module(path, package_context) {
return execute_module(info) return execute_module(info)
} }
// first looks in local
// then in dependencies
// then in core
// package_context: optional package context to resolve relative paths within
Shop.use = function(path, package_context) { Shop.use = function(path, package_context) {
var info = resolve_module_info(path, package_context) var info = resolve_module_info(path, package_context)
if (!info) if (!info)
@@ -1585,7 +1503,7 @@ Shop.remove_replacement = function(alias) {
// List all files in a package // List all files in a package
Shop.list_files = function(pkg) { Shop.list_files = function(pkg) {
var dir var dir
if (!pkg) dir = current_package_path || '.' if (!pkg) dir = '.'
else dir = get_modules_dir() + '/' + pkg else dir = get_modules_dir() + '/' + pkg
var files = [] var files = []

View File

@@ -11,7 +11,7 @@
#include "cell.h" #include "cell.h"
#include "cell_internal.h" #include "cell_internal.h"
#define ENGINE "scripts/engine.cm" #define ENGINE "internal/engine.cm"
#define CELL_SHOP_DIR ".cell" #define CELL_SHOP_DIR ".cell"
#define CELL_CORE_DIR "core" #define CELL_CORE_DIR "core"

View File

@@ -69,7 +69,7 @@ static const JSCFunctionListEntry js_time_funcs[] = {
}; };
JSValue JSValue
js_time_use(JSContext *ctx) js_internal_time_use(JSContext *ctx)
{ {
JSValue obj = JS_NewObject(ctx); JSValue obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, obj, JS_SetPropertyFunctionList(ctx, obj,
@@ -78,3 +78,9 @@ js_time_use(JSContext *ctx)
sizeof(js_time_funcs[0])); sizeof(js_time_funcs[0]));
return obj; return obj;
} }
JSValue
js_time_use(JSContext *ctx)
{
return js_internal_time_use(ctx);
}