Compare commits
10 Commits
runtime_re
...
pitweb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7de20b39da | ||
|
|
ee646db394 | ||
|
|
ff80e0d30d | ||
|
|
d9f41db891 | ||
|
|
860632e0fa | ||
|
|
dcc9659e6b | ||
|
|
2f7f2233b8 | ||
|
|
eee06009b9 | ||
|
|
86609c27f8 | ||
|
|
8ec56e85fa |
@@ -103,6 +103,10 @@ var v = a[] // pop: v is 3, a is [1, 2]
|
|||||||
- Most files don't have headers; files in a package are not shared between packages
|
- Most files don't have headers; files in a package are not shared between packages
|
||||||
- No undefined in C API: use `JS_IsNull` and `JS_NULL` only
|
- No undefined in C API: use `JS_IsNull` and `JS_NULL` only
|
||||||
- A C file with correct macros (`CELL_USE_FUNCS` etc) is loaded as a module by its name (e.g., `png.c` in a package → `use('<package>/png')`)
|
- A C file with correct macros (`CELL_USE_FUNCS` etc) is loaded as a module by its name (e.g., `png.c` in a package → `use('<package>/png')`)
|
||||||
|
- C symbol naming: `js_<pkg>_<file>_use` (e.g., `js_core_math_radians_use` for `core/math/radians`)
|
||||||
|
- Core is the `core` package — its symbols follow the same `js_core_<name>_use` pattern as all other packages
|
||||||
|
- Package directories should contain only source files (no `.mach`/`.mcode` alongside source)
|
||||||
|
- Build cache files in `build/` are bare hashes (no extensions)
|
||||||
- Use `JS_FRAME`/`JS_ROOT`/`JS_RETURN` macros for any C function that allocates multiple heap objects. Any `JS_New*`/`JS_SetProperty*` call can trigger GC.
|
- Use `JS_FRAME`/`JS_ROOT`/`JS_RETURN` macros for any C function that allocates multiple heap objects. Any `JS_New*`/`JS_SetProperty*` call can trigger GC.
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
|
|||||||
33
add.ce
33
add.ce
@@ -13,6 +13,10 @@ var fd = use('fd')
|
|||||||
|
|
||||||
var locator = null
|
var locator = null
|
||||||
var alias = null
|
var alias = null
|
||||||
|
var resolved = null
|
||||||
|
var parts = null
|
||||||
|
var cwd = null
|
||||||
|
var build_target = null
|
||||||
|
|
||||||
array(args, function(arg) {
|
array(args, function(arg) {
|
||||||
if (arg == '--help' || arg == '-h') {
|
if (arg == '--help' || arg == '-h') {
|
||||||
@@ -41,7 +45,7 @@ if (!locator) {
|
|||||||
|
|
||||||
// Resolve relative paths to absolute paths
|
// Resolve relative paths to absolute paths
|
||||||
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
||||||
var resolved = fd.realpath(locator)
|
resolved = fd.realpath(locator)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
locator = resolved
|
locator = resolved
|
||||||
}
|
}
|
||||||
@@ -50,7 +54,7 @@ if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../')
|
|||||||
// Generate default alias from locator
|
// Generate default alias from locator
|
||||||
if (!alias) {
|
if (!alias) {
|
||||||
// Use the last component of the locator as alias
|
// Use the last component of the locator as alias
|
||||||
var parts = array(locator, '/')
|
parts = array(locator, '/')
|
||||||
alias = parts[length(parts) - 1]
|
alias = parts[length(parts) - 1]
|
||||||
// Remove any version suffix
|
// Remove any version suffix
|
||||||
if (search(alias, '@') != null) {
|
if (search(alias, '@') != null) {
|
||||||
@@ -59,7 +63,7 @@ if (!alias) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check we're in a package directory
|
// Check we're in a package directory
|
||||||
var cwd = fd.realpath('.')
|
cwd = fd.realpath('.')
|
||||||
if (!fd.is_file(cwd + '/cell.toml')) {
|
if (!fd.is_file(cwd + '/cell.toml')) {
|
||||||
log.error("Not in a package directory (no cell.toml found)")
|
log.error("Not in a package directory (no cell.toml found)")
|
||||||
$stop()
|
$stop()
|
||||||
@@ -68,16 +72,17 @@ if (!fd.is_file(cwd + '/cell.toml')) {
|
|||||||
log.console("Adding " + locator + " as '" + alias + "'...")
|
log.console("Adding " + locator + " as '" + alias + "'...")
|
||||||
|
|
||||||
// Add to local project's cell.toml
|
// Add to local project's cell.toml
|
||||||
try {
|
var _add_dep = function() {
|
||||||
pkg.add_dependency(null, locator, alias)
|
pkg.add_dependency(null, locator, alias)
|
||||||
log.console(" Added to cell.toml")
|
log.console(" Added to cell.toml")
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error("Failed to update cell.toml: " + e)
|
log.error("Failed to update cell.toml")
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
_add_dep()
|
||||||
|
|
||||||
// Install to shop
|
// Install to shop
|
||||||
try {
|
var _install = function() {
|
||||||
shop.get(locator)
|
shop.get(locator)
|
||||||
shop.extract(locator)
|
shop.extract(locator)
|
||||||
|
|
||||||
@@ -85,18 +90,20 @@ try {
|
|||||||
shop.build_package_scripts(locator)
|
shop.build_package_scripts(locator)
|
||||||
|
|
||||||
// Build C code if any
|
// Build C code if any
|
||||||
try {
|
var _build_c = function() {
|
||||||
var target = build.detect_host_target()
|
build_target = build.detect_host_target()
|
||||||
build.build_dynamic(locator, target, 'release')
|
build.build_dynamic(locator, build_target, 'release')
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Not all packages have C code
|
// Not all packages have C code
|
||||||
}
|
}
|
||||||
|
_build_c()
|
||||||
|
|
||||||
log.console(" Installed to shop")
|
log.console(" Installed to shop")
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error("Failed to install: " + e)
|
log.error("Failed to install")
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
_install()
|
||||||
|
|
||||||
log.console("Added " + alias + " (" + locator + ")")
|
log.console("Added " + alias + " (" + locator + ")")
|
||||||
|
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ static const JSCFunctionListEntry js_reader_funcs[] = {
|
|||||||
JS_CFUNC_DEF("count", 0, js_reader_count),
|
JS_CFUNC_DEF("count", 0, js_reader_count),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_miniz_use(JSContext *js)
|
JSValue js_core_miniz_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
|
|
||||||
|
|||||||
183
bench_native.ce
Normal file
183
bench_native.ce
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// bench_native.ce — compare VM vs native execution speed
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cell --dev bench_native.ce <module.cm> [iterations]
|
||||||
|
//
|
||||||
|
// Compiles (if needed) and benchmarks a module via both VM and native dylib.
|
||||||
|
// Reports median/mean timing per benchmark + speedup ratio.
|
||||||
|
|
||||||
|
var os = use('os')
|
||||||
|
var fd = use('fd')
|
||||||
|
|
||||||
|
if (length(args) < 1) {
|
||||||
|
print('usage: cell --dev bench_native.ce <module.cm> [iterations]')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = args[0]
|
||||||
|
var name = file
|
||||||
|
if (ends_with(name, '.cm')) {
|
||||||
|
name = text(name, 0, length(name) - 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
var iterations = 11
|
||||||
|
if (length(args) > 1) {
|
||||||
|
iterations = number(args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
def WARMUP = 3
|
||||||
|
|
||||||
|
var safe = replace(replace(name, '/', '_'), '-', '_')
|
||||||
|
var symbol = 'js_' + safe + '_use'
|
||||||
|
var dylib_path = './' + file + '.dylib'
|
||||||
|
|
||||||
|
// --- Statistics ---
|
||||||
|
|
||||||
|
var stat_sort = function(arr) {
|
||||||
|
return sort(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat_median = function(arr) {
|
||||||
|
if (length(arr) == 0) return 0
|
||||||
|
var sorted = stat_sort(arr)
|
||||||
|
var mid = floor(length(arr) / 2)
|
||||||
|
if (length(arr) % 2 == 0) {
|
||||||
|
return (sorted[mid - 1] + sorted[mid]) / 2
|
||||||
|
}
|
||||||
|
return sorted[mid]
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat_mean = function(arr) {
|
||||||
|
if (length(arr) == 0) return 0
|
||||||
|
var sum = reduce(arr, function(a, b) { return a + b })
|
||||||
|
return sum / length(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var format_ns = function(ns) {
|
||||||
|
if (ns < 1000) return text(round(ns)) + 'ns'
|
||||||
|
if (ns < 1000000) return text(round(ns / 1000 * 100) / 100) + 'us'
|
||||||
|
if (ns < 1000000000) return text(round(ns / 1000000 * 100) / 100) + 'ms'
|
||||||
|
return text(round(ns / 1000000000 * 100) / 100) + 's'
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Collect benchmarks from module ---
|
||||||
|
|
||||||
|
var collect_benches = function(mod) {
|
||||||
|
var benches = []
|
||||||
|
if (is_function(mod)) {
|
||||||
|
push(benches, {name: 'main', fn: mod})
|
||||||
|
} else if (is_object(mod)) {
|
||||||
|
var keys = array(mod)
|
||||||
|
var i = 0
|
||||||
|
while (i < length(keys)) {
|
||||||
|
var k = keys[i]
|
||||||
|
if (is_function(mod[k])) {
|
||||||
|
push(benches, {name: k, fn: mod[k]})
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return benches
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Run one benchmark function ---
|
||||||
|
|
||||||
|
var run_bench = function(fn, label) {
|
||||||
|
var samples = []
|
||||||
|
var i = 0
|
||||||
|
var t1 = 0
|
||||||
|
var t2 = 0
|
||||||
|
|
||||||
|
// warmup
|
||||||
|
i = 0
|
||||||
|
while (i < WARMUP) {
|
||||||
|
fn(1)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect samples
|
||||||
|
i = 0
|
||||||
|
while (i < iterations) {
|
||||||
|
t1 = os.now()
|
||||||
|
fn(1)
|
||||||
|
t2 = os.now()
|
||||||
|
push(samples, t2 - t1)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: label,
|
||||||
|
median: stat_median(samples),
|
||||||
|
mean: stat_mean(samples)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Load VM module ---
|
||||||
|
|
||||||
|
print('loading VM module: ' + file)
|
||||||
|
var vm_mod = use(name)
|
||||||
|
var vm_benches = collect_benches(vm_mod)
|
||||||
|
|
||||||
|
if (length(vm_benches) == 0) {
|
||||||
|
print('no benchmarkable functions found in ' + file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Load native module ---
|
||||||
|
|
||||||
|
var native_mod = null
|
||||||
|
var native_benches = []
|
||||||
|
var has_native = fd.is_file(dylib_path)
|
||||||
|
|
||||||
|
if (has_native) {
|
||||||
|
print('loading native module: ' + dylib_path)
|
||||||
|
var lib = os.dylib_open(dylib_path)
|
||||||
|
native_mod = os.dylib_symbol(lib, symbol)
|
||||||
|
native_benches = collect_benches(native_mod)
|
||||||
|
} else {
|
||||||
|
print('no ' + dylib_path + ' found -- VM-only benchmarking')
|
||||||
|
print(' hint: cell --dev compile.ce ' + file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Run benchmarks ---
|
||||||
|
|
||||||
|
print('')
|
||||||
|
print('samples: ' + text(iterations) + ' (warmup: ' + text(WARMUP) + ')')
|
||||||
|
print('')
|
||||||
|
|
||||||
|
var pad = function(s, n) {
|
||||||
|
while (length(s) < n) s = s + ' '
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while (i < length(vm_benches)) {
|
||||||
|
var b = vm_benches[i]
|
||||||
|
var vm_result = run_bench(b.fn, 'vm')
|
||||||
|
|
||||||
|
print(pad(b.name, 20) + ' VM: ' + pad(format_ns(vm_result.median), 12) + ' (median) ' + format_ns(vm_result.mean) + ' (mean)')
|
||||||
|
|
||||||
|
// find matching native bench
|
||||||
|
var j = 0
|
||||||
|
var found = false
|
||||||
|
while (j < length(native_benches)) {
|
||||||
|
if (native_benches[j].name == b.name) {
|
||||||
|
var nat_result = run_bench(native_benches[j].fn, 'native')
|
||||||
|
print(pad('', 20) + ' NT: ' + pad(format_ns(nat_result.median), 12) + ' (median) ' + format_ns(nat_result.mean) + ' (mean)')
|
||||||
|
|
||||||
|
if (nat_result.median > 0) {
|
||||||
|
var speedup = vm_result.median / nat_result.median
|
||||||
|
print(pad('', 20) + ' speedup: ' + text(round(speedup * 100) / 100) + 'x')
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_native && !found) {
|
||||||
|
print(pad('', 20) + ' NT: (no matching function)')
|
||||||
|
}
|
||||||
|
|
||||||
|
print('')
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
19632
boot/engine.cm.mcode
19632
boot/engine.cm.mcode
File diff suppressed because it is too large
Load Diff
2244
boot/fd.cm.mcode
Normal file
2244
boot/fd.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
128766
boot/fold.cm.mcode
128766
boot/fold.cm.mcode
File diff suppressed because it is too large
Load Diff
60251
boot/internal_shop.cm.mcode
Normal file
60251
boot/internal_shop.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
14083
boot/link.cm.mcode
Normal file
14083
boot/link.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
157316
boot/mcode.cm.mcode
157316
boot/mcode.cm.mcode
File diff suppressed because it is too large
Load Diff
14693
boot/package.cm.mcode
Normal file
14693
boot/package.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
177224
boot/parse.cm.mcode
177224
boot/parse.cm.mcode
File diff suppressed because it is too large
Load Diff
8189
boot/pronto.cm.mcode
Normal file
8189
boot/pronto.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
7663
boot/qbe.cm.mcode
7663
boot/qbe.cm.mcode
File diff suppressed because it is too large
Load Diff
129366
boot/qbe_emit.cm.mcode
129366
boot/qbe_emit.cm.mcode
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
// Hidden env: os, core_path, shop_path, args, json
|
// Hidden env: os, core_path, shop_path, args, json
|
||||||
|
|
||||||
var load_internal = os.load_internal
|
var load_internal = os.load_internal
|
||||||
var fd = load_internal("js_fd_use")
|
var fd = load_internal("js_core_internal_fd_use")
|
||||||
|
|
||||||
var use_cache = {}
|
var use_cache = {}
|
||||||
use_cache['fd'] = fd
|
use_cache['fd'] = fd
|
||||||
@@ -15,7 +15,7 @@ use_cache['json'] = json
|
|||||||
function use_basic(path) {
|
function use_basic(path) {
|
||||||
if (use_cache[path])
|
if (use_cache[path])
|
||||||
return use_cache[path]
|
return use_cache[path]
|
||||||
var result = load_internal("js_" + replace(path, '/', '_') + "_use")
|
var result = load_internal("js_core_" + replace(path, '/', '_') + "_use")
|
||||||
if (result) {
|
if (result) {
|
||||||
use_cache[path] = result
|
use_cache[path] = result
|
||||||
return result
|
return result
|
||||||
@@ -86,7 +86,7 @@ function use_fn(path) {
|
|||||||
return use_cache[path]
|
return use_cache[path]
|
||||||
|
|
||||||
// Try C embed
|
// Try C embed
|
||||||
result = load_internal("js_" + replace(path, '/', '_') + "_use")
|
result = load_internal("js_core_" + replace(path, '/', '_') + "_use")
|
||||||
if (result) {
|
if (result) {
|
||||||
use_cache[path] = result
|
use_cache[path] = result
|
||||||
return result
|
return result
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
73485
boot/streamline.cm.mcode
73485
boot/streamline.cm.mcode
File diff suppressed because it is too large
Load Diff
14564
boot/time.cm.mcode
Normal file
14564
boot/time.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
66285
boot/tokenize.cm.mcode
66285
boot/tokenize.cm.mcode
File diff suppressed because it is too large
Load Diff
12640
boot/toml.cm.mcode
Normal file
12640
boot/toml.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
4422
boot/toolchains.cm.mcode
Normal file
4422
boot/toolchains.cm.mcode
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
38
build.ce
38
build.ce
@@ -17,8 +17,16 @@ var target_package = null
|
|||||||
var buildtype = 'release'
|
var buildtype = 'release'
|
||||||
var force_rebuild = false
|
var force_rebuild = false
|
||||||
var dry_run = false
|
var dry_run = false
|
||||||
|
var i = 0
|
||||||
|
var targets = null
|
||||||
|
var t = 0
|
||||||
|
var resolved = null
|
||||||
|
var lib = null
|
||||||
|
var results = null
|
||||||
|
var success = 0
|
||||||
|
var failed = 0
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '-t' || args[i] == '--target') {
|
if (args[i] == '-t' || args[i] == '--target') {
|
||||||
if (i + 1 < length(args)) {
|
if (i + 1 < length(args)) {
|
||||||
target = args[++i]
|
target = args[++i]
|
||||||
@@ -51,8 +59,8 @@ for (var i = 0; i < length(args); i++) {
|
|||||||
dry_run = true
|
dry_run = true
|
||||||
} else if (args[i] == '--list-targets') {
|
} else if (args[i] == '--list-targets') {
|
||||||
log.console('Available targets:')
|
log.console('Available targets:')
|
||||||
var targets = build.list_targets()
|
targets = build.list_targets()
|
||||||
for (var t = 0; t < length(targets); t++) {
|
for (t = 0; t < length(targets); t++) {
|
||||||
log.console(' ' + targets[t])
|
log.console(' ' + targets[t])
|
||||||
}
|
}
|
||||||
$stop()
|
$stop()
|
||||||
@@ -65,7 +73,7 @@ for (var i = 0; i < length(args); i++) {
|
|||||||
// Resolve local paths to absolute paths
|
// Resolve local paths to absolute paths
|
||||||
if (target_package) {
|
if (target_package) {
|
||||||
if (target_package == '.' || starts_with(target_package, './') || starts_with(target_package, '../') || fd.is_dir(target_package)) {
|
if (target_package == '.' || starts_with(target_package, './') || starts_with(target_package, '../') || fd.is_dir(target_package)) {
|
||||||
var resolved = fd.realpath(target_package)
|
resolved = fd.realpath(target_package)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_package = resolved
|
target_package = resolved
|
||||||
}
|
}
|
||||||
@@ -91,33 +99,35 @@ arrfor(packages, function(package) {
|
|||||||
shop.extract(package)
|
shop.extract(package)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var _build = null
|
||||||
if (target_package) {
|
if (target_package) {
|
||||||
// Build single package
|
// Build single package
|
||||||
log.console('Building ' + target_package + '...')
|
log.console('Building ' + target_package + '...')
|
||||||
try {
|
_build = function() {
|
||||||
var lib = build.build_dynamic(target_package, target, buildtype)
|
lib = build.build_dynamic(target_package, target, buildtype)
|
||||||
if (lib) {
|
if (lib) {
|
||||||
log.console('Built: ' + lib)
|
log.console('Built: ' + lib)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error('Build failed: ' + e)
|
log.error('Build failed')
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
_build()
|
||||||
} else {
|
} else {
|
||||||
// Build all packages
|
// Build all packages
|
||||||
log.console('Building all packages...')
|
log.console('Building all packages...')
|
||||||
var results = build.build_all_dynamic(target, buildtype)
|
results = build.build_all_dynamic(target, buildtype)
|
||||||
|
|
||||||
var success = 0
|
success = 0
|
||||||
var failed = 0
|
failed = 0
|
||||||
for (var i = 0; i < length(results); i++) {
|
for (i = 0; i < length(results); i++) {
|
||||||
if (results[i].library) {
|
if (results[i].library) {
|
||||||
success++
|
success++
|
||||||
} else if (results[i].error) {
|
} else if (results[i].error) {
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console(`Build complete: ${success} libraries built${failed > 0 ? `, ${failed} failed` : ''}`)
|
log.console(`Build complete: ${success} libraries built${failed > 0 ? `, ${failed} failed` : ''}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
153
build.cm
153
build.cm
@@ -85,7 +85,8 @@ function ensure_dir(path) {
|
|||||||
if (fd.stat(path).isDirectory) return
|
if (fd.stat(path).isDirectory) return
|
||||||
var parts = array(path, '/')
|
var parts = array(path, '/')
|
||||||
var current = starts_with(path, '/') ? '/' : ''
|
var current = starts_with(path, '/') ? '/' : ''
|
||||||
for (var i = 0; i < length(parts); i++) {
|
var i = 0
|
||||||
|
for (i = 0; i < length(parts); i++) {
|
||||||
if (parts[i] == '') continue
|
if (parts[i] == '') continue
|
||||||
current += parts[i] + '/'
|
current += parts[i] + '/'
|
||||||
if (!fd.stat(current).isDirectory) fd.mkdir(current)
|
if (!fd.stat(current).isDirectory) fd.mkdir(current)
|
||||||
@@ -100,12 +101,13 @@ Build.ensure_dir = ensure_dir
|
|||||||
|
|
||||||
// Compile a single C file for a package
|
// Compile a single C file for a package
|
||||||
// Returns the object file path (content-addressed in .cell/build)
|
// Returns the object file path (content-addressed in .cell/build)
|
||||||
Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
Build.compile_file = function(pkg, file, target, buildtype) {
|
||||||
|
var _buildtype = buildtype || 'release'
|
||||||
var pkg_dir = shop.get_package_dir(pkg)
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
var src_path = pkg_dir + '/' + file
|
var src_path = pkg_dir + '/' + file
|
||||||
|
|
||||||
if (!fd.is_file(src_path)) {
|
if (!fd.is_file(src_path)) {
|
||||||
throw Error('Source file not found: ' + src_path)
|
print('Source file not found: ' + src_path); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get flags (with sigil replacement)
|
// Get flags (with sigil replacement)
|
||||||
@@ -120,11 +122,11 @@ Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
|||||||
var cmd_parts = [cc, '-c', '-fPIC']
|
var cmd_parts = [cc, '-c', '-fPIC']
|
||||||
|
|
||||||
// Add buildtype-specific flags
|
// Add buildtype-specific flags
|
||||||
if (buildtype == 'release') {
|
if (_buildtype == 'release') {
|
||||||
cmd_parts = array(cmd_parts, ['-O3', '-DNDEBUG'])
|
cmd_parts = array(cmd_parts, ['-O3', '-DNDEBUG'])
|
||||||
} else if (buildtype == 'debug') {
|
} else if (_buildtype == 'debug') {
|
||||||
cmd_parts = array(cmd_parts, ['-O2', '-g'])
|
cmd_parts = array(cmd_parts, ['-O2', '-g'])
|
||||||
} else if (buildtype == 'minsize') {
|
} else if (_buildtype == 'minsize') {
|
||||||
cmd_parts = array(cmd_parts, ['-Os', '-DNDEBUG'])
|
cmd_parts = array(cmd_parts, ['-Os', '-DNDEBUG'])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,10 +135,11 @@ Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
|||||||
|
|
||||||
// Add package CFLAGS (resolve relative -I paths)
|
// Add package CFLAGS (resolve relative -I paths)
|
||||||
arrfor(cflags, function(flag) {
|
arrfor(cflags, function(flag) {
|
||||||
if (starts_with(flag, '-I') && !starts_with(flag, '-I/')) {
|
var f = flag
|
||||||
flag = '-I"' + pkg_dir + '/' + text(flag, 2) + '"'
|
if (starts_with(f, '-I') && !starts_with(f, '-I/')) {
|
||||||
|
f = '-I"' + pkg_dir + '/' + text(f, 2) + '"'
|
||||||
}
|
}
|
||||||
push(cmd_parts, flag)
|
push(cmd_parts, f)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add target CFLAGS
|
// Add target CFLAGS
|
||||||
@@ -167,7 +170,7 @@ Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
|||||||
log.console('Compiling ' + file)
|
log.console('Compiling ' + file)
|
||||||
var ret = os.system(full_cmd)
|
var ret = os.system(full_cmd)
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
throw Error('Compilation failed: ' + file)
|
print('Compilation failed: ' + file); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj_path
|
return obj_path
|
||||||
@@ -175,12 +178,14 @@ Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
|||||||
|
|
||||||
// Build all C files for a package
|
// Build all C files for a package
|
||||||
// Returns array of object file paths
|
// Returns array of object file paths
|
||||||
Build.build_package = function(pkg, target = Build.detect_host_target(), exclude_main, buildtype = 'release') {
|
Build.build_package = function(pkg, target, exclude_main, buildtype) {
|
||||||
var c_files = pkg_tools.get_c_files(pkg, target, exclude_main)
|
var _target = target || Build.detect_host_target()
|
||||||
|
var _buildtype = buildtype || 'release'
|
||||||
|
var c_files = pkg_tools.get_c_files(pkg, _target, exclude_main)
|
||||||
var objects = []
|
var objects = []
|
||||||
|
|
||||||
arrfor(c_files, function(file) {
|
arrfor(c_files, function(file) {
|
||||||
var obj = Build.compile_file(pkg, file, target, buildtype)
|
var obj = Build.compile_file(pkg, file, _target, _buildtype)
|
||||||
push(objects, obj)
|
push(objects, obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -192,14 +197,14 @@ Build.build_package = function(pkg, target = Build.detect_host_target(), exclude
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// Compute link key from all inputs that affect the dylib output
|
// Compute link key from all inputs that affect the dylib output
|
||||||
function compute_link_key(objects, ldflags, target_ldflags, target, cc) {
|
function compute_link_key(objects, ldflags, target_ldflags, opts) {
|
||||||
// Sort objects for deterministic hash
|
// Sort objects for deterministic hash
|
||||||
var sorted_objects = sort(objects)
|
var sorted_objects = sort(objects)
|
||||||
|
|
||||||
// Build a string representing all link inputs
|
// Build a string representing all link inputs
|
||||||
var parts = []
|
var parts = []
|
||||||
push(parts, 'target:' + target)
|
push(parts, 'target:' + opts.target)
|
||||||
push(parts, 'cc:' + cc)
|
push(parts, 'cc:' + opts.cc)
|
||||||
arrfor(sorted_objects, function(obj) {
|
arrfor(sorted_objects, function(obj) {
|
||||||
// Object paths are content-addressed, so the path itself is the hash
|
// Object paths are content-addressed, so the path itself is the hash
|
||||||
push(parts, 'obj:' + obj)
|
push(parts, 'obj:' + obj)
|
||||||
@@ -232,14 +237,15 @@ Build.build_module_dylib = function(pkg, file, target, buildtype) {
|
|||||||
var target_ldflags = tc.c_link_args || []
|
var target_ldflags = tc.c_link_args || []
|
||||||
var resolved_ldflags = []
|
var resolved_ldflags = []
|
||||||
arrfor(ldflags, function(flag) {
|
arrfor(ldflags, function(flag) {
|
||||||
if (starts_with(flag, '-L') && !starts_with(flag, '-L/')) {
|
var f = flag
|
||||||
flag = '-L"' + pkg_dir + '/' + text(flag, 2) + '"'
|
if (starts_with(f, '-L') && !starts_with(f, '-L/')) {
|
||||||
|
f = '-L"' + pkg_dir + '/' + text(f, 2) + '"'
|
||||||
}
|
}
|
||||||
push(resolved_ldflags, flag)
|
push(resolved_ldflags, f)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Content-addressed output: hash of (object + link flags + target)
|
// Content-addressed output: hash of (object + link flags + target)
|
||||||
var link_key = compute_link_key([obj], resolved_ldflags, target_ldflags, _target, cc)
|
var link_key = compute_link_key([obj], resolved_ldflags, target_ldflags, {target: _target, cc: cc})
|
||||||
var build_dir = get_build_dir()
|
var build_dir = get_build_dir()
|
||||||
ensure_dir(build_dir)
|
ensure_dir(build_dir)
|
||||||
var dylib_path = build_dir + '/' + link_key + '.' + _target + dylib_ext
|
var dylib_path = build_dir + '/' + link_key + '.' + _target + dylib_ext
|
||||||
@@ -281,6 +287,17 @@ Build.build_module_dylib = function(pkg, file, target, buildtype) {
|
|||||||
print('Linking failed: ' + file); disrupt
|
print('Linking failed: ' + file); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install to deterministic lib/<pkg>/<stem>.dylib
|
||||||
|
var file_stem = fd.stem(file)
|
||||||
|
var install_dir = shop.get_lib_dir() + '/' + shop.lib_name_for_package(pkg)
|
||||||
|
var stem_dir = fd.dirname(file_stem)
|
||||||
|
if (stem_dir && stem_dir != '.') {
|
||||||
|
install_dir = install_dir + '/' + stem_dir
|
||||||
|
}
|
||||||
|
ensure_dir(install_dir)
|
||||||
|
var install_path = shop.get_lib_dir() + '/' + shop.lib_name_for_package(pkg) + '/' + file_stem + dylib_ext
|
||||||
|
fd.slurpwrite(install_path, fd.slurp(dylib_path))
|
||||||
|
|
||||||
return dylib_path
|
return dylib_path
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,25 +309,13 @@ Build.build_dynamic = function(pkg, target, buildtype) {
|
|||||||
var _buildtype = buildtype || 'release'
|
var _buildtype = buildtype || 'release'
|
||||||
var c_files = pkg_tools.get_c_files(pkg, _target, true)
|
var c_files = pkg_tools.get_c_files(pkg, _target, true)
|
||||||
var results = []
|
var results = []
|
||||||
var manifest = {}
|
|
||||||
|
|
||||||
arrfor(c_files, function(file) {
|
arrfor(c_files, function(file) {
|
||||||
var sym_name = shop.c_symbol_for_file(pkg, file)
|
var sym_name = shop.c_symbol_for_file(pkg, file)
|
||||||
var dylib = Build.build_module_dylib(pkg, file, _target, _buildtype)
|
var dylib = Build.build_module_dylib(pkg, file, _target, _buildtype)
|
||||||
push(results, {file: file, symbol: sym_name, dylib: dylib})
|
push(results, {file: file, symbol: sym_name, dylib: dylib})
|
||||||
manifest[sym_name] = dylib
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Write manifest so the loader can find per-module dylibs by symbol
|
|
||||||
if (length(results) > 0) {
|
|
||||||
var lib_dir = shop.get_lib_dir()
|
|
||||||
ensure_dir(lib_dir)
|
|
||||||
var lib_name = shop.lib_name_for_package(pkg)
|
|
||||||
var manifest_path = lib_dir + '/' + lib_name + '.manifest.json'
|
|
||||||
var json = use('json')
|
|
||||||
fd.slurpwrite(manifest_path, stone(blob(json.encode(manifest))))
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +326,9 @@ Build.build_dynamic = function(pkg, target, buildtype) {
|
|||||||
// Build a static binary from multiple packages
|
// Build a static binary from multiple packages
|
||||||
// packages: array of package names
|
// packages: array of package names
|
||||||
// output: output binary path
|
// output: output binary path
|
||||||
Build.build_static = function(packages, target = Build.detect_host_target(), output, buildtype = 'release') {
|
Build.build_static = function(packages, target, output, buildtype) {
|
||||||
|
var _target = target || Build.detect_host_target()
|
||||||
|
var _buildtype = buildtype || 'release'
|
||||||
var all_objects = []
|
var all_objects = []
|
||||||
var all_ldflags = []
|
var all_ldflags = []
|
||||||
var seen_flags = {}
|
var seen_flags = {}
|
||||||
@@ -331,14 +338,14 @@ Build.build_static = function(packages, target = Build.detect_host_target(), out
|
|||||||
var is_core = (pkg == 'core')
|
var is_core = (pkg == 'core')
|
||||||
|
|
||||||
// For core, include main.c; for others, exclude it
|
// For core, include main.c; for others, exclude it
|
||||||
var objects = Build.build_package(pkg, target, !is_core, buildtype)
|
var objects = Build.build_package(pkg, _target, !is_core, _buildtype)
|
||||||
|
|
||||||
arrfor(objects, function(obj) {
|
arrfor(objects, function(obj) {
|
||||||
push(all_objects, obj)
|
push(all_objects, obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Collect LDFLAGS (with sigil replacement)
|
// Collect LDFLAGS (with sigil replacement)
|
||||||
var ldflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'LDFLAGS', target))
|
var ldflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'LDFLAGS', _target))
|
||||||
var pkg_dir = shop.get_package_dir(pkg)
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
|
|
||||||
// Deduplicate based on the entire LDFLAGS string for this package
|
// Deduplicate based on the entire LDFLAGS string for this package
|
||||||
@@ -346,28 +353,29 @@ Build.build_static = function(packages, target = Build.detect_host_target(), out
|
|||||||
if (!seen_flags[ldflags_key]) {
|
if (!seen_flags[ldflags_key]) {
|
||||||
seen_flags[ldflags_key] = true
|
seen_flags[ldflags_key] = true
|
||||||
arrfor(ldflags, function(flag) {
|
arrfor(ldflags, function(flag) {
|
||||||
// Resolve relative -L paths
|
var f = flag
|
||||||
if (starts_with(flag, '-L') && !starts_with(flag, '-L/')) {
|
if (starts_with(f, '-L') && !starts_with(f, '-L/')) {
|
||||||
flag = '-L"' + pkg_dir + '/' + text(flag, 2) + '"'
|
f = '-L"' + pkg_dir + '/' + text(f, 2) + '"'
|
||||||
}
|
}
|
||||||
push(all_ldflags, flag)
|
push(all_ldflags, f)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (length(all_objects) == 0) {
|
if (length(all_objects) == 0) {
|
||||||
throw Error('No object files to link')
|
print('No object files to link'); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
var cc = toolchains[target].c
|
var cc = toolchains[_target].c
|
||||||
var target_ldflags = toolchains[target].c_link_args || []
|
var target_ldflags = toolchains[_target].c_link_args || []
|
||||||
var exe_ext = toolchains[target].system == 'windows' ? '.exe' : ''
|
var exe_ext = toolchains[_target].system == 'windows' ? '.exe' : ''
|
||||||
|
|
||||||
if (!ends_with(output, exe_ext) && exe_ext) {
|
var out_path = output
|
||||||
output = output + exe_ext
|
if (!ends_with(out_path, exe_ext) && exe_ext) {
|
||||||
|
out_path = out_path + exe_ext
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd_parts = [cc]
|
var cmd_parts = [cc]
|
||||||
|
|
||||||
arrfor(all_objects, function(obj) {
|
arrfor(all_objects, function(obj) {
|
||||||
@@ -382,18 +390,18 @@ Build.build_static = function(packages, target = Build.detect_host_target(), out
|
|||||||
push(cmd_parts, flag)
|
push(cmd_parts, flag)
|
||||||
})
|
})
|
||||||
|
|
||||||
push(cmd_parts, '-o', '"' + output + '"')
|
push(cmd_parts, '-o', '"' + out_path + '"')
|
||||||
|
|
||||||
var cmd_str = text(cmd_parts, ' ')
|
var cmd_str = text(cmd_parts, ' ')
|
||||||
|
|
||||||
log.console('Linking ' + output)
|
log.console('Linking ' + out_path)
|
||||||
var ret = os.system(cmd_str)
|
var ret = os.system(cmd_str)
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
throw Error('Linking failed with command: ' + cmd_str)
|
print('Linking failed: ' + cmd_str); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console('Built ' + output)
|
log.console('Built ' + out_path)
|
||||||
return output
|
return out_path
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -431,9 +439,13 @@ function qbe_insert_dead_labels(il_text) {
|
|||||||
|
|
||||||
// Compile a .cm source file to a native .dylib via QBE
|
// Compile a .cm source file to a native .dylib via QBE
|
||||||
// Returns the content-addressed dylib path
|
// Returns the content-addressed dylib path
|
||||||
Build.compile_native = function(src_path, target, buildtype) {
|
Build.compile_native = function(src_path, target, buildtype, pkg) {
|
||||||
var _target = target || Build.detect_host_target()
|
var _target = target || Build.detect_host_target()
|
||||||
var _buildtype = buildtype || 'release'
|
var _buildtype = buildtype || 'release'
|
||||||
|
var qbe_rt_path = null
|
||||||
|
var native_stem = null
|
||||||
|
var native_install_dir = null
|
||||||
|
var native_install_path = null
|
||||||
|
|
||||||
if (!fd.is_file(src_path)) {
|
if (!fd.is_file(src_path)) {
|
||||||
print('Source file not found: ' + src_path); disrupt
|
print('Source file not found: ' + src_path); disrupt
|
||||||
@@ -461,7 +473,11 @@ Build.compile_native = function(src_path, target, buildtype) {
|
|||||||
var optimized = streamline_mod(compiled)
|
var optimized = streamline_mod(compiled)
|
||||||
|
|
||||||
// Step 2: Generate QBE IL
|
// Step 2: Generate QBE IL
|
||||||
var il = qbe_emit(optimized, qbe_macros)
|
var sym_name = null
|
||||||
|
if (pkg) {
|
||||||
|
sym_name = shop.c_symbol_for_file(pkg, fd.basename(src_path))
|
||||||
|
}
|
||||||
|
var il = qbe_emit(optimized, qbe_macros, sym_name)
|
||||||
|
|
||||||
// Step 3: Post-process (insert dead labels)
|
// Step 3: Post-process (insert dead labels)
|
||||||
il = qbe_insert_dead_labels(il)
|
il = qbe_insert_dead_labels(il)
|
||||||
@@ -498,7 +514,7 @@ Build.compile_native = function(src_path, target, buildtype) {
|
|||||||
|
|
||||||
// Step 7: Compile QBE runtime stubs if needed
|
// Step 7: Compile QBE runtime stubs if needed
|
||||||
if (!fd.is_file(rt_o_path)) {
|
if (!fd.is_file(rt_o_path)) {
|
||||||
var qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c'
|
qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c'
|
||||||
rc = os.system(cc + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC')
|
rc = os.system(cc + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC')
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
print('QBE runtime stubs compilation failed'); disrupt
|
print('QBE runtime stubs compilation failed'); disrupt
|
||||||
@@ -520,6 +536,16 @@ Build.compile_native = function(src_path, target, buildtype) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.console('Built native: ' + fd.basename(dylib_path))
|
log.console('Built native: ' + fd.basename(dylib_path))
|
||||||
|
|
||||||
|
// Install to deterministic lib/<pkg>/<stem>.dylib
|
||||||
|
if (pkg) {
|
||||||
|
native_stem = fd.stem(fd.basename(src_path))
|
||||||
|
native_install_dir = shop.get_lib_dir() + '/' + shop.lib_name_for_package(pkg)
|
||||||
|
ensure_dir(native_install_dir)
|
||||||
|
native_install_path = native_install_dir + '/' + native_stem + dylib_ext
|
||||||
|
fd.slurpwrite(native_install_path, fd.slurp(dylib_path))
|
||||||
|
}
|
||||||
|
|
||||||
return dylib_path
|
return dylib_path
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,10 +638,11 @@ Build.build_all_dynamic = function(target, buildtype) {
|
|||||||
|
|
||||||
var packages = shop.list_packages()
|
var packages = shop.list_packages()
|
||||||
var results = []
|
var results = []
|
||||||
|
var core_mods = null
|
||||||
|
|
||||||
// Build core first
|
// Build core first
|
||||||
if (find(packages, function(p) { return p == 'core' }) != null) {
|
if (find(packages, function(p) { return p == 'core' }) != null) {
|
||||||
var core_mods = Build.build_dynamic('core', _target, _buildtype)
|
core_mods = Build.build_dynamic('core', _target, _buildtype)
|
||||||
push(results, {package: 'core', modules: core_mods})
|
push(results, {package: 'core', modules: core_mods})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
97
cellfs.cm
97
cellfs.cm
@@ -22,55 +22,55 @@ function normalize_path(path) {
|
|||||||
|
|
||||||
// Check if a file exists in a specific mount
|
// Check if a file exists in a specific mount
|
||||||
function mount_exists(mount, path) {
|
function mount_exists(mount, path) {
|
||||||
|
var result = false
|
||||||
|
var _check = null
|
||||||
if (mount.type == 'zip') {
|
if (mount.type == 'zip') {
|
||||||
try {
|
_check = function() {
|
||||||
mount.handle.mod(path)
|
mount.handle.mod(path)
|
||||||
return true
|
result = true
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false
|
_check()
|
||||||
}
|
|
||||||
} else if (mount.type == 'qop') {
|
} else if (mount.type == 'qop') {
|
||||||
try {
|
_check = function() {
|
||||||
return mount.handle.stat(path) != null
|
result = mount.handle.stat(path) != null
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false
|
_check()
|
||||||
}
|
} else {
|
||||||
} else { // fs
|
|
||||||
var full_path = fd.join_paths(mount.source, path)
|
var full_path = fd.join_paths(mount.source, path)
|
||||||
try {
|
_check = function() {
|
||||||
var st = fd.stat(full_path)
|
var st = fd.stat(full_path)
|
||||||
return st.isFile || st.isDirectory
|
result = st.isFile || st.isDirectory
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false
|
_check()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a path refers to a directory in a specific mount
|
// Check if a path refers to a directory in a specific mount
|
||||||
function is_directory(path) {
|
function is_directory(path) {
|
||||||
var res = resolve(path)
|
var res = resolve(path)
|
||||||
var mount = res.mount
|
var mount = res.mount
|
||||||
|
var result = false
|
||||||
|
var _check = null
|
||||||
if (mount.type == 'zip') {
|
if (mount.type == 'zip') {
|
||||||
try {
|
_check = function() {
|
||||||
return mount.handle.is_directory(path);
|
result = mount.handle.is_directory(path)
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false;
|
_check()
|
||||||
}
|
|
||||||
} else if (mount.type == 'qop') {
|
} else if (mount.type == 'qop') {
|
||||||
try {
|
_check = function() {
|
||||||
return mount.handle.is_directory(path);
|
result = mount.handle.is_directory(path)
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false;
|
_check()
|
||||||
}
|
} else {
|
||||||
} else { // fs
|
|
||||||
var full_path = fd.join_paths(mount.source, path)
|
var full_path = fd.join_paths(mount.source, path)
|
||||||
try {
|
_check = function() {
|
||||||
var st = fd.stat(full_path)
|
var st = fd.stat(full_path)
|
||||||
return st.isDirectory
|
result = st.isDirectory
|
||||||
} catch (e) {
|
} disruption {}
|
||||||
return false
|
_check()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a path to a specific mount and relative path
|
// Resolve a path to a specific mount and relative path
|
||||||
@@ -102,7 +102,7 @@ function resolve(path, must_exist) {
|
|||||||
}, false, true)
|
}, false, true)
|
||||||
|
|
||||||
if (!mount) {
|
if (!mount) {
|
||||||
throw Error("Unknown mount point: @" + mount_name)
|
print("Unknown mount point: @" + mount_name); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
return { mount: mount, path: rel_path }
|
return { mount: mount, path: rel_path }
|
||||||
@@ -122,7 +122,7 @@ function resolve(path, must_exist) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (must_exist) {
|
if (must_exist) {
|
||||||
throw Error("File not found in any mount: " + path)
|
print("File not found in any mount: " + path); disrupt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,12 +144,11 @@ function mount(source, name) {
|
|||||||
} else if (st.isFile) {
|
} else if (st.isFile) {
|
||||||
var blob = fd.slurp(source)
|
var blob = fd.slurp(source)
|
||||||
|
|
||||||
// Try QOP first (it's likely faster to fail?) or Zip?
|
|
||||||
// QOP open checks magic.
|
|
||||||
var qop_archive = null
|
var qop_archive = null
|
||||||
try {
|
var _try_qop = function() {
|
||||||
qop_archive = qop.open(blob)
|
qop_archive = qop.open(blob)
|
||||||
} catch(e) {}
|
} disruption {}
|
||||||
|
_try_qop()
|
||||||
|
|
||||||
if (qop_archive) {
|
if (qop_archive) {
|
||||||
mount_info.type = 'qop'
|
mount_info.type = 'qop'
|
||||||
@@ -158,7 +157,7 @@ function mount(source, name) {
|
|||||||
} else {
|
} else {
|
||||||
var zip = miniz.read(blob)
|
var zip = miniz.read(blob)
|
||||||
if (!is_object(zip) || !is_function(zip.count)) {
|
if (!is_object(zip) || !is_function(zip.count)) {
|
||||||
throw Error("Invalid archive file (not zip or qop): " + source)
|
print("Invalid archive file (not zip or qop): " + source); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
mount_info.type = 'zip'
|
mount_info.type = 'zip'
|
||||||
@@ -166,7 +165,7 @@ function mount(source, name) {
|
|||||||
mount_info.zip_blob = blob // keep blob alive
|
mount_info.zip_blob = blob // keep blob alive
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Error("Unsupported mount source type: " + source)
|
print("Unsupported mount source type: " + source); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
push(mounts, mount_info)
|
push(mounts, mount_info)
|
||||||
@@ -182,13 +181,13 @@ function unmount(name_or_source) {
|
|||||||
// Read file
|
// Read file
|
||||||
function slurp(path) {
|
function slurp(path) {
|
||||||
var res = resolve(path, true)
|
var res = resolve(path, true)
|
||||||
if (!res) throw Error("File not found: " + path)
|
if (!res) { print("File not found: " + path); disrupt }
|
||||||
|
|
||||||
if (res.mount.type == 'zip') {
|
if (res.mount.type == 'zip') {
|
||||||
return res.mount.handle.slurp(res.path)
|
return res.mount.handle.slurp(res.path)
|
||||||
} else if (res.mount.type == 'qop') {
|
} else if (res.mount.type == 'qop') {
|
||||||
var data = res.mount.handle.read(res.path)
|
var data = res.mount.handle.read(res.path)
|
||||||
if (!data) throw Error("File not found in qop: " + path)
|
if (!data) { print("File not found in qop: " + path); disrupt }
|
||||||
return data
|
return data
|
||||||
} else {
|
} else {
|
||||||
var full_path = fd.join_paths(res.mount.source, res.path)
|
var full_path = fd.join_paths(res.mount.source, res.path)
|
||||||
@@ -217,8 +216,8 @@ function exists(path) {
|
|||||||
// Stat
|
// Stat
|
||||||
function stat(path) {
|
function stat(path) {
|
||||||
var res = resolve(path, true)
|
var res = resolve(path, true)
|
||||||
if (!res) throw Error("File not found: " + path)
|
if (!res) { print("File not found: " + path); disrupt }
|
||||||
|
|
||||||
if (res.mount.type == 'zip') {
|
if (res.mount.type == 'zip') {
|
||||||
var mod = res.mount.handle.mod(res.path)
|
var mod = res.mount.handle.mod(res.path)
|
||||||
return {
|
return {
|
||||||
@@ -228,7 +227,7 @@ function stat(path) {
|
|||||||
}
|
}
|
||||||
} else if (res.mount.type == 'qop') {
|
} else if (res.mount.type == 'qop') {
|
||||||
var s = res.mount.handle.stat(res.path)
|
var s = res.mount.handle.stat(res.path)
|
||||||
if (!s) throw Error("File not found in qop: " + path)
|
if (!s) { print("File not found in qop: " + path); disrupt }
|
||||||
return {
|
return {
|
||||||
filesize: s.size,
|
filesize: s.size,
|
||||||
modtime: s.modtime,
|
modtime: s.modtime,
|
||||||
@@ -261,7 +260,7 @@ function mount_package(name) {
|
|||||||
var dir = shop.get_package_dir(name)
|
var dir = shop.get_package_dir(name)
|
||||||
|
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
throw Error("Package not found: " + name)
|
print("Package not found: " + name); disrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(dir, name)
|
mount(dir, name)
|
||||||
@@ -275,7 +274,7 @@ function match(str, pattern) {
|
|||||||
|
|
||||||
function rm(path) {
|
function rm(path) {
|
||||||
var res = resolve(path, true)
|
var res = resolve(path, true)
|
||||||
if (res.mount.type != 'fs') throw Error("Cannot delete from non-fs mount")
|
if (res.mount.type != 'fs') { print("Cannot delete from non-fs mount"); disrupt }
|
||||||
|
|
||||||
var full_path = fd.join_paths(res.mount.source, res.path)
|
var full_path = fd.join_paths(res.mount.source, res.path)
|
||||||
var st = fd.stat(full_path)
|
var st = fd.stat(full_path)
|
||||||
|
|||||||
32
clean.ce
32
clean.ce
@@ -23,8 +23,11 @@ var clean_build = false
|
|||||||
var clean_fetch = false
|
var clean_fetch = false
|
||||||
var deep = false
|
var deep = false
|
||||||
var dry_run = false
|
var dry_run = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
var deps = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--build') {
|
if (args[i] == '--build') {
|
||||||
clean_build = true
|
clean_build = true
|
||||||
} else if (args[i] == '--fetch') {
|
} else if (args[i] == '--fetch') {
|
||||||
@@ -74,7 +77,7 @@ var is_world_scope = (scope == 'world')
|
|||||||
|
|
||||||
if (!is_shop_scope && !is_world_scope) {
|
if (!is_shop_scope && !is_world_scope) {
|
||||||
if (scope == '.' || starts_with(scope, './') || starts_with(scope, '../') || fd.is_dir(scope)) {
|
if (scope == '.' || starts_with(scope, './') || starts_with(scope, '../') || fd.is_dir(scope)) {
|
||||||
var resolved = fd.realpath(scope)
|
resolved = fd.realpath(scope)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
scope = resolved
|
scope = resolved
|
||||||
}
|
}
|
||||||
@@ -96,15 +99,17 @@ if (is_shop_scope) {
|
|||||||
// Single package
|
// Single package
|
||||||
push(packages_to_clean, scope)
|
push(packages_to_clean, scope)
|
||||||
|
|
||||||
|
var _gather = null
|
||||||
if (deep) {
|
if (deep) {
|
||||||
try {
|
_gather = function() {
|
||||||
var deps = pkg.gather_dependencies(scope)
|
deps = pkg.gather_dependencies(scope)
|
||||||
arrfor(deps, function(dep) {
|
arrfor(deps, function(dep) {
|
||||||
push(packages_to_clean, dep)
|
push(packages_to_clean, dep)
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Skip if can't read dependencies
|
// Skip if can't read dependencies
|
||||||
}
|
}
|
||||||
|
_gather()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +173,7 @@ if (clean_fetch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute or report
|
// Execute or report
|
||||||
|
var deleted_count = 0
|
||||||
if (dry_run) {
|
if (dry_run) {
|
||||||
log.console("Would delete:")
|
log.console("Would delete:")
|
||||||
if (length(files_to_delete) == 0 && length(dirs_to_delete) == 0) {
|
if (length(files_to_delete) == 0 && length(dirs_to_delete) == 0) {
|
||||||
@@ -181,20 +187,19 @@ if (dry_run) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var deleted_count = 0
|
|
||||||
|
|
||||||
arrfor(files_to_delete, function(f) {
|
arrfor(files_to_delete, function(f) {
|
||||||
try {
|
var _del = function() {
|
||||||
fd.unlink(f)
|
fd.unlink(f)
|
||||||
log.console("Deleted: " + f)
|
log.console("Deleted: " + f)
|
||||||
deleted_count++
|
deleted_count++
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error("Failed to delete " + f + ": " + e)
|
log.error("Failed to delete " + f)
|
||||||
}
|
}
|
||||||
|
_del()
|
||||||
})
|
})
|
||||||
|
|
||||||
arrfor(dirs_to_delete, function(d) {
|
arrfor(dirs_to_delete, function(d) {
|
||||||
try {
|
var _del = function() {
|
||||||
if (fd.is_link(d)) {
|
if (fd.is_link(d)) {
|
||||||
fd.unlink(d)
|
fd.unlink(d)
|
||||||
} else {
|
} else {
|
||||||
@@ -202,9 +207,10 @@ if (dry_run) {
|
|||||||
}
|
}
|
||||||
log.console("Deleted: " + d)
|
log.console("Deleted: " + d)
|
||||||
deleted_count++
|
deleted_count++
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error("Failed to delete " + d + ": " + e)
|
log.error("Failed to delete " + d)
|
||||||
}
|
}
|
||||||
|
_del()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (deleted_count == 0) {
|
if (deleted_count == 0) {
|
||||||
|
|||||||
65
clone.ce
65
clone.ce
@@ -7,11 +7,14 @@ var fd = use('fd')
|
|||||||
var http = use('http')
|
var http = use('http')
|
||||||
var miniz = use('miniz')
|
var miniz = use('miniz')
|
||||||
|
|
||||||
|
var resolved = null
|
||||||
|
var cwd = null
|
||||||
|
var parent = null
|
||||||
|
|
||||||
if (length(args) < 2) {
|
if (length(args) < 2) {
|
||||||
log.console("Usage: cell clone <origin> <path>")
|
log.console("Usage: cell clone <origin> <path>")
|
||||||
log.console("Clones a cell package to a local path and links it.")
|
log.console("Clones a cell package to a local path and links it.")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var origin = args[0]
|
var origin = args[0]
|
||||||
@@ -19,19 +22,19 @@ var target_path = args[1]
|
|||||||
|
|
||||||
// Resolve target path to absolute
|
// Resolve target path to absolute
|
||||||
if (target_path == '.' || starts_with(target_path, './') || starts_with(target_path, '../')) {
|
if (target_path == '.' || starts_with(target_path, './') || starts_with(target_path, '../')) {
|
||||||
var resolved = fd.realpath(target_path)
|
resolved = fd.realpath(target_path)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_path = resolved
|
target_path = resolved
|
||||||
} else {
|
} else {
|
||||||
// Path doesn't exist yet, resolve relative to cwd
|
// Path doesn't exist yet, resolve relative to cwd
|
||||||
var cwd = fd.realpath('.')
|
cwd = fd.realpath('.')
|
||||||
if (target_path == '.') {
|
if (target_path == '.') {
|
||||||
target_path = cwd
|
target_path = cwd
|
||||||
} else if (starts_with(target_path, './')) {
|
} else if (starts_with(target_path, './')) {
|
||||||
target_path = cwd + text(target_path, 1)
|
target_path = cwd + text(target_path, 1)
|
||||||
} else if (starts_with(target_path, '../')) {
|
} else if (starts_with(target_path, '../')) {
|
||||||
// Go up one directory from cwd
|
// Go up one directory from cwd
|
||||||
var parent = fd.dirname(cwd)
|
parent = fd.dirname(cwd)
|
||||||
target_path = parent + text(target_path, 2)
|
target_path = parent + text(target_path, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +44,6 @@ if (target_path == '.' || starts_with(target_path, './') || starts_with(target_p
|
|||||||
if (fd.is_dir(target_path)) {
|
if (fd.is_dir(target_path)) {
|
||||||
log.console("Error: " + target_path + " already exists")
|
log.console("Error: " + target_path + " already exists")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console("Cloning " + origin + " to " + target_path + "...")
|
log.console("Cloning " + origin + " to " + target_path + "...")
|
||||||
@@ -51,7 +53,6 @@ var info = shop.resolve_package_info(origin)
|
|||||||
if (!info || info == 'local') {
|
if (!info || info == 'local') {
|
||||||
log.console("Error: " + origin + " is not a remote package")
|
log.console("Error: " + origin + " is not a remote package")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update to get the commit hash
|
// Update to get the commit hash
|
||||||
@@ -59,7 +60,6 @@ var update_result = shop.update(origin)
|
|||||||
if (!update_result) {
|
if (!update_result) {
|
||||||
log.console("Error: Could not fetch " + origin)
|
log.console("Error: Could not fetch " + origin)
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch and extract to the target path
|
// Fetch and extract to the target path
|
||||||
@@ -68,54 +68,61 @@ var entry = lock[origin]
|
|||||||
if (!entry || !entry.commit) {
|
if (!entry || !entry.commit) {
|
||||||
log.console("Error: No commit found for " + origin)
|
log.console("Error: No commit found for " + origin)
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var download_url = shop.get_download_url(origin, entry.commit)
|
var download_url = shop.get_download_url(origin, entry.commit)
|
||||||
log.console("Downloading from " + download_url)
|
log.console("Downloading from " + download_url)
|
||||||
|
|
||||||
try {
|
var zip_blob = null
|
||||||
var zip_blob = http.fetch(download_url)
|
var zip = null
|
||||||
|
var count = 0
|
||||||
|
var i = 0
|
||||||
|
var filename = null
|
||||||
|
var first_slash = null
|
||||||
|
var rel_path = null
|
||||||
|
var full_path = null
|
||||||
|
var dir_path = null
|
||||||
|
|
||||||
|
var _clone = function() {
|
||||||
|
zip_blob = http.fetch(download_url)
|
||||||
|
|
||||||
// Extract zip to target path
|
// Extract zip to target path
|
||||||
var zip = miniz.read(zip_blob)
|
zip = miniz.read(zip_blob)
|
||||||
if (!zip) {
|
if (!zip) {
|
||||||
log.console("Error: Failed to read zip archive")
|
log.console("Error: Failed to read zip archive")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create target directory
|
// Create target directory
|
||||||
fd.mkdir(target_path)
|
fd.mkdir(target_path)
|
||||||
|
|
||||||
var count = zip.count()
|
count = zip.count()
|
||||||
for (var i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
if (zip.is_directory(i)) continue
|
if (zip.is_directory(i)) continue
|
||||||
var filename = zip.get_filename(i)
|
filename = zip.get_filename(i)
|
||||||
var first_slash = search(filename, '/')
|
first_slash = search(filename, '/')
|
||||||
if (first_slash == null) continue
|
if (first_slash == null) continue
|
||||||
if (first_slash + 1 >= length(filename)) continue
|
if (first_slash + 1 >= length(filename)) continue
|
||||||
|
|
||||||
var rel_path = text(filename, first_slash + 1)
|
rel_path = text(filename, first_slash + 1)
|
||||||
var full_path = target_path + '/' + rel_path
|
full_path = target_path + '/' + rel_path
|
||||||
var dir_path = fd.dirname(full_path)
|
dir_path = fd.dirname(full_path)
|
||||||
|
|
||||||
// Ensure directory exists
|
// Ensure directory exists
|
||||||
if (!fd.is_dir(dir_path)) {
|
if (!fd.is_dir(dir_path)) {
|
||||||
fd.mkdir(dir_path)
|
fd.mkdir(dir_path)
|
||||||
}
|
}
|
||||||
fd.slurpwrite(full_path, zip.slurp(filename))
|
fd.slurpwrite(full_path, zip.slurp(filename))
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console("Extracted to " + target_path)
|
log.console("Extracted to " + target_path)
|
||||||
|
|
||||||
// Link the origin to the cloned path
|
// Link the origin to the cloned path
|
||||||
link.add(origin, target_path, shop)
|
link.add(origin, target_path, shop)
|
||||||
log.console("Linked " + origin + " -> " + target_path)
|
log.console("Linked " + origin + " -> " + target_path)
|
||||||
|
} disruption {
|
||||||
} catch (e) {
|
log.console("Error during clone")
|
||||||
log.console("Error: " + e.message)
|
|
||||||
if (e.stack) log.console(e.stack)
|
|
||||||
}
|
}
|
||||||
|
_clone()
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
31
compile.ce
31
compile.ce
@@ -1,7 +1,7 @@
|
|||||||
// compile.ce — compile a .cm module to native .dylib via QBE
|
// compile.ce — compile a .cm module to native .dylib via QBE
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// cell --core . compile.ce <file.cm>
|
// cell --dev compile.ce <file.cm>
|
||||||
//
|
//
|
||||||
// Produces <file>.dylib in the current directory.
|
// Produces <file>.dylib in the current directory.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ var fd = use('fd')
|
|||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
|
||||||
if (length(args) < 1) {
|
if (length(args) < 1) {
|
||||||
print('usage: cell --core . compile.ce <file.cm>')
|
print('usage: cell --dev compile.ce <file.cm>')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,39 +26,22 @@ var ssa_path = tmp + '.ssa'
|
|||||||
var s_path = tmp + '.s'
|
var s_path = tmp + '.s'
|
||||||
var o_path = tmp + '.o'
|
var o_path = tmp + '.o'
|
||||||
var rt_o_path = '/tmp/qbe_rt.o'
|
var rt_o_path = '/tmp/qbe_rt.o'
|
||||||
var dylib_path = base + '.dylib'
|
var dylib_path = file + '.dylib'
|
||||||
var cwd = fd.getcwd()
|
var cwd = fd.getcwd()
|
||||||
var rc = 0
|
var rc = 0
|
||||||
|
|
||||||
// Step 1: emit QBE IL
|
// Step 1: emit QBE IL
|
||||||
print('emit qbe...')
|
print('emit qbe...')
|
||||||
rc = os.system('cd ' + cwd + ' && ./cell --core . --emit-qbe ' + file + ' > ' + ssa_path)
|
rc = os.system('cd ' + cwd + ' && ./cell --dev qbe.ce ' + file + ' > ' + ssa_path)
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
print('failed to emit qbe il')
|
print('failed to emit qbe il')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: post-process — insert dead labels after ret/jmp, append wrapper
|
// Step 2: append wrapper function — called as symbol(ctx) by os.dylib_symbol.
|
||||||
// Use awk via shell to avoid blob/slurpwrite issues with long strings
|
|
||||||
print('post-process...')
|
|
||||||
var awk_cmd = `awk '
|
|
||||||
need_label && /^[[:space:]]*[^@}]/ && NF > 0 {
|
|
||||||
print "@_dead_" dead_id; dead_id++; need_label=0
|
|
||||||
}
|
|
||||||
/^@/ || /^}/ || NF==0 { need_label=0 }
|
|
||||||
/^[[:space:]]*ret / || /^[[:space:]]*jmp / { need_label=1; print; next }
|
|
||||||
{ print }
|
|
||||||
' ` + ssa_path + ` > ` + tmp + `_fixed.ssa`
|
|
||||||
rc = os.system(awk_cmd)
|
|
||||||
if (rc != 0) {
|
|
||||||
print('post-process failed')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append wrapper function — called as symbol(ctx) by os.dylib_symbol.
|
|
||||||
// Delegates to cell_rt_module_entry which heap-allocates a frame
|
// Delegates to cell_rt_module_entry which heap-allocates a frame
|
||||||
// (so closures survive) and calls cell_main.
|
// (so closures survive) and calls cell_main.
|
||||||
var wrapper_cmd = `printf '\nexport function l $` + symbol + `(l %%ctx) {\n@entry\n %%result =l call $cell_rt_module_entry(l %%ctx)\n ret %%result\n}\n' >> ` + tmp + `_fixed.ssa`
|
var wrapper_cmd = `printf '\nexport function l $` + symbol + `(l %%ctx) {\n@entry\n %%result =l call $cell_rt_module_entry(l %%ctx)\n ret %%result\n}\n' >> ` + ssa_path
|
||||||
rc = os.system(wrapper_cmd)
|
rc = os.system(wrapper_cmd)
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
print('wrapper append failed')
|
print('wrapper append failed')
|
||||||
@@ -67,7 +50,7 @@ if (rc != 0) {
|
|||||||
|
|
||||||
// Step 3: compile QBE IL to assembly
|
// Step 3: compile QBE IL to assembly
|
||||||
print('qbe compile...')
|
print('qbe compile...')
|
||||||
rc = os.system('~/.local/bin/qbe -o ' + s_path + ' ' + tmp + '_fixed.ssa')
|
rc = os.system('qbe -o ' + s_path + ' ' + ssa_path)
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
print('qbe compilation failed')
|
print('qbe compilation failed')
|
||||||
return
|
return
|
||||||
|
|||||||
98
compile_seed.ce
Normal file
98
compile_seed.ce
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// compile_seed.ce — compile a .cm module to native .dylib via QBE (seed mode)
|
||||||
|
// Usage: ./cell --dev --seed compile_seed <file.cm>
|
||||||
|
|
||||||
|
var fd = use("fd")
|
||||||
|
var os = use("os")
|
||||||
|
var tokenize = use("tokenize")
|
||||||
|
var parse = use("parse")
|
||||||
|
var fold = use("fold")
|
||||||
|
var mcode = use("mcode")
|
||||||
|
var streamline = use("streamline")
|
||||||
|
var qbe_macros = use("qbe")
|
||||||
|
var qbe_emit = use("qbe_emit")
|
||||||
|
|
||||||
|
if (length(args) < 1) {
|
||||||
|
print("usage: cell --dev --seed compile_seed <file.cm>")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = args[0]
|
||||||
|
var base = file
|
||||||
|
if (ends_with(base, ".cm")) {
|
||||||
|
base = text(base, 0, length(base) - 3)
|
||||||
|
} else if (ends_with(base, ".ce")) {
|
||||||
|
base = text(base, 0, length(base) - 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
var safe = replace(replace(replace(base, "/", "_"), "-", "_"), ".", "_")
|
||||||
|
var symbol = "js_" + safe + "_use"
|
||||||
|
var tmp = "/tmp/qbe_" + safe
|
||||||
|
var ssa_path = tmp + ".ssa"
|
||||||
|
var s_path = tmp + ".s"
|
||||||
|
var o_path = tmp + ".o"
|
||||||
|
var rt_o_path = "/tmp/qbe_rt.o"
|
||||||
|
var dylib_path = file + ".dylib"
|
||||||
|
var rc = 0
|
||||||
|
|
||||||
|
// Step 1: compile to QBE IL
|
||||||
|
print("compiling " + file + " to QBE IL...")
|
||||||
|
var src = text(fd.slurp(file))
|
||||||
|
var result = tokenize(src, file)
|
||||||
|
var ast = parse(result.tokens, src, file, tokenize)
|
||||||
|
var folded = fold(ast)
|
||||||
|
var compiled = mcode(folded)
|
||||||
|
var optimized = streamline(compiled)
|
||||||
|
var il = qbe_emit(optimized, qbe_macros)
|
||||||
|
|
||||||
|
// Step 2: append wrapper function
|
||||||
|
var wrapper = `
|
||||||
|
export function l $${symbol}(l %ctx) {
|
||||||
|
@entry
|
||||||
|
%result =l call $cell_rt_module_entry(l %ctx)
|
||||||
|
ret %result
|
||||||
|
}
|
||||||
|
`
|
||||||
|
il = il + wrapper
|
||||||
|
|
||||||
|
// Write IL to file — remove old file first to avoid leftover content
|
||||||
|
if (fd.is_file(ssa_path)) fd.unlink(ssa_path)
|
||||||
|
var out_fd = fd.open(ssa_path, 1537, 420)
|
||||||
|
fd.write(out_fd, il)
|
||||||
|
fd.close(out_fd)
|
||||||
|
print("wrote " + ssa_path + " (" + text(length(il)) + " bytes)")
|
||||||
|
|
||||||
|
// Step 3: compile QBE IL to assembly
|
||||||
|
print("qbe compile...")
|
||||||
|
rc = os.system("qbe -o " + s_path + " " + ssa_path)
|
||||||
|
if (rc != 0) {
|
||||||
|
print("qbe compilation failed")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: assemble
|
||||||
|
print("assemble...")
|
||||||
|
rc = os.system("cc -c " + s_path + " -o " + o_path)
|
||||||
|
if (rc != 0) {
|
||||||
|
print("assembly failed")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: compile runtime stubs
|
||||||
|
if (!fd.is_file(rt_o_path)) {
|
||||||
|
print("compile runtime stubs...")
|
||||||
|
rc = os.system("cc -c source/qbe_helpers.c -o " + rt_o_path + " -fPIC -Isource")
|
||||||
|
if (rc != 0) {
|
||||||
|
print("runtime stubs compilation failed")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: link dylib
|
||||||
|
print("link...")
|
||||||
|
rc = os.system("cc -shared -fPIC -undefined dynamic_lookup " + o_path + " " + rt_o_path + " -o " + dylib_path)
|
||||||
|
if (rc != 0) {
|
||||||
|
print("linking failed")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
print("built: " + dylib_path)
|
||||||
261
config.ce
261
config.ce
@@ -47,8 +47,10 @@ function get_nested(obj, path) {
|
|||||||
// Set a value in nested object using path
|
// Set a value in nested object using path
|
||||||
function set_nested(obj, path, value) {
|
function set_nested(obj, path, value) {
|
||||||
var current = obj
|
var current = obj
|
||||||
for (var i = 0; i < length(path) - 1; i++) {
|
var i = 0
|
||||||
var segment = path[i]
|
var segment = null
|
||||||
|
for (i = 0; i < length(path) - 1; i++) {
|
||||||
|
segment = path[i]
|
||||||
if (is_null(current[segment]) || !is_object(current[segment])) {
|
if (is_null(current[segment]) || !is_object(current[segment])) {
|
||||||
current[segment] = {}
|
current[segment] = {}
|
||||||
}
|
}
|
||||||
@@ -59,15 +61,17 @@ function set_nested(obj, path, value) {
|
|||||||
|
|
||||||
// Parse value string into appropriate type
|
// Parse value string into appropriate type
|
||||||
function parse_value(str) {
|
function parse_value(str) {
|
||||||
|
var num_str = null
|
||||||
|
var n = null
|
||||||
// Boolean
|
// Boolean
|
||||||
if (str == 'true') return true
|
if (str == 'true') return true
|
||||||
if (str == 'false') return false
|
if (str == 'false') return false
|
||||||
|
|
||||||
// Number (including underscores)
|
// Number
|
||||||
var num_str = replace(str, /_/g, '')
|
num_str = replace(str, /_/g, '')
|
||||||
if (/^-?\d+$/.test(num_str)) return parseInt(num_str)
|
n = number(num_str)
|
||||||
if (/^-?\d*\.\d+$/.test(num_str)) return parseFloat(num_str)
|
if (n != null) return n
|
||||||
|
|
||||||
// String
|
// String
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
@@ -75,22 +79,19 @@ function parse_value(str) {
|
|||||||
// Format value for display
|
// Format value for display
|
||||||
function format_value(val) {
|
function format_value(val) {
|
||||||
if (is_text(val)) return '"' + val + '"'
|
if (is_text(val)) return '"' + val + '"'
|
||||||
if (is_number(val) && val >= 1000) {
|
|
||||||
// Add underscores to large numbers
|
|
||||||
return replace(val.toString(), /\B(?=(\d{3})+(?!\d))/g, '_')
|
|
||||||
}
|
|
||||||
return text(val)
|
return text(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print configuration tree recursively
|
// Print configuration tree recursively
|
||||||
function print_config(obj, prefix = '') {
|
function print_config(obj, pfx) {
|
||||||
|
var p = pfx || ''
|
||||||
arrfor(array(obj), function(key) {
|
arrfor(array(obj), function(key) {
|
||||||
var val = obj[key]
|
var val = obj[key]
|
||||||
var full_key = prefix ? prefix + '.' + key : key
|
var full_key = p ? p + '.' + key : key
|
||||||
|
|
||||||
if (is_object(val))
|
if (is_object(val))
|
||||||
print_config(val, full_key)
|
print_config(val, full_key)
|
||||||
else
|
else if (!is_null(val))
|
||||||
log.console(full_key + ' = ' + format_value(val))
|
log.console(full_key + ' = ' + format_value(val))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -99,151 +100,123 @@ function print_config(obj, prefix = '') {
|
|||||||
if (length(args) == 0) {
|
if (length(args) == 0) {
|
||||||
print_help()
|
print_help()
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = pkg.load_config()
|
var config = pkg.load_config()
|
||||||
if (!config) {
|
if (!config) {
|
||||||
log.error("Failed to load cell.toml")
|
log.error("Failed to load cell.toml")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var command = args[0]
|
var command = args[0]
|
||||||
var key
|
var key = null
|
||||||
var path
|
var path = null
|
||||||
var value
|
var value = null
|
||||||
|
var value_str = null
|
||||||
|
var valid_system_keys = null
|
||||||
|
var actor_name = null
|
||||||
|
var actor_cmd = null
|
||||||
|
|
||||||
switch (command) {
|
if (command == 'help' || command == '-h' || command == '--help') {
|
||||||
case 'help':
|
print_help()
|
||||||
case '-h':
|
} else if (command == 'list') {
|
||||||
case '--help':
|
log.console("# Cell Configuration")
|
||||||
print_help()
|
log.console("")
|
||||||
break
|
print_config(config)
|
||||||
|
} else if (command == 'get') {
|
||||||
case 'list':
|
if (length(args) < 2) {
|
||||||
log.console("# Cell Configuration")
|
log.error("Usage: cell config get <key>")
|
||||||
log.console("")
|
$stop()
|
||||||
print_config(config)
|
}
|
||||||
break
|
key = args[1]
|
||||||
|
path = parse_key(key)
|
||||||
case 'get':
|
value = get_nested(config, path)
|
||||||
if (length(args) < 2) {
|
|
||||||
log.error("Usage: cell config get <key>")
|
if (value == null) {
|
||||||
|
log.error("Key not found: " + key)
|
||||||
|
} else if (is_object(value)) {
|
||||||
|
print_config(value, key)
|
||||||
|
} else {
|
||||||
|
log.console(key + ' = ' + format_value(value))
|
||||||
|
}
|
||||||
|
} else if (command == 'set') {
|
||||||
|
if (length(args) < 3) {
|
||||||
|
log.error("Usage: cell config set <key> <value>")
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
key = args[1]
|
||||||
|
value_str = args[2]
|
||||||
|
path = parse_key(key)
|
||||||
|
value = parse_value(value_str)
|
||||||
|
|
||||||
|
if (path[0] == 'system') {
|
||||||
|
valid_system_keys = [
|
||||||
|
'ar_timer', 'actor_memory', 'net_service',
|
||||||
|
'reply_timeout', 'actor_max', 'stack_max'
|
||||||
|
]
|
||||||
|
if (find(valid_system_keys, path[1]) == null) {
|
||||||
|
log.error("Invalid system key. Valid keys: " + text(valid_system_keys, ', '))
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
key = args[1]
|
}
|
||||||
path = parse_key(key)
|
|
||||||
value = get_nested(config, path)
|
set_nested(config, path, value)
|
||||||
|
pkg.save_config(config)
|
||||||
if (value == null) {
|
log.console("Set " + key + " = " + format_value(value))
|
||||||
log.error("Key not found: " + key)
|
} else if (command == 'actor') {
|
||||||
} else if (isa(value, object)) {
|
if (length(args) < 3) {
|
||||||
// Print all nested values
|
log.error("Usage: cell config actor <name> <command> [options]")
|
||||||
print_config(value, key)
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
actor_name = args[1]
|
||||||
|
actor_cmd = args[2]
|
||||||
|
|
||||||
|
config.actors = config.actors || {}
|
||||||
|
config.actors[actor_name] = config.actors[actor_name] || {}
|
||||||
|
|
||||||
|
if (actor_cmd == 'list') {
|
||||||
|
if (length(array(config.actors[actor_name])) == 0) {
|
||||||
|
log.console("No configuration for actor: " + actor_name)
|
||||||
} else {
|
} else {
|
||||||
log.console(key + ' = ' + format_value(value))
|
log.console("# Configuration for actor: " + actor_name)
|
||||||
|
log.console("")
|
||||||
|
print_config(config.actors[actor_name], 'actors.' + actor_name)
|
||||||
}
|
}
|
||||||
break
|
} else if (actor_cmd == 'get') {
|
||||||
|
if (length(args) < 4) {
|
||||||
case 'set':
|
log.error("Usage: cell config actor <name> get <key>")
|
||||||
if (length(args) < 3) {
|
|
||||||
log.error("Usage: cell config set <key> <value>")
|
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var key = args[1]
|
key = args[3]
|
||||||
var value_str = args[2]
|
path = parse_key(key)
|
||||||
var path = parse_key(key)
|
value = get_nested(config.actors[actor_name], path)
|
||||||
var value = parse_value(value_str)
|
|
||||||
|
if (value == null) {
|
||||||
// Validate system keys
|
log.error("Key not found for actor " + actor_name + ": " + key)
|
||||||
if (path[0] == 'system') {
|
} else {
|
||||||
var valid_system_keys = [
|
log.console('actors.' + actor_name + '.' + key + ' = ' + format_value(value))
|
||||||
'ar_timer', 'actor_memory', 'net_service',
|
|
||||||
'reply_timeout', 'actor_max', 'stack_max'
|
|
||||||
]
|
|
||||||
if (find(valid_system_keys, path[1]) == null) {
|
|
||||||
log.error("Invalid system key. Valid keys: " + text(valid_system_keys, ', '))
|
|
||||||
$stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (actor_cmd == 'set') {
|
||||||
set_nested(config, path, value)
|
if (length(args) < 5) {
|
||||||
|
log.error("Usage: cell config actor <name> set <key> <value>")
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
key = args[3]
|
||||||
|
value_str = args[4]
|
||||||
|
path = parse_key(key)
|
||||||
|
value = parse_value(value_str)
|
||||||
|
|
||||||
|
set_nested(config.actors[actor_name], path, value)
|
||||||
pkg.save_config(config)
|
pkg.save_config(config)
|
||||||
log.console("Set " + key + " = " + format_value(value))
|
log.console("Set actors." + actor_name + "." + key + " = " + format_value(value))
|
||||||
break
|
} else {
|
||||||
|
log.error("Unknown actor command: " + actor_cmd)
|
||||||
case 'actor':
|
log.console("Valid commands: list, get, set")
|
||||||
// Handle actor-specific configuration
|
}
|
||||||
if (length(args) < 3) {
|
} else {
|
||||||
log.error("Usage: cell config actor <name> <command> [options]")
|
log.error("Unknown command: " + command)
|
||||||
$stop()
|
print_help()
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var actor_name = args[1]
|
|
||||||
var actor_cmd = args[2]
|
|
||||||
|
|
||||||
// Initialize actors section if needed
|
|
||||||
config.actors = config.actors || {}
|
|
||||||
config.actors[actor_name] = config.actors[actor_name] || {}
|
|
||||||
|
|
||||||
switch (actor_cmd) {
|
|
||||||
case 'list':
|
|
||||||
if (length(array(config.actors[actor_name])) == 0) {
|
|
||||||
log.console("No configuration for actor: " + actor_name)
|
|
||||||
} else {
|
|
||||||
log.console("# Configuration for actor: " + actor_name)
|
|
||||||
log.console("")
|
|
||||||
print_config(config.actors[actor_name], 'actors.' + actor_name)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'get':
|
|
||||||
if (length(args) < 4) {
|
|
||||||
log.error("Usage: cell config actor <name> get <key>")
|
|
||||||
$stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
key = args[3]
|
|
||||||
path = parse_key(key)
|
|
||||||
value = get_nested(config.actors[actor_name], path)
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
log.error("Key not found for actor " + actor_name + ": " + key)
|
|
||||||
} else {
|
|
||||||
log.console('actors.' + actor_name + '.' + key + ' = ' + format_value(value))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'set':
|
|
||||||
if (length(args) < 5) {
|
|
||||||
log.error("Usage: cell config actor <name> set <key> <value>")
|
|
||||||
$stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
key = args[3]
|
|
||||||
var value_str = args[4]
|
|
||||||
path = parse_key(key)
|
|
||||||
value = parse_value(value_str)
|
|
||||||
|
|
||||||
set_nested(config.actors[actor_name], path, value)
|
|
||||||
pkg.save_config(config)
|
|
||||||
log.console("Set actors." + actor_name + "." + key + " = " + format_value(value))
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.error("Unknown actor command: " + actor_cmd)
|
|
||||||
log.console("Valid commands: list, get, set")
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.error("Unknown command: " + command)
|
|
||||||
print_help()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
2
crypto.c
2
crypto.c
@@ -238,7 +238,7 @@ static const JSCFunctionListEntry js_crypto_funcs[] = {
|
|||||||
JS_CFUNC_DEF("unlock", 3, js_crypto_unlock),
|
JS_CFUNC_DEF("unlock", 3, js_crypto_unlock),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_crypto_use(JSContext *js)
|
JSValue js_core_crypto_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ static const JSCFunctionListEntry js_debug_funcs[] = {
|
|||||||
MIST_FUNC_DEF(debug, backtrace_fns,0),
|
MIST_FUNC_DEF(debug, backtrace_fns,0),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_debug_use(JSContext *js) {
|
JSValue js_core_debug_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_debug_funcs, countof(js_debug_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_debug_funcs, countof(js_debug_funcs));
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static const JSCFunctionListEntry js_js_funcs[] = {
|
|||||||
MIST_FUNC_DEF(js, fn_info, 1),
|
MIST_FUNC_DEF(js, fn_info, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_js_use(JSContext *js) {
|
JSValue js_core_js_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_js_funcs, countof(js_js_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_js_funcs, countof(js_js_funcs));
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ use('json') // core json module
|
|||||||
use('otherlib/foo') // dependency 'otherlib', file foo.cm
|
use('otherlib/foo') // dependency 'otherlib', file foo.cm
|
||||||
```
|
```
|
||||||
|
|
||||||
Files starting with underscore (`_helper.cm`) are private to the package.
|
Files in the `internal/` directory are private to the package.
|
||||||
|
|
||||||
## Example: Simple Actor System
|
## Example: Simple Actor System
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ Where:
|
|||||||
Examples:
|
Examples:
|
||||||
- `mypackage/math.c` -> `js_mypackage_math_use`
|
- `mypackage/math.c` -> `js_mypackage_math_use`
|
||||||
- `gitea.pockle.world/john/lib/render.c` -> `js_gitea_pockle_world_john_lib_render_use`
|
- `gitea.pockle.world/john/lib/render.c` -> `js_gitea_pockle_world_john_lib_render_use`
|
||||||
|
- `mypackage/game.ce` (AOT actor) -> `js_mypackage_game_program`
|
||||||
|
|
||||||
|
Actor files (`.ce`) use the `_program` suffix instead of `_use`.
|
||||||
|
|
||||||
|
**Note:** Having both a `.cm` and `.c` file with the same stem at the same scope is a build error.
|
||||||
|
|
||||||
## Required Headers
|
## Required Headers
|
||||||
|
|
||||||
@@ -216,34 +221,6 @@ var n = vector.normalize(3, 4) // {x: 0.6, y: 0.8}
|
|||||||
var d = vector.dot(1, 0, 0, 1) // 0
|
var d = vector.dot(1, 0, 0, 1) // 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Combining C and ƿit
|
|
||||||
|
|
||||||
A common pattern is to have a C file provide low-level functions and a `.cm` file provide a higher-level API:
|
|
||||||
|
|
||||||
```c
|
|
||||||
// _vector_native.c
|
|
||||||
// ... raw C functions ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// vector.cm
|
|
||||||
var native = this // C module passed as 'this'
|
|
||||||
|
|
||||||
var Vector = function(x, y) {
|
|
||||||
return {x: x, y: y}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector.length = function(v) {
|
|
||||||
return native.length(v.x, v.y)
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector.normalize = function(v) {
|
|
||||||
return native.normalize(v.x, v.y)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Vector
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build Process
|
## Build Process
|
||||||
|
|
||||||
C files are automatically compiled when you run:
|
C files are automatically compiled when you run:
|
||||||
@@ -253,7 +230,7 @@ pit build
|
|||||||
pit update
|
pit update
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting dynamic library is placed in `~/.pit/lib/`.
|
Each C file is compiled into a per-file dynamic library at `~/.pit/lib/<pkg>/<stem>.dylib`.
|
||||||
|
|
||||||
## Platform-Specific Code
|
## Platform-Specific Code
|
||||||
|
|
||||||
|
|||||||
120
docs/cli.md
120
docs/cli.md
@@ -70,10 +70,11 @@ pit ls <package> # list files in specified package
|
|||||||
|
|
||||||
### pit build
|
### pit build
|
||||||
|
|
||||||
Build the current package.
|
Build the current package. Compiles C files into per-file dynamic libraries and installs them to `~/.pit/lib/<pkg>/<stem>.dylib`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pit build
|
pit build # build current package
|
||||||
|
pit build <package> # build specific package
|
||||||
```
|
```
|
||||||
|
|
||||||
### pit test
|
### pit test
|
||||||
@@ -122,6 +123,103 @@ Clean build artifacts.
|
|||||||
pit clean
|
pit clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### pit add
|
||||||
|
|
||||||
|
Add a dependency to the current package. Updates `cell.toml` and installs the package to the shop.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit add gitea.pockle.world/john/prosperon # default alias
|
||||||
|
pit add gitea.pockle.world/john/prosperon myalias # custom alias
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit clone
|
||||||
|
|
||||||
|
Clone a package to a local path and link it for development.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit clone gitea.pockle.world/john/prosperon ./prosperon
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit unlink
|
||||||
|
|
||||||
|
Remove a link created by `pit link` or `pit clone` and restore the original package.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit unlink gitea.pockle.world/john/prosperon
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit search
|
||||||
|
|
||||||
|
Search for packages, actors, or modules matching a query.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit search math
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit why
|
||||||
|
|
||||||
|
Show which installed packages depend on a given package (reverse dependency lookup).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit why gitea.pockle.world/john/prosperon
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit resolve
|
||||||
|
|
||||||
|
Print the fully resolved dependency closure for a package.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit resolve # resolve current package
|
||||||
|
pit resolve <package> # resolve specific package
|
||||||
|
pit resolve --locked # show lock state without links
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit graph
|
||||||
|
|
||||||
|
Emit a dependency graph.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit graph # tree of current package
|
||||||
|
pit graph --format dot # graphviz dot output
|
||||||
|
pit graph --format json # json output
|
||||||
|
pit graph --world # graph all installed packages
|
||||||
|
pit graph --locked # show lock view without links
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit verify
|
||||||
|
|
||||||
|
Verify integrity and consistency of packages, links, and builds.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit verify # verify current package
|
||||||
|
pit verify shop # verify entire shop
|
||||||
|
pit verify --deep # traverse full dependency closure
|
||||||
|
pit verify --target <triple>
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit pack
|
||||||
|
|
||||||
|
Build a statically linked binary from a package and all its dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit pack <package> # build static binary (output: app)
|
||||||
|
pit pack <package> -o myapp # specify output name
|
||||||
|
pit pack <package> -t <triple> # cross-compile for target
|
||||||
|
```
|
||||||
|
|
||||||
|
### pit config
|
||||||
|
|
||||||
|
Manage system and actor configuration values in `cell.toml`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pit config list # list all config
|
||||||
|
pit config get system.ar_timer # get a value
|
||||||
|
pit config set system.ar_timer 5.0 # set a value
|
||||||
|
pit config actor <name> list # list actor config
|
||||||
|
pit config actor <name> get <key> # get actor config
|
||||||
|
pit config actor <name> set <key> <val> # set actor config
|
||||||
|
```
|
||||||
|
|
||||||
### pit help
|
### pit help
|
||||||
|
|
||||||
Display help information.
|
Display help information.
|
||||||
@@ -131,16 +229,6 @@ pit help
|
|||||||
pit help <command>
|
pit help <command>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running Scripts
|
|
||||||
|
|
||||||
Any `.ce` file in the ƿit core can be run as a command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit version # runs version.ce
|
|
||||||
pit build # runs build.ce
|
|
||||||
pit test # runs test.ce
|
|
||||||
```
|
|
||||||
|
|
||||||
## Package Locators
|
## Package Locators
|
||||||
|
|
||||||
Packages are identified by locators:
|
Packages are identified by locators:
|
||||||
@@ -159,9 +247,11 @@ pit install /Users/john/work/mylib
|
|||||||
|
|
||||||
```
|
```
|
||||||
~/.pit/
|
~/.pit/
|
||||||
├── packages/ # installed packages
|
├── packages/ # installed package sources
|
||||||
├── lib/ # compiled dynamic libraries
|
├── lib/ # installed per-file dylibs and mach (persistent)
|
||||||
├── build/ # build cache
|
│ ├── core/ # core package: .dylib and .mach files
|
||||||
|
│ └── <pkg>/ # per-package subdirectories
|
||||||
|
├── build/ # ephemeral build cache (safe to delete)
|
||||||
├── cache/ # downloaded archives
|
├── cache/ # downloaded archives
|
||||||
├── lock.toml # installed package versions
|
├── lock.toml # installed package versions
|
||||||
└── link.toml # local development links
|
└── link.toml # local development links
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ mypackage/
|
|||||||
├── helper/
|
├── helper/
|
||||||
│ └── math.cm # nested module
|
│ └── math.cm # nested module
|
||||||
├── render.c # C extension
|
├── render.c # C extension
|
||||||
└── _internal.cm # private module (underscore prefix)
|
└── internal/
|
||||||
|
└── helpers.cm # private module (internal/ only)
|
||||||
```
|
```
|
||||||
|
|
||||||
## pit.toml
|
## pit.toml
|
||||||
@@ -60,12 +61,12 @@ use('json') // core module
|
|||||||
|
|
||||||
### Private Modules
|
### Private Modules
|
||||||
|
|
||||||
Files starting with underscore are private:
|
Files in the `internal/` directory are private to their package:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// _internal.cm is only accessible within the same package
|
// internal/helpers.cm is only accessible within the same package
|
||||||
use('internal') // OK from same package
|
use('internal/helpers') // OK from same package
|
||||||
use('myapp/internal') // Error from other packages
|
use('myapp/internal/helpers') // Error from other packages
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package Locators
|
## Package Locators
|
||||||
@@ -105,8 +106,11 @@ Local packages are symlinked into the shop, making development seamless.
|
|||||||
│ └── work/
|
│ └── work/
|
||||||
│ └── mylib -> /Users/john/work/mylib
|
│ └── mylib -> /Users/john/work/mylib
|
||||||
├── lib/
|
├── lib/
|
||||||
│ ├── local.dylib
|
│ ├── core/
|
||||||
│ └── gitea_pockle_world_john_prosperon.dylib
|
│ │ ├── fd.dylib
|
||||||
|
│ │ └── time.mach
|
||||||
|
│ └── gitea_pockle_world_john_prosperon/
|
||||||
|
│ └── sprite.dylib
|
||||||
├── build/
|
├── build/
|
||||||
│ └── <content-addressed cache>
|
│ └── <content-addressed cache>
|
||||||
├── cache/
|
├── cache/
|
||||||
@@ -171,16 +175,16 @@ pit link delete gitea.pockle.world/john/prosperon
|
|||||||
|
|
||||||
## C Extensions
|
## C Extensions
|
||||||
|
|
||||||
C files in a package are compiled into a dynamic library:
|
C files in a package are compiled into per-file dynamic libraries:
|
||||||
|
|
||||||
```
|
```
|
||||||
mypackage/
|
mypackage/
|
||||||
├── pit.toml
|
├── pit.toml
|
||||||
├── render.c # compiled to mypackage.dylib
|
├── render.c # compiled to lib/mypackage/render.dylib
|
||||||
└── render.cm # optional ƿit wrapper
|
└── physics.c # compiled to lib/mypackage/physics.dylib
|
||||||
```
|
```
|
||||||
|
|
||||||
The library is named after the package and placed in `~/.pit/lib/`.
|
Each `.c` file gets its own `.dylib` in `~/.pit/lib/<pkg>/`. A `.c` file and `.cm` file with the same stem at the same scope is a build error — use distinct names.
|
||||||
|
|
||||||
See [Writing C Modules](/docs/c-modules/) for details.
|
See [Writing C Modules](/docs/c-modules/) for details.
|
||||||
|
|
||||||
|
|||||||
75
docs/shop.md
75
docs/shop.md
@@ -62,27 +62,30 @@ Every module goes through a content-addressed caching pipeline. The cache key is
|
|||||||
When loading a module, the shop checks (in order):
|
When loading a module, the shop checks (in order):
|
||||||
|
|
||||||
1. **In-memory cache** — `use_cache[key]`, checked first on every `use()` call
|
1. **In-memory cache** — `use_cache[key]`, checked first on every `use()` call
|
||||||
2. **Native dylib** — pre-compiled platform-specific `.dylib` in the content-addressed store
|
2. **Installed dylib** — per-file `.dylib` in `~/.pit/lib/<pkg>/<stem>.dylib`
|
||||||
3. **Cached .mach blob** — binary bytecode in `~/.pit/build/<hash>.mach`
|
3. **Internal symbols** — statically linked into the `pit` binary (fat builds)
|
||||||
4. **Cached .mcode IR** — JSON IR in `~/.pit/build/<hash>.mcode`
|
4. **Installed mach** — pre-compiled bytecode in `~/.pit/lib/<pkg>/<stem>.mach`
|
||||||
5. **Adjacent .mach/.mcode** — files alongside the source (e.g., `sprite.mach`)
|
5. **Cached bytecode** — content-addressed in `~/.pit/build/<hash>` (no extension)
|
||||||
6. **Source compilation** — full pipeline: analyze, mcode, streamline, serialize
|
6. **Cached .mcode IR** — JSON IR in `~/.pit/build/<hash>.mcode`
|
||||||
|
7. **Adjacent .mach/.mcode** — files alongside the source (e.g., `sprite.mach`)
|
||||||
|
8. **Source compilation** — full pipeline: analyze, mcode, streamline, serialize
|
||||||
|
|
||||||
Results from steps 4-6 are cached back to the content-addressed store for future loads.
|
When both a `.dylib` and `.mach` exist for the same module in `lib/`, the dylib is selected. Dylib resolution also wins over internal symbols, so a dylib in `lib/` can hot-patch a fat binary. Delete the dylib to fall back to mach or static.
|
||||||
|
|
||||||
|
Results from steps 6-8 are cached back to the content-addressed store for future loads.
|
||||||
|
|
||||||
### Content-Addressed Store
|
### Content-Addressed Store
|
||||||
|
|
||||||
All cached artifacts live in `~/.pit/build/` named by the BLAKE2 hash of their source content:
|
The build cache at `~/.pit/build/` stores ephemeral artifacts named by the BLAKE2 hash of their inputs:
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.pit/build/
|
~/.pit/build/
|
||||||
├── a1b2c3d4...mach # compiled bytecode blob
|
├── a1b2c3d4... # cached bytecode blob (no extension)
|
||||||
├── e5f6a7b8...mach # another compiled module
|
├── c9d0e1f2...mcode # cached JSON IR
|
||||||
├── c9d0e1f2...mcode # cached JSON IR
|
└── f3a4b5c6... # compiled dylib (checked before copying to lib/)
|
||||||
└── f3a4b5c6...macos_arm64.dylib # native compiled module
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This scheme provides automatic cache invalidation: when source changes, its hash changes, and the old cache entry is simply never looked up again.
|
This scheme provides automatic cache invalidation: when source changes, its hash changes, and the old cache entry is simply never looked up again. When building a dylib, the build cache is checked first — if a matching hash exists, it is copied to `lib/` without recompiling.
|
||||||
|
|
||||||
### Core Module Caching
|
### Core Module Caching
|
||||||
|
|
||||||
@@ -102,34 +105,14 @@ symbol: js_gitea_pockle_world_john_prosperon_sprite_use
|
|||||||
|
|
||||||
### C Resolution Sources
|
### C Resolution Sources
|
||||||
|
|
||||||
1. **Internal symbols** — statically linked into the `pit` binary (core modules)
|
1. **Installed dylibs** — per-file dylibs in `~/.pit/lib/<pkg>/<stem>.dylib` (deterministic paths, no manifests)
|
||||||
2. **Per-module dylibs** — loaded from `~/.pit/lib/` via a manifest file
|
2. **Internal symbols** — statically linked into the `pit` binary (fat builds)
|
||||||
|
|
||||||
### Manifest Files
|
Dylibs are checked first at each resolution scope, so an installed dylib always wins over a statically linked symbol. This enables hot-patching fat binaries by placing a dylib in `lib/`.
|
||||||
|
|
||||||
Each package with C extensions has a manifest at `~/.pit/lib/<package>.manifest.json` mapping symbol names to dylib paths:
|
### Name Collisions
|
||||||
|
|
||||||
```json
|
Having both a `.cm` script and a `.c` file with the same stem at the same scope is a **build error**. For example, `render.cm` and `render.c` in the same directory will fail. Use distinct names — e.g., `render.c` for the C implementation and `render_utils.cm` for the script wrapper.
|
||||||
{
|
|
||||||
"js_mypackage_render_use": "/Users/john/.pit/lib/mypackage_render.dylib",
|
|
||||||
"js_mypackage_audio_use": "/Users/john/.pit/lib/mypackage_audio.dylib"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The shop loads manifests lazily on first access and caches them.
|
|
||||||
|
|
||||||
### Combined Resolution
|
|
||||||
|
|
||||||
When both a `.cm` script and a C symbol exist for the same module name, both are resolved. The C module is loaded first (as the base), then the `.cm` script can extend it:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// render.cm — extends the C render module
|
|
||||||
var c_render = use('internal/render_c')
|
|
||||||
// Add ƿit-level helpers on top of C functions
|
|
||||||
return record(c_render, {
|
|
||||||
draw_circle: function(x, y, r) { /* ... */ }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Injection
|
## Environment Injection
|
||||||
|
|
||||||
@@ -147,11 +130,19 @@ The set of injected capabilities is controlled by `script_inject_for()`, which c
|
|||||||
~/.pit/
|
~/.pit/
|
||||||
├── packages/ # installed packages (directories and symlinks)
|
├── packages/ # installed packages (directories and symlinks)
|
||||||
│ └── core -> ... # symlink to the ƿit core
|
│ └── core -> ... # symlink to the ƿit core
|
||||||
├── lib/ # compiled C extension dylibs + manifests
|
├── lib/ # INSTALLED per-file artifacts (persistent, human-readable)
|
||||||
├── build/ # content-addressed compilation cache
|
│ ├── core/
|
||||||
│ ├── <hash>.mach # cached bytecode blobs
|
│ │ ├── fd.dylib
|
||||||
│ ├── <hash>.mcode # cached JSON IR
|
│ │ ├── time.mach
|
||||||
│ └── <hash>.<target>.dylib # native compiled modules
|
│ │ ├── time.dylib
|
||||||
|
│ │ └── internal/
|
||||||
|
│ │ └── os.dylib
|
||||||
|
│ └── gitea_pockle_world_john_prosperon/
|
||||||
|
│ ├── sprite.dylib
|
||||||
|
│ └── render.dylib
|
||||||
|
├── build/ # EPHEMERAL cache (safe to delete anytime)
|
||||||
|
│ ├── <hash> # cached bytecode or dylib blobs (no extension)
|
||||||
|
│ └── <hash>.mcode # cached JSON IR
|
||||||
├── cache/ # downloaded package zip archives
|
├── cache/ # downloaded package zip archives
|
||||||
├── lock.toml # installed package versions and commit hashes
|
├── lock.toml # installed package versions and commit hashes
|
||||||
└── link.toml # local development link overrides
|
└── link.toml # local development link overrides
|
||||||
|
|||||||
2
fd.cm
2
fd.cm
@@ -1,4 +1,4 @@
|
|||||||
var fd = use('internal/fd_c')
|
var fd = use('internal/fd')
|
||||||
var wildstar = use('wildstar')
|
var wildstar = use('wildstar')
|
||||||
|
|
||||||
function last_pos(str, sep) {
|
function last_pos(str, sep) {
|
||||||
|
|||||||
3
fetch.ce
3
fetch.ce
@@ -12,8 +12,9 @@ var shop = use('internal/shop')
|
|||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
var target_pkg = null
|
var target_pkg = null
|
||||||
|
var i = 0
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--help' || args[i] == '-h') {
|
if (args[i] == '--help' || args[i] == '-h') {
|
||||||
log.console("Usage: cell fetch [package]")
|
log.console("Usage: cell fetch [package]")
|
||||||
log.console("Fetch package zips from remote sources.")
|
log.console("Fetch package zips from remote sources.")
|
||||||
|
|||||||
2
fit.c
2
fit.c
@@ -248,7 +248,7 @@ static const JSCFunctionListEntry js_fit_funcs[] = {
|
|||||||
MIST_FUNC_DEF(fit, zeros, 1),
|
MIST_FUNC_DEF(fit, zeros, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_fit_use(JSContext *js)
|
JSValue js_core_fit_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
|
|||||||
112
fix_pipeline.md
Normal file
112
fix_pipeline.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Fix Compilation Pipeline Bootstrap
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
After merging `fix_gc` into `pitweb`, the compilation pipeline `.cm` source files
|
||||||
|
(tokenize.cm, parse.cm, fold.cm, mcode.cm, streamline.cm) cannot bootstrap themselves.
|
||||||
|
|
||||||
|
The old pitweb pipeline mcode compiles the merged `.cm` source without errors, but the
|
||||||
|
resulting new pipeline mcode is **semantically broken** — it can't even compile
|
||||||
|
`var x = 42; print(x)`.
|
||||||
|
|
||||||
|
Both branches worked independently. The merge introduced no syntax errors, but the old
|
||||||
|
pitweb compiler produces incorrect bytecode from the merged pipeline source. This is a
|
||||||
|
classic bootstrapping problem: the new pipeline needs a compatible compiler to build
|
||||||
|
itself, but the only available compiler (old pitweb) miscompiles it.
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
- `boot/tokenize.cm.mcode` through `boot/streamline.cm.mcode` contain the **old pitweb**
|
||||||
|
pipeline mcode (pre-merge). These pass 641/641 vm_suite tests.
|
||||||
|
- All other boot mcode files (engine, bootstrap, seed_bootstrap, plus core modules like
|
||||||
|
fd, time, toml, etc.) are compiled from the merged source and work correctly.
|
||||||
|
- The merged pipeline `.cm` source has changes from fix_gc that are **not active** — the
|
||||||
|
runtime uses the old pitweb pipeline mcode.
|
||||||
|
|
||||||
|
**The old pitweb pipeline is NOT fully working.** While it passes the test suite, it
|
||||||
|
miscompiles nested function declarations. This breaks:
|
||||||
|
|
||||||
|
- `toml.encode()` — the encoder uses nested `function` declarations inside `encode_toml`
|
||||||
|
- `Shop.save_lock()` — calls `toml.encode()`, so any lock.toml mutation fails
|
||||||
|
- Any other `.cm` module that uses nested named function declarations
|
||||||
|
|
||||||
|
This means the **ID-based package symbol naming** (Phase 2 in the plan) is blocked: it
|
||||||
|
needs `save_lock()` to persist package IDs to lock.toml.
|
||||||
|
|
||||||
|
The shop.cm changes for ID-based naming are already written and correct — they just need
|
||||||
|
a working pipeline underneath. Once the pipeline is fixed, the ID system will work.
|
||||||
|
|
||||||
|
## What Changed in the Pipeline
|
||||||
|
|
||||||
|
The fix_gc merge brought these changes to the pipeline `.cm` files:
|
||||||
|
|
||||||
|
- **mcode.cm**: Type-guarded arithmetic (`emit_add_decomposed` now generates `is_text`/`is_num`
|
||||||
|
checks instead of letting the VM dispatch), `emit_numeric_binop` for subtract/multiply/etc.,
|
||||||
|
`sensory_ops` lookup table, array/record literal count args (`["array", dest, count]`
|
||||||
|
instead of `["array", dest, 0]`)
|
||||||
|
- **fold.cm**: Lookup tables (`binary_ops`, `unary_ops`, `assign_ops`, etc.) replacing
|
||||||
|
if-chains, combined `"array"` and `"text literal"` handling
|
||||||
|
- **tokenize.cm**: ~500 lines of changes
|
||||||
|
- **streamline.cm**: ~700 lines of changes
|
||||||
|
- **parse.cm**: ~40 lines of changes (minor)
|
||||||
|
|
||||||
|
## Regen Flags
|
||||||
|
|
||||||
|
`regen.ce` now has two modes:
|
||||||
|
|
||||||
|
```
|
||||||
|
./cell --dev --seed regen # default: skip pipeline files
|
||||||
|
./cell --dev --seed regen --all # include pipeline files (tokenize/parse/fold/mcode/streamline)
|
||||||
|
```
|
||||||
|
|
||||||
|
The default mode is safe — it regenerates everything except the 5 pipeline files,
|
||||||
|
preserving the working old pitweb pipeline mcode.
|
||||||
|
|
||||||
|
## How to Fix
|
||||||
|
|
||||||
|
The goal is to get the merged pipeline `.cm` source to produce working mcode when
|
||||||
|
compiled by the current (old pitweb) pipeline. The process:
|
||||||
|
|
||||||
|
1. Start from the current repo state (old pitweb pipeline mcode in boot/)
|
||||||
|
2. Edit one or more pipeline `.cm` files to fix the issue
|
||||||
|
3. Regen with `--all` to recompile everything including pipeline:
|
||||||
|
```
|
||||||
|
./cell --dev --seed regen --all
|
||||||
|
```
|
||||||
|
4. Test the new pipeline with a simple sanity check:
|
||||||
|
```
|
||||||
|
rm -rf .cell/build/*
|
||||||
|
echo 'var x = 42; print(x)' > /tmp/test.ce
|
||||||
|
./cell --dev --seed /tmp/test
|
||||||
|
```
|
||||||
|
5. If that works, run the full test suite:
|
||||||
|
```
|
||||||
|
rm -rf .cell/build/*
|
||||||
|
./cell --dev vm_suite
|
||||||
|
```
|
||||||
|
6. If tests pass, regen again (the new pipeline compiles itself):
|
||||||
|
```
|
||||||
|
./cell --dev --seed regen --all
|
||||||
|
```
|
||||||
|
7. Repeat steps 4-6 until **idempotent** — two consecutive `regen --all` runs produce
|
||||||
|
identical boot mcode and all tests pass.
|
||||||
|
|
||||||
|
## Debugging Tips
|
||||||
|
|
||||||
|
- The old pitweb pipeline mcode is always available via:
|
||||||
|
```
|
||||||
|
git checkout HEAD^1 -- boot/tokenize.cm.mcode boot/parse.cm.mcode \
|
||||||
|
boot/fold.cm.mcode boot/mcode.cm.mcode boot/streamline.cm.mcode
|
||||||
|
```
|
||||||
|
- Use `--seed` mode for testing compilation — it bypasses the engine entirely and
|
||||||
|
loads the pipeline directly from boot mcode.
|
||||||
|
- The failure mode is silent: the old compiler compiles the new source without errors
|
||||||
|
but produces wrong bytecode.
|
||||||
|
- Known broken patterns with the old pitweb pipeline:
|
||||||
|
- `var x = 42; print(x)` fails when compiled by the regenned pipeline mcode
|
||||||
|
- Nested named function declarations (`function foo() {}` inside another function)
|
||||||
|
produce "not a function" errors — this breaks `toml.encode()`
|
||||||
|
- Test with: `echo 'var toml = use("toml"); print(toml.encode({a: 1}))' > /tmp/t.ce && ./cell --dev /tmp/t.ce`
|
||||||
|
- The most likely culprits are the mcode.cm changes (type-guarded arithmetic, array/record
|
||||||
|
count args) since these change the bytecode format. The fold.cm changes (lookup tables)
|
||||||
|
are more likely safe refactors.
|
||||||
39
graph.ce
39
graph.ce
@@ -22,8 +22,10 @@ var target_locator = null
|
|||||||
var format = 'tree'
|
var format = 'tree'
|
||||||
var show_locked = false
|
var show_locked = false
|
||||||
var show_world = false
|
var show_world = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--format' || args[i] == '-f') {
|
if (args[i] == '--format' || args[i] == '-f') {
|
||||||
if (i + 1 < length(args)) {
|
if (i + 1 < length(args)) {
|
||||||
format = args[++i]
|
format = args[++i]
|
||||||
@@ -91,7 +93,7 @@ function gather_graph(locator, visited) {
|
|||||||
|
|
||||||
add_node(locator)
|
add_node(locator)
|
||||||
|
|
||||||
try {
|
var _gather = function() {
|
||||||
var deps = pkg.dependencies(locator)
|
var deps = pkg.dependencies(locator)
|
||||||
if (deps) {
|
if (deps) {
|
||||||
arrfor(array(deps), function(alias) {
|
arrfor(array(deps), function(alias) {
|
||||||
@@ -101,17 +103,19 @@ function gather_graph(locator, visited) {
|
|||||||
gather_graph(dep_locator, visited)
|
gather_graph(dep_locator, visited)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Package might not have dependencies
|
// Package might not have dependencies
|
||||||
}
|
}
|
||||||
|
_gather()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather graph from roots
|
// Gather graph from roots
|
||||||
var roots = []
|
var roots = []
|
||||||
|
|
||||||
|
var packages = null
|
||||||
if (show_world) {
|
if (show_world) {
|
||||||
// Use all packages in shop as roots
|
// Use all packages in shop as roots
|
||||||
var packages = shop.list_packages()
|
packages = shop.list_packages()
|
||||||
arrfor(packages, function(p) {
|
arrfor(packages, function(p) {
|
||||||
if (p != 'core') {
|
if (p != 'core') {
|
||||||
push(roots, p)
|
push(roots, p)
|
||||||
@@ -125,7 +129,7 @@ if (show_world) {
|
|||||||
|
|
||||||
// Resolve local paths
|
// Resolve local paths
|
||||||
if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) {
|
if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) {
|
||||||
var resolved = fd.realpath(target_locator)
|
resolved = fd.realpath(target_locator)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_locator = resolved
|
target_locator = resolved
|
||||||
}
|
}
|
||||||
@@ -141,14 +145,19 @@ arrfor(roots, function(root) {
|
|||||||
// Output based on format
|
// Output based on format
|
||||||
if (format == 'tree') {
|
if (format == 'tree') {
|
||||||
function print_tree(locator, prefix, is_last, visited) {
|
function print_tree(locator, prefix, is_last, visited) {
|
||||||
|
var node = null
|
||||||
|
var suffix = null
|
||||||
|
var children = null
|
||||||
|
var j = 0
|
||||||
|
var child_prefix = null
|
||||||
if (visited[locator]) {
|
if (visited[locator]) {
|
||||||
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + " (circular)")
|
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + " (circular)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
visited[locator] = true
|
visited[locator] = true
|
||||||
|
|
||||||
var node = nodes[locator]
|
node = nodes[locator]
|
||||||
var suffix = ""
|
suffix = ""
|
||||||
if (node.linked) suffix += " -> " + node.effective
|
if (node.linked) suffix += " -> " + node.effective
|
||||||
if (node.commit) suffix += " @" + node.commit
|
if (node.commit) suffix += " @" + node.commit
|
||||||
if (node.local) suffix += " (local)"
|
if (node.local) suffix += " (local)"
|
||||||
@@ -156,30 +165,32 @@ if (format == 'tree') {
|
|||||||
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + suffix)
|
log.console(prefix + (is_last ? "\\-- " : "|-- ") + locator + suffix)
|
||||||
|
|
||||||
// Get children
|
// Get children
|
||||||
var children = []
|
children = []
|
||||||
arrfor(edges, function(e) {
|
arrfor(edges, function(e) {
|
||||||
if (e.from == locator) {
|
if (e.from == locator) {
|
||||||
push(children, e)
|
push(children, e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for (var i = 0; i < length(children); i++) {
|
for (j = 0; j < length(children); j++) {
|
||||||
var child_prefix = prefix + (is_last ? " " : "| ")
|
child_prefix = prefix + (is_last ? " " : "| ")
|
||||||
print_tree(children[i].to, child_prefix, i == length(children) - 1, visited)
|
print_tree(children[j].to, child_prefix, j == length(children) - 1, visited)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < length(roots); i++) {
|
var children = null
|
||||||
|
var j = 0
|
||||||
|
for (i = 0; i < length(roots); i++) {
|
||||||
log.console(roots[i])
|
log.console(roots[i])
|
||||||
|
|
||||||
var children = []
|
children = []
|
||||||
arrfor(edges, function(e) {
|
arrfor(edges, function(e) {
|
||||||
if (e.from == roots[i]) {
|
if (e.from == roots[i]) {
|
||||||
push(children, e)
|
push(children, e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for (var j = 0; j < length(children); j++) {
|
for (j = 0; j < length(children); j++) {
|
||||||
print_tree(children[j].to, "", j == length(children) - 1, {})
|
print_tree(children[j].to, "", j == length(children) - 1, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
help.ce
16
help.ce
@@ -3,27 +3,29 @@
|
|||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
|
|
||||||
var command = length(args) > 0 ? args[0] : null
|
var command = length(args) > 0 ? args[0] : null
|
||||||
|
var man_file = null
|
||||||
|
var stat = null
|
||||||
|
var content = null
|
||||||
|
|
||||||
// Display specific command help
|
// Display specific command help
|
||||||
if (command) {
|
if (command) {
|
||||||
var man_file = 'scripts/man/' + command + '.man'
|
man_file = 'scripts/man/' + command + '.man'
|
||||||
var stat = fd.stat(man_file);
|
stat = fd.stat(man_file)
|
||||||
if (stat && stat.isFile) {
|
if (stat && stat.isFile) {
|
||||||
var content = text(fd.slurp(man_file))
|
content = text(fd.slurp(man_file))
|
||||||
log.console(content)
|
log.console(content)
|
||||||
} else {
|
} else {
|
||||||
log.error("No help available for command: " + command)
|
log.error("No help available for command: " + command)
|
||||||
log.console("Run 'cell help' to see available commands.")
|
log.console("Run 'cell help' to see available commands.")
|
||||||
}
|
}
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display general help
|
// Display general help
|
||||||
var cell_man = 'scripts/man/cell.man'
|
man_file = 'scripts/man/cell.man'
|
||||||
var stat = fd.stat(cell_man);
|
stat = fd.stat(man_file)
|
||||||
if (stat && stat.isFile) {
|
if (stat && stat.isFile) {
|
||||||
var content = text(fd.slurp(cell_man))
|
content = text(fd.slurp(man_file))
|
||||||
log.console(content)
|
log.console(content)
|
||||||
} else {
|
} else {
|
||||||
// Fallback if man file doesn't exist
|
// Fallback if man file doesn't exist
|
||||||
|
|||||||
27
install.ce
27
install.ce
@@ -28,8 +28,10 @@ var locator = null
|
|||||||
var target_triple = null
|
var target_triple = null
|
||||||
var refresh = false
|
var refresh = false
|
||||||
var dry_run = false
|
var dry_run = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--target' || args[i] == '-t') {
|
if (args[i] == '--target' || args[i] == '-t') {
|
||||||
if (i + 1 < length(args)) {
|
if (i + 1 < length(args)) {
|
||||||
target_triple = args[++i]
|
target_triple = args[++i]
|
||||||
@@ -64,7 +66,7 @@ if (!locator) {
|
|||||||
// Resolve relative paths to absolute paths
|
// Resolve relative paths to absolute paths
|
||||||
// Local paths like '.' or '../foo' need to be converted to absolute paths
|
// Local paths like '.' or '../foo' need to be converted to absolute paths
|
||||||
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
||||||
var resolved = fd.realpath(locator)
|
resolved = fd.realpath(locator)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
locator = resolved
|
locator = resolved
|
||||||
}
|
}
|
||||||
@@ -83,6 +85,9 @@ var skipped_packages = []
|
|||||||
var visited = {}
|
var visited = {}
|
||||||
|
|
||||||
function gather_packages(pkg_locator) {
|
function gather_packages(pkg_locator) {
|
||||||
|
var lock = null
|
||||||
|
var update_result = null
|
||||||
|
var deps = null
|
||||||
if (visited[pkg_locator]) return
|
if (visited[pkg_locator]) return
|
||||||
visited[pkg_locator] = true
|
visited[pkg_locator] = true
|
||||||
|
|
||||||
@@ -96,12 +101,12 @@ function gather_packages(pkg_locator) {
|
|||||||
push(packages_to_install, pkg_locator)
|
push(packages_to_install, pkg_locator)
|
||||||
|
|
||||||
// Try to read dependencies
|
// Try to read dependencies
|
||||||
try {
|
var _gather = function() {
|
||||||
// For packages not yet extracted, we need to update and extract first to read deps
|
// For packages not yet extracted, we need to update and extract first to read deps
|
||||||
var lock = shop.load_lock()
|
lock = shop.load_lock()
|
||||||
if (!lock[pkg_locator]) {
|
if (!lock[pkg_locator]) {
|
||||||
if (!dry_run) {
|
if (!dry_run) {
|
||||||
var update_result = shop.update(pkg_locator)
|
update_result = shop.update(pkg_locator)
|
||||||
if (update_result) {
|
if (update_result) {
|
||||||
shop.extract(pkg_locator)
|
shop.extract(pkg_locator)
|
||||||
} else {
|
} else {
|
||||||
@@ -117,19 +122,20 @@ function gather_packages(pkg_locator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var deps = pkg.dependencies(pkg_locator)
|
deps = pkg.dependencies(pkg_locator)
|
||||||
if (deps) {
|
if (deps) {
|
||||||
arrfor(array(deps), function(alias) {
|
arrfor(array(deps), function(alias) {
|
||||||
var dep_locator = deps[alias]
|
var dep_locator = deps[alias]
|
||||||
gather_packages(dep_locator)
|
gather_packages(dep_locator)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Package might not have dependencies or cell.toml issue
|
// Package might not have dependencies or cell.toml issue
|
||||||
if (!dry_run) {
|
if (!dry_run) {
|
||||||
log.console(`Warning: Could not read dependencies for ${pkg_locator}: ${e.message}`)
|
log.console(`Warning: Could not read dependencies for ${pkg_locator}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_gather()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather all packages
|
// Gather all packages
|
||||||
@@ -164,11 +170,12 @@ function install_package(pkg_locator) {
|
|||||||
shop.build_package_scripts(pkg_locator)
|
shop.build_package_scripts(pkg_locator)
|
||||||
|
|
||||||
// Build C code
|
// Build C code
|
||||||
try {
|
var _build_c = function() {
|
||||||
build.build_dynamic(pkg_locator, target_triple, 'release')
|
build.build_dynamic(pkg_locator, target_triple, 'release')
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Not all packages have C code
|
// Not all packages have C code
|
||||||
}
|
}
|
||||||
|
_build_c()
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfor(packages_to_install, function(p) {
|
arrfor(packages_to_install, function(p) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// Hidden vars come from env:
|
// Hidden vars come from env:
|
||||||
// CLI mode (cell_init): os, args, core_path, shop_path
|
// CLI mode (cell_init): os, args, core_path, shop_path
|
||||||
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path
|
// Actor spawn (script_startup): os, json, actorsym, init, core_path, shop_path
|
||||||
// args[0] = script name, args[1..] = user args
|
// args[0] = script name, args[1..] = user args
|
||||||
var load_internal = os.load_internal
|
var load_internal = os.load_internal
|
||||||
function use_embed(name) {
|
function use_embed(name) {
|
||||||
return load_internal("js_" + name + "_use")
|
return load_internal("js_core_" + name + "_use")
|
||||||
}
|
}
|
||||||
|
|
||||||
var fd = use_embed('fd')
|
var fd = use_embed('internal_fd')
|
||||||
var json = use_embed('json')
|
var json = use_embed('json')
|
||||||
var crypto = use_embed('crypto')
|
var crypto = use_embed('crypto')
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function content_hash(content) {
|
|||||||
|
|
||||||
function cache_path(hash) {
|
function cache_path(hash) {
|
||||||
if (!shop_path) return null
|
if (!shop_path) return null
|
||||||
return shop_path + '/build/' + hash + '.mach'
|
return shop_path + '/build/' + hash
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensure_build_dir() {
|
function ensure_build_dir() {
|
||||||
@@ -267,7 +267,7 @@ if (args != null) {
|
|||||||
// Actor spawn mode — load engine.cm with full actor env
|
// Actor spawn mode — load engine.cm with full actor env
|
||||||
load_engine({
|
load_engine({
|
||||||
os: os, actorsym: actorsym, init: init,
|
os: os, actorsym: actorsym, init: init,
|
||||||
core_path: core_path, shop_path: shop_path, json: json, nota: nota, wota: wota,
|
core_path: core_path, shop_path: shop_path, json: json,
|
||||||
analyze: analyze, run_ast_fn: run_ast, run_ast_noopt_fn: run_ast_noopt,
|
analyze: analyze, run_ast_fn: run_ast, run_ast_noopt_fn: run_ast_noopt,
|
||||||
use_cache: use_cache,
|
use_cache: use_cache,
|
||||||
content_hash: content_hash, cache_path: cache_path,
|
content_hash: content_hash, cache_path: cache_path,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// Hidden vars (os, actorsym, init, core_path, shop_path, analyze, run_ast_fn, run_ast_noopt_fn, json, use_cache, content_hash, cache_path, ensure_build_dir, compile_to_blob_fn) come from env
|
// Hidden vars (os, actorsym, init, core_path, shop_path, analyze, run_ast_fn, run_ast_noopt_fn, json, use_cache, content_hash, cache_path, ensure_build_dir, compile_to_blob_fn) come from env
|
||||||
// In actor spawn mode, also: nota, wota
|
|
||||||
var ACTORDATA = actorsym
|
var ACTORDATA = actorsym
|
||||||
var SYSYM = '__SYSTEM__'
|
var SYSYM = '__SYSTEM__'
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ var ACTOR_EXT = '.ce'
|
|||||||
|
|
||||||
var load_internal = os.load_internal
|
var load_internal = os.load_internal
|
||||||
function use_embed(name) {
|
function use_embed(name) {
|
||||||
return load_internal("js_" + name + "_use")
|
return load_internal("js_core_" + name + "_use")
|
||||||
}
|
}
|
||||||
|
|
||||||
function logical(val1) {
|
function logical(val1) {
|
||||||
@@ -46,7 +45,7 @@ function ends_with(str, suffix) {
|
|||||||
return search(str, suffix, -length(suffix)) != null
|
return search(str, suffix, -length(suffix)) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
var fd = use_embed('fd')
|
var fd = use_embed('internal_fd')
|
||||||
var js = use_embed('js')
|
var js = use_embed('js')
|
||||||
|
|
||||||
// core_path and shop_path come from env (bootstrap.cm passes them through)
|
// core_path and shop_path come from env (bootstrap.cm passes them through)
|
||||||
@@ -69,34 +68,20 @@ function use_core(path) {
|
|||||||
var result = null
|
var result = null
|
||||||
var script = null
|
var script = null
|
||||||
var ast = null
|
var ast = null
|
||||||
var c_cache_key = null
|
var mcode_path = null
|
||||||
|
var mcode_blob = null
|
||||||
// If C embed exists, register it so .cm modules can use('internal/<name>_c')
|
|
||||||
if (sym) {
|
|
||||||
c_cache_key = 'core/internal/' + path + '_c'
|
|
||||||
if (!use_cache[c_cache_key])
|
|
||||||
use_cache[c_cache_key] = sym
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build env: merge core_extras
|
// Build env: merge core_extras
|
||||||
env = {use: use_core}
|
env = {use: use_core}
|
||||||
arrfor(array(core_extras), function(k) { env[k] = core_extras[k] })
|
arrfor(array(core_extras), function(k) { env[k] = core_extras[k] })
|
||||||
|
|
||||||
// Check for pre-compiled .cm.mach file first
|
|
||||||
var mach_path = core_path + '/' + path + '.cm.mach'
|
|
||||||
if (fd.is_file(mach_path)) {
|
|
||||||
result = mach_load(fd.slurp(mach_path), env)
|
|
||||||
use_cache[cache_key] = result
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for .cm.mcode JSON IR
|
|
||||||
var mcode_path = core_path + '/' + path + '.cm.mcode'
|
|
||||||
var mcode_blob = null
|
|
||||||
var hash = null
|
var hash = null
|
||||||
var cached_path = null
|
var cached_path = null
|
||||||
var mach_blob = null
|
var mach_blob = null
|
||||||
var source_blob = null
|
var source_blob = null
|
||||||
|
|
||||||
|
// Check for pre-compiled .cm.mcode JSON IR (generated by regen)
|
||||||
|
mcode_path = core_path + '/boot/' + replace(path, '/', '_') + '.cm.mcode'
|
||||||
if (fd.is_file(mcode_path)) {
|
if (fd.is_file(mcode_path)) {
|
||||||
mcode_blob = fd.slurp(mcode_path)
|
mcode_blob = fd.slurp(mcode_path)
|
||||||
hash = content_hash(mcode_blob)
|
hash = content_hash(mcode_blob)
|
||||||
@@ -115,7 +100,7 @@ function use_core(path) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to source .cm file — compile at runtime
|
// Compile from source .cm file
|
||||||
var file_path = core_path + '/' + path + MOD_EXT
|
var file_path = core_path + '/' + path + MOD_EXT
|
||||||
if (fd.is_file(file_path)) {
|
if (fd.is_file(file_path)) {
|
||||||
source_blob = fd.slurp(file_path)
|
source_blob = fd.slurp(file_path)
|
||||||
|
|||||||
@@ -754,9 +754,9 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
|
|||||||
MIST_FUNC_DEF(fd, readlink, 1),
|
MIST_FUNC_DEF(fd, readlink, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_fd_use(JSContext *js) {
|
JSValue js_core_internal_fd_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_fd_funcs, countof(js_fd_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_fd_funcs, countof(js_fd_funcs));
|
||||||
JS_RETURN(mod.val);
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ static const JSCFunctionListEntry js_kim_funcs[] = {
|
|||||||
MIST_FUNC_DEF(kim, decode, 1),
|
MIST_FUNC_DEF(kim, decode, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_kim_use(JSContext *js)
|
JSValue js_core_kim_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
|
|||||||
@@ -427,6 +427,22 @@ static JSValue js_os_dylib_has_symbol(JSContext *js, JSValue self, int argc, JSV
|
|||||||
return JS_NewBool(js, symbol != NULL);
|
return JS_NewBool(js, symbol != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue js_os_dylib_close(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js, "dylib_close requires a dylib object");
|
||||||
|
void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
|
||||||
|
if (handle) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
FreeLibrary((HMODULE)handle);
|
||||||
|
#else
|
||||||
|
dlclose(handle);
|
||||||
|
#endif
|
||||||
|
JS_SetOpaque(argv[0], NULL);
|
||||||
|
}
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Load a native .cm module from a dylib handle.
|
/* Load a native .cm module from a dylib handle.
|
||||||
Uses cell_rt_native_module_load from qbe_helpers.c */
|
Uses cell_rt_native_module_load from qbe_helpers.c */
|
||||||
extern JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle);
|
extern JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle);
|
||||||
@@ -615,6 +631,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
|||||||
MIST_FUNC_DEF(os, exit, 0),
|
MIST_FUNC_DEF(os, exit, 0),
|
||||||
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_close, 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, dylib_has_symbol, 2),
|
||||||
MIST_FUNC_DEF(os, native_module_load, 1),
|
MIST_FUNC_DEF(os, native_module_load, 1),
|
||||||
@@ -626,7 +643,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
|||||||
MIST_FUNC_DEF(os, getenv, 1),
|
MIST_FUNC_DEF(os, getenv, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_os_use(JSContext *js) {
|
JSValue js_core_os_use(JSContext *js) {
|
||||||
JS_NewClassID(&js_dylib_class_id);
|
JS_NewClassID(&js_dylib_class_id);
|
||||||
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
|
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
|
||||||
|
|
||||||
|
|||||||
333
internal/shop.cm
333
internal/shop.cm
@@ -21,6 +21,54 @@ var my$_ = actor_api
|
|||||||
|
|
||||||
var core = "core"
|
var core = "core"
|
||||||
|
|
||||||
|
// Generate a random 5-letter uppercase identifier for package symbol naming.
|
||||||
|
// Avoids collisions with existing IDs and "CORE".
|
||||||
|
function generate_package_id() {
|
||||||
|
var lock = Shop.load_lock()
|
||||||
|
var existing = {}
|
||||||
|
var keys = array(lock)
|
||||||
|
var _i = 0
|
||||||
|
while (_i < length(keys)) {
|
||||||
|
if (lock[keys[_i]] && lock[keys[_i]].id)
|
||||||
|
existing[lock[keys[_i]].id] = true
|
||||||
|
_i = _i + 1
|
||||||
|
}
|
||||||
|
existing["CORE"] = true
|
||||||
|
|
||||||
|
var id = null
|
||||||
|
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
var _j = 0
|
||||||
|
while (true) {
|
||||||
|
id = ""
|
||||||
|
_j = 0
|
||||||
|
while (_j < 5) {
|
||||||
|
id = id + chars[abs(os.random()) % 26]
|
||||||
|
_j = _j + 1
|
||||||
|
}
|
||||||
|
if (!existing[id]) return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the assigned ID for a package, generating one if needed.
|
||||||
|
// Core always returns "core". Other packages get a random 5-letter ID
|
||||||
|
// assigned lazily on first use and persisted in lock.toml.
|
||||||
|
function get_package_id(pkg) {
|
||||||
|
if (pkg == core) return core
|
||||||
|
|
||||||
|
var lock = Shop.load_lock()
|
||||||
|
var entry = lock[pkg]
|
||||||
|
if (entry && entry.id) return entry.id
|
||||||
|
|
||||||
|
var id = generate_package_id()
|
||||||
|
if (!entry) {
|
||||||
|
entry = {}
|
||||||
|
lock[pkg] = entry
|
||||||
|
}
|
||||||
|
entry.id = id
|
||||||
|
Shop.save_lock(lock)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
function pull_from_cache(content)
|
function pull_from_cache(content)
|
||||||
{
|
{
|
||||||
var path = hash_path(content)
|
var path = hash_path(content)
|
||||||
@@ -265,11 +313,6 @@ function package_cache_path(pkg)
|
|||||||
return global_shop_path + '/cache/' + replace(replace(pkg, '/', '_'), '@', '_')
|
return global_shop_path + '/cache/' + replace(replace(pkg, '/', '_'), '@', '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_shared_lib_path()
|
|
||||||
{
|
|
||||||
return get_global_build_dir() + '/' + 'lib'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load lock.toml configuration (from global shop)
|
// Load lock.toml configuration (from global shop)
|
||||||
var _lock = null
|
var _lock = null
|
||||||
Shop.load_lock = function() {
|
Shop.load_lock = function() {
|
||||||
@@ -372,9 +415,7 @@ Shop.extract_commit_hash = function(pkg, response) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var dylib_visited = {}
|
|
||||||
var open_dls = {}
|
var open_dls = {}
|
||||||
var loaded_manifests = {}
|
|
||||||
|
|
||||||
// Host target detection for native dylib resolution
|
// Host target detection for native dylib resolution
|
||||||
function detect_host_target() {
|
function detect_host_target() {
|
||||||
@@ -391,12 +432,12 @@ function detect_host_target() {
|
|||||||
|
|
||||||
var host_target = detect_host_target()
|
var host_target = detect_host_target()
|
||||||
|
|
||||||
// Check for a native .cm dylib in the content-addressed cache
|
// Check for a native .cm dylib at the deterministic lib path
|
||||||
// Returns the loaded module value, or null if no native dylib exists
|
// Returns the loaded module value, or null if no native dylib exists
|
||||||
function try_native_dylib(content_key) {
|
function try_native_mod_dylib(pkg, stem) {
|
||||||
var native_path = hash_path(content_key) + '.' + host_target + dylib_ext
|
var dylib_path = get_dylib_path(pkg, stem)
|
||||||
if (!fd.is_file(native_path)) return null
|
if (!fd.is_file(dylib_path)) return null
|
||||||
var handle = os.dylib_open(native_path)
|
var handle = os.dylib_open(dylib_path)
|
||||||
if (!handle) return null
|
if (!handle) return null
|
||||||
return os.native_module_load(handle)
|
return os.native_module_load(handle)
|
||||||
}
|
}
|
||||||
@@ -465,11 +506,19 @@ function resolve_mod_fn(path, pkg) {
|
|||||||
var optimized = null
|
var optimized = null
|
||||||
var mcode_json = null
|
var mcode_json = null
|
||||||
var cached_mcode_path = null
|
var cached_mcode_path = null
|
||||||
|
var _pkg_dir = null
|
||||||
|
var _stem = null
|
||||||
|
|
||||||
// Check for native .cm dylib first (highest performance)
|
// Check for native .cm dylib at deterministic path first
|
||||||
native_result = try_native_dylib(content_key)
|
if (pkg) {
|
||||||
if (native_result != null) {
|
_pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
|
||||||
return {_native: true, value: native_result}
|
if (starts_with(path, _pkg_dir + '/')) {
|
||||||
|
_stem = fd.stem(text(path, length(_pkg_dir) + 1))
|
||||||
|
native_result = try_native_mod_dylib(pkg, _stem)
|
||||||
|
if (native_result != null) {
|
||||||
|
return {_native: true, value: native_result}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cache for pre-compiled .mach blob
|
// Check cache for pre-compiled .mach blob
|
||||||
@@ -478,8 +527,8 @@ function resolve_mod_fn(path, pkg) {
|
|||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for cached mcode in content-addressed store
|
// Check for cached mcode in content-addressed store (salted hash to distinguish from mach)
|
||||||
cached_mcode_path = hash_path(content_key) + '.mcode'
|
cached_mcode_path = global_shop_path + '/build/' + content_hash(stone(blob(text(content_key) + "\nmcode")))
|
||||||
if (fd.is_file(cached_mcode_path)) {
|
if (fd.is_file(cached_mcode_path)) {
|
||||||
mcode_json = text(fd.slurp(cached_mcode_path))
|
mcode_json = text(fd.slurp(cached_mcode_path))
|
||||||
compiled = mach_compile_mcode_bin(path, mcode_json)
|
compiled = mach_compile_mcode_bin(path, mcode_json)
|
||||||
@@ -487,22 +536,6 @@ function resolve_mod_fn(path, pkg) {
|
|||||||
return compiled
|
return compiled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for pre-compiled .mach or .mcode file alongside .cm source
|
|
||||||
if (ends_with(path, '.cm')) {
|
|
||||||
mach_path = text(path, 0, length(path) - 3) + '.mach'
|
|
||||||
if (fd.is_file(mach_path)) {
|
|
||||||
mach_blob = fd.slurp(mach_path)
|
|
||||||
put_into_cache(content_key, mach_blob)
|
|
||||||
return mach_blob
|
|
||||||
}
|
|
||||||
mcode_path = path + '.mcode'
|
|
||||||
if (fd.is_file(mcode_path)) {
|
|
||||||
compiled = mach_compile_mcode_bin(path, text(fd.slurp(mcode_path)))
|
|
||||||
put_into_cache(content_key, compiled)
|
|
||||||
return compiled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile via full pipeline: analyze → mcode → streamline → serialize
|
// Compile via full pipeline: analyze → mcode → streamline → serialize
|
||||||
if (!_mcode_mod) _mcode_mod = Shop.use("mcode", null)
|
if (!_mcode_mod) _mcode_mod = Shop.use("mcode", null)
|
||||||
if (!_streamline_mod) _streamline_mod = Shop.use("streamline", null)
|
if (!_streamline_mod) _streamline_mod = Shop.use("streamline", null)
|
||||||
@@ -608,39 +641,19 @@ function resolve_locator(path, ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate symbol name for a C module file
|
// Generate symbol name for a C module file
|
||||||
// Uses the same format as Shop.c_symbol_for_file
|
// Uses the package's assigned ID (5-letter random or "core") instead of the full name
|
||||||
// Symbol names are based on canonical package names, not link targets
|
|
||||||
function make_c_symbol(pkg, file) {
|
function make_c_symbol(pkg, file) {
|
||||||
var pkg_safe = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
var pkg_id = get_package_id(pkg)
|
||||||
var file_safe = replace(replace(replace(file, '/', '_'), '.', '_'), '-', '_')
|
var file_safe = replace(replace(replace(file, '/', '_'), '.', '_'), '-', '_')
|
||||||
return 'js_' + pkg_safe + '_' + file_safe + '_use'
|
return 'js_' + pkg_id + '_' + file_safe + '_use'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the library path for a package in .cell/lib
|
// Get the deterministic dylib path for a module in lib/<pkg>/<stem>.dylib
|
||||||
// Library names are based on canonical package names, not link targets
|
function get_dylib_path(pkg, stem) {
|
||||||
function get_lib_path(pkg) {
|
return global_shop_path + '/lib/' + safe_package_path(pkg) + '/' + stem + dylib_ext
|
||||||
var lib_name = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
|
||||||
return global_shop_path + '/lib/' + lib_name + dylib_ext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the manifest for a package's per-module dylibs
|
// Open a per-module dylib and return the dlopen handle
|
||||||
// Returns a map of symbol_name -> dylib_path, or null if no manifest
|
|
||||||
function load_package_manifest(pkg) {
|
|
||||||
if (loaded_manifests[pkg] != null) return loaded_manifests[pkg]
|
|
||||||
|
|
||||||
var lib_name = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
|
||||||
var manifest_path = global_shop_path + '/lib/' + lib_name + '.manifest.json'
|
|
||||||
if (!fd.is_file(manifest_path)) {
|
|
||||||
loaded_manifests[pkg] = false
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var content = text(fd.slurp(manifest_path))
|
|
||||||
var manifest = json.decode(content)
|
|
||||||
loaded_manifests[pkg] = manifest
|
|
||||||
return manifest
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open a per-module dylib from a manifest and return the dlopen handle
|
|
||||||
function open_module_dylib(dylib_path) {
|
function open_module_dylib(dylib_path) {
|
||||||
if (open_dls[dylib_path]) return open_dls[dylib_path]
|
if (open_dls[dylib_path]) return open_dls[dylib_path]
|
||||||
if (!fd.is_file(dylib_path)) return null
|
if (!fd.is_file(dylib_path)) return null
|
||||||
@@ -648,57 +661,18 @@ function open_module_dylib(dylib_path) {
|
|||||||
return open_dls[dylib_path]
|
return open_dls[dylib_path]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a C symbol from per-module dylibs for a package
|
// Try to resolve a C symbol from the deterministic dylib path
|
||||||
// Returns a loader function or null
|
// Returns a loader function or null
|
||||||
function resolve_dylib_symbol(sym, pkg) {
|
function try_dylib_symbol(sym, pkg, file_stem) {
|
||||||
var manifest = load_package_manifest(pkg)
|
var dylib_path = get_dylib_path(pkg, file_stem)
|
||||||
if (!manifest) return null
|
|
||||||
var dylib_path = manifest[sym]
|
|
||||||
if (!dylib_path) return null
|
|
||||||
var handle = open_module_dylib(dylib_path)
|
var handle = open_module_dylib(dylib_path)
|
||||||
if (!handle) return null
|
if (!handle) return null
|
||||||
if (!os.dylib_has_symbol(handle, sym)) return null
|
if (!os.dylib_has_symbol(handle, sym)) return null
|
||||||
return function() { return os.dylib_symbol(handle, sym) }
|
return function() { return os.dylib_symbol(handle, sym) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a package's dynamic libraries (loads manifest + dependency manifests)
|
|
||||||
Shop.open_package_dylib = function(pkg) {
|
|
||||||
if (pkg == 'core' || !pkg) return
|
|
||||||
if (dylib_visited[pkg]) return
|
|
||||||
dylib_visited[pkg] = true
|
|
||||||
|
|
||||||
var link_target = link.get_target(pkg)
|
|
||||||
var resolved_pkg = link_target ? link_target : pkg
|
|
||||||
|
|
||||||
var pkg_dir = null
|
|
||||||
if (starts_with(resolved_pkg, '/')) {
|
|
||||||
pkg_dir = resolved_pkg
|
|
||||||
} else {
|
|
||||||
pkg_dir = get_packages_dir() + '/' + safe_package_path(resolved_pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
var toml_path = pkg_dir + '/cell.toml'
|
|
||||||
var content = null
|
|
||||||
var cfg = null
|
|
||||||
if (fd.is_file(toml_path)) {
|
|
||||||
content = text(fd.slurp(toml_path))
|
|
||||||
cfg = toml.decode(content)
|
|
||||||
if (cfg.dependencies) {
|
|
||||||
arrfor(array(cfg.dependencies), function(alias, i) {
|
|
||||||
var dep_pkg = cfg.dependencies[alias]
|
|
||||||
Shop.open_package_dylib(dep_pkg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-load the manifest
|
|
||||||
load_package_manifest(pkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve a C symbol by searching:
|
// Resolve a C symbol by searching:
|
||||||
// 1. If package_context is null, only check core internal symbols
|
// At each scope: check lib/ dylib first, then internal (static)
|
||||||
// 2. Otherwise: own package (internal then per-module dylib) -> aliased packages -> core (internal only)
|
|
||||||
// Core is never loaded as a dynamic library via dlopen
|
|
||||||
function resolve_c_symbol(path, package_context) {
|
function resolve_c_symbol(path, package_context) {
|
||||||
var explicit = split_explicit_package_import(path)
|
var explicit = split_explicit_package_import(path)
|
||||||
var sym = null
|
var sym = null
|
||||||
@@ -707,6 +681,7 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
var core_sym = null
|
var core_sym = null
|
||||||
var canon_pkg = null
|
var canon_pkg = null
|
||||||
var mod_name = null
|
var mod_name = null
|
||||||
|
var file_stem = null
|
||||||
|
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
if (is_internal_path(explicit.path) && package_context && explicit.package != package_context)
|
if (is_internal_path(explicit.path) && package_context && explicit.package != package_context)
|
||||||
@@ -714,17 +689,10 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
}
|
}
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
sym = make_c_symbol(explicit.package, explicit.path)
|
sym = make_c_symbol(explicit.package, explicit.path)
|
||||||
if (os.internal_exists(sym)) {
|
file_stem = replace(explicit.path, '.c', '')
|
||||||
return {
|
|
||||||
symbol: function() { return os.load_internal(sym) },
|
|
||||||
scope: SCOPE_PACKAGE,
|
|
||||||
package: explicit.package,
|
|
||||||
path: sym
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.open_package_dylib(explicit.package)
|
// Check lib/ dylib first
|
||||||
loader = resolve_dylib_symbol(sym, explicit.package)
|
loader = try_dylib_symbol(sym, explicit.package, file_stem)
|
||||||
if (loader) {
|
if (loader) {
|
||||||
return {
|
return {
|
||||||
symbol: loader,
|
symbol: loader,
|
||||||
@@ -733,12 +701,32 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
path: sym
|
path: sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then check internal/static
|
||||||
|
if (os.internal_exists(sym)) {
|
||||||
|
return {
|
||||||
|
symbol: function() { return os.load_internal(sym) },
|
||||||
|
scope: SCOPE_PACKAGE,
|
||||||
|
package: explicit.package,
|
||||||
|
path: sym
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no package context, only check core internal symbols
|
// If no package context, only check core
|
||||||
if (!package_context || package_context == 'core') {
|
if (!package_context || package_context == 'core') {
|
||||||
_path = replace(path, '/', '_')
|
core_sym = make_c_symbol('core', path)
|
||||||
core_sym = `js_${_path}_use`
|
|
||||||
|
// Check lib/ dylib first for core
|
||||||
|
loader = try_dylib_symbol(core_sym, 'core', path)
|
||||||
|
if (loader) {
|
||||||
|
return {
|
||||||
|
symbol: loader,
|
||||||
|
scope: SCOPE_CORE,
|
||||||
|
path: core_sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (os.internal_exists(core_sym)) {
|
if (os.internal_exists(core_sym)) {
|
||||||
return {
|
return {
|
||||||
symbol: function() { return os.load_internal(core_sym) },
|
symbol: function() { return os.load_internal(core_sym) },
|
||||||
@@ -749,21 +737,21 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check own package first (internal, then per-module dylib)
|
// 1. Check own package (dylib first, then internal)
|
||||||
sym = make_c_symbol(package_context, path)
|
sym = make_c_symbol(package_context, path)
|
||||||
if (os.internal_exists(sym)) {
|
|
||||||
|
loader = try_dylib_symbol(sym, package_context, path)
|
||||||
|
if (loader) {
|
||||||
return {
|
return {
|
||||||
symbol: function() { return os.load_internal(sym) },
|
symbol: loader,
|
||||||
scope: SCOPE_LOCAL,
|
scope: SCOPE_LOCAL,
|
||||||
path: sym
|
path: sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shop.open_package_dylib(package_context)
|
if (os.internal_exists(sym)) {
|
||||||
loader = resolve_dylib_symbol(sym, package_context)
|
|
||||||
if (loader) {
|
|
||||||
return {
|
return {
|
||||||
symbol: loader,
|
symbol: function() { return os.load_internal(sym) },
|
||||||
scope: SCOPE_LOCAL,
|
scope: SCOPE_LOCAL,
|
||||||
path: sym
|
path: sym
|
||||||
}
|
}
|
||||||
@@ -780,17 +768,7 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
mod_name = get_import_name(path)
|
mod_name = get_import_name(path)
|
||||||
sym = make_c_symbol(canon_pkg, mod_name)
|
sym = make_c_symbol(canon_pkg, mod_name)
|
||||||
|
|
||||||
if (os.internal_exists(sym)) {
|
loader = try_dylib_symbol(sym, canon_pkg, mod_name)
|
||||||
return {
|
|
||||||
symbol: function() { return os.load_internal(sym) },
|
|
||||||
scope: SCOPE_PACKAGE,
|
|
||||||
package: canon_pkg,
|
|
||||||
path: sym
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shop.open_package_dylib(canon_pkg)
|
|
||||||
loader = resolve_dylib_symbol(sym, canon_pkg)
|
|
||||||
if (loader) {
|
if (loader) {
|
||||||
return {
|
return {
|
||||||
symbol: loader,
|
symbol: loader,
|
||||||
@@ -799,11 +777,30 @@ function resolve_c_symbol(path, package_context) {
|
|||||||
path: sym
|
path: sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (os.internal_exists(sym)) {
|
||||||
|
return {
|
||||||
|
symbol: function() { return os.load_internal(sym) },
|
||||||
|
scope: SCOPE_PACKAGE,
|
||||||
|
package: canon_pkg,
|
||||||
|
path: sym
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check core (dylib first, then internal)
|
||||||
|
core_sym = make_c_symbol('core', path)
|
||||||
|
|
||||||
|
loader = try_dylib_symbol(core_sym, 'core', path)
|
||||||
|
if (loader) {
|
||||||
|
return {
|
||||||
|
symbol: loader,
|
||||||
|
scope: SCOPE_CORE,
|
||||||
|
path: core_sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Check core internal symbols (core is never a dynamic library)
|
|
||||||
core_sym = `js_${replace(path, '/', '_')}_use`
|
|
||||||
if (os.internal_exists(core_sym)) {
|
if (os.internal_exists(core_sym)) {
|
||||||
return {
|
return {
|
||||||
symbol: function() { return os.load_internal(core_sym) },
|
symbol: function() { return os.load_internal(core_sym) },
|
||||||
@@ -1322,26 +1319,38 @@ Shop.file_reload = function(file)
|
|||||||
|
|
||||||
Shop.module_reload = function(path, package) {
|
Shop.module_reload = function(path, package) {
|
||||||
if (!Shop.is_loaded(path,package)) return
|
if (!Shop.is_loaded(path,package)) return
|
||||||
|
|
||||||
// Clear the module info cache for this path
|
// Clear the module info cache for this path
|
||||||
var lookup_key = package ? package + ':' + path : ':' + path
|
var lookup_key = package ? package + ':' + path : ':' + path
|
||||||
module_info_cache[lookup_key] = null
|
module_info_cache[lookup_key] = null
|
||||||
|
|
||||||
|
// Close old dylib handle if any
|
||||||
|
var old_dylib_path = null
|
||||||
|
if (package) {
|
||||||
|
old_dylib_path = get_dylib_path(package, path)
|
||||||
|
if (open_dls[old_dylib_path]) {
|
||||||
|
os.dylib_close(open_dls[old_dylib_path])
|
||||||
|
open_dls[old_dylib_path] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var info = resolve_module_info(path, package)
|
var info = resolve_module_info(path, package)
|
||||||
if (!info) return
|
if (!info) return
|
||||||
|
|
||||||
var cache_key = info.cache_key
|
var cache_key = info.cache_key
|
||||||
var old = use_cache[cache_key]
|
var old = use_cache[cache_key]
|
||||||
|
use_cache[cache_key] = null
|
||||||
|
|
||||||
var newmod = get_module(path, package)
|
var newmod = get_module(path, package)
|
||||||
|
use_cache[cache_key] = newmod
|
||||||
|
|
||||||
arrfor(array(newmod), function(i, idx) {
|
if (old && is_object(old) && is_object(newmod)) {
|
||||||
old[i] = newmod[i]
|
arrfor(array(newmod), function(k) { old[k] = newmod[k] })
|
||||||
})
|
arrfor(array(old), function(k) {
|
||||||
|
if (!(k in newmod)) old[k] = null
|
||||||
arrfor(array(old), function(i, idx) {
|
})
|
||||||
if (!(i in newmod))
|
use_cache[cache_key] = old
|
||||||
old[i] = null
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_package_scripts(package)
|
function get_package_scripts(package)
|
||||||
@@ -1398,28 +1407,38 @@ Shop.get_package_dir = function(pkg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate C symbol name for a file within a package
|
// Generate C symbol name for a file within a package
|
||||||
// e.g., c_symbol_for_file('gitea.pockle.world/john/prosperon', 'sprite.c')
|
// Uses the package's assigned ID (e.g., "WAWOF") instead of the full name
|
||||||
// -> 'js_gitea_pockle_world_john_prosperon_sprite_use'
|
// e.g., c_symbol_for_file('gitea.pockle.world/john/prosperon', 'sprite.c')
|
||||||
|
// -> 'js_WAWOF_sprite_use'
|
||||||
Shop.c_symbol_for_file = function(pkg, file) {
|
Shop.c_symbol_for_file = function(pkg, file) {
|
||||||
var pkg_safe = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
var pkg_id = get_package_id(pkg)
|
||||||
var file_safe = replace(replace(fd.stem(file), '/', '_'), '.', '_')
|
var file_safe = replace(replace(fd.stem(file), '/', '_'), '.', '_')
|
||||||
return 'js_' + pkg_safe + '_' + file_safe + '_use'
|
var suffix = ends_with(file, '.ce') ? '_program' : '_use'
|
||||||
|
return 'js_' + pkg_id + '_' + file_safe + suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate C symbol prefix for a package
|
// Generate C symbol prefix for a package
|
||||||
// e.g., c_symbol_prefix('gitea.pockle.world/john/prosperon') -> 'js_gitea_pockle_world_john_prosperon_'
|
// e.g., c_symbol_prefix('gitea.pockle.world/john/prosperon') -> 'js_WAWOF_'
|
||||||
Shop.c_symbol_prefix = function(pkg) {
|
Shop.c_symbol_prefix = function(pkg) {
|
||||||
var pkg_safe = replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
var pkg_id = get_package_id(pkg)
|
||||||
return 'js_' + pkg_safe + '_'
|
return 'js_' + pkg_id + '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the library name for a package (without extension)
|
// Get the library name for a package
|
||||||
// e.g., 'gitea.pockle.world/john/prosperon' -> 'gitea_pockle_world_john_prosperon'
|
// e.g., 'gitea.pockle.world/john/prosperon' -> 'WAWOF'
|
||||||
Shop.lib_name_for_package = function(pkg) {
|
Shop.lib_name_for_package = function(pkg) {
|
||||||
return replace(replace(replace(pkg, '/', '_'), '.', '_'), '-', '_')
|
return get_package_id(pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the assigned package ID (public API)
|
||||||
|
Shop.get_package_id = get_package_id
|
||||||
|
|
||||||
// Returns { ok: bool, results: [{pkg, ok, error}] }
|
// Returns { ok: bool, results: [{pkg, ok, error}] }
|
||||||
|
// Get the deterministic dylib path for a module (public API)
|
||||||
|
Shop.get_dylib_path = function(pkg, stem) {
|
||||||
|
return get_dylib_path(pkg, stem)
|
||||||
|
}
|
||||||
|
|
||||||
Shop.audit_packages = function() {
|
Shop.audit_packages = function() {
|
||||||
var packages = Shop.list_packages()
|
var packages = Shop.list_packages()
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ static const JSCFunctionListEntry js_time_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue
|
JSValue
|
||||||
js_internal_time_use(JSContext *ctx)
|
js_core_internal_time_use(JSContext *ctx)
|
||||||
{
|
{
|
||||||
JS_FRAME(ctx);
|
JS_FRAME(ctx);
|
||||||
JS_ROOT(mod, JS_NewObject(ctx));
|
JS_ROOT(mod, JS_NewObject(ctx));
|
||||||
@@ -79,9 +79,3 @@ js_internal_time_use(JSContext *ctx)
|
|||||||
sizeof(js_time_funcs[0]));
|
sizeof(js_time_funcs[0]));
|
||||||
JS_RETURN(mod.val);
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue
|
|
||||||
js_time_use(JSContext *ctx)
|
|
||||||
{
|
|
||||||
return js_internal_time_use(ctx);
|
|
||||||
}
|
|
||||||
113
link.ce
113
link.ce
@@ -17,6 +17,24 @@ var shop = use('internal/shop')
|
|||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
var toml = use('toml')
|
var toml = use('toml')
|
||||||
|
|
||||||
|
var links = null
|
||||||
|
var count = 0
|
||||||
|
var result = null
|
||||||
|
var i = 0
|
||||||
|
var pkg = null
|
||||||
|
var cmd = null
|
||||||
|
var pkg_name = null
|
||||||
|
var target = null
|
||||||
|
var start_idx = 0
|
||||||
|
var arg1 = null
|
||||||
|
var arg2 = null
|
||||||
|
var cwd = null
|
||||||
|
var toml_path = null
|
||||||
|
var content = null
|
||||||
|
var _restore = null
|
||||||
|
var _read_toml = null
|
||||||
|
var _add_link = null
|
||||||
|
|
||||||
if (length(args) < 1) {
|
if (length(args) < 1) {
|
||||||
log.console("Usage: link <command> [args] or link [package] <target>")
|
log.console("Usage: link <command> [args] or link [package] <target>")
|
||||||
log.console("Commands:")
|
log.console("Commands:")
|
||||||
@@ -27,154 +45,149 @@ if (length(args) < 1) {
|
|||||||
log.console(" <path> Link the package in <path> to that path")
|
log.console(" <path> Link the package in <path> to that path")
|
||||||
log.console(" <package> <target> Link <package> to <target> (path or package)")
|
log.console(" <package> <target> Link <package> to <target> (path or package)")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd = args[0]
|
cmd = args[0]
|
||||||
|
|
||||||
if (cmd == 'list') {
|
if (cmd == 'list') {
|
||||||
var links = link.load()
|
links = link.load()
|
||||||
var count = 0
|
count = 0
|
||||||
arrfor(array(links), function(k) {
|
arrfor(array(links), function(k) {
|
||||||
log.console(k + " -> " + links[k])
|
log.console(k + " -> " + links[k])
|
||||||
count++
|
count++
|
||||||
})
|
})
|
||||||
if (count == 0) log.console("No links.")
|
if (count == 0) log.console("No links.")
|
||||||
|
|
||||||
} else if (cmd == 'sync') {
|
} else if (cmd == 'sync') {
|
||||||
log.console("Syncing links...")
|
log.console("Syncing links...")
|
||||||
var result = link.sync_all(shop)
|
result = link.sync_all(shop)
|
||||||
log.console("Synced " + result.synced + " link(s)")
|
log.console("Synced " + result.synced + " link(s)")
|
||||||
if (length(result.errors) > 0) {
|
if (length(result.errors) > 0) {
|
||||||
log.console("Errors:")
|
log.console("Errors:")
|
||||||
for (var i = 0; i < length(result.errors); i++) {
|
for (i = 0; i < length(result.errors); i++) {
|
||||||
log.console(" " + result.errors[i])
|
log.console(" " + result.errors[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (cmd == 'delete' || cmd == 'rm') {
|
} else if (cmd == 'delete' || cmd == 'rm') {
|
||||||
if (length(args) < 2) {
|
if (length(args) < 2) {
|
||||||
log.console("Usage: link delete <package>")
|
log.console("Usage: link delete <package>")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkg = args[1]
|
pkg = args[1]
|
||||||
|
|
||||||
|
var _restore = null
|
||||||
if (link.remove(pkg)) {
|
if (link.remove(pkg)) {
|
||||||
// Try to restore the original package
|
// Try to restore the original package
|
||||||
log.console("Restoring " + pkg + "...")
|
log.console("Restoring " + pkg + "...")
|
||||||
try {
|
_restore = function() {
|
||||||
shop.fetch(pkg)
|
shop.fetch(pkg)
|
||||||
shop.extract(pkg)
|
shop.extract(pkg)
|
||||||
log.console("Restored " + pkg)
|
log.console("Restored " + pkg)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.console("Could not restore: " + e.message)
|
log.console("Could not restore")
|
||||||
log.console("Run 'cell update " + pkg + "' to restore")
|
log.console("Run 'cell update " + pkg + "' to restore")
|
||||||
}
|
}
|
||||||
|
_restore()
|
||||||
} else {
|
} else {
|
||||||
log.console("No link found for " + pkg)
|
log.console("No link found for " + pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (cmd == 'clear') {
|
} else if (cmd == 'clear') {
|
||||||
link.clear()
|
link.clear()
|
||||||
log.console("Links cleared. Run 'cell update' to restore packages.")
|
log.console("Links cleared. Run 'cell update' to restore packages.")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Linking logic
|
// Linking logic
|
||||||
var pkg_name = null
|
pkg_name = null
|
||||||
var target = null
|
target = null
|
||||||
|
|
||||||
// Check for 'add' compatibility
|
// Check for 'add' compatibility
|
||||||
var start_idx = 0
|
start_idx = 0
|
||||||
if (cmd == 'add') {
|
if (cmd == 'add') {
|
||||||
start_idx = 1
|
start_idx = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var arg1 = args[start_idx]
|
arg1 = args[start_idx]
|
||||||
var arg2 = (length(args) > start_idx + 1) ? args[start_idx + 1] : null
|
arg2 = (length(args) > start_idx + 1) ? args[start_idx + 1] : null
|
||||||
|
|
||||||
if (!arg1) {
|
if (!arg1) {
|
||||||
log.console("Error: target or package required")
|
log.console("Error: target or package required")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg2) {
|
if (arg2) {
|
||||||
// Two arguments: explicit package name and target
|
// Two arguments: explicit package name and target
|
||||||
pkg_name = arg1
|
pkg_name = arg1
|
||||||
target = arg2
|
target = arg2
|
||||||
|
|
||||||
// Resolve target if it's a local path
|
// Resolve target if it's a local path
|
||||||
if (target == '.' || fd.is_dir(target)) {
|
if (target == '.' || fd.is_dir(target)) {
|
||||||
target = fd.realpath(target)
|
target = fd.realpath(target)
|
||||||
} else if (starts_with(target, './') || starts_with(target, '../')) {
|
} else if (starts_with(target, './') || starts_with(target, '../')) {
|
||||||
// Relative path that doesn't exist yet - try to resolve anyway
|
// Relative path that doesn't exist yet - try to resolve anyway
|
||||||
var cwd = fd.realpath('.')
|
cwd = fd.realpath('.')
|
||||||
if (starts_with(target, './')) {
|
if (starts_with(target, './')) {
|
||||||
target = cwd + text(target, 1)
|
target = cwd + text(target, 1)
|
||||||
} else {
|
} else {
|
||||||
// For ../ paths, var fd.realpath handle it if possible
|
// For ../ paths, let fd.realpath handle it if possible
|
||||||
target = fd.realpath(target) || target
|
target = fd.realpath(target) || target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise target is a package name (e.g., github.com/prosperon)
|
// Otherwise target is a package name (e.g., github.com/prosperon)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// One argument: assume it's a local path, infer package name from cell.toml
|
// One argument: assume it's a local path, infer package name from cell.toml
|
||||||
target = arg1
|
target = arg1
|
||||||
|
|
||||||
// Resolve path
|
// Resolve path
|
||||||
if (target == '.' || fd.is_dir(target)) {
|
if (target == '.' || fd.is_dir(target)) {
|
||||||
target = fd.realpath(target)
|
target = fd.realpath(target)
|
||||||
} else if (starts_with(target, './') || starts_with(target, '../')) {
|
} else if (starts_with(target, './') || starts_with(target, '../')) {
|
||||||
target = fd.realpath(target) || target
|
target = fd.realpath(target) || target
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be a local path with cell.toml
|
// Must be a local path with cell.toml
|
||||||
var toml_path = target + '/cell.toml'
|
toml_path = target + '/cell.toml'
|
||||||
if (!fd.is_file(toml_path)) {
|
if (!fd.is_file(toml_path)) {
|
||||||
log.console("Error: No cell.toml found at " + target)
|
log.console("Error: No cell.toml found at " + target)
|
||||||
log.console("For linking to another package, use: link <package> <target>")
|
log.console("For linking to another package, use: link <package> <target>")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read package name from cell.toml
|
// Read package name from cell.toml
|
||||||
try {
|
_read_toml = function() {
|
||||||
var content = toml.decode(text(fd.slurp(toml_path)))
|
content = toml.decode(text(fd.slurp(toml_path)))
|
||||||
if (content.package) {
|
if (content.package) {
|
||||||
pkg_name = content.package
|
pkg_name = content.package
|
||||||
} else {
|
} else {
|
||||||
log.console("Error: cell.toml at " + target + " does not define 'package'")
|
log.console("Error: cell.toml at " + target + " does not define 'package'")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.console("Error reading cell.toml: " + e)
|
log.console("Error reading cell.toml")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
_read_toml()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate: if target is a local path, it must have cell.toml
|
// Validate: if target is a local path, it must have cell.toml
|
||||||
if (starts_with(target, '/')) {
|
if (starts_with(target, '/')) {
|
||||||
if (!fd.is_file(target + '/cell.toml')) {
|
if (!fd.is_file(target + '/cell.toml')) {
|
||||||
log.console("Error: " + target + " is not a valid package (no cell.toml)")
|
log.console("Error: " + target + " is not a valid package (no cell.toml)")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the link (this also creates the symlink)
|
// Add the link (this also creates the symlink)
|
||||||
try {
|
_add_link = function() {
|
||||||
link.add(pkg_name, target, shop)
|
link.add(pkg_name, target, shop)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.console("Error: " + e.message)
|
log.console("Error adding link")
|
||||||
log.error(e)
|
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
_add_link()
|
||||||
}
|
}
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
48
list.ce
48
list.ce
@@ -12,6 +12,13 @@ var fd = use('fd')
|
|||||||
|
|
||||||
var mode = 'local'
|
var mode = 'local'
|
||||||
var target_pkg = null
|
var target_pkg = null
|
||||||
|
var resolved = null
|
||||||
|
var i = 0
|
||||||
|
var deps = null
|
||||||
|
var packages = null
|
||||||
|
var local_pkgs = null
|
||||||
|
var linked_pkgs = null
|
||||||
|
var remote_pkgs = null
|
||||||
|
|
||||||
if (args && length(args) > 0) {
|
if (args && length(args) > 0) {
|
||||||
if (args[0] == 'shop') {
|
if (args[0] == 'shop') {
|
||||||
@@ -32,7 +39,7 @@ if (args && length(args) > 0) {
|
|||||||
|
|
||||||
// Resolve local paths
|
// Resolve local paths
|
||||||
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
||||||
var resolved = fd.realpath(target_pkg)
|
resolved = fd.realpath(target_pkg)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_pkg = resolved
|
target_pkg = resolved
|
||||||
}
|
}
|
||||||
@@ -44,21 +51,23 @@ var links = link.load()
|
|||||||
var lock = shop.load_lock()
|
var lock = shop.load_lock()
|
||||||
|
|
||||||
function print_deps(ctx, indent) {
|
function print_deps(ctx, indent) {
|
||||||
|
var aliases = null
|
||||||
indent = indent || ""
|
indent = indent || ""
|
||||||
var deps
|
deps = null
|
||||||
try {
|
var _read = function() {
|
||||||
deps = pkg.dependencies(ctx)
|
deps = pkg.dependencies(ctx)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.console(indent + " (could not read dependencies)")
|
log.console(indent + " (could not read dependencies)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
_read()
|
||||||
|
|
||||||
if (!deps) {
|
if (!deps) {
|
||||||
log.console(indent + " (none)")
|
log.console(indent + " (none)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var aliases = array(deps)
|
aliases = array(deps)
|
||||||
aliases = sort(aliases)
|
aliases = sort(aliases)
|
||||||
|
|
||||||
if (length(aliases) == 0) {
|
if (length(aliases) == 0) {
|
||||||
@@ -66,19 +75,26 @@ function print_deps(ctx, indent) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < length(aliases); i++) {
|
var j = 0
|
||||||
var alias = aliases[i]
|
var alias = null
|
||||||
var locator = deps[alias]
|
var locator = null
|
||||||
var link_target = links[locator]
|
var link_target = null
|
||||||
var lock_entry = lock[locator]
|
var lock_entry = null
|
||||||
|
var line = null
|
||||||
|
var status = null
|
||||||
|
for (j = 0; j < length(aliases); j++) {
|
||||||
|
alias = aliases[j]
|
||||||
|
locator = deps[alias]
|
||||||
|
link_target = links[locator]
|
||||||
|
lock_entry = lock[locator]
|
||||||
|
|
||||||
var line = indent + " " + alias
|
line = indent + " " + alias
|
||||||
if (alias != locator) {
|
if (alias != locator) {
|
||||||
line += " -> " + locator
|
line += " -> " + locator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add status indicators
|
// Add status indicators
|
||||||
var status = []
|
status = []
|
||||||
if (link_target) {
|
if (link_target) {
|
||||||
push(status, "linked -> " + link_target)
|
push(status, "linked -> " + link_target)
|
||||||
}
|
}
|
||||||
@@ -110,16 +126,16 @@ if (mode == 'local') {
|
|||||||
log.console("Shop packages:")
|
log.console("Shop packages:")
|
||||||
log.console("")
|
log.console("")
|
||||||
|
|
||||||
var packages = shop.list_packages()
|
packages = shop.list_packages()
|
||||||
if (length(packages) == 0) {
|
if (length(packages) == 0) {
|
||||||
log.console(" (none)")
|
log.console(" (none)")
|
||||||
} else {
|
} else {
|
||||||
packages = sort(packages)
|
packages = sort(packages)
|
||||||
|
|
||||||
// Group by type
|
// Group by type
|
||||||
var local_pkgs = []
|
local_pkgs = []
|
||||||
var linked_pkgs = []
|
linked_pkgs = []
|
||||||
var remote_pkgs = []
|
remote_pkgs = []
|
||||||
|
|
||||||
arrfor(packages, function(p) {
|
arrfor(packages, function(p) {
|
||||||
if (p == 'core') return
|
if (p == 'core') return
|
||||||
|
|||||||
5
ls.ce
5
ls.ce
@@ -9,13 +9,14 @@ var ctx = null
|
|||||||
var pkg = args[0] || package.find_package_dir('.')
|
var pkg = args[0] || package.find_package_dir('.')
|
||||||
var modules = package.list_modules(pkg)
|
var modules = package.list_modules(pkg)
|
||||||
var programs = package.list_programs(pkg)
|
var programs = package.list_programs(pkg)
|
||||||
|
var i = 0
|
||||||
|
|
||||||
log.console("Modules in " + pkg + ":")
|
log.console("Modules in " + pkg + ":")
|
||||||
modules = sort(modules)
|
modules = sort(modules)
|
||||||
if (length(modules) == 0) {
|
if (length(modules) == 0) {
|
||||||
log.console(" (none)")
|
log.console(" (none)")
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < length(modules); i++) {
|
for (i = 0; i < length(modules); i++) {
|
||||||
log.console(" " + modules[i])
|
log.console(" " + modules[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,7 @@ programs = sort(programs)
|
|||||||
if (length(programs) == 0) {
|
if (length(programs) == 0) {
|
||||||
log.console(" (none)")
|
log.console(" (none)")
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < length(programs); i++) {
|
for (i = 0; i < length(programs); i++) {
|
||||||
log.console(" " + programs[i])
|
log.console(" " + programs[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
mcode.cm
79
mcode.cm
@@ -280,19 +280,70 @@ var mcode = function(ast) {
|
|||||||
return node.kind == "null"
|
return node.kind == "null"
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit_add_decomposed: emit generic add (VM dispatches int/float/text)
|
// emit_add_decomposed: emit type-dispatched add (text → concat, num → add)
|
||||||
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
|
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
|
||||||
var emit_add_decomposed = function() {
|
var emit_add_decomposed = function() {
|
||||||
// Known text+text → concat directly (skip numeric check in VM)
|
|
||||||
if (is_known_text(_bp_ln) && is_known_text(_bp_rn)) {
|
if (is_known_text(_bp_ln) && is_known_text(_bp_rn)) {
|
||||||
emit_3("concat", _bp_dest, _bp_left, _bp_right)
|
emit_3("concat", _bp_dest, _bp_left, _bp_right)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
if (is_known_number(_bp_ln) && is_known_number(_bp_rn)) {
|
||||||
|
emit_3("add", _bp_dest, _bp_left, _bp_right)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// Unknown types: emit full dispatch
|
||||||
|
var t0 = alloc_slot()
|
||||||
|
var t1 = alloc_slot()
|
||||||
|
var done = gen_label("add_done")
|
||||||
|
var check_num = gen_label("add_cn")
|
||||||
|
|
||||||
|
// Check text path first (since add doubles as concat)
|
||||||
|
emit_2("is_text", t0, _bp_left)
|
||||||
|
emit_jump_cond("jump_false", t0, check_num)
|
||||||
|
emit_2("is_text", t1, _bp_right)
|
||||||
|
emit_jump_cond("jump_false", t1, check_num)
|
||||||
|
emit_3("concat", _bp_dest, _bp_left, _bp_right)
|
||||||
|
emit_jump(done)
|
||||||
|
|
||||||
|
// Numeric path
|
||||||
|
var err = gen_label("add_err")
|
||||||
|
emit_label(check_num)
|
||||||
|
emit_2("is_num", t0, _bp_left)
|
||||||
|
emit_jump_cond("jump_false", t0, err)
|
||||||
|
emit_2("is_num", t1, _bp_right)
|
||||||
|
emit_jump_cond("jump_false", t1, err)
|
||||||
emit_3("add", _bp_dest, _bp_left, _bp_right)
|
emit_3("add", _bp_dest, _bp_left, _bp_right)
|
||||||
|
emit_jump(done)
|
||||||
|
|
||||||
|
emit_label(err)
|
||||||
|
emit_0("disrupt")
|
||||||
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit_numeric_binop removed — generic ops emitted directly via passthrough
|
// emit_numeric_binop: emit type-guarded numeric binary op
|
||||||
|
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
|
||||||
|
var emit_numeric_binop = function(op_str) {
|
||||||
|
if (is_known_number(_bp_ln) && is_known_number(_bp_rn)) {
|
||||||
|
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var t0 = alloc_slot()
|
||||||
|
var t1 = alloc_slot()
|
||||||
|
var err = gen_label("num_err")
|
||||||
|
var done = gen_label("num_done")
|
||||||
|
emit_2("is_num", t0, _bp_left)
|
||||||
|
emit_jump_cond("jump_false", t0, err)
|
||||||
|
emit_2("is_num", t1, _bp_right)
|
||||||
|
emit_jump_cond("jump_false", t1, err)
|
||||||
|
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
|
||||||
|
emit_jump(done)
|
||||||
|
|
||||||
|
emit_label(err)
|
||||||
|
emit_0("disrupt")
|
||||||
|
emit_label(done)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false)
|
// emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false)
|
||||||
// reads _bp_dest, _bp_left, _bp_right from closure
|
// reads _bp_dest, _bp_left, _bp_right from closure
|
||||||
@@ -518,9 +569,23 @@ var mcode = function(ast) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit_neg_decomposed: emit generic negate (VM dispatches int/float)
|
// emit_neg_decomposed: emit type-guarded negate
|
||||||
var emit_neg_decomposed = function(dest, src, src_node) {
|
var emit_neg_decomposed = function(dest, src, src_node) {
|
||||||
|
if (is_known_number(src_node)) {
|
||||||
|
emit_2("negate", dest, src)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var t0 = alloc_slot()
|
||||||
|
var err = gen_label("neg_err")
|
||||||
|
var done = gen_label("neg_done")
|
||||||
|
emit_2("is_num", t0, src)
|
||||||
|
emit_jump_cond("jump_false", t0, err)
|
||||||
emit_2("negate", dest, src)
|
emit_2("negate", dest, src)
|
||||||
|
emit_jump(done)
|
||||||
|
|
||||||
|
emit_label(err)
|
||||||
|
emit_0("disrupt")
|
||||||
|
emit_label(done)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,9 +612,11 @@ var mcode = function(ast) {
|
|||||||
rel = relational_ops[op_str]
|
rel = relational_ops[op_str]
|
||||||
if (rel != null) {
|
if (rel != null) {
|
||||||
emit_relational(rel[0], rel[1], rel[2])
|
emit_relational(rel[0], rel[1], rel[2])
|
||||||
|
} else if (op_str == "subtract" || op_str == "multiply" ||
|
||||||
|
op_str == "divide" || op_str == "modulo" || op_str == "pow") {
|
||||||
|
emit_numeric_binop(op_str)
|
||||||
} else {
|
} else {
|
||||||
// Passthrough for subtract, multiply, divide, modulo,
|
// Passthrough for bitwise, in, etc.
|
||||||
// bitwise, pow, in, etc.
|
|
||||||
emit_3(op_str, dest, left, right)
|
emit_3(op_str, dest, left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,10 +67,10 @@ scripts = [
|
|||||||
'fit.c',
|
'fit.c',
|
||||||
'crypto.c',
|
'crypto.c',
|
||||||
'internal/kim.c',
|
'internal/kim.c',
|
||||||
'time.c',
|
'internal/time.c',
|
||||||
'debug/debug.c',
|
'debug/debug.c',
|
||||||
'internal/os.c',
|
'internal/os.c',
|
||||||
'fd.c',
|
'internal/fd.c',
|
||||||
'net/http.c',
|
'net/http.c',
|
||||||
'net/enet.c',
|
'net/enet.c',
|
||||||
'wildstar.c',
|
'wildstar.c',
|
||||||
|
|||||||
@@ -568,7 +568,7 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
|
|||||||
// JS_CGETSET_DEF("address", js_enet_peer_get_address, NULL),
|
// JS_CGETSET_DEF("address", js_enet_peer_get_address, NULL),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_enet_use(JSContext *ctx)
|
JSValue js_core_enet_use(JSContext *ctx)
|
||||||
{
|
{
|
||||||
JS_FRAME(ctx);
|
JS_FRAME(ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ static const JSCFunctionListEntry js_http_funcs[] = {
|
|||||||
JS_CFUNC_DEF("fetch", 2, js_fetch_picoparser),
|
JS_CFUNC_DEF("fetch", 2, js_fetch_picoparser),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_http_use(JSContext *js) {
|
JSValue js_core_http_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
par_easycurl_init(0); // Initialize platform HTTP backend
|
par_easycurl_init(0); // Initialize platform HTTP backend
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
|
|||||||
@@ -594,7 +594,7 @@ static const JSCFunctionListEntry js_socket_funcs[] = {
|
|||||||
MIST_FUNC_DEF(socket, close, 1),
|
MIST_FUNC_DEF(socket, close, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_socket_use(JSContext *js) {
|
JSValue js_core_socket_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_socket_funcs, countof(js_socket_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_socket_funcs, countof(js_socket_funcs));
|
||||||
|
|||||||
16
pack.ce
16
pack.ce
@@ -13,6 +13,7 @@ var target = null
|
|||||||
var output_name = 'app'
|
var output_name = 'app'
|
||||||
var target_package = null
|
var target_package = null
|
||||||
var buildtype = 'debug'
|
var buildtype = 'debug'
|
||||||
|
var i = 0
|
||||||
|
|
||||||
if (length(args) < 1) {
|
if (length(args) < 1) {
|
||||||
log.error('Usage: cell pack <package> [options]')
|
log.error('Usage: cell pack <package> [options]')
|
||||||
@@ -24,12 +25,11 @@ if (length(args) < 1) {
|
|||||||
log.error('')
|
log.error('')
|
||||||
log.error('Available targets: ' + text(build.list_targets(), ', '))
|
log.error('Available targets: ' + text(build.list_targets(), ', '))
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target_package = args[0]
|
target_package = args[0]
|
||||||
|
|
||||||
for (var i = 1; i < length(args); i++) {
|
for (i = 1; i < length(args); i++) {
|
||||||
if (args[i] == '-t' || args[i] == '--target') {
|
if (args[i] == '-t' || args[i] == '--target') {
|
||||||
if (i + 1 < length(args)) {
|
if (i + 1 < length(args)) {
|
||||||
target = args[++i]
|
target = args[++i]
|
||||||
@@ -87,7 +87,7 @@ if (target && !build.has_target(target)) {
|
|||||||
var packages = ['core']
|
var packages = ['core']
|
||||||
var deps = pkg_tools.gather_dependencies(target_package)
|
var deps = pkg_tools.gather_dependencies(target_package)
|
||||||
|
|
||||||
for (var i = 0; i < length(deps); i++) {
|
for (i = 0; i < length(deps); i++) {
|
||||||
push(packages, deps[i])
|
push(packages, deps[i])
|
||||||
}
|
}
|
||||||
push(packages, target_package)
|
push(packages, target_package)
|
||||||
@@ -95,7 +95,7 @@ push(packages, target_package)
|
|||||||
// Remove duplicates
|
// Remove duplicates
|
||||||
var unique_packages = []
|
var unique_packages = []
|
||||||
var seen = {}
|
var seen = {}
|
||||||
for (var i = 0; i < length(packages); i++) {
|
for (i = 0; i < length(packages); i++) {
|
||||||
if (!seen[packages[i]]) {
|
if (!seen[packages[i]]) {
|
||||||
seen[packages[i]] = true
|
seen[packages[i]] = true
|
||||||
push(unique_packages, packages[i])
|
push(unique_packages, packages[i])
|
||||||
@@ -111,13 +111,13 @@ arrfor(packages, function(package) {
|
|||||||
|
|
||||||
log.console('Building static binary from ' + text(length(packages)) + ' packages: ' + text(packages, ', '))
|
log.console('Building static binary from ' + text(length(packages)) + ' packages: ' + text(packages, ', '))
|
||||||
|
|
||||||
try {
|
var _build = function() {
|
||||||
var result = build.build_static(packages, target, output_name, buildtype)
|
var result = build.build_static(packages, target, output_name, buildtype)
|
||||||
log.console('Build complete: ' + result)
|
log.console('Build complete: ' + result)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error('Build failed: ')
|
log.error('Build failed')
|
||||||
log.error(e)
|
|
||||||
$stop()
|
$stop()
|
||||||
}
|
}
|
||||||
|
_build()
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
356
qbe.cm
356
qbe.cm
@@ -98,6 +98,7 @@ var is_text = function(p, v) {
|
|||||||
jmp @${p}.done
|
jmp @${p}.done
|
||||||
@${p}.no
|
@${p}.no
|
||||||
%${p} =w copy 0
|
%${p} =w copy 0
|
||||||
|
jmp @${p}.done
|
||||||
@${p}.done
|
@${p}.done
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -174,6 +175,7 @@ var to_float64 = function(p, v) {
|
|||||||
%${p}.fbits =l or %${p}.fs63, %${p}.fe52
|
%${p}.fbits =l or %${p}.fs63, %${p}.fe52
|
||||||
%${p}.fbits =l or %${p}.fbits, %${p}.fmant
|
%${p}.fbits =l or %${p}.fbits, %${p}.fmant
|
||||||
%${p} =d cast %${p}.fbits
|
%${p} =d cast %${p}.fbits
|
||||||
|
jmp @${p}.done
|
||||||
@${p}.done
|
@${p}.done
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -199,201 +201,37 @@ var new_bool = function(p, b) {
|
|||||||
|
|
||||||
// new_float64 — C call to __JS_NewFloat64(ctx, val). Result: %{p}
|
// new_float64 — C call to __JS_NewFloat64(ctx, val). Result: %{p}
|
||||||
var new_float64 = function(p, ctx, d) {
|
var new_float64 = function(p, ctx, d) {
|
||||||
return ` %${p} =l call $__JS_NewFloat64(l ${ctx}, d ${d})
|
return ` %${p} =l call $qbe_new_float64(l ${ctx}, d ${d})
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Arithmetic — add(p, ctx, a, b)
|
// Arithmetic — add/sub/mul/div/mod(p, ctx, a, b)
|
||||||
// Int fast path inline, text concat and float as C calls.
|
// Simple C call wrappers. Type dispatch is handled in mcode.cm.
|
||||||
// Jumps to @disrupt on type mismatch.
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
var add = function(p, ctx, a, b) {
|
var add = function(p, ctx, a, b) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b})
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =l sar ${a}, 1
|
|
||||||
%${p}.ib =l sar ${b}, 1
|
|
||||||
%${p}.sum =l add %${p}.ia, %${p}.ib
|
|
||||||
%${p}.lo =w csltl %${p}.sum, ${int32_min}
|
|
||||||
%${p}.hi =w csgtl %${p}.sum, ${int32_max}
|
|
||||||
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
||||||
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.rw =w copy %${p}.sum
|
|
||||||
%${p}.rext =l extuw %${p}.rw
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_overflow
|
|
||||||
%${p}.fd =d sltof %${p}.sum
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.not_both_int
|
|
||||||
%${p}.a_is_text =w call $JS_IsText(l ${a})
|
|
||||||
%${p}.b_is_text =w call $JS_IsText(l ${b})
|
|
||||||
%${p}.both_text =w and %${p}.a_is_text, %${p}.b_is_text
|
|
||||||
jnz %${p}.both_text, @${p}.text_path, @${p}.chk_num
|
|
||||||
@${p}.text_path
|
|
||||||
%${p} =l call $JS_ConcatString(l ${ctx}, l ${a}, l ${b})
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.chk_num
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_path, @disrupt
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var sub = function(p, ctx, a, b) {
|
var sub = function(p, ctx, a, b) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b})
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =l sar ${a}, 1
|
|
||||||
%${p}.ib =l sar ${b}, 1
|
|
||||||
%${p}.diff =l sub %${p}.ia, %${p}.ib
|
|
||||||
%${p}.lo =w csltl %${p}.diff, ${int32_min}
|
|
||||||
%${p}.hi =w csgtl %${p}.diff, ${int32_max}
|
|
||||||
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
||||||
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.rw =w copy %${p}.diff
|
|
||||||
%${p}.rext =l extuw %${p}.rw
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_overflow
|
|
||||||
%${p}.fd =d sltof %${p}.diff
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.not_both_int
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_path, @disrupt
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var mul = function(p, ctx, a, b) {
|
var mul = function(p, ctx, a, b) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b})
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =l sar ${a}, 1
|
|
||||||
%${p}.ib =l sar ${b}, 1
|
|
||||||
%${p}.prod =l mul %${p}.ia, %${p}.ib
|
|
||||||
%${p}.lo =w csltl %${p}.prod, ${int32_min}
|
|
||||||
%${p}.hi =w csgtl %${p}.prod, ${int32_max}
|
|
||||||
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
||||||
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.rw =w copy %${p}.prod
|
|
||||||
%${p}.rext =l extuw %${p}.rw
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_overflow
|
|
||||||
%${p}.fd =d sltof %${p}.prod
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.not_both_int
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_path, @disrupt
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var div = function(p, ctx, a, b) {
|
var div = function(p, ctx, a, b) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b})
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =w copy 0
|
|
||||||
%${p}.tmp =l sar ${a}, 1
|
|
||||||
%${p}.ia =w copy %${p}.tmp
|
|
||||||
%${p}.ib =w copy 0
|
|
||||||
%${p}.tmp2 =l sar ${b}, 1
|
|
||||||
%${p}.ib =w copy %${p}.tmp2
|
|
||||||
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
||||||
jnz %${p}.div0, @${p}.ret_null, @${p}.chk_exact
|
|
||||||
@${p}.ret_null
|
|
||||||
%${p} =l copy ${js_null}
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.chk_exact
|
|
||||||
%${p}.rem =w rem %${p}.ia, %${p}.ib
|
|
||||||
%${p}.exact =w ceqw %${p}.rem, 0
|
|
||||||
jnz %${p}.exact, @${p}.int_div, @${p}.int_to_float
|
|
||||||
@${p}.int_div
|
|
||||||
%${p}.q =w div %${p}.ia, %${p}.ib
|
|
||||||
%${p}.qext =l extuw %${p}.q
|
|
||||||
%${p} =l shl %${p}.qext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_to_float
|
|
||||||
%${p}.da =d swtof %${p}.ia
|
|
||||||
%${p}.db =d swtof %${p}.ib
|
|
||||||
%${p}.dr =d div %${p}.da, %${p}.db
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.dr)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.not_both_int
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_path, @disrupt
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var mod = function(p, ctx, a, b) {
|
var mod = function(p, ctx, a, b) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b})
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =w copy 0
|
|
||||||
%${p}.tmp =l sar ${a}, 1
|
|
||||||
%${p}.ia =w copy %${p}.tmp
|
|
||||||
%${p}.ib =w copy 0
|
|
||||||
%${p}.tmp2 =l sar ${b}, 1
|
|
||||||
%${p}.ib =w copy %${p}.tmp2
|
|
||||||
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
||||||
jnz %${p}.div0, @${p}.ret_null, @${p}.do_mod
|
|
||||||
@${p}.ret_null
|
|
||||||
%${p} =l copy ${js_null}
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.do_mod
|
|
||||||
%${p}.r =w rem %${p}.ia, %${p}.ib
|
|
||||||
%${p}.rext =l extuw %${p}.r
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.not_both_int
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_path, @disrupt
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,6 +322,7 @@ var cmp = function(p, ctx, a, b) {
|
|||||||
jmp @${p}.done
|
jmp @${p}.done
|
||||||
@${p}.mismatch
|
@${p}.mismatch
|
||||||
%${p} =l copy ${mismatch_val}
|
%${p} =l copy ${mismatch_val}
|
||||||
|
jmp @${p}.done
|
||||||
@${p}.done
|
@${p}.done
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
@@ -518,90 +357,28 @@ var gt = function(p, ctx, a, b) {
|
|||||||
|
|
||||||
var ge = function(p, ctx, a, b) {
|
var ge = function(p, ctx, a, b) {
|
||||||
_qflags = {int_cmp_op: "csgew", float_id: 5, is_eq: false, is_ne: false, null_true: true}
|
_qflags = {int_cmp_op: "csgew", float_id: 5, is_eq: false, is_ne: false, null_true: true}
|
||||||
|
return cmp(p, ctx, a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Unary Ops
|
// Unary Ops
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// neg(p, ctx, v) — negate. Int fast path (INT32_MIN edge case), else C call.
|
// neg(p, ctx, v) — negate via C call (type guards in mcode)
|
||||||
var neg = function(p, ctx, v) {
|
var neg = function(p, ctx, v) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_neg(l ${ctx}, l ${v})
|
||||||
%${p}.tag =l and ${v}, 1
|
|
||||||
%${p}.is_int =w ceql %${p}.tag, 0
|
|
||||||
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.sl =l sar ${v}, 1
|
|
||||||
%${p}.iw =w copy %${p}.sl
|
|
||||||
%${p}.is_min =w ceqw %${p}.iw, ${int32_min}
|
|
||||||
jnz %${p}.is_min, @${p}.min_overflow, @${p}.int_ok
|
|
||||||
@${p}.min_overflow
|
|
||||||
%${p}.fd =d swtof %${p}.iw
|
|
||||||
%${p}.fdn =d neg %${p}.fd
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fdn)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.ni =w sub 0, %${p}.iw
|
|
||||||
%${p}.niext =l extuw %${p}.ni
|
|
||||||
%${p} =l shl %${p}.niext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_neg(l ${ctx}, l ${v})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// inc(p, ctx, v) — increment. Int fast path (INT32_MAX edge case), else C call.
|
// inc(p, ctx, v) — increment via C call (type guards in mcode)
|
||||||
var inc = function(p, ctx, v) {
|
var inc = function(p, ctx, v) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_inc(l ${ctx}, l ${v})
|
||||||
%${p}.tag =l and ${v}, 1
|
|
||||||
%${p}.is_int =w ceql %${p}.tag, 0
|
|
||||||
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.sl =l sar ${v}, 1
|
|
||||||
%${p}.iw =w copy %${p}.sl
|
|
||||||
%${p}.is_max =w ceqw %${p}.iw, ${int32_max}
|
|
||||||
jnz %${p}.is_max, @${p}.max_overflow, @${p}.int_ok
|
|
||||||
@${p}.max_overflow
|
|
||||||
%${p}.fd =d swtof %${p}.iw
|
|
||||||
%${p}.fd1 =d add %${p}.fd, d_1.0
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd1)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.ni =w add %${p}.iw, 1
|
|
||||||
%${p}.niext =l extuw %${p}.ni
|
|
||||||
%${p} =l shl %${p}.niext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_inc(l ${ctx}, l ${v})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// dec(p, ctx, v) — decrement. Int fast path (INT32_MIN edge case), else C call.
|
// dec(p, ctx, v) — decrement via C call (type guards in mcode)
|
||||||
var dec = function(p, ctx, v) {
|
var dec = function(p, ctx, v) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_float_dec(l ${ctx}, l ${v})
|
||||||
%${p}.tag =l and ${v}, 1
|
|
||||||
%${p}.is_int =w ceql %${p}.tag, 0
|
|
||||||
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.sl =l sar ${v}, 1
|
|
||||||
%${p}.iw =w copy %${p}.sl
|
|
||||||
%${p}.is_min =w ceqw %${p}.iw, ${int32_min}
|
|
||||||
jnz %${p}.is_min, @${p}.min_overflow, @${p}.int_ok
|
|
||||||
@${p}.min_overflow
|
|
||||||
%${p}.fd =d swtof %${p}.iw
|
|
||||||
%${p}.fd1 =d sub %${p}.fd, d_1.0
|
|
||||||
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd1)
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.int_ok
|
|
||||||
%${p}.ni =w sub %${p}.iw, 1
|
|
||||||
%${p}.niext =l extuw %${p}.ni
|
|
||||||
%${p} =l shl %${p}.niext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.float_path
|
|
||||||
%${p} =l call $qbe_float_dec(l ${ctx}, l ${v})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,22 +392,9 @@ var lnot = function(p, ctx, v) {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// bnot(p, ctx, v) — bitwise not. Convert to int32, ~, re-tag.
|
// bnot(p, ctx, v) — bitwise not via C call
|
||||||
var bnot = function(p, ctx, v) {
|
var bnot = function(p, ctx, v) {
|
||||||
return `@${p}.start
|
return ` %${p} =l call $qbe_bnot(l ${ctx}, l ${v})
|
||||||
%${p}.tag =l and ${v}, 1
|
|
||||||
%${p}.is_int =w ceql %${p}.tag, 0
|
|
||||||
jnz %${p}.is_int, @${p}.int_path, @${p}.slow_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.sl =l sar ${v}, 1
|
|
||||||
%${p}.iw =w copy %${p}.sl
|
|
||||||
%${p}.nw =w xor %${p}.iw, -1
|
|
||||||
%${p}.nex =l extuw %${p}.nw
|
|
||||||
%${p} =l shl %${p}.nex, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.slow_path
|
|
||||||
%${p} =l call $qbe_bnot(l ${ctx}, l ${v})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,92 +403,34 @@ var bnot = function(p, ctx, v) {
|
|||||||
// Both operands must be numeric. Int fast path, float -> convert to int32.
|
// Both operands must be numeric. Int fast path, float -> convert to int32.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// reads _qop from closure
|
var band = function(p, ctx, a, b) {
|
||||||
var bitwise_op = function(p, ctx, a, b) {
|
return ` %${p} =l call $qbe_bitwise_and(l ${ctx}, l ${a}, l ${b})
|
||||||
var qbe_op = _qop
|
|
||||||
return `@${p}.start
|
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.slow_path, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =l sar ${a}, 1
|
|
||||||
%${p}.iaw =w copy %${p}.ia
|
|
||||||
%${p}.ib =l sar ${b}, 1
|
|
||||||
%${p}.ibw =w copy %${p}.ib
|
|
||||||
%${p}.rw =w ${qbe_op} %${p}.iaw, %${p}.ibw
|
|
||||||
%${p}.rext =l extuw %${p}.rw
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.slow_path
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_to_int, @disrupt
|
|
||||||
@${p}.float_to_int
|
|
||||||
%${p} =l call $qbe_bitwise_${qbe_op}(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var band = function(p, ctx, a, b) {
|
|
||||||
_qop = "and"
|
|
||||||
return bitwise_op(p, ctx, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bor = function(p, ctx, a, b) {
|
var bor = function(p, ctx, a, b) {
|
||||||
_qop = "or"
|
return ` %${p} =l call $qbe_bitwise_or(l ${ctx}, l ${a}, l ${b})
|
||||||
return bitwise_op(p, ctx, a, b)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var bxor = function(p, ctx, a, b) {
|
var bxor = function(p, ctx, a, b) {
|
||||||
_qop = "xor"
|
return ` %${p} =l call $qbe_bitwise_xor(l ${ctx}, l ${a}, l ${b})
|
||||||
return bitwise_op(p, ctx, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift ops: mask shift amount to 5 bits (& 31)
|
|
||||||
// reads _qop from closure
|
|
||||||
var shift_op = function(p, ctx, a, b) {
|
|
||||||
var qbe_op = _qop
|
|
||||||
return `@${p}.start
|
|
||||||
%${p}.at =l and ${a}, 1
|
|
||||||
%${p}.bt =l and ${b}, 1
|
|
||||||
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
||||||
jnz %${p}.not_int, @${p}.slow_path, @${p}.int_path
|
|
||||||
@${p}.int_path
|
|
||||||
%${p}.ia =l sar ${a}, 1
|
|
||||||
%${p}.iaw =w copy %${p}.ia
|
|
||||||
%${p}.ib =l sar ${b}, 1
|
|
||||||
%${p}.ibw =w copy %${p}.ib
|
|
||||||
%${p}.sh =w and %${p}.ibw, 31
|
|
||||||
%${p}.rw =w ${qbe_op} %${p}.iaw, %${p}.sh
|
|
||||||
%${p}.rext =l extuw %${p}.rw
|
|
||||||
%${p} =l shl %${p}.rext, 1
|
|
||||||
jmp @${p}.done
|
|
||||||
@${p}.slow_path
|
|
||||||
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
||||||
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
||||||
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
||||||
jnz %${p}.both_num, @${p}.float_to_int, @disrupt
|
|
||||||
@${p}.float_to_int
|
|
||||||
%${p} =l call $qbe_shift_${qbe_op}(l ${ctx}, l ${a}, l ${b})
|
|
||||||
@${p}.done
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var shl = function(p, ctx, a, b) {
|
var shl = function(p, ctx, a, b) {
|
||||||
_qop = "shl"
|
return ` %${p} =l call $qbe_shift_shl(l ${ctx}, l ${a}, l ${b})
|
||||||
return shift_op(p, ctx, a, b)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var shr = function(p, ctx, a, b) {
|
var shr = function(p, ctx, a, b) {
|
||||||
_qop = "sar"
|
return ` %${p} =l call $qbe_shift_sar(l ${ctx}, l ${a}, l ${b})
|
||||||
return shift_op(p, ctx, a, b)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
var ushr = function(p, ctx, a, b) {
|
var ushr = function(p, ctx, a, b) {
|
||||||
_qop = "shr"
|
return ` %${p} =l call $qbe_shift_shr(l ${ctx}, l ${a}, l ${b})
|
||||||
return shift_op(p, ctx, a, b)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
262
qbe_emit.cm
262
qbe_emit.cm
@@ -3,7 +3,7 @@
|
|||||||
// a complete QBE IL program ready for the qbe compiler.
|
// a complete QBE IL program ready for the qbe compiler.
|
||||||
// qbe module is passed via env as 'qbe'
|
// qbe module is passed via env as 'qbe'
|
||||||
|
|
||||||
var qbe_emit = function(ir, qbe) {
|
var qbe_emit = function(ir, qbe, export_name) {
|
||||||
var out = []
|
var out = []
|
||||||
var data_out = []
|
var data_out = []
|
||||||
var str_table = {}
|
var str_table = {}
|
||||||
@@ -76,7 +76,8 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
var instrs = fn.instructions
|
var instrs = fn.instructions
|
||||||
var nr_slots = fn.nr_slots
|
var nr_slots = fn.nr_slots
|
||||||
var nr_args = fn.nr_args
|
var nr_args = fn.nr_args
|
||||||
var name = is_main ? "cell_main" : "cell_fn_" + text(fn_idx)
|
var captured = build_captured(fn)
|
||||||
|
var name = is_main ? (export_name ? export_name : "cell_main") : "cell_fn_" + text(fn_idx)
|
||||||
name = sanitize(name)
|
name = sanitize(name)
|
||||||
var i = 0
|
var i = 0
|
||||||
var instr = null
|
var instr = null
|
||||||
@@ -88,6 +89,7 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
var p = null
|
var p = null
|
||||||
var pn = null
|
var pn = null
|
||||||
var sl = null
|
var sl = null
|
||||||
|
var lbl = null
|
||||||
var fop_id = 0
|
var fop_id = 0
|
||||||
var nr_elems = 0
|
var nr_elems = 0
|
||||||
var ei = 0
|
var ei = 0
|
||||||
@@ -113,22 +115,45 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
emit(` storel ${s(slot)}, %p${text(slot)}`)
|
emit(` storel ${s(slot)}, %p${text(slot)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload captured slots from frame (after invoke, closures may have modified them)
|
||||||
|
var reload_captured = function() {
|
||||||
|
var ri = 0
|
||||||
|
while (ri < nr_slots) {
|
||||||
|
if (captured[text(ri)] == true) {
|
||||||
|
emit(` ${s(ri)} =l loadl %p${text(ri)}`)
|
||||||
|
}
|
||||||
|
ri = ri + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Walk instructions
|
// Walk instructions
|
||||||
|
// Slot loads above are not terminators
|
||||||
|
var last_was_term = false
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(instrs)) {
|
while (i < length(instrs)) {
|
||||||
instr = instrs[i]
|
instr = instrs[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
// Labels are plain strings
|
// Labels are plain strings; skip _nop_ur_ pseudo-labels from streamline
|
||||||
if (is_text(instr)) {
|
if (is_text(instr)) {
|
||||||
emit("@" + sanitize(instr))
|
if (starts_with(instr, "_nop_ur_")) continue
|
||||||
|
lbl = sanitize(instr)
|
||||||
|
if (!last_was_term) {
|
||||||
|
emit(` jmp @${lbl}`)
|
||||||
|
}
|
||||||
|
emit("@" + lbl)
|
||||||
|
last_was_term = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip dead code: non-label instructions after a terminator are unreachable
|
||||||
|
if (last_was_term) continue
|
||||||
|
|
||||||
op = instr[0]
|
op = instr[0]
|
||||||
a1 = instr[1]
|
a1 = instr[1]
|
||||||
a2 = instr[2]
|
a2 = instr[2]
|
||||||
a3 = instr[3]
|
a3 = instr[3]
|
||||||
|
last_was_term = false
|
||||||
|
|
||||||
// --- Constants ---
|
// --- Constants ---
|
||||||
|
|
||||||
@@ -157,11 +182,11 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
if (is_integer(a2)) {
|
if (is_integer(a2)) {
|
||||||
emit(` ${s(a1)} =l copy ${text(a2 * 2)}`)
|
emit(` ${s(a1)} =l copy ${text(a2 * 2)}`)
|
||||||
} else {
|
} else {
|
||||||
emit(` ${s(a1)} =l call $__JS_NewFloat64(l %ctx, d d_${text(a2)})`)
|
emit(` ${s(a1)} =l call $qbe_new_float64(l %ctx, d d_${text(a2)})`)
|
||||||
}
|
}
|
||||||
} else if (is_text(a2)) {
|
} else if (is_text(a2)) {
|
||||||
sl = intern_str(a2)
|
sl = intern_str(a2)
|
||||||
emit(` ${s(a1)} =l call $JS_NewString(l %ctx, l ${sl})`)
|
emit(` ${s(a1)} =l call $qbe_new_string(l %ctx, l ${sl})`)
|
||||||
} else if (is_object(a2)) {
|
} else if (is_object(a2)) {
|
||||||
if (a2.make == "intrinsic") {
|
if (a2.make == "intrinsic") {
|
||||||
sl = intern_str(a2.name)
|
sl = intern_str(a2.name)
|
||||||
@@ -170,13 +195,13 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
if (a2.number != null && is_integer(a2.number)) {
|
if (a2.number != null && is_integer(a2.number)) {
|
||||||
emit(` ${s(a1)} =l copy ${text(a2.number * 2)}`)
|
emit(` ${s(a1)} =l copy ${text(a2.number * 2)}`)
|
||||||
} else if (a2.number != null) {
|
} else if (a2.number != null) {
|
||||||
emit(` ${s(a1)} =l call $__JS_NewFloat64(l %ctx, d d_${text(a2.number)})`)
|
emit(` ${s(a1)} =l call $qbe_new_float64(l %ctx, d d_${text(a2.number)})`)
|
||||||
} else {
|
} else {
|
||||||
emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`)
|
emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`)
|
||||||
}
|
}
|
||||||
} else if (a2.kind == "text") {
|
} else if (a2.kind == "text") {
|
||||||
sl = intern_str(a2.value)
|
sl = intern_str(a2.value)
|
||||||
emit(` ${s(a1)} =l call $JS_NewString(l %ctx, l ${sl})`)
|
emit(` ${s(a1)} =l call $qbe_new_string(l %ctx, l ${sl})`)
|
||||||
} else if (a2.kind == "true") {
|
} else if (a2.kind == "true") {
|
||||||
emit(` ${s(a1)} =l copy ${text(qbe.js_true)}`)
|
emit(` ${s(a1)} =l copy ${text(qbe.js_true)}`)
|
||||||
} else if (a2.kind == "false") {
|
} else if (a2.kind == "false") {
|
||||||
@@ -205,7 +230,7 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
|
|
||||||
if (op == "add") {
|
if (op == "add") {
|
||||||
p = fresh()
|
p = fresh()
|
||||||
emit(qbe.add(p, "%ctx", s(a2), s(a3)))
|
emit(` %${p} =l call $cell_rt_add(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
||||||
emit(` ${s(a1)} =l copy %${p}`)
|
emit(` ${s(a1)} =l copy %${p}`)
|
||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
@@ -246,6 +271,12 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op == "pow") {
|
||||||
|
emit(` ${s(a1)} =l call $qbe_float_pow(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// --- String concat ---
|
// --- String concat ---
|
||||||
|
|
||||||
if (op == "concat") {
|
if (op == "concat") {
|
||||||
@@ -305,6 +336,46 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (op == "is_array") {
|
||||||
|
p = fresh()
|
||||||
|
emit(` %${p} =w call $JS_IsArray(l ${s(a2)})`)
|
||||||
|
emit(qbe.new_bool(p + ".r", "%" + p))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}.r`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "is_func") {
|
||||||
|
p = fresh()
|
||||||
|
emit(` %${p} =w call $JS_IsFunction(l ${s(a2)})`)
|
||||||
|
emit(qbe.new_bool(p + ".r", "%" + p))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}.r`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "is_record") {
|
||||||
|
p = fresh()
|
||||||
|
emit(` %${p} =w call $JS_IsRecord(l ${s(a2)})`)
|
||||||
|
emit(qbe.new_bool(p + ".r", "%" + p))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}.r`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "is_stone") {
|
||||||
|
p = fresh()
|
||||||
|
emit(` %${p} =w call $JS_IsStone(l ${s(a2)})`)
|
||||||
|
emit(qbe.new_bool(p + ".r", "%" + p))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}.r`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "is_proxy") {
|
||||||
|
p = fresh()
|
||||||
|
emit(` %${p} =w call $cell_rt_is_proxy(l %ctx, l ${s(a2)})`)
|
||||||
|
emit(qbe.new_bool(p + ".r", "%" + p))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}.r`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// --- Comparisons (int path) ---
|
// --- Comparisons (int path) ---
|
||||||
|
|
||||||
@@ -367,14 +438,30 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "lt_float" || op == "gt_float" || op == "le_float" || op == "ge_float") {
|
if (op == "lt_float") {
|
||||||
p = fresh()
|
p = fresh()
|
||||||
fop_id = 0
|
emit(qbe.lt_float(p, "%ctx", s(a2), s(a3)))
|
||||||
if (op == "lt_float") fop_id = 2
|
emit(` ${s(a1)} =l copy %${p}`)
|
||||||
else if (op == "le_float") fop_id = 3
|
wb(a1)
|
||||||
else if (op == "gt_float") fop_id = 4
|
continue
|
||||||
else if (op == "ge_float") fop_id = 5
|
}
|
||||||
emit(qbe.cmp_float != null ? qbe.cmp_float(p, "%ctx", s(a2), s(a3), fop_id) : ` %${p} =l call $qbe_float_cmp(l %ctx, w ${text(fop_id)}, l ${s(a2)}, l ${s(a3)})`)
|
if (op == "le_float") {
|
||||||
|
p = fresh()
|
||||||
|
emit(qbe.le_float(p, "%ctx", s(a2), s(a3)))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "gt_float") {
|
||||||
|
p = fresh()
|
||||||
|
emit(qbe.gt_float(p, "%ctx", s(a2), s(a3)))
|
||||||
|
emit(` ${s(a1)} =l copy %${p}`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "ge_float") {
|
||||||
|
p = fresh()
|
||||||
|
emit(qbe.ge_float(p, "%ctx", s(a2), s(a3)))
|
||||||
emit(` ${s(a1)} =l copy %${p}`)
|
emit(` ${s(a1)} =l copy %${p}`)
|
||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
@@ -494,7 +581,10 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
// --- Property access — runtime calls ---
|
// --- Property access — runtime calls ---
|
||||||
|
|
||||||
if (op == "load_field") {
|
if (op == "load_field") {
|
||||||
pn = prop_name(a3)
|
pn = null
|
||||||
|
if (is_text(a3)) pn = a3
|
||||||
|
else if (is_object(a3) && a3.name != null) pn = a3.name
|
||||||
|
else if (is_object(a3) && a3.value != null) pn = a3.value
|
||||||
if (pn != null) {
|
if (pn != null) {
|
||||||
sl = intern_str(pn)
|
sl = intern_str(pn)
|
||||||
emit(` ${s(a1)} =l call $cell_rt_load_field(l %ctx, l ${s(a2)}, l ${sl})`)
|
emit(` ${s(a1)} =l call $cell_rt_load_field(l %ctx, l ${s(a2)}, l ${sl})`)
|
||||||
@@ -510,13 +600,28 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "load_dynamic") {
|
if (op == "load_dynamic") {
|
||||||
emit(` ${s(a1)} =l call $cell_rt_load_dynamic(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
pn = null
|
||||||
|
if (is_text(a3)) pn = a3
|
||||||
|
else if (is_object(a3) && a3.name != null) pn = a3.name
|
||||||
|
else if (is_object(a3) && a3.value != null) pn = a3.value
|
||||||
|
if (pn != null) {
|
||||||
|
sl = intern_str(pn)
|
||||||
|
emit(` ${s(a1)} =l call $cell_rt_load_field(l %ctx, l ${s(a2)}, l ${sl})`)
|
||||||
|
} else {
|
||||||
|
emit(` ${s(a1)} =l call $cell_rt_load_dynamic(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
||||||
|
}
|
||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "store_field") {
|
if (op == "store_field") {
|
||||||
// IR: ["store_field", obj, val, prop] → C: (ctx, val, obj, name)
|
// IR: ["store_field", obj, val, prop] → C: (ctx, val, obj, name)
|
||||||
pn = prop_name(a3)
|
pn = null
|
||||||
|
if (is_text(a3)) {
|
||||||
|
pn = a3
|
||||||
|
} else if (is_object(a3)) {
|
||||||
|
if (a3.name != null) pn = a3.name
|
||||||
|
else if (a3.value != null) pn = a3.value
|
||||||
|
}
|
||||||
if (pn != null) {
|
if (pn != null) {
|
||||||
sl = intern_str(pn)
|
sl = intern_str(pn)
|
||||||
emit(` call $cell_rt_store_field(l %ctx, l ${s(a2)}, l ${s(a1)}, l ${sl})`)
|
emit(` call $cell_rt_store_field(l %ctx, l ${s(a2)}, l ${s(a1)}, l ${sl})`)
|
||||||
@@ -532,19 +637,30 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
}
|
}
|
||||||
if (op == "store_dynamic") {
|
if (op == "store_dynamic") {
|
||||||
// IR: ["store_dynamic", obj, val, key] → C: (ctx, val, obj, key)
|
// IR: ["store_dynamic", obj, val, key] → C: (ctx, val, obj, key)
|
||||||
emit(` call $cell_rt_store_dynamic(l %ctx, l ${s(a2)}, l ${s(a1)}, l ${s(a3)})`)
|
pn = null
|
||||||
|
if (is_text(a3)) pn = a3
|
||||||
|
else if (is_object(a3) && a3.name != null) pn = a3.name
|
||||||
|
else if (is_object(a3) && a3.value != null) pn = a3.value
|
||||||
|
if (pn != null) {
|
||||||
|
sl = intern_str(pn)
|
||||||
|
emit(` call $cell_rt_store_field(l %ctx, l ${s(a2)}, l ${s(a1)}, l ${sl})`)
|
||||||
|
} else {
|
||||||
|
emit(` call $cell_rt_store_dynamic(l %ctx, l ${s(a2)}, l ${s(a1)}, l ${s(a3)})`)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Closure access ---
|
// --- Closure access ---
|
||||||
|
|
||||||
if (op == "get") {
|
if (op == "get") {
|
||||||
emit(` ${s(a1)} =l call $cell_rt_get_closure(l %ctx, l %fp, l ${text(a2)}, l ${text(a3)})`)
|
// mcode: get(dest, slot, depth) — a2=slot, a3=depth
|
||||||
|
emit(` ${s(a1)} =l call $cell_rt_get_closure(l %ctx, l %fp, l ${text(a3)}, l ${text(a2)})`)
|
||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "put") {
|
if (op == "put") {
|
||||||
emit(` call $cell_rt_put_closure(l %ctx, l %fp, l ${s(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
// mcode: put(val, slot, depth) — a2=slot, a3=depth
|
||||||
|
emit(` call $cell_rt_put_closure(l %ctx, l %fp, l ${s(a1)}, l ${text(a3)}, l ${text(a2)})`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,6 +668,7 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
|
|
||||||
if (op == "jump") {
|
if (op == "jump") {
|
||||||
emit(` jmp @${sanitize(a1)}`)
|
emit(` jmp @${sanitize(a1)}`)
|
||||||
|
last_was_term = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "jump_true") {
|
if (op == "jump_true") {
|
||||||
@@ -611,6 +728,13 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
if (op == "invoke") {
|
if (op == "invoke") {
|
||||||
emit(` ${s(a2)} =l call $cell_rt_invoke(l %ctx, l ${s(a1)})`)
|
emit(` ${s(a2)} =l call $cell_rt_invoke(l %ctx, l ${s(a1)})`)
|
||||||
wb(a2)
|
wb(a2)
|
||||||
|
reload_captured()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "tail_invoke") {
|
||||||
|
emit(` ${s(a2)} =l call $cell_rt_invoke(l %ctx, l ${s(a1)})`)
|
||||||
|
wb(a2)
|
||||||
|
reload_captured()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "goframe") {
|
if (op == "goframe") {
|
||||||
@@ -621,6 +745,7 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
if (op == "goinvoke") {
|
if (op == "goinvoke") {
|
||||||
emit(` %_goret =l call $cell_rt_goinvoke(l %ctx, l ${s(a1)})`)
|
emit(` %_goret =l call $cell_rt_goinvoke(l %ctx, l ${s(a1)})`)
|
||||||
emit(` ret %_goret`)
|
emit(` ret %_goret`)
|
||||||
|
last_was_term = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,19 +789,38 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Length ---
|
||||||
|
|
||||||
|
if (op == "length") {
|
||||||
|
emit(` ${s(a1)} =l call $JS_CellLength(l %ctx, l ${s(a2)})`)
|
||||||
|
wb(a1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// --- Misc ---
|
// --- Misc ---
|
||||||
|
|
||||||
if (op == "return") {
|
if (op == "return") {
|
||||||
emit(` ret ${s(a1)}`)
|
emit(` ret ${s(a1)}`)
|
||||||
|
last_was_term = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "disrupt") {
|
if (op == "disrupt") {
|
||||||
emit(` call $cell_rt_disrupt(l %ctx)`)
|
emit(` call $cell_rt_disrupt(l %ctx)`)
|
||||||
emit(` ret ${text(qbe.js_null)}`)
|
emit(` ret ${text(qbe.js_null)}`)
|
||||||
|
last_was_term = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "delete") {
|
if (op == "delete") {
|
||||||
emit(` ${s(a1)} =l call $cell_rt_delete(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
pn = null
|
||||||
|
if (is_text(a3)) pn = a3
|
||||||
|
else if (is_object(a3) && a3.name != null) pn = a3.name
|
||||||
|
else if (is_object(a3) && a3.value != null) pn = a3.value
|
||||||
|
if (pn != null) {
|
||||||
|
sl = intern_str(pn)
|
||||||
|
emit(` ${s(a1)} =l call $cell_rt_delete(l %ctx, l ${s(a2)}, l ${sl})`)
|
||||||
|
} else {
|
||||||
|
emit(` ${s(a1)} =l call $cell_rt_delete(l %ctx, l ${s(a2)}, l ${s(a3)})`)
|
||||||
|
}
|
||||||
wb(a1)
|
wb(a1)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -690,6 +834,14 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
emit(` # unknown: ${op}`)
|
emit(` # unknown: ${op}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit @disrupt landing pad for arithmetic type-error branches
|
||||||
|
if (!last_was_term) {
|
||||||
|
emit(" jmp @disrupt")
|
||||||
|
}
|
||||||
|
emit("@disrupt")
|
||||||
|
emit(` call $cell_rt_disrupt(l %ctx)`)
|
||||||
|
emit(` ret ${text(qbe.js_null)}`)
|
||||||
|
|
||||||
emit("}")
|
emit("}")
|
||||||
emit("")
|
emit("")
|
||||||
}
|
}
|
||||||
@@ -698,6 +850,70 @@ var qbe_emit = function(ir, qbe) {
|
|||||||
// Main: compile all functions then main
|
// Main: compile all functions then main
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Pre-scan: find which slots each function has that are modified
|
||||||
|
// by child closures (via "put" instructions at depth=1).
|
||||||
|
// Build a map: fn_idx → array of captured slot numbers.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// For each function, find which fn_idxes it creates via "function" op
|
||||||
|
var find_children = function(fn_instrs) {
|
||||||
|
var children = []
|
||||||
|
var ci = 0
|
||||||
|
var cinstr = null
|
||||||
|
while (ci < length(fn_instrs)) {
|
||||||
|
cinstr = fn_instrs[ci]
|
||||||
|
ci = ci + 1
|
||||||
|
if (!is_array(cinstr)) continue
|
||||||
|
if (cinstr[0] == "function") {
|
||||||
|
push(children, cinstr[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a child function, find which parent slots it writes to via put(val, slot, depth=1)
|
||||||
|
var find_put_slots = function(fn_instrs) {
|
||||||
|
var slots = []
|
||||||
|
var pi = 0
|
||||||
|
var pinstr = null
|
||||||
|
while (pi < length(fn_instrs)) {
|
||||||
|
pinstr = fn_instrs[pi]
|
||||||
|
pi = pi + 1
|
||||||
|
if (!is_array(pinstr)) continue
|
||||||
|
// put format: ["put", val, slot, depth]
|
||||||
|
if (pinstr[0] == "put" && pinstr[3] == 1) {
|
||||||
|
push(slots, pinstr[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build captured_slots for each function (and main)
|
||||||
|
var build_captured = function(fn) {
|
||||||
|
var children = find_children(fn.instructions)
|
||||||
|
var captured = {}
|
||||||
|
var bi = 0
|
||||||
|
var child_idx = 0
|
||||||
|
var child_fn = null
|
||||||
|
var pslots = null
|
||||||
|
var si = 0
|
||||||
|
while (bi < length(children)) {
|
||||||
|
child_idx = children[bi]
|
||||||
|
bi = bi + 1
|
||||||
|
if (child_idx >= 0 && child_idx < length(ir.functions)) {
|
||||||
|
child_fn = ir.functions[child_idx]
|
||||||
|
pslots = find_put_slots(child_fn.instructions)
|
||||||
|
si = 0
|
||||||
|
while (si < length(pslots)) {
|
||||||
|
captured[text(pslots[si])] = true
|
||||||
|
si = si + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return captured
|
||||||
|
}
|
||||||
|
|
||||||
var fi = 0
|
var fi = 0
|
||||||
while (fi < length(ir.functions)) {
|
while (fi < length(ir.functions)) {
|
||||||
compile_fn(ir.functions[fi], fi, false)
|
compile_fn(ir.functions[fi], fi, false)
|
||||||
|
|||||||
5
qop.c
5
qop.c
@@ -456,9 +456,8 @@ static const JSCFunctionListEntry js_qop_funcs[] = {
|
|||||||
JS_PROP_INT32_DEF("FLAG_ENCRYPTED", QOP_FLAG_ENCRYPTED, 0),
|
JS_PROP_INT32_DEF("FLAG_ENCRYPTED", QOP_FLAG_ENCRYPTED, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_qop_use(JSContext *js) {
|
JSValue js_core_qop_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
|
|
||||||
JS_NewClassID(&js_qop_archive_class_id);
|
JS_NewClassID(&js_qop_archive_class_id);
|
||||||
JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class);
|
JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class);
|
||||||
JS_ROOT(archive_proto, JS_NewObject(js));
|
JS_ROOT(archive_proto, JS_NewObject(js));
|
||||||
@@ -474,4 +473,4 @@ JSValue js_qop_use(JSContext *js) {
|
|||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_qop_funcs, countof(js_qop_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_qop_funcs, countof(js_qop_funcs));
|
||||||
JS_RETURN(mod.val);
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
89
qopconv.ce
89
qopconv.ce
@@ -9,19 +9,20 @@ function print_usage() {
|
|||||||
log.console(" <sources...> <archive> .. create archive from sources")
|
log.console(" <sources...> <archive> .. create archive from sources")
|
||||||
}
|
}
|
||||||
|
|
||||||
function list(archive_path) {
|
function list_archive(archive_path) {
|
||||||
var blob = fd.slurp(archive_path)
|
var blob = fd.slurp(archive_path)
|
||||||
|
var archive = null
|
||||||
if (!blob) {
|
if (!blob) {
|
||||||
log.console("Could not open archive " + archive_path)
|
log.console("Could not open archive " + archive_path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var archive = null
|
var _open = function() {
|
||||||
try {
|
|
||||||
archive = qop.open(blob)
|
archive = qop.open(blob)
|
||||||
} catch(e) {
|
} disruption {
|
||||||
log.console("Could not open archive " + archive_path + ": " + e.message)
|
log.console("Could not open archive " + archive_path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
_open()
|
||||||
|
|
||||||
var files = archive.list()
|
var files = archive.list()
|
||||||
arrfor(files, function(f) {
|
arrfor(files, function(f) {
|
||||||
@@ -35,34 +36,41 @@ function list(archive_path) {
|
|||||||
|
|
||||||
function unpack(archive_path) {
|
function unpack(archive_path) {
|
||||||
var blob = fd.slurp(archive_path)
|
var blob = fd.slurp(archive_path)
|
||||||
|
var archive = null
|
||||||
if (!blob) {
|
if (!blob) {
|
||||||
log.console("Could not open archive " + archive_path)
|
log.console("Could not open archive " + archive_path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var archive = null
|
var _open = function() {
|
||||||
try {
|
|
||||||
archive = qop.open(blob)
|
archive = qop.open(blob)
|
||||||
} catch(e) {
|
} disruption {
|
||||||
log.console("Could not open archive " + archive_path + ": " + e.message)
|
log.console("Could not open archive " + archive_path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
_open()
|
||||||
|
|
||||||
var files = archive.list()
|
var files = archive.list()
|
||||||
arrfor(files, function(f) {
|
arrfor(files, function(f) {
|
||||||
var data = archive.read(f)
|
var data = archive.read(f)
|
||||||
|
var dir = null
|
||||||
|
var parts = null
|
||||||
|
var curr = null
|
||||||
|
var fh = null
|
||||||
|
var _mk = null
|
||||||
if (data) {
|
if (data) {
|
||||||
// Ensure directory exists
|
// Ensure directory exists
|
||||||
var dir = fd.dirname(f)
|
dir = fd.dirname(f)
|
||||||
if (dir) {
|
if (dir) {
|
||||||
// recursive mkdir
|
// recursive mkdir
|
||||||
var parts = array(dir, '/')
|
parts = array(dir, '/')
|
||||||
var curr = "."
|
curr = "."
|
||||||
arrfor(parts, function(p) {
|
arrfor(parts, function(p) {
|
||||||
curr += "/" + p
|
curr += "/" + p
|
||||||
try { fd.mkdir(curr) } catch(e) {}
|
_mk = function() { fd.mkdir(curr) } disruption {}
|
||||||
|
_mk()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
var fh = fd.open(f, "w")
|
fh = fd.open(f, "w")
|
||||||
fd.write(fh, data)
|
fd.write(fh, data)
|
||||||
fd.close(fh)
|
fd.close(fh)
|
||||||
log.console("Extracted " + f)
|
log.console("Extracted " + f)
|
||||||
@@ -73,9 +81,9 @@ function unpack(archive_path) {
|
|||||||
|
|
||||||
function pack(sources, archive_path, read_dir) {
|
function pack(sources, archive_path, read_dir) {
|
||||||
var writer = qop.write(archive_path)
|
var writer = qop.write(archive_path)
|
||||||
|
|
||||||
var base_dir = read_dir || "."
|
var base_dir = read_dir || "."
|
||||||
|
|
||||||
function add_recursive(path) {
|
function add_recursive(path) {
|
||||||
var full_path = base_dir + "/" + path
|
var full_path = base_dir + "/" + path
|
||||||
if (path == ".") full_path = base_dir
|
if (path == ".") full_path = base_dir
|
||||||
@@ -86,7 +94,7 @@ function pack(sources, archive_path, read_dir) {
|
|||||||
log.console("Could not stat " + full_path)
|
log.console("Could not stat " + full_path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.isDirectory) {
|
if (st.isDirectory) {
|
||||||
var list = fd.readdir(full_path)
|
var list = fd.readdir(full_path)
|
||||||
arrfor(list, function(item) {
|
arrfor(list, function(item) {
|
||||||
@@ -102,39 +110,44 @@ function pack(sources, archive_path, read_dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfor(sources, function(s) {
|
arrfor(sources, function(s) {
|
||||||
add_recursive(s)
|
add_recursive(s)
|
||||||
})
|
})
|
||||||
|
|
||||||
writer.finalize()
|
writer.finalize()
|
||||||
log.console("Created " + archive_path)
|
log.console("Created " + archive_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array(arg) || length(arg) < 1) {
|
var sources = null
|
||||||
|
var archive = null
|
||||||
|
var read_dir = null
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
if (!is_array(args) || length(args) < 1) {
|
||||||
print_usage()
|
print_usage()
|
||||||
} else {
|
} else {
|
||||||
if (arg[0] == "-l") {
|
if (args[0] == "-l") {
|
||||||
if (length(arg) < 2) print_usage()
|
if (length(args) < 2) print_usage()
|
||||||
else list(arg[1])
|
else list_archive(args[1])
|
||||||
} else if (arg[0] == "-u") {
|
} else if (args[0] == "-u") {
|
||||||
if (length(arg) < 2) print_usage()
|
if (length(args) < 2) print_usage()
|
||||||
else unpack(arg[1])
|
else unpack(args[1])
|
||||||
} else {
|
} else {
|
||||||
var sources = []
|
sources = []
|
||||||
var archive = null
|
archive = null
|
||||||
var read_dir = null
|
read_dir = null
|
||||||
var i = 0
|
i = 0
|
||||||
if (arg[0] == "-d") {
|
if (args[0] == "-d") {
|
||||||
read_dir = arg[1]
|
read_dir = args[1]
|
||||||
i = 2
|
i = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; i < length(arg) - 1; i++) {
|
for (; i < length(args) - 1; i++) {
|
||||||
push(sources, arg[i])
|
push(sources, args[i])
|
||||||
}
|
}
|
||||||
archive = arg[length(arg) - 1]
|
archive = args[length(args) - 1]
|
||||||
|
|
||||||
if (length(sources) == 0) {
|
if (length(sources) == 0) {
|
||||||
print_usage()
|
print_usage()
|
||||||
} else {
|
} else {
|
||||||
@@ -143,4 +156,4 @@ if (!is_array(arg) || length(arg) < 1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
35
regen.ce
35
regen.ce
@@ -9,22 +9,41 @@ var fold = use("fold")
|
|||||||
var mcode = use("mcode")
|
var mcode = use("mcode")
|
||||||
var streamline = use("streamline")
|
var streamline = use("streamline")
|
||||||
|
|
||||||
var files = [
|
// Pipeline files (tokenize/parse/fold/mcode/streamline) are only regenerated
|
||||||
|
// with --all flag since they require a self-consistent compiler to bootstrap.
|
||||||
|
var pipeline_files = [
|
||||||
{src: "tokenize.cm", name: "tokenize", out: "boot/tokenize.cm.mcode"},
|
{src: "tokenize.cm", name: "tokenize", out: "boot/tokenize.cm.mcode"},
|
||||||
{src: "parse.cm", name: "parse", out: "boot/parse.cm.mcode"},
|
{src: "parse.cm", name: "parse", out: "boot/parse.cm.mcode"},
|
||||||
{src: "fold.cm", name: "fold", out: "boot/fold.cm.mcode"},
|
{src: "fold.cm", name: "fold", out: "boot/fold.cm.mcode"},
|
||||||
{src: "mcode.cm", name: "mcode", out: "boot/mcode.cm.mcode"},
|
{src: "mcode.cm", name: "mcode", out: "boot/mcode.cm.mcode"},
|
||||||
{src: "streamline.cm", name: "streamline", out: "boot/streamline.cm.mcode"},
|
{src: "streamline.cm", name: "streamline", out: "boot/streamline.cm.mcode"}
|
||||||
|
]
|
||||||
|
|
||||||
|
var files = [
|
||||||
{src: "qbe.cm", name: "qbe", out: "boot/qbe.cm.mcode"},
|
{src: "qbe.cm", name: "qbe", out: "boot/qbe.cm.mcode"},
|
||||||
{src: "qbe_emit.cm", name: "qbe_emit", out: "boot/qbe_emit.cm.mcode"},
|
{src: "qbe_emit.cm", name: "qbe_emit", out: "boot/qbe_emit.cm.mcode"},
|
||||||
{src: "verify_ir.cm", name: "verify_ir", out: "boot/verify_ir.cm.mcode"},
|
{src: "verify_ir.cm", name: "verify_ir", out: "boot/verify_ir.cm.mcode"},
|
||||||
{src: "internal/bootstrap.cm", name: "bootstrap", out: "boot/bootstrap.cm.mcode"},
|
{src: "internal/bootstrap.cm", name: "bootstrap", out: "boot/bootstrap.cm.mcode"},
|
||||||
{src: "internal/engine.cm", name: "engine", out: "boot/engine.cm.mcode"},
|
{src: "internal/engine.cm", name: "engine", out: "boot/engine.cm.mcode"},
|
||||||
{src: "boot/seed_bootstrap.cm", name: "seed_bootstrap", out: "boot/seed_bootstrap.cm.mcode"}
|
{src: "boot/seed_bootstrap.cm", name: "seed_bootstrap", out: "boot/seed_bootstrap.cm.mcode"},
|
||||||
|
{src: "fd.cm", name: "fd", out: "boot/fd.cm.mcode"},
|
||||||
|
{src: "time.cm", name: "time", out: "boot/time.cm.mcode"},
|
||||||
|
{src: "pronto.cm", name: "pronto", out: "boot/pronto.cm.mcode"},
|
||||||
|
{src: "toml.cm", name: "toml", out: "boot/toml.cm.mcode"},
|
||||||
|
{src: "link.cm", name: "link", out: "boot/link.cm.mcode"},
|
||||||
|
{src: "toolchains.cm", name: "toolchains", out: "boot/toolchains.cm.mcode"},
|
||||||
|
{src: "package.cm", name: "package", out: "boot/package.cm.mcode"},
|
||||||
|
{src: "internal/shop.cm", name: "internal_shop", out: "boot/internal_shop.cm.mcode"}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Resolve shop_path for cache writes
|
// Include pipeline files with --all flag
|
||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
var regen_all = args != null && length(args) > 0 && args[0] == "--all"
|
||||||
|
if (regen_all) {
|
||||||
|
files = array(pipeline_files, files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve shop_path for cache writes
|
||||||
var shop = os.getenv('CELL_SHOP')
|
var shop = os.getenv('CELL_SHOP')
|
||||||
var home = null
|
var home = null
|
||||||
var cache_dir = null
|
var cache_dir = null
|
||||||
@@ -58,6 +77,7 @@ var errs = null
|
|||||||
var ei = 0
|
var ei = 0
|
||||||
var e = null
|
var e = null
|
||||||
var had_errors = false
|
var had_errors = false
|
||||||
|
var compact_mcode = null
|
||||||
|
|
||||||
while (i < length(files)) {
|
while (i < length(files)) {
|
||||||
entry = files[i]
|
entry = files[i]
|
||||||
@@ -93,9 +113,10 @@ while (i < length(files)) {
|
|||||||
if (cache_dir) {
|
if (cache_dir) {
|
||||||
mcode_blob = stone(blob(mcode_text))
|
mcode_blob = stone(blob(mcode_text))
|
||||||
hash = text(crypto.blake2(mcode_blob), 'h')
|
hash = text(crypto.blake2(mcode_blob), 'h')
|
||||||
mach_blob = mach_compile_mcode_bin(entry.name, mcode_text)
|
compact_mcode = json.encode(optimized)
|
||||||
fd.slurpwrite(cache_dir + '/' + hash + '.mach', mach_blob)
|
mach_blob = mach_compile_mcode_bin(entry.name, compact_mcode)
|
||||||
print(` cached ${hash}.mach`)
|
fd.slurpwrite(cache_dir + '/' + hash, mach_blob)
|
||||||
|
print(` cached ${hash}`)
|
||||||
}
|
}
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
|
|||||||
20
remove.ce
20
remove.ce
@@ -16,8 +16,10 @@ var fd = use('fd')
|
|||||||
var target_pkg = null
|
var target_pkg = null
|
||||||
var prune = false
|
var prune = false
|
||||||
var dry_run = false
|
var dry_run = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--prune') {
|
if (args[i] == '--prune') {
|
||||||
prune = true
|
prune = true
|
||||||
} else if (args[i] == '--dry-run') {
|
} else if (args[i] == '--dry-run') {
|
||||||
@@ -43,7 +45,7 @@ if (!target_pkg) {
|
|||||||
|
|
||||||
// Resolve relative paths to absolute paths
|
// Resolve relative paths to absolute paths
|
||||||
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
||||||
var resolved = fd.realpath(target_pkg)
|
resolved = fd.realpath(target_pkg)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_pkg = resolved
|
target_pkg = resolved
|
||||||
}
|
}
|
||||||
@@ -51,27 +53,31 @@ if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg
|
|||||||
|
|
||||||
var packages_to_remove = [target_pkg]
|
var packages_to_remove = [target_pkg]
|
||||||
|
|
||||||
|
var lock = null
|
||||||
|
var all_packages = null
|
||||||
|
var needed = null
|
||||||
if (prune) {
|
if (prune) {
|
||||||
// Find packages no longer needed
|
// Find packages no longer needed
|
||||||
// Get all dependencies of remaining packages
|
// Get all dependencies of remaining packages
|
||||||
var lock = shop.load_lock()
|
lock = shop.load_lock()
|
||||||
var all_packages = shop.list_packages()
|
all_packages = shop.list_packages()
|
||||||
|
|
||||||
// Build set of all needed packages (excluding target)
|
// Build set of all needed packages (excluding target)
|
||||||
var needed = {}
|
needed = {}
|
||||||
arrfor(all_packages, function(p) {
|
arrfor(all_packages, function(p) {
|
||||||
if (p == target_pkg || p == 'core') return
|
if (p == target_pkg || p == 'core') return
|
||||||
|
|
||||||
// Mark this package and its deps as needed
|
// Mark this package and its deps as needed
|
||||||
needed[p] = true
|
needed[p] = true
|
||||||
try {
|
var _gather = function() {
|
||||||
var deps = pkg.gather_dependencies(p)
|
var deps = pkg.gather_dependencies(p)
|
||||||
arrfor(deps, function(dep) {
|
arrfor(deps, function(dep) {
|
||||||
needed[dep] = true
|
needed[dep] = true
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Skip if can't read deps
|
// Skip if can't read deps
|
||||||
}
|
}
|
||||||
|
_gather()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Find packages that are NOT needed
|
// Find packages that are NOT needed
|
||||||
|
|||||||
90
resolve.ce
90
resolve.ce
@@ -20,8 +20,10 @@ var target_locator = null
|
|||||||
var target_triple = null
|
var target_triple = null
|
||||||
var show_locked = false
|
var show_locked = false
|
||||||
var refresh_first = false
|
var refresh_first = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--target' || args[i] == '-t') {
|
if (args[i] == '--target' || args[i] == '-t') {
|
||||||
if (i + 1 < length(args)) {
|
if (i + 1 < length(args)) {
|
||||||
target_triple = args[++i]
|
target_triple = args[++i]
|
||||||
@@ -55,16 +57,17 @@ if (!target_locator) {
|
|||||||
|
|
||||||
// Resolve local paths
|
// Resolve local paths
|
||||||
if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) {
|
if (target_locator == '.' || starts_with(target_locator, './') || starts_with(target_locator, '../') || fd.is_dir(target_locator)) {
|
||||||
var resolved = fd.realpath(target_locator)
|
resolved = fd.realpath(target_locator)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_locator = resolved
|
target_locator = resolved
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's a valid package
|
// Check if it's a valid package
|
||||||
|
var pkg_dir = null
|
||||||
if (!fd.is_file(target_locator + '/cell.toml')) {
|
if (!fd.is_file(target_locator + '/cell.toml')) {
|
||||||
// Try to find it in the shop
|
// Try to find it in the shop
|
||||||
var pkg_dir = shop.get_package_dir(target_locator)
|
pkg_dir = shop.get_package_dir(target_locator)
|
||||||
if (!fd.is_file(pkg_dir + '/cell.toml')) {
|
if (!fd.is_file(pkg_dir + '/cell.toml')) {
|
||||||
log.error("Not a valid package: " + target_locator)
|
log.error("Not a valid package: " + target_locator)
|
||||||
$stop()
|
$stop()
|
||||||
@@ -89,7 +92,7 @@ function gather_deps(locator, depth) {
|
|||||||
|
|
||||||
all_deps[locator] = { depth: depth }
|
all_deps[locator] = { depth: depth }
|
||||||
|
|
||||||
try {
|
var _gather = function() {
|
||||||
var deps = pkg.dependencies(locator)
|
var deps = pkg.dependencies(locator)
|
||||||
if (deps) {
|
if (deps) {
|
||||||
arrfor(array(deps), function(alias) {
|
arrfor(array(deps), function(alias) {
|
||||||
@@ -97,9 +100,10 @@ function gather_deps(locator, depth) {
|
|||||||
gather_deps(dep_locator, depth + 1)
|
gather_deps(dep_locator, depth + 1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Package might not have dependencies
|
// Package might not have dependencies
|
||||||
}
|
}
|
||||||
|
_gather()
|
||||||
}
|
}
|
||||||
|
|
||||||
gather_deps(target_locator, 0)
|
gather_deps(target_locator, 0)
|
||||||
@@ -114,51 +118,74 @@ var sorted = array(array(all_deps), function(locator) { return { locator: locato
|
|||||||
sorted = sort(sorted, "locator")
|
sorted = sort(sorted, "locator")
|
||||||
sorted = sort(sorted, "depth")
|
sorted = sort(sorted, "depth")
|
||||||
|
|
||||||
for (var i = 0; i < length(sorted); i++) {
|
var j = 0
|
||||||
var locator = sorted[i].locator
|
var locator = null
|
||||||
var depth = sorted[i].depth
|
var depth = 0
|
||||||
|
var indent = null
|
||||||
|
var info = null
|
||||||
|
var lock_entry = null
|
||||||
|
var link_target = null
|
||||||
|
var effective_locator = null
|
||||||
|
var is_linked = false
|
||||||
|
var is_in_lock = false
|
||||||
|
var is_local = false
|
||||||
|
var is_fetched = false
|
||||||
|
var lib_dir = null
|
||||||
|
var lib_name = null
|
||||||
|
var dylib_ext = null
|
||||||
|
var lib_path = null
|
||||||
|
var is_built = false
|
||||||
|
var status_parts = null
|
||||||
|
var commit_str = null
|
||||||
|
var line = null
|
||||||
|
var cflags = null
|
||||||
|
var ldflags = null
|
||||||
|
|
||||||
var indent = ""
|
for (i = 0; i < length(sorted); i++) {
|
||||||
for (var j = 0; j < depth; j++) indent += " "
|
locator = sorted[i].locator
|
||||||
|
depth = sorted[i].depth
|
||||||
|
|
||||||
|
indent = ""
|
||||||
|
for (j = 0; j < depth; j++) indent += " "
|
||||||
|
|
||||||
// Get info about this package
|
// Get info about this package
|
||||||
var info = shop.resolve_package_info(locator)
|
info = shop.resolve_package_info(locator)
|
||||||
var lock_entry = lock[locator]
|
lock_entry = lock[locator]
|
||||||
var link_target = show_locked ? null : links[locator]
|
link_target = show_locked ? null : links[locator]
|
||||||
var effective_locator = link_target || locator
|
effective_locator = link_target || locator
|
||||||
|
|
||||||
// Check status
|
// Check status
|
||||||
var is_linked = link_target != null
|
is_linked = link_target != null
|
||||||
var is_in_lock = lock_entry != null
|
is_in_lock = lock_entry != null
|
||||||
var is_local = info == 'local'
|
is_local = info == 'local'
|
||||||
|
|
||||||
// Check if fetched (package directory exists)
|
// Check if fetched (package directory exists)
|
||||||
var pkg_dir = shop.get_package_dir(locator)
|
pkg_dir = shop.get_package_dir(locator)
|
||||||
var is_fetched = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)
|
is_fetched = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)
|
||||||
|
|
||||||
// Check if built (library exists)
|
// Check if built (library exists)
|
||||||
var lib_dir = shop.get_lib_dir()
|
lib_dir = shop.get_lib_dir()
|
||||||
var lib_name = shop.lib_name_for_package(locator)
|
lib_name = shop.lib_name_for_package(locator)
|
||||||
var dylib_ext = '.dylib' // TODO: detect from target
|
dylib_ext = '.dylib' // TODO: detect from target
|
||||||
var lib_path = lib_dir + '/' + lib_name + dylib_ext
|
lib_path = lib_dir + '/' + lib_name + dylib_ext
|
||||||
var is_built = fd.is_file(lib_path)
|
is_built = fd.is_file(lib_path)
|
||||||
|
|
||||||
// Format output
|
// Format output
|
||||||
var status_parts = []
|
status_parts = []
|
||||||
if (is_linked) push(status_parts, "linked")
|
if (is_linked) push(status_parts, "linked")
|
||||||
if (is_local) push(status_parts, "local")
|
if (is_local) push(status_parts, "local")
|
||||||
if (!is_in_lock) push(status_parts, "not in lock")
|
if (!is_in_lock) push(status_parts, "not in lock")
|
||||||
if (!is_fetched) push(status_parts, "not fetched")
|
if (!is_fetched) push(status_parts, "not fetched")
|
||||||
if (is_built) push(status_parts, "built")
|
if (is_built) push(status_parts, "built")
|
||||||
|
|
||||||
var commit_str = ""
|
commit_str = ""
|
||||||
if (lock_entry && lock_entry.commit) {
|
if (lock_entry && lock_entry.commit) {
|
||||||
commit_str = " @" + text(lock_entry.commit, 0, 8)
|
commit_str = " @" + text(lock_entry.commit, 0, 8)
|
||||||
} else if (lock_entry && lock_entry.type == 'local') {
|
} else if (lock_entry && lock_entry.type == 'local') {
|
||||||
commit_str = " (local)"
|
commit_str = " (local)"
|
||||||
}
|
}
|
||||||
|
|
||||||
var line = indent + locator + commit_str
|
line = indent + locator + commit_str
|
||||||
|
|
||||||
if (is_linked && !show_locked) {
|
if (is_linked && !show_locked) {
|
||||||
line += " -> " + link_target
|
line += " -> " + link_target
|
||||||
@@ -172,9 +199,9 @@ for (var i = 0; i < length(sorted); i++) {
|
|||||||
|
|
||||||
// Show compilation inputs if requested (verbose)
|
// Show compilation inputs if requested (verbose)
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
try {
|
var _show_flags = function() {
|
||||||
var cflags = pkg.get_flags(locator, 'CFLAGS', target_triple)
|
cflags = pkg.get_flags(locator, 'CFLAGS', target_triple)
|
||||||
var ldflags = pkg.get_flags(locator, 'LDFLAGS', target_triple)
|
ldflags = pkg.get_flags(locator, 'LDFLAGS', target_triple)
|
||||||
if (length(cflags) > 0 || length(ldflags) > 0) {
|
if (length(cflags) > 0 || length(ldflags) > 0) {
|
||||||
log.console(indent + " Compilation inputs:")
|
log.console(indent + " Compilation inputs:")
|
||||||
if (length(cflags) > 0) {
|
if (length(cflags) > 0) {
|
||||||
@@ -184,9 +211,10 @@ for (var i = 0; i < length(sorted); i++) {
|
|||||||
log.console(indent + " LDFLAGS: " + text(ldflags, ' '))
|
log.console(indent + " LDFLAGS: " + text(ldflags, ' '))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Skip if can't read config
|
// Skip if can't read config
|
||||||
}
|
}
|
||||||
|
_show_flags()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// run_native.ce — load a module both interpreted and native, compare speed
|
// run_native.ce — load a module both interpreted and native, compare speed
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// cell --core . run_native.ce <module>
|
// cell --dev run_native.ce <module>
|
||||||
//
|
//
|
||||||
// Loads <module>.cm via use() (interpreted) and <module>.dylib (native),
|
// Loads <module>.cm via use() (interpreted) and <module>.cm.dylib (native),
|
||||||
// runs both and compares results and timing.
|
// runs both and compares results and timing.
|
||||||
|
|
||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
|
||||||
if (length(args) < 1) {
|
if (length(args) < 1) {
|
||||||
print('usage: cell --core . run_native.ce <module>')
|
print('usage: cell --dev run_native.ce <module>')
|
||||||
print(' e.g. cell --core . run_native.ce num_torture')
|
print(' e.g. cell --dev run_native.ce num_torture')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ if (ends_with(name, '.cm')) {
|
|||||||
|
|
||||||
var safe = replace(replace(name, '/', '_'), '-', '_')
|
var safe = replace(replace(name, '/', '_'), '-', '_')
|
||||||
var symbol = 'js_' + safe + '_use'
|
var symbol = 'js_' + safe + '_use'
|
||||||
var dylib_path = './' + name + '.dylib'
|
var dylib_path = './' + name + '.cm.dylib'
|
||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
|
|
||||||
// --- Test argument for function-returning modules ---
|
// --- Test argument for function-returning modules ---
|
||||||
|
|||||||
78
run_native_seed.ce
Normal file
78
run_native_seed.ce
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// run_native_seed.ce — load and run a native .dylib module (seed mode)
|
||||||
|
// Usage: ./cell --dev --seed run_native_seed benches/fibonacci
|
||||||
|
|
||||||
|
var fd = use("fd")
|
||||||
|
var os = use("os")
|
||||||
|
|
||||||
|
if (length(args) < 1) {
|
||||||
|
print("usage: cell --dev --seed run_native_seed <module>")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = args[0]
|
||||||
|
if (ends_with(name, ".cm")) {
|
||||||
|
name = text(name, 0, length(name) - 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
var safe = replace(replace(name, "/", "_"), "-", "_")
|
||||||
|
var symbol = "js_" + safe + "_use"
|
||||||
|
var dylib_path = "./" + name + ".cm.dylib"
|
||||||
|
|
||||||
|
var test_arg = 30
|
||||||
|
if (length(args) > 1) {
|
||||||
|
test_arg = number(args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Interpreted run ---
|
||||||
|
print("--- interpreted ---")
|
||||||
|
var t1 = os.now()
|
||||||
|
var mod_interp = use(name)
|
||||||
|
var t2 = os.now()
|
||||||
|
var result_interp = null
|
||||||
|
if (is_function(mod_interp)) {
|
||||||
|
print("module returns a function, calling with " + text(test_arg))
|
||||||
|
t1 = os.now()
|
||||||
|
result_interp = mod_interp(test_arg)
|
||||||
|
t2 = os.now()
|
||||||
|
}
|
||||||
|
result_interp = result_interp != null ? result_interp : mod_interp
|
||||||
|
var ms_interp = (t2 - t1) / 1000000
|
||||||
|
print("result: " + text(result_interp))
|
||||||
|
print("time: " + text(ms_interp) + " ms")
|
||||||
|
|
||||||
|
// --- Native run ---
|
||||||
|
if (!fd.is_file(dylib_path)) {
|
||||||
|
print("\nno " + dylib_path + " found")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n--- native ---")
|
||||||
|
var t3 = os.now()
|
||||||
|
var lib = os.dylib_open(dylib_path)
|
||||||
|
var t4 = os.now()
|
||||||
|
var mod_native = os.dylib_symbol(lib, symbol)
|
||||||
|
var t5 = os.now()
|
||||||
|
var result_native = null
|
||||||
|
if (is_function(mod_native)) {
|
||||||
|
print("module returns a function, calling with " + text(test_arg))
|
||||||
|
t4 = os.now()
|
||||||
|
result_native = mod_native(test_arg)
|
||||||
|
t5 = os.now()
|
||||||
|
}
|
||||||
|
result_native = result_native != null ? result_native : mod_native
|
||||||
|
var ms_native = (t5 - t3) / 1000000
|
||||||
|
var ms_exec = (t5 - t4) / 1000000
|
||||||
|
print("result: " + text(result_native))
|
||||||
|
print("load: " + text((t4 - t3) / 1000000) + " ms")
|
||||||
|
print("exec: " + text(ms_exec) + " ms")
|
||||||
|
print("total: " + text(ms_native) + " ms")
|
||||||
|
|
||||||
|
// --- Comparison ---
|
||||||
|
print("\n--- comparison ---")
|
||||||
|
print("match: " + text(result_interp == result_native))
|
||||||
|
if (ms_native > 0) {
|
||||||
|
print("speedup: " + text(ms_interp / ms_native) + "x (total)")
|
||||||
|
}
|
||||||
|
if (ms_exec > 0) {
|
||||||
|
print("speedup: " + text(ms_interp / ms_exec) + "x (exec only)")
|
||||||
|
}
|
||||||
5
runtime.cm
Normal file
5
runtime.cm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Runtime configuration — available to all modules via use('runtime')
|
||||||
|
return stone({
|
||||||
|
shop_path: shop_path,
|
||||||
|
core_path: core_path
|
||||||
|
})
|
||||||
16
search.ce
16
search.ce
@@ -8,7 +8,6 @@ if (length(args) < 1) {
|
|||||||
log.console("Usage: cell search <query>")
|
log.console("Usage: cell search <query>")
|
||||||
log.console("Searches for packages, actors, or modules matching the query.")
|
log.console("Searches for packages, actors, or modules matching the query.")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = args[0]
|
var query = args[0]
|
||||||
@@ -24,25 +23,26 @@ arrfor(packages, function(package_name) {
|
|||||||
if (search(package_name, query) != null) {
|
if (search(package_name, query) != null) {
|
||||||
push(found_packages, package_name)
|
push(found_packages, package_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search modules and actors within the package
|
// Search modules and actors within the package
|
||||||
try {
|
var _search = function() {
|
||||||
var modules = pkg.list_modules(package_name)
|
var modules = pkg.list_modules(package_name)
|
||||||
arrfor(modules, function(mod) {
|
arrfor(modules, function(mod) {
|
||||||
if (search(mod, query) != null) {
|
if (search(mod, query) != null) {
|
||||||
push(found_modules, package_name + ':' + mod)
|
push(found_modules, package_name + ':' + mod)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var actors = pkg.list_programs(package_name)
|
var actors = pkg.list_programs(package_name)
|
||||||
arrfor(actors, function(actor) {
|
arrfor(actors, function(actor) {
|
||||||
if (search(actor, query) != null) {
|
if (search(actor, query) != null) {
|
||||||
push(found_actors, package_name + ':' + actor)
|
push(found_actors, package_name + ':' + actor)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Skip packages that can't be read
|
// Skip packages that can't be read
|
||||||
}
|
}
|
||||||
|
_search()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Print results
|
// Print results
|
||||||
@@ -53,7 +53,7 @@ if (total == 0) {
|
|||||||
} else {
|
} else {
|
||||||
log.console("Found " + text(total) + " result(s) for '" + query + "':")
|
log.console("Found " + text(total) + " result(s) for '" + query + "':")
|
||||||
log.console("")
|
log.console("")
|
||||||
|
|
||||||
if (length(found_packages) > 0) {
|
if (length(found_packages) > 0) {
|
||||||
log.console("Packages:")
|
log.console("Packages:")
|
||||||
arrfor(found_packages, function(p) {
|
arrfor(found_packages, function(p) {
|
||||||
@@ -61,7 +61,7 @@ if (total == 0) {
|
|||||||
})
|
})
|
||||||
log.console("")
|
log.console("")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length(found_modules) > 0) {
|
if (length(found_modules) > 0) {
|
||||||
log.console("Modules:")
|
log.console("Modules:")
|
||||||
arrfor(found_modules, function(m) {
|
arrfor(found_modules, function(m) {
|
||||||
@@ -69,7 +69,7 @@ if (total == 0) {
|
|||||||
})
|
})
|
||||||
log.console("")
|
log.console("")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length(found_actors) > 0) {
|
if (length(found_actors) > 0) {
|
||||||
log.console("Actors:")
|
log.console("Actors:")
|
||||||
arrfor(found_actors, function(a) {
|
arrfor(found_actors, function(a) {
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ static char *compute_blake2_hex(const char *data, size_t size) {
|
|||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build cache path: shop_path/build/<hex>.mach (caller must free)
|
// Build cache path: shop_path/build/<hex> (caller must free)
|
||||||
static char *build_cache_path(const char *hex) {
|
static char *build_cache_path(const char *hex) {
|
||||||
if (!shop_path) return NULL;
|
if (!shop_path) return NULL;
|
||||||
size_t len = strlen(shop_path) + strlen("/build/") + 64 + strlen(".mach") + 1;
|
size_t len = strlen(shop_path) + strlen("/build/") + 64 + 1;
|
||||||
char *path = malloc(len);
|
char *path = malloc(len);
|
||||||
snprintf(path, len, "%s/build/%s.mach", shop_path, hex);
|
snprintf(path, len, "%s/build/%s", shop_path, hex);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,11 +225,8 @@ void actor_disrupt(cell_rt *crt)
|
|||||||
actor_free(crt);
|
actor_free(crt);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue js_os_use(JSContext *js);
|
JSValue js_core_os_use(JSContext *js);
|
||||||
JSValue js_math_use(JSContext *js);
|
JSValue js_core_json_use(JSContext *js);
|
||||||
JSValue js_json_use(JSContext *js);
|
|
||||||
JSValue js_nota_use(JSContext *js);
|
|
||||||
JSValue js_wota_use(JSContext *js);
|
|
||||||
|
|
||||||
void script_startup(cell_rt *prt)
|
void script_startup(cell_rt *prt)
|
||||||
{
|
{
|
||||||
@@ -255,7 +252,7 @@ void script_startup(cell_rt *prt)
|
|||||||
prt->actor_sym_ref.val = JS_NULL;
|
prt->actor_sym_ref.val = JS_NULL;
|
||||||
|
|
||||||
cell_rt *crt = JS_GetContextOpaque(js);
|
cell_rt *crt = JS_GetContextOpaque(js);
|
||||||
JS_FreeValue(js, js_blob_use(js));
|
JS_FreeValue(js, js_core_blob_use(js));
|
||||||
|
|
||||||
// Load pre-compiled bootstrap .mcode
|
// Load pre-compiled bootstrap .mcode
|
||||||
size_t boot_size;
|
size_t boot_size;
|
||||||
@@ -281,14 +278,10 @@ void script_startup(cell_rt *prt)
|
|||||||
JS_AddGCRef(js, &env_ref);
|
JS_AddGCRef(js, &env_ref);
|
||||||
env_ref.val = JS_NewObject(js);
|
env_ref.val = JS_NewObject(js);
|
||||||
JSValue tmp;
|
JSValue tmp;
|
||||||
tmp = js_os_use(js);
|
tmp = js_core_os_use(js);
|
||||||
JS_SetPropertyStr(js, env_ref.val, "os", tmp);
|
JS_SetPropertyStr(js, env_ref.val, "os", tmp);
|
||||||
tmp = js_json_use(js);
|
tmp = js_core_json_use(js);
|
||||||
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
||||||
tmp = js_nota_use(js);
|
|
||||||
JS_SetPropertyStr(js, env_ref.val, "nota", tmp);
|
|
||||||
tmp = js_wota_use(js);
|
|
||||||
JS_SetPropertyStr(js, env_ref.val, "wota", tmp);
|
|
||||||
|
|
||||||
crt->actor_sym_ref.val = JS_NewObject(js);
|
crt->actor_sym_ref.val = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
||||||
@@ -528,25 +521,21 @@ int cell_init(int argc, char **argv)
|
|||||||
|
|
||||||
root_cell = cli_rt;
|
root_cell = cli_rt;
|
||||||
|
|
||||||
JS_FreeValue(ctx, js_blob_use(ctx));
|
JS_FreeValue(ctx, js_core_blob_use(ctx));
|
||||||
|
|
||||||
JSGCRef env_ref;
|
JSGCRef env_ref;
|
||||||
JS_AddGCRef(ctx, &env_ref);
|
JS_AddGCRef(ctx, &env_ref);
|
||||||
env_ref.val = JS_NewObject(ctx);
|
env_ref.val = JS_NewObject(ctx);
|
||||||
JSValue tmp;
|
JSValue tmp;
|
||||||
tmp = js_os_use(ctx);
|
tmp = js_core_os_use(ctx);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "os", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "os", tmp);
|
||||||
tmp = JS_NewString(ctx, core_path);
|
tmp = JS_NewString(ctx, core_path);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
||||||
tmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
tmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "shop_path", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "shop_path", tmp);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
||||||
tmp = js_json_use(ctx);
|
tmp = js_core_json_use(ctx);
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
||||||
tmp = js_nota_use(ctx);
|
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "nota", tmp);
|
|
||||||
tmp = js_wota_use(ctx);
|
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "wota", tmp);
|
|
||||||
JS_SetPropertyStr(ctx, env_ref.val, "init", JS_NULL);
|
JS_SetPropertyStr(ctx, env_ref.val, "init", JS_NULL);
|
||||||
JSGCRef args_ref;
|
JSGCRef args_ref;
|
||||||
JS_AddGCRef(ctx, &args_ref);
|
JS_AddGCRef(ctx, &args_ref);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// blob fns
|
// blob fns
|
||||||
JSValue js_blob_use(JSContext *js);
|
JSValue js_core_blob_use(JSContext *js);
|
||||||
JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes);
|
JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes);
|
||||||
void *js_get_blob_data(JSContext *js, size_t *size, JSValue v); // bytes
|
void *js_get_blob_data(JSContext *js, size_t *size, JSValue v); // bytes
|
||||||
void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v); // bits
|
void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v); // bits
|
||||||
@@ -31,9 +31,7 @@ void *value2wota(JSContext *js, JSValue v, JSValue replacer, size_t *bytes);
|
|||||||
JSValue nota2value(JSContext *js, void *nota);
|
JSValue nota2value(JSContext *js, void *nota);
|
||||||
void *value2nota(JSContext *js, JSValue v);
|
void *value2nota(JSContext *js, JSValue v);
|
||||||
|
|
||||||
JSValue js_json_use(JSContext *js);
|
JSValue js_core_json_use(JSContext *js);
|
||||||
JSValue js_nota_use(JSContext *js);
|
|
||||||
JSValue js_wota_use(JSContext *js);
|
|
||||||
|
|
||||||
#define CELL_HOOK_ENTER 1
|
#define CELL_HOOK_ENTER 1
|
||||||
#define CELL_HOOK_EXIT 2
|
#define CELL_HOOK_EXIT 2
|
||||||
@@ -237,6 +235,9 @@ JSValue CELL_USE_NAME(JSContext *js) { \
|
|||||||
JS_SetPropertyFunctionList(js, mod, FUNCS, countof(FUNCS)); \
|
JS_SetPropertyFunctionList(js, mod, FUNCS, countof(FUNCS)); \
|
||||||
return mod; }
|
return mod; }
|
||||||
|
|
||||||
|
#define CELL_PROGRAM_INIT(c) \
|
||||||
|
JSValue CELL_USE_NAME(JSContext *js) { do { c ; } while(0); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
#include "quickjs-internal.h"
|
#include "quickjs-internal.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Non-inline wrappers for static inline functions in quickjs.h */
|
||||||
|
JSValue qbe_new_float64(JSContext *ctx, double d) {
|
||||||
|
return __JS_NewFloat64(ctx, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue qbe_new_string(JSContext *ctx, const char *str) {
|
||||||
|
return JS_NewString(ctx, str);
|
||||||
|
}
|
||||||
|
|
||||||
/* Comparison op IDs (must match qbe.cm float_cmp_op_id values) */
|
/* Comparison op IDs (must match qbe.cm float_cmp_op_id values) */
|
||||||
enum {
|
enum {
|
||||||
QBE_CMP_EQ = 0,
|
QBE_CMP_EQ = 0,
|
||||||
@@ -42,6 +51,16 @@ JSValue qbe_float_add(JSContext *ctx, JSValue a, JSValue b) {
|
|||||||
return qbe_float_binop(ctx, a, b, op_add);
|
return qbe_float_binop(ctx, a, b, op_add);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Generic add: concat if both text, float add if both numeric, else type error */
|
||||||
|
JSValue cell_rt_add(JSContext *ctx, JSValue a, JSValue b) {
|
||||||
|
if (JS_IsText(a) && JS_IsText(b))
|
||||||
|
return JS_ConcatString(ctx, a, b);
|
||||||
|
if (JS_IsNumber(a) && JS_IsNumber(b))
|
||||||
|
return qbe_float_binop(ctx, a, b, op_add);
|
||||||
|
JS_ThrowTypeError(ctx, "cannot add incompatible types");
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
JSValue qbe_float_sub(JSContext *ctx, JSValue a, JSValue b) {
|
JSValue qbe_float_sub(JSContext *ctx, JSValue a, JSValue b) {
|
||||||
return qbe_float_binop(ctx, a, b, op_sub);
|
return qbe_float_binop(ctx, a, b, op_sub);
|
||||||
}
|
}
|
||||||
@@ -446,6 +465,15 @@ JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b) {
|
|||||||
return JS_NewBool(ctx, a != b);
|
return JS_NewBool(ctx, a != b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Type check: is_proxy (function with arity 2) --- */
|
||||||
|
|
||||||
|
int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
||||||
|
(void)ctx;
|
||||||
|
if (!JS_IsFunction(v)) return 0;
|
||||||
|
JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
|
||||||
|
return fn->length == 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Disruption --- */
|
/* --- Disruption --- */
|
||||||
|
|
||||||
void cell_rt_disrupt(JSContext *ctx) {
|
void cell_rt_disrupt(JSContext *ctx) {
|
||||||
@@ -480,6 +508,31 @@ JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load a native module from a dylib handle, trying a named symbol first.
|
||||||
|
Falls back to cell_main if the named symbol is not found. */
|
||||||
|
JSValue cell_rt_native_module_load_named(JSContext *ctx, void *dl_handle, const char *sym_name) {
|
||||||
|
cell_compiled_fn fn = NULL;
|
||||||
|
if (sym_name)
|
||||||
|
fn = (cell_compiled_fn)dlsym(dl_handle, sym_name);
|
||||||
|
if (!fn)
|
||||||
|
fn = (cell_compiled_fn)dlsym(dl_handle, "cell_main");
|
||||||
|
if (!fn)
|
||||||
|
return JS_ThrowTypeError(ctx, "symbol not found in native module dylib");
|
||||||
|
|
||||||
|
void *prev_handle = g_current_dl_handle;
|
||||||
|
g_current_dl_handle = dl_handle;
|
||||||
|
|
||||||
|
JSValue *frame = calloc(512, sizeof(JSValue));
|
||||||
|
if (!frame) {
|
||||||
|
g_current_dl_handle = prev_handle;
|
||||||
|
return JS_ThrowTypeError(ctx, "frame allocation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue result = fn(ctx, frame);
|
||||||
|
g_current_dl_handle = prev_handle;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Backward-compat: uses RTLD_DEFAULT (works when dylib opened with RTLD_GLOBAL) */
|
/* Backward-compat: uses RTLD_DEFAULT (works when dylib opened with RTLD_GLOBAL) */
|
||||||
JSValue cell_rt_module_entry(JSContext *ctx) {
|
JSValue cell_rt_module_entry(JSContext *ctx) {
|
||||||
void *handle = dlopen(NULL, RTLD_LAZY);
|
void *handle = dlopen(NULL, RTLD_LAZY);
|
||||||
|
|||||||
@@ -155,9 +155,9 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
|
|||||||
MIST_FUNC_DEF(actor, clock, 1),
|
MIST_FUNC_DEF(actor, clock, 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_actor_use(JSContext *js) {
|
JSValue js_core_actor_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_actor_funcs, countof(js_actor_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_actor_funcs, countof(js_actor_funcs));
|
||||||
JS_RETURN(mod.val);
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9585,7 +9585,7 @@ static const JSCFunctionListEntry js_blob_proto_funcs[] = {
|
|||||||
|
|
||||||
/* Initialize blob - called during context setup (but we do it in
|
/* Initialize blob - called during context setup (but we do it in
|
||||||
* JS_AddIntrinsicBaseObjects now) */
|
* JS_AddIntrinsicBaseObjects now) */
|
||||||
JSValue js_blob_use (JSContext *js) {
|
JSValue js_core_blob_use (JSContext *js) {
|
||||||
return JS_GetPropertyStr (js, js->global_obj, "blob");
|
return JS_GetPropertyStr (js, js->global_obj, "blob");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11073,7 +11073,7 @@ static const JSCFunctionListEntry js_cell_json_funcs[] = {
|
|||||||
JS_CFUNC_DEF ("decode", 1, js_cell_json_decode),
|
JS_CFUNC_DEF ("decode", 1, js_cell_json_decode),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_json_use (JSContext *ctx) {
|
JSValue js_core_json_use (JSContext *ctx) {
|
||||||
JSGCRef obj_ref;
|
JSGCRef obj_ref;
|
||||||
JS_PushGCRef (ctx, &obj_ref);
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
obj_ref.val = JS_NewObject (ctx);
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
@@ -11485,7 +11485,7 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
|
|||||||
JS_CFUNC_DEF ("decode", 1, js_nota_decode),
|
JS_CFUNC_DEF ("decode", 1, js_nota_decode),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_nota_use (JSContext *js) {
|
JSValue js_core_nota_use (JSContext *js) {
|
||||||
JSGCRef export_ref;
|
JSGCRef export_ref;
|
||||||
JS_PushGCRef (js, &export_ref);
|
JS_PushGCRef (js, &export_ref);
|
||||||
export_ref.val = JS_NewObject (js);
|
export_ref.val = JS_NewObject (js);
|
||||||
@@ -11919,7 +11919,7 @@ static const JSCFunctionListEntry js_wota_funcs[] = {
|
|||||||
JS_CFUNC_DEF ("decode", 2, js_wota_decode),
|
JS_CFUNC_DEF ("decode", 2, js_wota_decode),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_wota_use (JSContext *ctx) {
|
JSValue js_core_wota_use (JSContext *ctx) {
|
||||||
JSGCRef exports_ref;
|
JSGCRef exports_ref;
|
||||||
JS_PushGCRef (ctx, &exports_ref);
|
JS_PushGCRef (ctx, &exports_ref);
|
||||||
exports_ref.val = JS_NewObject (ctx);
|
exports_ref.val = JS_NewObject (ctx);
|
||||||
@@ -12034,7 +12034,7 @@ static const JSCFunctionListEntry js_math_radians_funcs[]
|
|||||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_math_radians_use (JSContext *ctx) {
|
JSValue js_core_math_radians_use (JSContext *ctx) {
|
||||||
JSGCRef obj_ref;
|
JSGCRef obj_ref;
|
||||||
JS_PushGCRef (ctx, &obj_ref);
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
obj_ref.val = JS_NewObject (ctx);
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
@@ -12104,7 +12104,7 @@ static const JSCFunctionListEntry js_math_degrees_funcs[]
|
|||||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_math_degrees_use (JSContext *ctx) {
|
JSValue js_core_math_degrees_use (JSContext *ctx) {
|
||||||
JSGCRef obj_ref;
|
JSGCRef obj_ref;
|
||||||
JS_PushGCRef (ctx, &obj_ref);
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
obj_ref.val = JS_NewObject (ctx);
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
@@ -12173,7 +12173,7 @@ static const JSCFunctionListEntry js_math_cycles_funcs[]
|
|||||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_math_cycles_use (JSContext *ctx) {
|
JSValue js_core_math_cycles_use (JSContext *ctx) {
|
||||||
JSGCRef obj_ref;
|
JSGCRef obj_ref;
|
||||||
JS_PushGCRef (ctx, &obj_ref);
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
obj_ref.val = JS_NewObject (ctx);
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
|
|||||||
76
tests/build_audit.cm
Normal file
76
tests/build_audit.cm
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Build system audit tests
|
||||||
|
// Tests deterministic dylib paths, symbol naming, and lib layout
|
||||||
|
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var runtime = use('runtime')
|
||||||
|
|
||||||
|
return {
|
||||||
|
// ========================================================================
|
||||||
|
// DETERMINISTIC DYLIB PATHS
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_dylib_path_deterministic: function() {
|
||||||
|
var path = shop.get_dylib_path('core', 'time')
|
||||||
|
if (!ends_with(path, '/lib/core/time.dylib')) return "dylib path should end with /lib/core/time.dylib, got: " + path
|
||||||
|
},
|
||||||
|
|
||||||
|
test_dylib_path_internal: function() {
|
||||||
|
var path = shop.get_dylib_path('core', 'internal/os')
|
||||||
|
if (!ends_with(path, '/lib/core/internal/os.dylib')) return "dylib path should end with /lib/core/internal/os.dylib, got: " + path
|
||||||
|
},
|
||||||
|
|
||||||
|
test_dylib_path_external_package: function() {
|
||||||
|
var path = shop.get_dylib_path('gitea.pockle.world/john/prosperon', 'sprite')
|
||||||
|
if (!ends_with(path, '/lib/gitea.pockle.world/john/prosperon/sprite.dylib'))
|
||||||
|
return "dylib path should mirror package layout, got: " + path
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// SYMBOL NAMING
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_c_symbol_module: function() {
|
||||||
|
var sym = shop.c_symbol_for_file('pkg', 'sprite.c')
|
||||||
|
if (sym != 'js_pkg_sprite_use') return "expected js_pkg_sprite_use, got: " + sym
|
||||||
|
},
|
||||||
|
|
||||||
|
test_c_symbol_program: function() {
|
||||||
|
var sym = shop.c_symbol_for_file('pkg', 'game.ce')
|
||||||
|
if (sym != 'js_pkg_game_program') return "expected js_pkg_game_program, got: " + sym
|
||||||
|
},
|
||||||
|
|
||||||
|
test_c_symbol_internal: function() {
|
||||||
|
var sym = shop.c_symbol_for_file('pkg', 'internal/os.c')
|
||||||
|
if (sym != 'js_pkg_internal_os_use') return "expected js_pkg_internal_os_use, got: " + sym
|
||||||
|
},
|
||||||
|
|
||||||
|
test_c_symbol_dotted_package: function() {
|
||||||
|
var sym = shop.c_symbol_for_file('gitea.pockle.world/john/prosperon', 'sprite.c')
|
||||||
|
if (sym != 'js_gitea_pockle_world_john_prosperon_sprite_use')
|
||||||
|
return "expected js_gitea_pockle_world_john_prosperon_sprite_use, got: " + sym
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// LIB NAME
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_lib_name: function() {
|
||||||
|
var name = shop.lib_name_for_package('gitea.pockle.world/john/prosperon')
|
||||||
|
if (name != 'gitea_pockle_world_john_prosperon')
|
||||||
|
return "expected gitea_pockle_world_john_prosperon, got: " + name
|
||||||
|
},
|
||||||
|
|
||||||
|
test_lib_name_simple: function() {
|
||||||
|
var name = shop.lib_name_for_package('core')
|
||||||
|
if (name != 'core') return "expected core, got: " + name
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// SYMBOL PREFIX
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_c_symbol_prefix: function() {
|
||||||
|
var prefix = shop.c_symbol_prefix('mypackage')
|
||||||
|
if (prefix != 'js_mypackage_') return "expected js_mypackage_, got: " + prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
85
tests/shop_audit.cm
Normal file
85
tests/shop_audit.cm
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// Shop system audit tests
|
||||||
|
// Tests module resolution, caching, and runtime paths
|
||||||
|
// accessible from user code via use()
|
||||||
|
|
||||||
|
var json = use('json')
|
||||||
|
var runtime = use('runtime')
|
||||||
|
var time_mod = use('time')
|
||||||
|
|
||||||
|
return {
|
||||||
|
// ========================================================================
|
||||||
|
// MODULE RESOLUTION
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_use_core_module: function() {
|
||||||
|
if (!is_object(json)) return "use('json') should return an object"
|
||||||
|
if (!is_function(json.encode)) return "json should have encode function"
|
||||||
|
if (!is_function(json.decode)) return "json should have decode function"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_returns_cached: function() {
|
||||||
|
var json2 = use('json')
|
||||||
|
if (json != json2) return "two use('json') calls should return same reference"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_time_module: function() {
|
||||||
|
if (!is_object(time_mod)) return "use('time') should return an object"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_nonexistent_disrupts: function() {
|
||||||
|
var caught = false
|
||||||
|
var _fn = function() {
|
||||||
|
var x = use('nonexistent_xyz_99')
|
||||||
|
} disruption {
|
||||||
|
caught = true
|
||||||
|
}
|
||||||
|
_fn()
|
||||||
|
if (!caught) return "use('nonexistent_xyz_99') should disrupt"
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// RUNTIME PATHS
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_runtime_shop_path: function() {
|
||||||
|
if (is_null(runtime.shop_path)) return "runtime.shop_path should not be null"
|
||||||
|
if (!is_text(runtime.shop_path)) return "runtime.shop_path should be text"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_runtime_core_path: function() {
|
||||||
|
if (is_null(runtime.core_path)) return "runtime.core_path should not be null"
|
||||||
|
if (!is_text(runtime.core_path)) return "runtime.core_path should be text"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_runtime_is_frozen: function() {
|
||||||
|
if (!is_stone(runtime)) return "runtime should be frozen (stoned)"
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// MODULE CACHING BEHAVIOR
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
test_json_encode_decode_roundtrip: function() {
|
||||||
|
var obj = {a: 1, b: "hello", c: [1, 2, 3]}
|
||||||
|
var encoded = json.encode(obj)
|
||||||
|
var decoded = json.decode(encoded)
|
||||||
|
if (decoded.a != 1) return "roundtrip failed for number"
|
||||||
|
if (decoded.b != "hello") return "roundtrip failed for text"
|
||||||
|
if (length(decoded.c) != 3) return "roundtrip failed for array"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_blob_module: function() {
|
||||||
|
var b = use('blob')
|
||||||
|
if (!is_object(b)) return "use('blob') should return an object"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_math_module: function() {
|
||||||
|
var m = use('math')
|
||||||
|
if (!is_object(m)) return "use('math') should return an object"
|
||||||
|
},
|
||||||
|
|
||||||
|
test_use_random_module: function() {
|
||||||
|
var r = use('random')
|
||||||
|
if (!is_object(r)) return "use('random') should return an object"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
time.cm
2
time.cm
@@ -1,5 +1,5 @@
|
|||||||
// epoch = 0000-01-01 00:00:00 +0000
|
// epoch = 0000-01-01 00:00:00 +0000
|
||||||
var time = use('internal/time_c')
|
var time = use('internal/time')
|
||||||
|
|
||||||
var now = time.now
|
var now = time.now
|
||||||
var computer_zone = time.computer_zone
|
var computer_zone = time.computer_zone
|
||||||
|
|||||||
11
unlink.ce
11
unlink.ce
@@ -8,24 +8,25 @@ if (length(args) < 1) {
|
|||||||
log.console("Usage: cell unlink <origin>")
|
log.console("Usage: cell unlink <origin>")
|
||||||
log.console("Removes a link and restores the original package.")
|
log.console("Removes a link and restores the original package.")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var origin = args[0]
|
var origin = args[0]
|
||||||
|
|
||||||
|
var _restore = null
|
||||||
if (link.remove(origin)) {
|
if (link.remove(origin)) {
|
||||||
log.console("Removed link for " + origin)
|
log.console("Removed link for " + origin)
|
||||||
|
|
||||||
// Try to restore the original package
|
// Try to restore the original package
|
||||||
log.console("Restoring " + origin + "...")
|
log.console("Restoring " + origin + "...")
|
||||||
try {
|
_restore = function() {
|
||||||
shop.fetch(origin)
|
shop.fetch(origin)
|
||||||
shop.extract(origin)
|
shop.extract(origin)
|
||||||
log.console("Restored " + origin)
|
log.console("Restored " + origin)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.console("Could not restore: " + e.message)
|
log.console("Could not restore")
|
||||||
log.console("Run 'cell update " + origin + "' to restore")
|
log.console("Run 'cell update " + origin + "' to restore")
|
||||||
}
|
}
|
||||||
|
_restore()
|
||||||
} else {
|
} else {
|
||||||
log.console("No link found for " + origin)
|
log.console("No link found for " + origin)
|
||||||
}
|
}
|
||||||
|
|||||||
43
update.ce
43
update.ce
@@ -18,9 +18,11 @@ var target_pkg = null
|
|||||||
var run_build = false
|
var run_build = false
|
||||||
var target_triple = null
|
var target_triple = null
|
||||||
var follow_links = false
|
var follow_links = false
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--help' || args[i] == '-h') {
|
if (args[i] == '--help' || args[i] == '-h') {
|
||||||
log.console("Usage: cell update [<locator>] [options]")
|
log.console("Usage: cell update [<locator>] [options]")
|
||||||
log.console("")
|
log.console("")
|
||||||
@@ -46,7 +48,7 @@ for (var i = 0; i < length(args); i++) {
|
|||||||
target_pkg = args[i]
|
target_pkg = args[i]
|
||||||
// Resolve relative paths to absolute paths
|
// Resolve relative paths to absolute paths
|
||||||
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
if (target_pkg == '.' || starts_with(target_pkg, './') || starts_with(target_pkg, '../') || fd.is_dir(target_pkg)) {
|
||||||
var resolved = fd.realpath(target_pkg)
|
resolved = fd.realpath(target_pkg)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
target_pkg = resolved
|
target_pkg = resolved
|
||||||
}
|
}
|
||||||
@@ -61,27 +63,29 @@ if (run_build && !target_triple) {
|
|||||||
|
|
||||||
var link = use('link')
|
var link = use('link')
|
||||||
|
|
||||||
function update_and_fetch(pkg)
|
function update_and_fetch(pkg) {
|
||||||
{
|
|
||||||
var lock = shop.load_lock()
|
var lock = shop.load_lock()
|
||||||
var old_entry = lock[pkg]
|
var old_entry = lock[pkg]
|
||||||
var old_commit = old_entry ? old_entry.commit : null
|
var old_commit = old_entry ? old_entry.commit : null
|
||||||
|
var effective_pkg = pkg
|
||||||
|
var link_target = null
|
||||||
|
var new_entry = null
|
||||||
|
var old_str = null
|
||||||
|
|
||||||
// Handle follow-links option
|
// Handle follow-links option
|
||||||
var effective_pkg = pkg
|
|
||||||
if (follow_links) {
|
if (follow_links) {
|
||||||
var link_target = link.get_target(pkg)
|
link_target = link.get_target(pkg)
|
||||||
if (link_target) {
|
if (link_target) {
|
||||||
effective_pkg = link_target
|
effective_pkg = link_target
|
||||||
log.console(" Following link: " + pkg + " -> " + effective_pkg)
|
log.console(" Following link: " + pkg + " -> " + effective_pkg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var new_entry = shop.update(effective_pkg)
|
new_entry = shop.update(effective_pkg)
|
||||||
|
|
||||||
if (new_entry) {
|
if (new_entry) {
|
||||||
if (new_entry.commit) {
|
if (new_entry.commit) {
|
||||||
var old_str = old_commit ? text(old_commit, 0, 8) : "(new)"
|
old_str = old_commit ? text(old_commit, 0, 8) : "(new)"
|
||||||
log.console(" " + effective_pkg + " " + old_str + " -> " + text(new_entry.commit, 0, 8))
|
log.console(" " + effective_pkg + " " + old_str + " -> " + text(new_entry.commit, 0, 8))
|
||||||
shop.fetch(effective_pkg)
|
shop.fetch(effective_pkg)
|
||||||
} else {
|
} else {
|
||||||
@@ -97,8 +101,12 @@ function update_and_fetch(pkg)
|
|||||||
|
|
||||||
var updated_packages = []
|
var updated_packages = []
|
||||||
|
|
||||||
|
var updated = null
|
||||||
|
var packages = null
|
||||||
|
var pkg_count = 0
|
||||||
|
var pkg = null
|
||||||
if (target_pkg) {
|
if (target_pkg) {
|
||||||
var updated = update_and_fetch(target_pkg)
|
updated = update_and_fetch(target_pkg)
|
||||||
if (updated) {
|
if (updated) {
|
||||||
push(updated_packages, updated)
|
push(updated_packages, updated)
|
||||||
log.console("Updated " + target_pkg + ".")
|
log.console("Updated " + target_pkg + ".")
|
||||||
@@ -106,15 +114,15 @@ if (target_pkg) {
|
|||||||
log.console(target_pkg + " is up to date.")
|
log.console(target_pkg + " is up to date.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var packages = shop.list_packages()
|
packages = shop.list_packages()
|
||||||
var pkg_count = length(packages)
|
pkg_count = length(packages)
|
||||||
log.console("Checking for updates (" + text(pkg_count) + " package" + (pkg_count == 1 ? "" : "s") + ")...")
|
log.console("Checking for updates (" + text(pkg_count) + " package" + (pkg_count == 1 ? "" : "s") + ")...")
|
||||||
|
|
||||||
for (var i = 0; i < length(packages); i++) {
|
for (i = 0; i < length(packages); i++) {
|
||||||
var pkg = packages[i]
|
pkg = packages[i]
|
||||||
if (pkg == 'core') continue
|
if (pkg == 'core') continue
|
||||||
|
|
||||||
var updated = update_and_fetch(pkg)
|
updated = update_and_fetch(pkg)
|
||||||
if (updated) {
|
if (updated) {
|
||||||
push(updated_packages, updated)
|
push(updated_packages, updated)
|
||||||
}
|
}
|
||||||
@@ -133,13 +141,14 @@ if (run_build && length(updated_packages) > 0) {
|
|||||||
log.console("Building updated packages...")
|
log.console("Building updated packages...")
|
||||||
|
|
||||||
arrfor(updated_packages, function(pkg) {
|
arrfor(updated_packages, function(pkg) {
|
||||||
try {
|
var _build = function() {
|
||||||
var lib = build.build_dynamic(pkg, target_triple, 'release')
|
var lib = build.build_dynamic(pkg, target_triple, 'release')
|
||||||
if (lib)
|
if (lib)
|
||||||
log.console(" Built: " + lib)
|
log.console(" Built: " + lib)
|
||||||
} catch (e) {
|
} disruption {
|
||||||
log.error(" Failed to build " + pkg + ": " + e)
|
log.error(" Failed to build " + pkg)
|
||||||
}
|
}
|
||||||
|
_build()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
upgrade.ce
10
upgrade.ce
@@ -2,13 +2,15 @@ var shop = use('internal/shop')
|
|||||||
var fd = use('fd')
|
var fd = use('fd')
|
||||||
|
|
||||||
var cmd = length(args) > 0 ? args[0] : null
|
var cmd = length(args) > 0 ? args[0] : null
|
||||||
|
var target = null
|
||||||
|
var core_dir = null
|
||||||
|
|
||||||
if (cmd == 'link') {
|
if (cmd == 'link') {
|
||||||
if (length(args) < 2) {
|
if (length(args) < 2) {
|
||||||
log.console("Usage: cell upgrade link <core_dir>")
|
log.console("Usage: cell upgrade link <core_dir>")
|
||||||
return
|
$stop()
|
||||||
}
|
}
|
||||||
var target = args[1]
|
target = args[1]
|
||||||
if (shop.link_core(target)) {
|
if (shop.link_core(target)) {
|
||||||
log.console("Linked core -> " + fd.realpath(target))
|
log.console("Linked core -> " + fd.realpath(target))
|
||||||
} else {
|
} else {
|
||||||
@@ -25,7 +27,7 @@ if (cmd == 'link') {
|
|||||||
} else {
|
} else {
|
||||||
// cell upgrade (no args)
|
// cell upgrade (no args)
|
||||||
if (shop.is_core_linked()) {
|
if (shop.is_core_linked()) {
|
||||||
var core_dir = shop.get_core_dir()
|
core_dir = shop.get_core_dir()
|
||||||
log.console("Core is linked to " + fd.readlink(core_dir))
|
log.console("Core is linked to " + fd.readlink(core_dir))
|
||||||
log.console("Unlink first to upgrade standard core.")
|
log.console("Unlink first to upgrade standard core.")
|
||||||
} else {
|
} else {
|
||||||
@@ -34,4 +36,4 @@ if (cmd == 'link') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$stop()
|
$stop()
|
||||||
|
|||||||
67
verify.ce
67
verify.ce
@@ -20,8 +20,10 @@ var fd = use('fd')
|
|||||||
var scope = null
|
var scope = null
|
||||||
var deep = false
|
var deep = false
|
||||||
var target_triple = null
|
var target_triple = null
|
||||||
|
var i = 0
|
||||||
|
var resolved = null
|
||||||
|
|
||||||
for (var i = 0; i < length(args); i++) {
|
for (i = 0; i < length(args); i++) {
|
||||||
if (args[i] == '--deep') {
|
if (args[i] == '--deep') {
|
||||||
deep = true
|
deep = true
|
||||||
} else if (args[i] == '--target' || args[i] == '-t') {
|
} else if (args[i] == '--target' || args[i] == '-t') {
|
||||||
@@ -74,12 +76,27 @@ function add_warning(msg) {
|
|||||||
|
|
||||||
// Verify a single package
|
// Verify a single package
|
||||||
function verify_package(locator) {
|
function verify_package(locator) {
|
||||||
|
var lock = null
|
||||||
|
var lock_entry = null
|
||||||
|
var links = null
|
||||||
|
var link_target = null
|
||||||
|
var pkg_dir = null
|
||||||
|
var dir_exists = false
|
||||||
|
var current_target = null
|
||||||
|
var expected_target = null
|
||||||
|
var target_dir = null
|
||||||
|
var lib_dir = null
|
||||||
|
var lib_name = null
|
||||||
|
var dylib_ext = null
|
||||||
|
var lib_path = null
|
||||||
|
var c_files = null
|
||||||
|
|
||||||
checked++
|
checked++
|
||||||
|
|
||||||
var lock = shop.load_lock()
|
lock = shop.load_lock()
|
||||||
var lock_entry = lock[locator]
|
lock_entry = lock[locator]
|
||||||
var links = link.load()
|
links = link.load()
|
||||||
var link_target = links[locator]
|
link_target = links[locator]
|
||||||
|
|
||||||
// Check lock entry exists
|
// Check lock entry exists
|
||||||
if (!lock_entry) {
|
if (!lock_entry) {
|
||||||
@@ -87,8 +104,8 @@ function verify_package(locator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check package directory exists
|
// Check package directory exists
|
||||||
var pkg_dir = shop.get_package_dir(locator)
|
pkg_dir = shop.get_package_dir(locator)
|
||||||
var dir_exists = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)
|
dir_exists = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)
|
||||||
|
|
||||||
if (!dir_exists) {
|
if (!dir_exists) {
|
||||||
add_error(locator + ": package directory missing at " + pkg_dir)
|
add_error(locator + ": package directory missing at " + pkg_dir)
|
||||||
@@ -112,7 +129,7 @@ function verify_package(locator) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Package target
|
// Package target
|
||||||
var target_dir = shop.get_package_dir(link_target)
|
target_dir = shop.get_package_dir(link_target)
|
||||||
if (!fd.is_dir(target_dir) && !fd.is_link(target_dir)) {
|
if (!fd.is_dir(target_dir) && !fd.is_link(target_dir)) {
|
||||||
add_error(locator + ": link target package not found: " + link_target)
|
add_error(locator + ": link target package not found: " + link_target)
|
||||||
}
|
}
|
||||||
@@ -120,8 +137,8 @@ function verify_package(locator) {
|
|||||||
|
|
||||||
// Check symlink is correct
|
// Check symlink is correct
|
||||||
if (fd.is_link(pkg_dir)) {
|
if (fd.is_link(pkg_dir)) {
|
||||||
var current_target = fd.readlink(pkg_dir)
|
current_target = fd.readlink(pkg_dir)
|
||||||
var expected_target = starts_with(link_target, '/') ? link_target : shop.get_package_dir(link_target)
|
expected_target = starts_with(link_target, '/') ? link_target : shop.get_package_dir(link_target)
|
||||||
if (current_target != expected_target) {
|
if (current_target != expected_target) {
|
||||||
add_warning(locator + ": symlink target mismatch (expected " + expected_target + ", got " + current_target + ")")
|
add_warning(locator + ": symlink target mismatch (expected " + expected_target + ", got " + current_target + ")")
|
||||||
}
|
}
|
||||||
@@ -131,22 +148,23 @@ function verify_package(locator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check build output exists
|
// Check build output exists
|
||||||
var lib_dir = shop.get_lib_dir()
|
lib_dir = shop.get_lib_dir()
|
||||||
var lib_name = shop.lib_name_for_package(locator)
|
lib_name = shop.lib_name_for_package(locator)
|
||||||
var dylib_ext = '.dylib' // TODO: detect from target
|
dylib_ext = '.dylib' // TODO: detect from target
|
||||||
var lib_path = lib_dir + '/' + lib_name + dylib_ext
|
lib_path = lib_dir + '/' + lib_name + dylib_ext
|
||||||
|
|
||||||
// Only check for builds if package has C files
|
// Only check for builds if package has C files
|
||||||
try {
|
var _check_build = function() {
|
||||||
var c_files = pkg.get_c_files(locator, target_triple, true)
|
c_files = pkg.get_c_files(locator, target_triple, true)
|
||||||
if (c_files && length(c_files) > 0) {
|
if (c_files && length(c_files) > 0) {
|
||||||
if (!fd.is_file(lib_path)) {
|
if (!fd.is_file(lib_path)) {
|
||||||
add_warning(locator + ": library not built at " + lib_path)
|
add_warning(locator + ": library not built at " + lib_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} disruption {
|
||||||
// Skip build check if can't determine C files
|
// Skip build check if can't determine C files
|
||||||
}
|
}
|
||||||
|
_check_build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for link cycles
|
// Check for link cycles
|
||||||
@@ -154,19 +172,20 @@ function check_link_cycles() {
|
|||||||
var links = link.load()
|
var links = link.load()
|
||||||
|
|
||||||
function follow_chain(origin, visited) {
|
function follow_chain(origin, visited) {
|
||||||
|
var target = null
|
||||||
if (visited[origin]) {
|
if (visited[origin]) {
|
||||||
return origin // cycle detected
|
return origin // cycle detected
|
||||||
}
|
}
|
||||||
visited[origin] = true
|
visited[origin] = true
|
||||||
|
|
||||||
var target = links[origin]
|
target = links[origin]
|
||||||
if (target && links[target]) {
|
if (target && links[target]) {
|
||||||
return follow_chain(target, visited)
|
return follow_chain(target, visited)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfor(links, function(origin) {
|
arrfor(array(links), function(origin) {
|
||||||
var cycle_start = follow_chain(origin, {})
|
var cycle_start = follow_chain(origin, {})
|
||||||
if (cycle_start) {
|
if (cycle_start) {
|
||||||
add_error("Link cycle detected starting from: " + origin)
|
add_error("Link cycle detected starting from: " + origin)
|
||||||
@@ -190,20 +209,21 @@ function check_dangling_links() {
|
|||||||
|
|
||||||
// Gather packages to verify
|
// Gather packages to verify
|
||||||
var packages_to_verify = []
|
var packages_to_verify = []
|
||||||
|
var locator = null
|
||||||
|
var all_deps = null
|
||||||
|
|
||||||
if (scope == 'shop') {
|
if (scope == 'shop') {
|
||||||
packages_to_verify = shop.list_packages()
|
packages_to_verify = shop.list_packages()
|
||||||
} else if (scope == 'world') {
|
} else if (scope == 'world') {
|
||||||
// For now, world is the same as shop
|
// For now, world is the same as shop
|
||||||
// In future, this could be a separate concept
|
|
||||||
packages_to_verify = shop.list_packages()
|
packages_to_verify = shop.list_packages()
|
||||||
} else {
|
} else {
|
||||||
// Single package
|
// Single package
|
||||||
var locator = scope
|
locator = scope
|
||||||
|
|
||||||
// Resolve local paths
|
// Resolve local paths
|
||||||
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
||||||
var resolved = fd.realpath(locator)
|
resolved = fd.realpath(locator)
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
locator = resolved
|
locator = resolved
|
||||||
}
|
}
|
||||||
@@ -211,7 +231,7 @@ if (scope == 'shop') {
|
|||||||
|
|
||||||
if (deep) {
|
if (deep) {
|
||||||
// Gather all dependencies
|
// Gather all dependencies
|
||||||
var all_deps = pkg.gather_dependencies(locator)
|
all_deps = pkg.gather_dependencies(locator)
|
||||||
push(packages_to_verify, locator)
|
push(packages_to_verify, locator)
|
||||||
arrfor(all_deps, function(dep) {
|
arrfor(all_deps, function(dep) {
|
||||||
push(packages_to_verify, dep)
|
push(packages_to_verify, dep)
|
||||||
@@ -249,7 +269,6 @@ if (length(errors) > 0) {
|
|||||||
})
|
})
|
||||||
log.console("")
|
log.console("")
|
||||||
log.console("Verification FAILED: " + text(length(errors)) + " error(s), " + text(length(warnings)) + " warning(s)")
|
log.console("Verification FAILED: " + text(length(errors)) + " error(s), " + text(length(warnings)) + " warning(s)")
|
||||||
// Note: would use process.exit(1) if available
|
|
||||||
} else {
|
} else {
|
||||||
log.console("Verification PASSED: " + text(checked) + " package(s) checked, " + text(length(warnings)) + " warning(s)")
|
log.console("Verification PASSED: " + text(checked) + " package(s) checked, " + text(length(warnings)) + " warning(s)")
|
||||||
}
|
}
|
||||||
|
|||||||
76
vm_suite.ce
76
vm_suite.ce
@@ -4935,6 +4935,82 @@ run("object literal 300 function values returned from function", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// NESTED FUNCTION DECLARATIONS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
run("nested named function basic", function() {
|
||||||
|
function inner(x) {
|
||||||
|
return x + 1
|
||||||
|
}
|
||||||
|
assert_eq(inner(41), 42, "nested named function call")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("nested named function used by sibling", function() {
|
||||||
|
function helper(v) {
|
||||||
|
return v * 2
|
||||||
|
}
|
||||||
|
function caller(v) {
|
||||||
|
return helper(v) + 1
|
||||||
|
}
|
||||||
|
assert_eq(caller(5), 11, "sibling nested function call")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("nested named function in var function", function() {
|
||||||
|
var outer = function() {
|
||||||
|
function inner(a, b) {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
return inner(10, 20)
|
||||||
|
}
|
||||||
|
assert_eq(outer(), 30, "nested function inside var function")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("nested named function with closure", function() {
|
||||||
|
var multiplier = 3
|
||||||
|
function scale(x) {
|
||||||
|
return x * multiplier
|
||||||
|
}
|
||||||
|
assert_eq(scale(7), 21, "nested function closing over outer var")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("nested named function recursive", function() {
|
||||||
|
function factorial(n) {
|
||||||
|
if (n <= 1) return 1
|
||||||
|
return n * factorial(n - 1)
|
||||||
|
}
|
||||||
|
assert_eq(factorial(5), 120, "nested recursive function")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("deeply nested named functions", function() {
|
||||||
|
var outer = function() {
|
||||||
|
function mid(x) {
|
||||||
|
function inner(y) {
|
||||||
|
return y + 1
|
||||||
|
}
|
||||||
|
return inner(x) * 2
|
||||||
|
}
|
||||||
|
return mid(4)
|
||||||
|
}
|
||||||
|
assert_eq(outer(), 10, "deeply nested named functions")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("nested function used after definition", function() {
|
||||||
|
var result = []
|
||||||
|
function encode_value(v) {
|
||||||
|
if (is_text(v)) return '"' + v + '"'
|
||||||
|
if (is_number(v)) return text(v)
|
||||||
|
return "null"
|
||||||
|
}
|
||||||
|
function quote_key(k) {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
result[] = quote_key("a") + " = " + encode_value(1)
|
||||||
|
result[] = quote_key("b") + " = " + encode_value("hi")
|
||||||
|
assert_eq(result[0], "a = 1", "nested fn encode number")
|
||||||
|
assert_eq(result[1], 'b = "hi"', "nested fn encode text")
|
||||||
|
})
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// SUMMARY
|
// SUMMARY
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
5
website/content/capi/_index.md
Normal file
5
website/content/capi/_index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: "C API"
|
||||||
|
description: "Extending ƿit with native C code"
|
||||||
|
type: "capi"
|
||||||
|
---
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Command Line Interface"
|
|
||||||
description: "The pit tool"
|
|
||||||
type: "standalone"
|
|
||||||
---
|
|
||||||
|
|
||||||
ƿit provides a command-line interface for managing packages, running scripts, and building applications.
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit <command> [arguments]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
### pit version
|
|
||||||
|
|
||||||
Display the ƿit version.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit version
|
|
||||||
# 0.1.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit install
|
|
||||||
|
|
||||||
Install a package to the shop.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit install gitea.pockle.world/john/prosperon
|
|
||||||
pit install /Users/john/local/mypackage # local path
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit update
|
|
||||||
|
|
||||||
Update packages from remote sources.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit update # update all packages
|
|
||||||
pit update <package> # update specific package
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit remove
|
|
||||||
|
|
||||||
Remove a package from the shop.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit remove gitea.pockle.world/john/oldpackage
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit list
|
|
||||||
|
|
||||||
List installed packages.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit list # list all installed packages
|
|
||||||
pit list <package> # list dependencies of a package
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit ls
|
|
||||||
|
|
||||||
List modules and actors in a package.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit ls # list files in current project
|
|
||||||
pit ls <package> # list files in specified package
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit build
|
|
||||||
|
|
||||||
Build the current package.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit build
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit test
|
|
||||||
|
|
||||||
Run tests.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit test # run tests in current package
|
|
||||||
pit test all # run all tests
|
|
||||||
pit test <package> # run tests in specific package
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit link
|
|
||||||
|
|
||||||
Manage local package links for development.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit link add <canonical> <local_path> # link a package
|
|
||||||
pit link list # show all links
|
|
||||||
pit link delete <canonical> # remove a link
|
|
||||||
pit link clear # remove all links
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit fetch
|
|
||||||
|
|
||||||
Fetch package sources without extracting.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit fetch <package>
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit upgrade
|
|
||||||
|
|
||||||
Upgrade the ƿit installation itself.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit upgrade
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit clean
|
|
||||||
|
|
||||||
Clean build artifacts.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit clean
|
|
||||||
```
|
|
||||||
|
|
||||||
### pit help
|
|
||||||
|
|
||||||
Display help information.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit help
|
|
||||||
pit help <command>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Scripts
|
|
||||||
|
|
||||||
Any `.ce` file in the ƿit core can be run as a command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit version # runs version.ce
|
|
||||||
pit build # runs build.ce
|
|
||||||
pit test # runs test.ce
|
|
||||||
```
|
|
||||||
|
|
||||||
## Package Locators
|
|
||||||
|
|
||||||
Packages are identified by locators:
|
|
||||||
|
|
||||||
- **Remote**: `gitea.pockle.world/user/repo`
|
|
||||||
- **Local**: `/absolute/path/to/package`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pit install gitea.pockle.world/john/prosperon
|
|
||||||
pit install /Users/john/work/mylib
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
ƿit stores its data in `~/.pit/`:
|
|
||||||
|
|
||||||
```
|
|
||||||
~/.pit/
|
|
||||||
├── packages/ # installed packages
|
|
||||||
├── lib/ # compiled dynamic libraries
|
|
||||||
├── build/ # build cache
|
|
||||||
├── cache/ # downloaded archives
|
|
||||||
├── lock.toml # installed package versions
|
|
||||||
└── link.toml # local development links
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
ƿit reads the `HOME` environment variable to locate the shop directory.
|
|
||||||
|
|
||||||
## Exit Codes
|
|
||||||
|
|
||||||
- `0` — Success
|
|
||||||
- Non-zero — Error (check output for details)
|
|
||||||
5
website/content/tools/_index.md
Normal file
5
website/content/tools/_index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: "Packages"
|
||||||
|
description: "Package management, shop architecture, and the CLI"
|
||||||
|
type: "tools"
|
||||||
|
---
|
||||||
5
website/data/capi_sections.yaml
Normal file
5
website/data/capi_sections.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
sections:
|
||||||
|
- title: "Writing C Modules"
|
||||||
|
page: "/docs/c-modules/"
|
||||||
|
id: "c-modules"
|
||||||
|
group: "C API"
|
||||||
@@ -7,10 +7,6 @@ sections:
|
|||||||
url: "/docs/actors/"
|
url: "/docs/actors/"
|
||||||
- title: "Requestors"
|
- title: "Requestors"
|
||||||
url: "/docs/requestors/"
|
url: "/docs/requestors/"
|
||||||
- title: "Packages"
|
|
||||||
url: "/docs/packages/"
|
|
||||||
- title: "Shop Architecture"
|
|
||||||
url: "/docs/shop/"
|
|
||||||
- title: "Reference"
|
- title: "Reference"
|
||||||
pages:
|
pages:
|
||||||
- title: "Built-in Functions"
|
- title: "Built-in Functions"
|
||||||
@@ -37,9 +33,15 @@ sections:
|
|||||||
url: "/docs/library/json/"
|
url: "/docs/library/json/"
|
||||||
- title: "random"
|
- title: "random"
|
||||||
url: "/docs/library/random/"
|
url: "/docs/library/random/"
|
||||||
- title: "Tools"
|
- title: "Packages"
|
||||||
pages:
|
pages:
|
||||||
|
- title: "Packages"
|
||||||
|
url: "/docs/packages/"
|
||||||
|
- title: "Shop Architecture"
|
||||||
|
url: "/docs/shop/"
|
||||||
- title: "CLI"
|
- title: "CLI"
|
||||||
url: "/docs/cli/"
|
url: "/docs/cli/"
|
||||||
|
- title: "C API"
|
||||||
|
pages:
|
||||||
- title: "C Modules"
|
- title: "C Modules"
|
||||||
url: "/docs/c-modules/"
|
url: "/docs/c-modules/"
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ sections:
|
|||||||
page: "/docs/requestors/"
|
page: "/docs/requestors/"
|
||||||
id: "requestors"
|
id: "requestors"
|
||||||
group: "Language"
|
group: "Language"
|
||||||
- title: "Packages"
|
|
||||||
page: "/docs/packages/"
|
|
||||||
id: "packages"
|
|
||||||
group: "Language"
|
|
||||||
- title: "Built-in Functions"
|
- title: "Built-in Functions"
|
||||||
page: "/docs/functions/"
|
page: "/docs/functions/"
|
||||||
id: "functions"
|
id: "functions"
|
||||||
@@ -59,7 +55,3 @@ sections:
|
|||||||
page: "/docs/library/random/"
|
page: "/docs/library/random/"
|
||||||
id: "library-random"
|
id: "library-random"
|
||||||
group: "Standard Library"
|
group: "Standard Library"
|
||||||
- title: "Writing C Modules"
|
|
||||||
page: "/docs/c-modules/"
|
|
||||||
id: "c-modules"
|
|
||||||
group: "Tools"
|
|
||||||
|
|||||||
17
website/data/tools_sections.yaml
Normal file
17
website/data/tools_sections.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
sections:
|
||||||
|
- title: "Packages"
|
||||||
|
page: "/docs/packages/"
|
||||||
|
id: "packages"
|
||||||
|
group: "Packages"
|
||||||
|
- title: "Shop Architecture"
|
||||||
|
page: "/docs/shop/"
|
||||||
|
id: "shop"
|
||||||
|
group: "Packages"
|
||||||
|
- title: "Command Line Interface"
|
||||||
|
page: "/docs/cli/"
|
||||||
|
id: "cli"
|
||||||
|
group: "CLI"
|
||||||
|
- title: "Testing"
|
||||||
|
page: "/docs/testing/"
|
||||||
|
id: "testing"
|
||||||
|
group: "CLI"
|
||||||
@@ -21,17 +21,21 @@ theme = 'knr'
|
|||||||
pageRef = '/manual/'
|
pageRef = '/manual/'
|
||||||
weight = 20
|
weight = 20
|
||||||
[[menus.main]]
|
[[menus.main]]
|
||||||
name = 'Spec'
|
name = 'Packages'
|
||||||
pageRef = '/spec/'
|
pageRef = '/tools/'
|
||||||
weight = 30
|
weight = 30
|
||||||
[[menus.main]]
|
[[menus.main]]
|
||||||
name = 'CLI'
|
name = 'C API'
|
||||||
pageRef = '/cli/'
|
pageRef = '/capi/'
|
||||||
weight = 40
|
weight = 40
|
||||||
|
[[menus.main]]
|
||||||
|
name = 'Spec'
|
||||||
|
pageRef = '/spec/'
|
||||||
|
weight = 50
|
||||||
[[menus.main]]
|
[[menus.main]]
|
||||||
name = 'Contributing'
|
name = 'Contributing'
|
||||||
pageRef = '/contributing/'
|
pageRef = '/contributing/'
|
||||||
weight = 50
|
weight = 60
|
||||||
|
|
||||||
[module]
|
[module]
|
||||||
[[module.mounts]]
|
[[module.mounts]]
|
||||||
|
|||||||
19
website/themes/knr/layouts/capi/list.html
Normal file
19
website/themes/knr/layouts/capi/list.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<div class="longform-layout">
|
||||||
|
<nav class="toc-nav" id="toc-nav">
|
||||||
|
<h3>Contents</h3>
|
||||||
|
<ul id="toc-list"></ul>
|
||||||
|
</nav>
|
||||||
|
<article class="longform-content" id="longform-content">
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{ range .Site.Data.capi_sections.sections }}
|
||||||
|
<section id="{{ .id }}" data-toc-title="{{ .title }}" data-toc-group="{{ .group }}">
|
||||||
|
{{ with $.Site.GetPage .page }}
|
||||||
|
{{ .Content }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<script src="/js/toc.js"></script>
|
||||||
|
{{ end }}
|
||||||
19
website/themes/knr/layouts/tools/list.html
Normal file
19
website/themes/knr/layouts/tools/list.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<div class="longform-layout">
|
||||||
|
<nav class="toc-nav" id="toc-nav">
|
||||||
|
<h3>Contents</h3>
|
||||||
|
<ul id="toc-list"></ul>
|
||||||
|
</nav>
|
||||||
|
<article class="longform-content" id="longform-content">
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
{{ range .Site.Data.tools_sections.sections }}
|
||||||
|
<section id="{{ .id }}" data-toc-title="{{ .title }}" data-toc-group="{{ .group }}">
|
||||||
|
{{ with $.Site.GetPage .page }}
|
||||||
|
{{ .Content }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<script src="/js/toc.js"></script>
|
||||||
|
{{ end }}
|
||||||
85
why.ce
85
why.ce
@@ -4,7 +4,6 @@ var pkg = use('package')
|
|||||||
if (!args || length(args) < 1) {
|
if (!args || length(args) < 1) {
|
||||||
log.console("Usage: cell why <package>")
|
log.console("Usage: cell why <package>")
|
||||||
$stop()
|
$stop()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = args[0]
|
var target = args[0]
|
||||||
@@ -19,56 +18,65 @@ var found = false
|
|||||||
// current_pkg: canonical path of current package (null for root)
|
// current_pkg: canonical path of current package (null for root)
|
||||||
// stack: array of {alias, pkg} leading to current_pkg
|
// stack: array of {alias, pkg} leading to current_pkg
|
||||||
|
|
||||||
function search(current_pkg, stack) {
|
function search_deps(current_pkg, stack) {
|
||||||
var deps = pkg.dependencies(current_pkg)
|
var deps = pkg.dependencies(current_pkg)
|
||||||
|
|
||||||
// Sort for consistent output
|
|
||||||
var aliases = sort(array(deps))
|
var aliases = sort(array(deps))
|
||||||
|
var i = 0
|
||||||
for (var i = 0; i < length(aliases); i++) {
|
var alias = null
|
||||||
var alias = aliases[i]
|
var locator = null
|
||||||
var locator = deps[alias]
|
var parsed = null
|
||||||
var parsed = shop.parse_package(locator)
|
var canon = null
|
||||||
|
var locator_clean = null
|
||||||
|
var match = false
|
||||||
|
var node = null
|
||||||
|
var new_stack = null
|
||||||
|
var cycle = false
|
||||||
|
var j = 0
|
||||||
|
|
||||||
|
for (i = 0; i < length(aliases); i++) {
|
||||||
|
alias = aliases[i]
|
||||||
|
locator = deps[alias]
|
||||||
|
parsed = shop.parse_package(locator)
|
||||||
if (!parsed) continue
|
if (!parsed) continue
|
||||||
|
|
||||||
var canon = parsed.path
|
canon = parsed.path
|
||||||
|
|
||||||
var locator_clean = locator
|
locator_clean = locator
|
||||||
if (search(locator, '@') != null) locator_clean = array(locator, '@')[0]
|
if (search(locator, '@') != null) locator_clean = array(locator, '@')[0]
|
||||||
|
|
||||||
// Check if match
|
// Check if match
|
||||||
// 1. Alias matches
|
// 1. Alias matches
|
||||||
// 2. Package name matches
|
// 2. Package name matches
|
||||||
// 3. Canonical path matches (exact or clean)
|
// 3. Canonical path matches (exact or clean)
|
||||||
// 4. Locator matches (exact or clean)
|
// 4. Locator matches (exact or clean)
|
||||||
var match = (alias == target) ||
|
match = (alias == target) ||
|
||||||
(parsed.name == target) ||
|
(parsed.name == target) ||
|
||||||
(canon == target) ||
|
(canon == target) ||
|
||||||
(canon == target_clean) ||
|
(canon == target_clean) ||
|
||||||
(locator == target) ||
|
(locator == target) ||
|
||||||
(locator_clean == target)
|
(locator_clean == target)
|
||||||
|
|
||||||
var node = { alias: alias, pkg: canon, locator: locator }
|
node = { alias: alias, pkg: canon, locator: locator }
|
||||||
var new_stack = stack.concat([node])
|
new_stack = stack.concat([node])
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
found = true
|
found = true
|
||||||
print_stack(new_stack)
|
print_stack(new_stack)
|
||||||
// Don't recurse if we found the target in this branch
|
// Don't recurse if we found the target in this branch
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse if not seen in current stack (cycle detection)
|
// Recurse if not seen in current stack (cycle detection)
|
||||||
var cycle = false
|
cycle = false
|
||||||
for (var j = 0; j < length(stack); j++) {
|
for (j = 0; j < length(stack); j++) {
|
||||||
if (stack[j].pkg == canon) {
|
if (stack[j].pkg == canon) {
|
||||||
cycle = true
|
cycle = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cycle) {
|
if (!cycle) {
|
||||||
search(canon, new_stack)
|
search_deps(canon, new_stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,18 +84,23 @@ function search(current_pkg, stack) {
|
|||||||
function print_stack(stack) {
|
function print_stack(stack) {
|
||||||
// Calculate max width for alignment if needed, but simple tree is fine
|
// Calculate max width for alignment if needed, but simple tree is fine
|
||||||
var output = "project"
|
var output = "project"
|
||||||
|
var i = 0
|
||||||
|
var node = null
|
||||||
|
var indent = null
|
||||||
|
var j = 0
|
||||||
|
var info = null
|
||||||
log.console(output)
|
log.console(output)
|
||||||
|
|
||||||
for (var i = 0; i < length(stack); i++) {
|
for (i = 0; i < length(stack); i++) {
|
||||||
var node = stack[i]
|
node = stack[i]
|
||||||
var indent = ""
|
indent = ""
|
||||||
for (var j = 0; j <= i; j++) indent += " "
|
for (j = 0; j <= i; j++) indent += " "
|
||||||
|
|
||||||
var info = node.locator
|
info = node.locator
|
||||||
if (node.alias != parsed_name(node.locator)) {
|
if (node.alias != parsed_name(node.locator)) {
|
||||||
// info += " (aliased as " + node.alias + ")"
|
// info += " (aliased as " + node.alias + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.console(indent + "-> " + node.alias + " (" + info + ")")
|
log.console(indent + "-> " + node.alias + " (" + info + ")")
|
||||||
}
|
}
|
||||||
log.console("")
|
log.console("")
|
||||||
@@ -98,7 +111,7 @@ function parsed_name(locator) {
|
|||||||
return parsed ? parsed.name : ""
|
return parsed ? parsed.name : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
search(null, [])
|
search_deps(null, [])
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
log.console("Package '" + target + "' not found in dependency tree.")
|
log.console("Package '" + target + "' not found in dependency tree.")
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ static const JSCFunctionListEntry js_wildstar_funcs[] = {
|
|||||||
JS_PROP_INT32_DEF("WM_WILDSTAR", WM_WILDSTAR, 0),
|
JS_PROP_INT32_DEF("WM_WILDSTAR", WM_WILDSTAR, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_wildstar_use(JSContext *js) {
|
JSValue js_core_wildstar_use(JSContext *js) {
|
||||||
JS_FRAME(js);
|
JS_FRAME(js);
|
||||||
JS_ROOT(mod, JS_NewObject(js));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs));
|
||||||
|
|||||||
Reference in New Issue
Block a user