Merge branch 'fix_gc' into pitweb
This commit is contained in:
@@ -107,6 +107,7 @@ var v = a[] // pop: v is 3, a is [1, 2]
|
|||||||
- Core is the `core` package — its symbols follow the same `js_core_<name>_use` pattern as all other packages
|
- 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)
|
- Package directories should contain only source files (no `.mach`/`.mcode` alongside source)
|
||||||
- Build cache files in `build/` are bare hashes (no extensions)
|
- 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.
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
|
|
||||||
|
|||||||
@@ -381,19 +381,21 @@ static const JSCFunctionListEntry js_reader_funcs[] = {
|
|||||||
|
|
||||||
JSValue js_core_miniz_use(JSContext *js)
|
JSValue js_core_miniz_use(JSContext *js)
|
||||||
{
|
{
|
||||||
|
JS_FRAME(js);
|
||||||
|
|
||||||
JS_NewClassID(&js_reader_class_id);
|
JS_NewClassID(&js_reader_class_id);
|
||||||
JS_NewClass(js, js_reader_class_id, &js_reader_class);
|
JS_NewClass(js, js_reader_class_id, &js_reader_class);
|
||||||
JSValue reader_proto = JS_NewObject(js);
|
JS_ROOT(reader_proto, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, reader_proto, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
|
JS_SetPropertyFunctionList(js, reader_proto.val, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
|
||||||
JS_SetClassProto(js, js_reader_class_id, reader_proto);
|
JS_SetClassProto(js, js_reader_class_id, reader_proto.val);
|
||||||
|
|
||||||
JS_NewClassID(&js_writer_class_id);
|
JS_NewClassID(&js_writer_class_id);
|
||||||
JS_NewClass(js, js_writer_class_id, &js_writer_class);
|
JS_NewClass(js, js_writer_class_id, &js_writer_class);
|
||||||
JSValue writer_proto = JS_NewObject(js);
|
JS_ROOT(writer_proto, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, writer_proto, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
|
JS_SetPropertyFunctionList(js, writer_proto.val, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
|
||||||
JS_SetClassProto(js, js_writer_class_id, writer_proto);
|
JS_SetClassProto(js, js_writer_class_id, writer_proto.val);
|
||||||
|
|
||||||
JSValue export = JS_NewObject(js);
|
JS_ROOT(export, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
JS_SetPropertyFunctionList(js, export.val, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
||||||
return export;
|
JS_RETURN(export.val);
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
15188
boot/bootstrap.cm.mcode
15188
boot/bootstrap.cm.mcode
File diff suppressed because it is too large
Load Diff
48003
boot/engine.cm.mcode
48003
boot/engine.cm.mcode
File diff suppressed because it is too large
Load Diff
131573
boot/fold.cm.mcode
131573
boot/fold.cm.mcode
File diff suppressed because it is too large
Load Diff
166635
boot/mcode.cm.mcode
166635
boot/mcode.cm.mcode
File diff suppressed because it is too large
Load Diff
185766
boot/parse.cm.mcode
185766
boot/parse.cm.mcode
File diff suppressed because it is too large
Load Diff
14701
boot/qbe.cm.mcode
14701
boot/qbe.cm.mcode
File diff suppressed because it is too large
Load Diff
92578
boot/qbe_emit.cm.mcode
92578
boot/qbe_emit.cm.mcode
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
79913
boot/streamline.cm.mcode
79913
boot/streamline.cm.mcode
File diff suppressed because it is too large
Load Diff
67460
boot/tokenize.cm.mcode
67460
boot/tokenize.cm.mcode
File diff suppressed because it is too large
Load Diff
30637
boot/verify_ir.cm.mcode
30637
boot/verify_ir.cm.mcode
File diff suppressed because it is too large
Load Diff
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)
|
||||||
7
crypto.c
7
crypto.c
@@ -240,7 +240,8 @@ static const JSCFunctionListEntry js_crypto_funcs[] = {
|
|||||||
|
|
||||||
JSValue js_core_crypto_use(JSContext *js)
|
JSValue js_core_crypto_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JSValue obj = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, obj, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return obj;
|
JS_SetPropertyFunctionList(js, mod.val, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ static const JSCFunctionListEntry js_debug_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_debug_use(JSContext *js) {
|
JSValue js_core_debug_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js,mod,js_debug_funcs,countof(js_debug_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_debug_funcs, countof(js_debug_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ static const JSCFunctionListEntry js_js_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_js_use(JSContext *js) {
|
JSValue js_core_js_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_js_funcs, countof(js_js_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,34 @@ Source → Tokenize → Parse → Fold → Mcode → Streamline → Machine
|
|||||||
|
|
||||||
Mcode is produced by `mcode.cm`, optimized by `streamline.cm`, then either serialized to 32-bit bytecode for the Mach VM (`mach.c`), or lowered to QBE/LLVM IL for native compilation (`qbe_emit.cm`). See [Compilation Pipeline](pipeline.md) for the full overview.
|
Mcode is produced by `mcode.cm`, optimized by `streamline.cm`, then either serialized to 32-bit bytecode for the Mach VM (`mach.c`), or lowered to QBE/LLVM IL for native compilation (`qbe_emit.cm`). See [Compilation Pipeline](pipeline.md) for the full overview.
|
||||||
|
|
||||||
|
## Module Structure
|
||||||
|
|
||||||
|
An `.mcode` file is a JSON object representing a compiled module:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `name` | string | Module name (typically the source filename) |
|
||||||
|
| `filename` | string | Source filename |
|
||||||
|
| `data` | object | Constant pool — string and number literals used by instructions |
|
||||||
|
| `main` | function | The top-level function (module body) |
|
||||||
|
| `functions` | array | Nested function definitions (referenced by `function dest, id`) |
|
||||||
|
|
||||||
|
### Function Record
|
||||||
|
|
||||||
|
Each function (both `main` and entries in `functions`) has:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `name` | string | Function name (`"<anonymous>"` for lambdas) |
|
||||||
|
| `filename` | string | Source filename |
|
||||||
|
| `nr_args` | integer | Number of parameters |
|
||||||
|
| `nr_slots` | integer | Total register slots needed (args + locals + temporaries) |
|
||||||
|
| `nr_close_slots` | integer | Number of closure slots captured from parent scope |
|
||||||
|
| `disruption_pc` | integer | Instruction index of the disruption handler (0 if none) |
|
||||||
|
| `instructions` | array | Instruction arrays and label strings |
|
||||||
|
|
||||||
|
Slot 0 is reserved. Slots 1 through `nr_args` hold parameters. Remaining slots up to `nr_slots - 1` are locals and temporaries.
|
||||||
|
|
||||||
## Instruction Format
|
## Instruction Format
|
||||||
|
|
||||||
Each instruction is a JSON array. The first element is the instruction name (string), followed by operands. The last two elements are line and column numbers for source mapping:
|
Each instruction is a JSON array. The first element is the instruction name (string), followed by operands. The last two elements are line and column numbers for source mapping:
|
||||||
|
|||||||
7
fit.c
7
fit.c
@@ -250,7 +250,8 @@ static const JSCFunctionListEntry js_fit_funcs[] = {
|
|||||||
|
|
||||||
JSValue js_core_fit_use(JSContext *js)
|
JSValue js_core_fit_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, mod, js_fit_funcs, countof(js_fit_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_fit_funcs, countof(js_fit_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
59
fold.cm
59
fold.cm
@@ -5,6 +5,34 @@ var fold = function(ast) {
|
|||||||
var scopes = ast.scopes
|
var scopes = ast.scopes
|
||||||
var nr_scopes = length(scopes)
|
var nr_scopes = length(scopes)
|
||||||
|
|
||||||
|
var type_tag_map = {
|
||||||
|
array: "array", record: "record", text: "text",
|
||||||
|
number: "number", blob: "blob"
|
||||||
|
}
|
||||||
|
|
||||||
|
var binary_ops = {
|
||||||
|
"+": true, "-": true, "*": true, "/": true, "%": true,
|
||||||
|
"**": true, "==": true, "!=": true, "<": true, ">": true,
|
||||||
|
"<=": true, ">=": true, "&": true, "|": true, "^": true,
|
||||||
|
"<<": true, ">>": true, ">>>": true, "&&": true, "||": true,
|
||||||
|
",": true, in: true
|
||||||
|
}
|
||||||
|
var unary_ops = {
|
||||||
|
"!": true, "~": true, "-unary": true, "+unary": true, delete: true
|
||||||
|
}
|
||||||
|
var assign_ops = {
|
||||||
|
assign: true, "+=": true, "-=": true, "*=": true,
|
||||||
|
"/=": true, "%=": true, "<<=": true, ">>=": true,
|
||||||
|
">>>=": true, "&=": true, "^=": true, "|=": true,
|
||||||
|
"**=": true, "&&=": true, "||=": true
|
||||||
|
}
|
||||||
|
var arith_ops = {
|
||||||
|
"+": true, "-": true, "*": true, "/": true, "%": true, "**": true
|
||||||
|
}
|
||||||
|
var comparison_ops = {
|
||||||
|
"==": true, "!=": true, "<": true, ">": true, "<=": true, ">=": true
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Helpers
|
// Helpers
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -194,11 +222,7 @@ var fold = function(ast) {
|
|||||||
if (rhs_target != null && rhs_target.intrinsic == true) {
|
if (rhs_target != null && rhs_target.intrinsic == true) {
|
||||||
sv = scope_var(fn_nr, name)
|
sv = scope_var(fn_nr, name)
|
||||||
if (sv != null && sv.type_tag == null) {
|
if (sv != null && sv.type_tag == null) {
|
||||||
if (rhs_target.name == "array") sv.type_tag = "array"
|
if (type_tag_map[rhs_target.name] != null) sv.type_tag = type_tag_map[rhs_target.name]
|
||||||
else if (rhs_target.name == "record") sv.type_tag = "record"
|
|
||||||
else if (rhs_target.name == "text") sv.type_tag = "text"
|
|
||||||
else if (rhs_target.name == "number") sv.type_tag = "number"
|
|
||||||
else if (rhs_target.name == "blob") sv.type_tag = "blob"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,17 +381,13 @@ var fold = function(ast) {
|
|||||||
var arg = null
|
var arg = null
|
||||||
|
|
||||||
// Recurse into children first (bottom-up)
|
// Recurse into children first (bottom-up)
|
||||||
if (k == "+" || k == "-" || k == "*" || k == "/" || k == "%" ||
|
if (binary_ops[k] == true) {
|
||||||
k == "**" || k == "==" || k == "!=" || k == "<" || k == ">" ||
|
|
||||||
k == "<=" || k == ">=" || k == "&" || k == "|" || k == "^" ||
|
|
||||||
k == "<<" || k == ">>" || k == ">>>" || k == "&&" || k == "||" ||
|
|
||||||
k == "," || k == "in") {
|
|
||||||
expr.left = fold_expr(expr.left, fn_nr)
|
expr.left = fold_expr(expr.left, fn_nr)
|
||||||
expr.right = fold_expr(expr.right, fn_nr)
|
expr.right = fold_expr(expr.right, fn_nr)
|
||||||
} else if (k == "." || k == "[") {
|
} else if (k == "." || k == "[") {
|
||||||
expr.left = fold_expr(expr.left, fn_nr)
|
expr.left = fold_expr(expr.left, fn_nr)
|
||||||
if (k == "[" && expr.right != null) expr.right = fold_expr(expr.right, fn_nr)
|
if (k == "[" && expr.right != null) expr.right = fold_expr(expr.right, fn_nr)
|
||||||
} else if (k == "!" || k == "~" || k == "-unary" || k == "+unary" || k == "delete") {
|
} else if (unary_ops[k] == true) {
|
||||||
expr.expression = fold_expr(expr.expression, fn_nr)
|
expr.expression = fold_expr(expr.expression, fn_nr)
|
||||||
} else if (k == "++" || k == "--") {
|
} else if (k == "++" || k == "--") {
|
||||||
return expr
|
return expr
|
||||||
@@ -382,7 +402,7 @@ var fold = function(ast) {
|
|||||||
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
} else if (k == "array") {
|
} else if (k == "array" || k == "text literal") {
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(expr.list)) {
|
while (i < length(expr.list)) {
|
||||||
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
||||||
@@ -394,19 +414,10 @@ var fold = function(ast) {
|
|||||||
expr.list[i].right = fold_expr(expr.list[i].right, fn_nr)
|
expr.list[i].right = fold_expr(expr.list[i].right, fn_nr)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
} else if (k == "text literal") {
|
|
||||||
i = 0
|
|
||||||
while (i < length(expr.list)) {
|
|
||||||
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
} else if (k == "function") {
|
} else if (k == "function") {
|
||||||
fold_fn(expr)
|
fold_fn(expr)
|
||||||
return expr
|
return expr
|
||||||
} else if (k == "assign" || k == "+=" || k == "-=" || k == "*=" ||
|
} else if (assign_ops[k] == true) {
|
||||||
k == "/=" || k == "%=" || k == "<<=" || k == ">>=" ||
|
|
||||||
k == ">>>=" || k == "&=" || k == "^=" || k == "|=" ||
|
|
||||||
k == "**=" || k == "&&=" || k == "||=") {
|
|
||||||
expr.right = fold_expr(expr.right, fn_nr)
|
expr.right = fold_expr(expr.right, fn_nr)
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
@@ -428,7 +439,7 @@ var fold = function(ast) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Binary constant folding
|
// Binary constant folding
|
||||||
if (k == "+" || k == "-" || k == "*" || k == "/" || k == "%" || k == "**") {
|
if (arith_ops[k] == true) {
|
||||||
left = expr.left
|
left = expr.left
|
||||||
right = expr.right
|
right = expr.right
|
||||||
if (left != null && right != null && left.kind == "number" && right.kind == "number") {
|
if (left != null && right != null && left.kind == "number" && right.kind == "number") {
|
||||||
@@ -460,7 +471,7 @@ var fold = function(ast) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Comparison folding
|
// Comparison folding
|
||||||
if (k == "==" || k == "!=" || k == "<" || k == ">" || k == "<=" || k == ">=") {
|
if (comparison_ops[k] == true) {
|
||||||
left = expr.left
|
left = expr.left
|
||||||
right = expr.right
|
right = expr.right
|
||||||
if (left != null && right != null) {
|
if (left != null && right != null) {
|
||||||
|
|||||||
155
internal/fd.c
155
internal/fd.c
@@ -412,117 +412,117 @@ JSC_CCALL(fd_close,
|
|||||||
JSC_CCALL(fd_fstat,
|
JSC_CCALL(fd_fstat,
|
||||||
int fd = js2fd(js, argv[0]);
|
int fd = js2fd(js, argv[0]);
|
||||||
if (fd < 0) return JS_EXCEPTION;
|
if (fd < 0) return JS_EXCEPTION;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd, &st) != 0)
|
if (fstat(fd, &st) != 0)
|
||||||
return JS_ThrowInternalError(js, "fstat failed: %s", strerror(errno));
|
return JS_ThrowInternalError(js, "fstat failed: %s", strerror(errno));
|
||||||
|
|
||||||
JSValue obj = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
|
JS_ROOT(obj, JS_NewObject(js));
|
||||||
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
|
JS_SetPropertyStr(js, obj.val, "size", JS_NewInt64(js, st.st_size));
|
||||||
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
|
JS_SetPropertyStr(js, obj.val, "mode", JS_NewInt32(js, st.st_mode));
|
||||||
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
|
JS_SetPropertyStr(js, obj.val, "uid", JS_NewInt32(js, st.st_uid));
|
||||||
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
|
JS_SetPropertyStr(js, obj.val, "gid", JS_NewInt32(js, st.st_gid));
|
||||||
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
|
JS_SetPropertyStr(js, obj.val, "atime", JS_NewInt64(js, st.st_atime));
|
||||||
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
|
JS_SetPropertyStr(js, obj.val, "mtime", JS_NewInt64(js, st.st_mtime));
|
||||||
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
|
JS_SetPropertyStr(js, obj.val, "ctime", JS_NewInt64(js, st.st_ctime));
|
||||||
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
|
JS_SetPropertyStr(js, obj.val, "nlink", JS_NewInt32(js, st.st_nlink));
|
||||||
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
|
JS_SetPropertyStr(js, obj.val, "ino", JS_NewInt64(js, st.st_ino));
|
||||||
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
|
JS_SetPropertyStr(js, obj.val, "dev", JS_NewInt32(js, st.st_dev));
|
||||||
|
JS_SetPropertyStr(js, obj.val, "rdev", JS_NewInt32(js, st.st_rdev));
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
|
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_blocks));
|
||||||
#else
|
#else
|
||||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
|
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, 4096));
|
||||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
|
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_size / 512));
|
||||||
#endif
|
#endif
|
||||||
|
JS_SetPropertyStr(js, obj.val, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
||||||
// Add boolean properties for file type
|
JS_SetPropertyStr(js, obj.val, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
JS_RETURN(obj.val);
|
||||||
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(fd_stat,
|
JSC_CCALL(fd_stat,
|
||||||
const char *path = JS_ToCString(js, argv[0]);
|
const char *path = JS_ToCString(js, argv[0]);
|
||||||
if (!path) return JS_EXCEPTION;
|
if (!path) return JS_EXCEPTION;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path, &st) != 0) {
|
if (stat(path, &st) != 0) {
|
||||||
JS_FreeCString(js, path);
|
JS_FreeCString(js, path);
|
||||||
return JS_NewObject(js);
|
return JS_NewObject(js);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue obj = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
|
JS_ROOT(obj, JS_NewObject(js));
|
||||||
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
|
JS_SetPropertyStr(js, obj.val, "size", JS_NewInt64(js, st.st_size));
|
||||||
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
|
JS_SetPropertyStr(js, obj.val, "mode", JS_NewInt32(js, st.st_mode));
|
||||||
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
|
JS_SetPropertyStr(js, obj.val, "uid", JS_NewInt32(js, st.st_uid));
|
||||||
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
|
JS_SetPropertyStr(js, obj.val, "gid", JS_NewInt32(js, st.st_gid));
|
||||||
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
|
JS_SetPropertyStr(js, obj.val, "atime", JS_NewInt64(js, st.st_atime));
|
||||||
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
|
JS_SetPropertyStr(js, obj.val, "mtime", JS_NewInt64(js, st.st_mtime));
|
||||||
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
|
JS_SetPropertyStr(js, obj.val, "ctime", JS_NewInt64(js, st.st_ctime));
|
||||||
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
|
JS_SetPropertyStr(js, obj.val, "nlink", JS_NewInt32(js, st.st_nlink));
|
||||||
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
|
JS_SetPropertyStr(js, obj.val, "ino", JS_NewInt64(js, st.st_ino));
|
||||||
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
|
JS_SetPropertyStr(js, obj.val, "dev", JS_NewInt32(js, st.st_dev));
|
||||||
|
JS_SetPropertyStr(js, obj.val, "rdev", JS_NewInt32(js, st.st_rdev));
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
|
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_blocks));
|
||||||
#else
|
#else
|
||||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
|
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, 4096));
|
||||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
|
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_size / 512));
|
||||||
#endif
|
#endif
|
||||||
|
JS_SetPropertyStr(js, obj.val, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
||||||
// Add boolean properties for file type
|
JS_SetPropertyStr(js, obj.val, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
JS_SetPropertyStr(js, obj.val, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
||||||
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
|
||||||
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
|
||||||
|
|
||||||
JS_FreeCString(js, path);
|
JS_FreeCString(js, path);
|
||||||
return obj;
|
JS_RETURN(obj.val);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_SCALL(fd_readdir,
|
JSC_SCALL(fd_readdir,
|
||||||
|
JS_FRAME(js);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WIN32_FIND_DATA ffd;
|
WIN32_FIND_DATA ffd;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
snprintf(path, sizeof(path), "%s\\*", str);
|
snprintf(path, sizeof(path), "%s\\*", str);
|
||||||
HANDLE hFind = FindFirstFile(path, &ffd);
|
HANDLE hFind = FindFirstFile(path, &ffd);
|
||||||
if (hFind == INVALID_HANDLE_VALUE) {
|
if (hFind == INVALID_HANDLE_VALUE) {
|
||||||
ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path);
|
ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path);
|
||||||
} else {
|
} else {
|
||||||
ret = JS_NewArray(js);
|
JS_ROOT(arr, JS_NewArray(js));
|
||||||
do {
|
do {
|
||||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
||||||
JS_ArrayPush(js, &ret, JS_NewString(js, ffd.cFileName));
|
JS_ArrayPush(js, &arr.val, JS_NewString(js, ffd.cFileName));
|
||||||
} while (FindNextFile(hFind, &ffd) != 0);
|
} while (FindNextFile(hFind, &ffd) != 0);
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
|
ret = arr.val;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
DIR *d;
|
DIR *d;
|
||||||
struct dirent *dir;
|
struct dirent *dir;
|
||||||
d = opendir(str);
|
d = opendir(str);
|
||||||
if (d) {
|
if (d) {
|
||||||
ret = JS_NewArray(js);
|
JS_ROOT(arr, JS_NewArray(js));
|
||||||
while ((dir = readdir(d)) != NULL) {
|
while ((dir = readdir(d)) != NULL) {
|
||||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
||||||
JS_ArrayPush(js, &ret, JS_NewString(js, dir->d_name));
|
JS_ArrayPush(js, &arr.val, JS_NewString(js, dir->d_name));
|
||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
|
ret = arr.val;
|
||||||
} else {
|
} else {
|
||||||
ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno));
|
ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(fd_is_file,
|
JSC_CCALL(fd_is_file,
|
||||||
@@ -585,9 +585,9 @@ JSC_CCALL(fd_slurpwrite,
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Helper function for recursive enumeration
|
// Helper function for recursive enumeration
|
||||||
static void visit_directory(JSContext *js, JSValue results, int *result_count, const char *curr_path, const char *rel_prefix, int recurse) {
|
static void visit_directory(JSContext *js, JSValue *results, int *result_count, const char *curr_path, const char *rel_prefix, int recurse) {
|
||||||
if (!curr_path) return;
|
if (!curr_path) return;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WIN32_FIND_DATA ffd;
|
WIN32_FIND_DATA ffd;
|
||||||
char search_path[PATH_MAX];
|
char search_path[PATH_MAX];
|
||||||
@@ -602,7 +602,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
|
|||||||
} else {
|
} else {
|
||||||
strcpy(item_rel, ffd.cFileName);
|
strcpy(item_rel, ffd.cFileName);
|
||||||
}
|
}
|
||||||
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
|
JS_SetPropertyNumber(js, *results, (*result_count)++, JS_NewString(js, item_rel));
|
||||||
|
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -627,7 +627,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
|
|||||||
} else {
|
} else {
|
||||||
strcpy(item_rel, dir->d_name);
|
strcpy(item_rel, dir->d_name);
|
||||||
}
|
}
|
||||||
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
|
JS_SetPropertyNumber(js, *results, (*result_count)++, JS_NewString(js, item_rel));
|
||||||
|
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -651,14 +651,16 @@ JSC_SCALL(fd_enumerate,
|
|||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
recurse = JS_ToBool(js, argv[1]);
|
recurse = JS_ToBool(js, argv[1]);
|
||||||
|
|
||||||
JSValue results = JS_NewArray(js);
|
JS_FRAME(js);
|
||||||
|
JS_ROOT(arr, JS_NewArray(js));
|
||||||
int result_count = 0;
|
int result_count = 0;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
|
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
|
||||||
visit_directory(js, results, &result_count, path, "", recurse);
|
visit_directory(js, &arr.val, &result_count, path, "", recurse);
|
||||||
|
|
||||||
ret = results;
|
ret = arr.val;
|
||||||
|
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame);
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(fd_realpath,
|
JSC_CCALL(fd_realpath,
|
||||||
@@ -753,7 +755,8 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_internal_fd_use(JSContext *js) {
|
JSValue js_core_internal_fd_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_fd_funcs, countof(js_fd_funcs));
|
||||||
}
|
JS_RETURN(mod.val);
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ static const JSCFunctionListEntry js_kim_funcs[] = {
|
|||||||
|
|
||||||
JSValue js_core_kim_use(JSContext *js)
|
JSValue js_core_kim_use(JSContext *js)
|
||||||
{
|
{
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, mod, js_kim_funcs, countof(js_kim_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_kim_funcs, countof(js_kim_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
@@ -277,29 +277,31 @@ JSC_CCALL(os_mallinfo,
|
|||||||
)
|
)
|
||||||
|
|
||||||
static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||||
JSValue ret = JS_NULL;
|
JS_FRAME(js);
|
||||||
ret = JS_NewObject(js);
|
JS_ROOT(ret, JS_NewObject(js));
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
struct rusage jsmem;
|
struct rusage jsmem;
|
||||||
getrusage(RUSAGE_SELF, &jsmem);
|
getrusage(RUSAGE_SELF, &jsmem);
|
||||||
JSJMEMRET(ru_maxrss);
|
#define JSJMEMRET_GC(FIELD) JS_SetPropertyStr(js, ret.val, #FIELD, number2js(js, jsmem.FIELD));
|
||||||
JSJMEMRET(ru_ixrss);
|
JSJMEMRET_GC(ru_maxrss);
|
||||||
JSJMEMRET(ru_idrss);
|
JSJMEMRET_GC(ru_ixrss);
|
||||||
JSJMEMRET(ru_isrss);
|
JSJMEMRET_GC(ru_idrss);
|
||||||
JSJMEMRET(ru_minflt);
|
JSJMEMRET_GC(ru_isrss);
|
||||||
JSJMEMRET(ru_majflt);
|
JSJMEMRET_GC(ru_minflt);
|
||||||
JSJMEMRET(ru_nswap);
|
JSJMEMRET_GC(ru_majflt);
|
||||||
JSJMEMRET(ru_inblock);
|
JSJMEMRET_GC(ru_nswap);
|
||||||
JSJMEMRET(ru_oublock);
|
JSJMEMRET_GC(ru_inblock);
|
||||||
JSJMEMRET(ru_msgsnd);
|
JSJMEMRET_GC(ru_oublock);
|
||||||
JSJMEMRET(ru_msgrcv);
|
JSJMEMRET_GC(ru_msgsnd);
|
||||||
JSJMEMRET(ru_nsignals);
|
JSJMEMRET_GC(ru_msgrcv);
|
||||||
JSJMEMRET(ru_nvcsw);
|
JSJMEMRET_GC(ru_nsignals);
|
||||||
JSJMEMRET(ru_nivcsw);
|
JSJMEMRET_GC(ru_nvcsw);
|
||||||
|
JSJMEMRET_GC(ru_nivcsw);
|
||||||
|
#undef JSJMEMRET_GC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ret;
|
JS_RETURN(ret.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSC_SCALL(os_system,
|
JSC_SCALL(os_system,
|
||||||
@@ -645,7 +647,8 @@ 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);
|
||||||
|
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_os_funcs, countof(js_os_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,10 +71,11 @@ static const JSCFunctionListEntry js_time_funcs[] = {
|
|||||||
JSValue
|
JSValue
|
||||||
js_core_internal_time_use(JSContext *ctx)
|
js_core_internal_time_use(JSContext *ctx)
|
||||||
{
|
{
|
||||||
JSValue obj = JS_NewObject(ctx);
|
JS_FRAME(ctx);
|
||||||
JS_SetPropertyFunctionList(ctx, obj,
|
JS_ROOT(mod, JS_NewObject(ctx));
|
||||||
|
JS_SetPropertyFunctionList(ctx, mod.val,
|
||||||
js_time_funcs,
|
js_time_funcs,
|
||||||
sizeof(js_time_funcs) /
|
sizeof(js_time_funcs) /
|
||||||
sizeof(js_time_funcs[0]));
|
sizeof(js_time_funcs[0]));
|
||||||
return obj;
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
142
mcode.cm
142
mcode.cm
@@ -28,6 +28,13 @@ var mcode = function(ast) {
|
|||||||
"<<=": "shl", ">>=": "shr", ">>>=": "ushr"
|
"<<=": "shl", ">>=": "shr", ">>>=": "ushr"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sensory_ops = {
|
||||||
|
is_array: "is_array", is_function: "is_func", is_object: "is_record",
|
||||||
|
is_stone: "is_stone", is_integer: "is_int", is_text: "is_text",
|
||||||
|
is_number: "is_num", is_logical: "is_bool", is_null: "is_null",
|
||||||
|
length: "length"
|
||||||
|
}
|
||||||
|
|
||||||
// Compiler state
|
// Compiler state
|
||||||
var s_instructions = null
|
var s_instructions = null
|
||||||
var s_data = null
|
var s_data = null
|
||||||
@@ -273,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
|
||||||
@@ -511,15 +569,36 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Central router: maps op string to decomposition helper
|
// Central router: maps op string to decomposition helper
|
||||||
// Sets _bp_* closure vars then calls helper with reduced args
|
// Sets _bp_* closure vars then calls helper with reduced args
|
||||||
|
var relational_ops = {
|
||||||
|
lt: ["lt_int", "lt_float", "lt_text"],
|
||||||
|
le: ["le_int", "le_float", "le_text"],
|
||||||
|
gt: ["gt_int", "gt_float", "gt_text"],
|
||||||
|
ge: ["ge_int", "ge_float", "ge_text"]
|
||||||
|
}
|
||||||
var emit_binop = function(op_str, dest, left, right) {
|
var emit_binop = function(op_str, dest, left, right) {
|
||||||
|
var rel = null
|
||||||
_bp_dest = dest
|
_bp_dest = dest
|
||||||
_bp_left = left
|
_bp_left = left
|
||||||
_bp_right = right
|
_bp_right = right
|
||||||
@@ -529,18 +608,17 @@ var mcode = function(ast) {
|
|||||||
emit_eq_decomposed()
|
emit_eq_decomposed()
|
||||||
} else if (op_str == "ne") {
|
} else if (op_str == "ne") {
|
||||||
emit_ne_decomposed()
|
emit_ne_decomposed()
|
||||||
} else if (op_str == "lt") {
|
|
||||||
emit_relational("lt_int", "lt_float", "lt_text")
|
|
||||||
} else if (op_str == "le") {
|
|
||||||
emit_relational("le_int", "le_float", "le_text")
|
|
||||||
} else if (op_str == "gt") {
|
|
||||||
emit_relational("gt_int", "gt_float", "gt_text")
|
|
||||||
} else if (op_str == "ge") {
|
|
||||||
emit_relational("ge_int", "ge_float", "ge_text")
|
|
||||||
} else {
|
} else {
|
||||||
// Passthrough for subtract, multiply, divide, modulo,
|
rel = relational_ops[op_str]
|
||||||
// bitwise, pow, in, etc.
|
if (rel != null) {
|
||||||
emit_3(op_str, dest, left, right)
|
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 {
|
||||||
|
// Passthrough for bitwise, in, etc.
|
||||||
|
emit_3(op_str, dest, left, right)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -1670,37 +1748,11 @@ var mcode = function(ast) {
|
|||||||
fname = callee.name
|
fname = callee.name
|
||||||
nargs = args_list != null ? length(args_list) : 0
|
nargs = args_list != null ? length(args_list) : 0
|
||||||
// 1-arg type check intrinsics → direct opcode
|
// 1-arg type check intrinsics → direct opcode
|
||||||
if (nargs == 1) {
|
if (nargs == 1 && sensory_ops[fname] != null) {
|
||||||
if (fname == "is_array" || fname == "is_function" ||
|
|
||||||
fname == "is_object" || fname == "is_stone" ||
|
|
||||||
fname == "is_integer" || fname == "is_text" ||
|
|
||||||
fname == "is_number" || fname == "is_logical" ||
|
|
||||||
fname == "is_null" || fname == "length") {
|
|
||||||
a0 = gen_expr(args_list[0], -1)
|
a0 = gen_expr(args_list[0], -1)
|
||||||
d = alloc_slot()
|
d = alloc_slot()
|
||||||
if (fname == "is_array") {
|
emit_2(sensory_ops[fname], d, a0)
|
||||||
emit_2("is_array", d, a0)
|
|
||||||
} else if (fname == "is_function") {
|
|
||||||
emit_2("is_func", d, a0)
|
|
||||||
} else if (fname == "is_object") {
|
|
||||||
emit_2("is_record", d, a0)
|
|
||||||
} else if (fname == "is_stone") {
|
|
||||||
emit_2("is_stone", d, a0)
|
|
||||||
} else if (fname == "is_integer") {
|
|
||||||
emit_2("is_int", d, a0)
|
|
||||||
} else if (fname == "is_text") {
|
|
||||||
emit_2("is_text", d, a0)
|
|
||||||
} else if (fname == "is_number") {
|
|
||||||
emit_2("is_num", d, a0)
|
|
||||||
} else if (fname == "is_logical") {
|
|
||||||
emit_2("is_bool", d, a0)
|
|
||||||
} else if (fname == "is_null") {
|
|
||||||
emit_2("is_null", d, a0)
|
|
||||||
} else if (fname == "length") {
|
|
||||||
emit_2("length", d, a0)
|
|
||||||
}
|
|
||||||
return d
|
return d
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 2-arg push: push(arr, val) → guarded direct opcode
|
// 2-arg push: push(arr, val) → guarded direct opcode
|
||||||
if (nargs == 2 && fname == "push") {
|
if (nargs == 2 && fname == "push") {
|
||||||
@@ -1930,7 +1982,7 @@ var mcode = function(ast) {
|
|||||||
_i = _i + 1
|
_i = _i + 1
|
||||||
}
|
}
|
||||||
dest = alloc_slot()
|
dest = alloc_slot()
|
||||||
add_instr(["array", dest, 0])
|
add_instr(["array", dest, count])
|
||||||
_i = 0
|
_i = 0
|
||||||
while (_i < count) {
|
while (_i < count) {
|
||||||
emit_2("push", dest, elem_slots[_i])
|
emit_2("push", dest, elem_slots[_i])
|
||||||
@@ -1943,7 +1995,7 @@ var mcode = function(ast) {
|
|||||||
if (kind == "record") {
|
if (kind == "record") {
|
||||||
list = expr.list
|
list = expr.list
|
||||||
dest = alloc_slot()
|
dest = alloc_slot()
|
||||||
push(s_instructions, ["record", dest, 0])
|
push(s_instructions, ["record", dest, length(list)])
|
||||||
_i = 0
|
_i = 0
|
||||||
while (_i < length(list)) {
|
while (_i < length(list)) {
|
||||||
pair = list[_i]
|
pair = list[_i]
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ if get_option('validate_gc')
|
|||||||
add_project_arguments('-DVALIDATE_GC', language: 'c')
|
add_project_arguments('-DVALIDATE_GC', language: 'c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if get_option('force_gc')
|
||||||
|
add_project_arguments('-DFORCE_GC_AT_MALLOC', language: 'c')
|
||||||
|
endif
|
||||||
|
|
||||||
deps = []
|
deps = []
|
||||||
|
|
||||||
if host_machine.system() == 'darwin'
|
if host_machine.system() == 'darwin'
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
option('validate_gc', type: 'boolean', value: false,
|
option('validate_gc', type: 'boolean', value: false,
|
||||||
description: 'Enable GC validation checks (stale pointer detection, pre-GC frame validation)')
|
description: 'Enable GC validation checks (stale pointer detection, pre-GC frame validation)')
|
||||||
|
option('force_gc', type: 'boolean', value: false,
|
||||||
|
description: 'Force GC on every allocation (makes stale pointer bugs deterministic)')
|
||||||
|
|||||||
20
net/enet.c
20
net/enet.c
@@ -570,19 +570,21 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
|
|||||||
|
|
||||||
JSValue js_core_enet_use(JSContext *ctx)
|
JSValue js_core_enet_use(JSContext *ctx)
|
||||||
{
|
{
|
||||||
|
JS_FRAME(ctx);
|
||||||
|
|
||||||
JS_NewClassID(&enet_host_id);
|
JS_NewClassID(&enet_host_id);
|
||||||
JS_NewClass(ctx, enet_host_id, &enet_host);
|
JS_NewClass(ctx, enet_host_id, &enet_host);
|
||||||
JSValue host_proto = JS_NewObject(ctx);
|
JS_ROOT(host_proto, JS_NewObject(ctx));
|
||||||
JS_SetPropertyFunctionList(ctx, host_proto, js_enet_host_funcs, countof(js_enet_host_funcs));
|
JS_SetPropertyFunctionList(ctx, host_proto.val, js_enet_host_funcs, countof(js_enet_host_funcs));
|
||||||
JS_SetClassProto(ctx, enet_host_id, host_proto);
|
JS_SetClassProto(ctx, enet_host_id, host_proto.val);
|
||||||
|
|
||||||
JS_NewClassID(&enet_peer_class_id);
|
JS_NewClassID(&enet_peer_class_id);
|
||||||
JS_NewClass(ctx, enet_peer_class_id, &enet_peer_class);
|
JS_NewClass(ctx, enet_peer_class_id, &enet_peer_class);
|
||||||
JSValue peer_proto = JS_NewObject(ctx);
|
JS_ROOT(peer_proto, JS_NewObject(ctx));
|
||||||
JS_SetPropertyFunctionList(ctx, peer_proto, js_enet_peer_funcs, countof(js_enet_peer_funcs));
|
JS_SetPropertyFunctionList(ctx, peer_proto.val, js_enet_peer_funcs, countof(js_enet_peer_funcs));
|
||||||
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto);
|
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto.val);
|
||||||
|
|
||||||
JSValue export_obj = JS_NewObject(ctx);
|
JS_ROOT(export_obj, JS_NewObject(ctx));
|
||||||
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
|
JS_SetPropertyFunctionList(ctx, export_obj.val, js_enet_funcs, countof(js_enet_funcs));
|
||||||
return export_obj;
|
JS_RETURN(export_obj.val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,9 +319,10 @@ static const JSCFunctionListEntry js_http_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_http_use(JSContext *js) {
|
JSValue js_core_http_use(JSContext *js) {
|
||||||
|
JS_FRAME(js);
|
||||||
par_easycurl_init(0); // Initialize platform HTTP backend
|
par_easycurl_init(0); // Initialize platform HTTP backend
|
||||||
JSValue obj = JS_NewObject(js);
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, obj, js_http_funcs,
|
JS_SetPropertyFunctionList(js, mod.val, js_http_funcs,
|
||||||
sizeof(js_http_funcs)/sizeof(js_http_funcs[0]));
|
sizeof(js_http_funcs)/sizeof(js_http_funcs[0]));
|
||||||
return obj;
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
43
net/socket.c
43
net/socket.c
@@ -595,26 +595,27 @@ static const JSCFunctionListEntry js_socket_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_socket_use(JSContext *js) {
|
JSValue js_core_socket_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, mod, js_socket_funcs, countof(js_socket_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
|
JS_SetPropertyFunctionList(js, mod.val, js_socket_funcs, countof(js_socket_funcs));
|
||||||
|
|
||||||
// Add constants
|
// Add constants
|
||||||
JS_SetPropertyStr(js, mod, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC));
|
JS_SetPropertyStr(js, mod.val, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC));
|
||||||
JS_SetPropertyStr(js, mod, "AF_INET", JS_NewInt32(js, AF_INET));
|
JS_SetPropertyStr(js, mod.val, "AF_INET", JS_NewInt32(js, AF_INET));
|
||||||
JS_SetPropertyStr(js, mod, "AF_INET6", JS_NewInt32(js, AF_INET6));
|
JS_SetPropertyStr(js, mod.val, "AF_INET6", JS_NewInt32(js, AF_INET6));
|
||||||
JS_SetPropertyStr(js, mod, "AF_UNIX", JS_NewInt32(js, AF_UNIX));
|
JS_SetPropertyStr(js, mod.val, "AF_UNIX", JS_NewInt32(js, AF_UNIX));
|
||||||
|
|
||||||
JS_SetPropertyStr(js, mod, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM));
|
JS_SetPropertyStr(js, mod.val, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM));
|
||||||
JS_SetPropertyStr(js, mod, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM));
|
JS_SetPropertyStr(js, mod.val, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM));
|
||||||
|
|
||||||
JS_SetPropertyStr(js, mod, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE));
|
JS_SetPropertyStr(js, mod.val, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE));
|
||||||
|
|
||||||
JS_SetPropertyStr(js, mod, "SHUT_RD", JS_NewInt32(js, SHUT_RD));
|
JS_SetPropertyStr(js, mod.val, "SHUT_RD", JS_NewInt32(js, SHUT_RD));
|
||||||
JS_SetPropertyStr(js, mod, "SHUT_WR", JS_NewInt32(js, SHUT_WR));
|
JS_SetPropertyStr(js, mod.val, "SHUT_WR", JS_NewInt32(js, SHUT_WR));
|
||||||
JS_SetPropertyStr(js, mod, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR));
|
JS_SetPropertyStr(js, mod.val, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR));
|
||||||
|
|
||||||
JS_SetPropertyStr(js, mod, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET));
|
JS_SetPropertyStr(js, mod.val, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET));
|
||||||
JS_SetPropertyStr(js, mod, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR));
|
JS_SetPropertyStr(js, mod.val, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR));
|
||||||
|
|
||||||
return mod;
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
15
parse.cm
15
parse.cm
@@ -1,6 +1,11 @@
|
|||||||
var parse = function(tokens, src, filename, tokenizer) {
|
var parse = function(tokens, src, filename, tokenizer) {
|
||||||
var _src_len = length(src)
|
var _src_len = length(src)
|
||||||
|
|
||||||
|
var template_escape_map = {
|
||||||
|
n: "\n", t: "\t", r: "\r", "\\": "\\",
|
||||||
|
"`": "`", "$": "$", "0": character(0)
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Parser Cursor
|
// Parser Cursor
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -175,6 +180,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
var tc = null
|
var tc = null
|
||||||
var tq = null
|
var tq = null
|
||||||
var esc_ch = null
|
var esc_ch = null
|
||||||
|
var esc_val = null
|
||||||
var expr_tokens = null
|
var expr_tokens = null
|
||||||
var sub_ast = null
|
var sub_ast = null
|
||||||
var sub_stmt = null
|
var sub_stmt = null
|
||||||
@@ -223,13 +229,8 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
while (tvi < tvlen) {
|
while (tvi < tvlen) {
|
||||||
if (tv[tvi] == "\\" && tvi + 1 < tvlen) {
|
if (tv[tvi] == "\\" && tvi + 1 < tvlen) {
|
||||||
esc_ch = tv[tvi + 1]
|
esc_ch = tv[tvi + 1]
|
||||||
if (esc_ch == "n") { push(fmt_parts, "\n") }
|
esc_val = template_escape_map[esc_ch]
|
||||||
else if (esc_ch == "t") { push(fmt_parts, "\t") }
|
if (esc_val != null) { push(fmt_parts, esc_val) }
|
||||||
else if (esc_ch == "r") { push(fmt_parts, "\r") }
|
|
||||||
else if (esc_ch == "\\") { push(fmt_parts, "\\") }
|
|
||||||
else if (esc_ch == "`") { push(fmt_parts, "`") }
|
|
||||||
else if (esc_ch == "$") { push(fmt_parts, "$") }
|
|
||||||
else if (esc_ch == "0") { push(fmt_parts, character(0)) }
|
|
||||||
else { push(fmt_parts, esc_ch) }
|
else { push(fmt_parts, esc_ch) }
|
||||||
tvi = tvi + 2
|
tvi = tvi + 2
|
||||||
} else if (tv[tvi] == "$" && tvi + 1 < tvlen && tv[tvi + 1] == "{") {
|
} else if (tv[tvi] == "$" && tvi + 1 < tvlen && tv[tvi + 1] == "{") {
|
||||||
|
|||||||
116
prettify_mcode.ce
Normal file
116
prettify_mcode.ce
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// prettify_mcode.ce — reformat .mcode files to be human-readable
|
||||||
|
// Usage: ./cell --dev prettify_mcode boot/tokenize.cm.mcode
|
||||||
|
// ./cell --dev prettify_mcode boot/*.mcode
|
||||||
|
|
||||||
|
var fd = use("fd")
|
||||||
|
var json = use("json")
|
||||||
|
|
||||||
|
if (length(args) == 0) {
|
||||||
|
print("usage: cell prettify_mcode <file.mcode> [...]")
|
||||||
|
disrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapse leaf arrays (instruction arrays) onto single lines
|
||||||
|
var compact_arrays = function(json_text) {
|
||||||
|
var lines = array(json_text, "\n")
|
||||||
|
var result = []
|
||||||
|
var i = 0
|
||||||
|
var line = null
|
||||||
|
var trimmed = null
|
||||||
|
var collecting = false
|
||||||
|
var collected = null
|
||||||
|
var indent = null
|
||||||
|
var is_leaf = null
|
||||||
|
var j = 0
|
||||||
|
var inner = null
|
||||||
|
var parts = null
|
||||||
|
var trailing = null
|
||||||
|
var chars = null
|
||||||
|
var k = 0
|
||||||
|
|
||||||
|
while (i < length(lines)) {
|
||||||
|
line = lines[i]
|
||||||
|
trimmed = trim(line)
|
||||||
|
if (collecting == false && trimmed == "[") {
|
||||||
|
collecting = true
|
||||||
|
chars = array(line)
|
||||||
|
k = 0
|
||||||
|
while (k < length(chars) && chars[k] == " ") {
|
||||||
|
k = k + 1
|
||||||
|
}
|
||||||
|
indent = text(line, 0, k)
|
||||||
|
collected = []
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (collecting) {
|
||||||
|
if (trimmed == "]" || trimmed == "],") {
|
||||||
|
is_leaf = true
|
||||||
|
j = 0
|
||||||
|
while (j < length(collected)) {
|
||||||
|
inner = trim(collected[j])
|
||||||
|
if (starts_with(inner, "[") || starts_with(inner, "{")) {
|
||||||
|
is_leaf = false
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
if (is_leaf && length(collected) > 0) {
|
||||||
|
parts = []
|
||||||
|
j = 0
|
||||||
|
while (j < length(collected)) {
|
||||||
|
inner = trim(collected[j])
|
||||||
|
if (ends_with(inner, ",")) {
|
||||||
|
inner = text(inner, 0, length(inner) - 1)
|
||||||
|
}
|
||||||
|
parts[] = inner
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
trailing = ""
|
||||||
|
if (ends_with(trimmed, ",")) {
|
||||||
|
trailing = ","
|
||||||
|
}
|
||||||
|
result[] = `${indent}[${text(parts, ", ")}]${trailing}`
|
||||||
|
} else {
|
||||||
|
result[] = `${indent}[`
|
||||||
|
j = 0
|
||||||
|
while (j < length(collected)) {
|
||||||
|
result[] = collected[j]
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
result[] = line
|
||||||
|
}
|
||||||
|
collecting = false
|
||||||
|
} else {
|
||||||
|
collected[] = line
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[] = line
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return text(result, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
var path = null
|
||||||
|
var raw = null
|
||||||
|
var obj = null
|
||||||
|
var pretty = null
|
||||||
|
var f = null
|
||||||
|
while (i < length(args)) {
|
||||||
|
path = args[i]
|
||||||
|
if (!fd.is_file(path)) {
|
||||||
|
print(`skip ${path} (not found)`)
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
raw = text(fd.slurp(path))
|
||||||
|
obj = json.decode(raw)
|
||||||
|
pretty = compact_arrays(json.encode(obj, null, 2))
|
||||||
|
f = fd.open(path, "w")
|
||||||
|
fd.write(f, pretty)
|
||||||
|
fd.close(f)
|
||||||
|
print(`prettified ${path}`)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
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)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
258
qbe_emit.cm
258
qbe_emit.cm
@@ -76,6 +76,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
|||||||
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 captured = build_captured(fn)
|
||||||
var name = is_main ? (export_name ? export_name : "cell_main") : "cell_fn_" + text(fn_idx)
|
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
|
||||||
@@ -88,6 +89,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
|
|
||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
// --- 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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
}
|
}
|
||||||
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, export_name) {
|
|||||||
|
|
||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
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, export_name) {
|
|||||||
// 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)
|
||||||
|
|||||||
21
qop.c
21
qop.c
@@ -457,19 +457,20 @@ static const JSCFunctionListEntry js_qop_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_qop_use(JSContext *js) {
|
JSValue js_core_qop_use(JSContext *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);
|
||||||
JSValue archive_proto = JS_NewObject(js);
|
JS_ROOT(archive_proto, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, archive_proto, js_qop_archive_funcs, countof(js_qop_archive_funcs));
|
JS_SetPropertyFunctionList(js, archive_proto.val, js_qop_archive_funcs, countof(js_qop_archive_funcs));
|
||||||
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto);
|
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto.val);
|
||||||
|
|
||||||
JS_NewClassID(&js_qop_writer_class_id);
|
JS_NewClassID(&js_qop_writer_class_id);
|
||||||
JS_NewClass(js, js_qop_writer_class_id, &js_qop_writer_class);
|
JS_NewClass(js, js_qop_writer_class_id, &js_qop_writer_class);
|
||||||
JSValue writer_proto = JS_NewObject(js);
|
JS_ROOT(writer_proto, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, writer_proto, js_qop_writer_funcs, countof(js_qop_writer_funcs));
|
JS_SetPropertyFunctionList(js, writer_proto.val, js_qop_writer_funcs, countof(js_qop_writer_funcs));
|
||||||
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto);
|
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto.val);
|
||||||
|
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
JS_SetPropertyFunctionList(js, mod, js_qop_funcs, countof(js_qop_funcs));
|
JS_SetPropertyFunctionList(js, mod.val, js_qop_funcs, countof(js_qop_funcs));
|
||||||
return mod;
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
86
regen.ce
86
regen.ce
@@ -49,7 +49,6 @@ var ast = null
|
|||||||
var folded = null
|
var folded = null
|
||||||
var mcode_blob = null
|
var mcode_blob = null
|
||||||
var hash = null
|
var hash = null
|
||||||
var compact_mcode = null
|
|
||||||
var mach_blob = null
|
var mach_blob = null
|
||||||
var compiled = null
|
var compiled = null
|
||||||
var optimized = null
|
var optimized = null
|
||||||
@@ -59,88 +58,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
|
||||||
// Collapse leaf arrays (instruction arrays) onto single lines
|
|
||||||
var compact_arrays = function(json_text) {
|
|
||||||
var lines = array(json_text, "\n")
|
|
||||||
var result = []
|
|
||||||
var i = 0
|
|
||||||
var line = null
|
|
||||||
var trimmed = null
|
|
||||||
var collecting = false
|
|
||||||
var collected = null
|
|
||||||
var indent = null
|
|
||||||
var is_leaf = null
|
|
||||||
var j = 0
|
|
||||||
var inner = null
|
|
||||||
var parts = null
|
|
||||||
var trailing = null
|
|
||||||
var chars = null
|
|
||||||
var k = 0
|
|
||||||
|
|
||||||
while (i < length(lines)) {
|
|
||||||
line = lines[i]
|
|
||||||
trimmed = trim(line)
|
|
||||||
if (collecting == false && trimmed == "[") {
|
|
||||||
collecting = true
|
|
||||||
chars = array(line)
|
|
||||||
k = 0
|
|
||||||
while (k < length(chars) && chars[k] == " ") {
|
|
||||||
k = k + 1
|
|
||||||
}
|
|
||||||
indent = text(line, 0, k)
|
|
||||||
collected = []
|
|
||||||
i = i + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (collecting) {
|
|
||||||
if (trimmed == "]" || trimmed == "],") {
|
|
||||||
is_leaf = true
|
|
||||||
j = 0
|
|
||||||
while (j < length(collected)) {
|
|
||||||
inner = trim(collected[j])
|
|
||||||
if (starts_with(inner, "[") || starts_with(inner, "{")) {
|
|
||||||
is_leaf = false
|
|
||||||
}
|
|
||||||
j = j + 1
|
|
||||||
}
|
|
||||||
if (is_leaf && length(collected) > 0) {
|
|
||||||
parts = []
|
|
||||||
j = 0
|
|
||||||
while (j < length(collected)) {
|
|
||||||
inner = trim(collected[j])
|
|
||||||
if (ends_with(inner, ",")) {
|
|
||||||
inner = text(inner, 0, length(inner) - 1)
|
|
||||||
}
|
|
||||||
parts[] = inner
|
|
||||||
j = j + 1
|
|
||||||
}
|
|
||||||
trailing = ""
|
|
||||||
if (ends_with(trimmed, ",")) {
|
|
||||||
trailing = ","
|
|
||||||
}
|
|
||||||
result[] = `${indent}[${text(parts, ", ")}]${trailing}`
|
|
||||||
} else {
|
|
||||||
result[] = `${indent}[`
|
|
||||||
j = 0
|
|
||||||
while (j < length(collected)) {
|
|
||||||
result[] = collected[j]
|
|
||||||
j = j + 1
|
|
||||||
}
|
|
||||||
result[] = line
|
|
||||||
}
|
|
||||||
collecting = false
|
|
||||||
} else {
|
|
||||||
collected[] = line
|
|
||||||
}
|
|
||||||
i = i + 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result[] = line
|
|
||||||
i = i + 1
|
|
||||||
}
|
|
||||||
return text(result, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
while (i < length(files)) {
|
while (i < length(files)) {
|
||||||
entry = files[i]
|
entry = files[i]
|
||||||
@@ -167,7 +85,7 @@ while (i < length(files)) {
|
|||||||
folded = fold(ast)
|
folded = fold(ast)
|
||||||
compiled = mcode(folded)
|
compiled = mcode(folded)
|
||||||
optimized = streamline(compiled)
|
optimized = streamline(compiled)
|
||||||
mcode_text = compact_arrays(json.encode(optimized, null, 2))
|
mcode_text = json.encode(optimized)
|
||||||
f = fd.open(entry.out, "w")
|
f = fd.open(entry.out, "w")
|
||||||
fd.write(f, mcode_text)
|
fd.write(f, mcode_text)
|
||||||
fd.close(f)
|
fd.close(f)
|
||||||
|
|||||||
@@ -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)")
|
||||||
|
}
|
||||||
@@ -272,32 +272,43 @@ void script_startup(cell_rt *prt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create hidden environment
|
// Create hidden environment
|
||||||
JSValue hidden_env = JS_NewObject(js);
|
// Note: evaluate allocating calls into temporaries before passing to
|
||||||
JS_SetPropertyStr(js, hidden_env, "os", js_core_os_use(js));
|
// JS_SetPropertyStr, so env_ref.val is read AFTER GC may have moved it.
|
||||||
JS_SetPropertyStr(js, hidden_env, "json", js_core_json_use(js));
|
JSGCRef env_ref;
|
||||||
|
JS_AddGCRef(js, &env_ref);
|
||||||
|
env_ref.val = JS_NewObject(js);
|
||||||
|
JSValue tmp;
|
||||||
|
tmp = js_core_os_use(js);
|
||||||
|
JS_SetPropertyStr(js, env_ref.val, "os", tmp);
|
||||||
|
tmp = js_core_json_use(js);
|
||||||
|
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
||||||
|
|
||||||
crt->actor_sym_ref.val = JS_NewObject(js);
|
crt->actor_sym_ref.val = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
||||||
|
|
||||||
// Always set init (even if null)
|
// Always set init (even if null)
|
||||||
if (crt->init_wota) {
|
if (crt->init_wota) {
|
||||||
JS_SetPropertyStr(js, hidden_env, "init", wota2value(js, crt->init_wota));
|
tmp = wota2value(js, crt->init_wota);
|
||||||
|
JS_SetPropertyStr(js, env_ref.val, "init", tmp);
|
||||||
free(crt->init_wota);
|
free(crt->init_wota);
|
||||||
crt->init_wota = NULL;
|
crt->init_wota = NULL;
|
||||||
} else {
|
} else {
|
||||||
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
|
JS_SetPropertyStr(js, env_ref.val, "init", JS_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set args to null for actor spawn (not CLI mode)
|
// Set args to null for actor spawn (not CLI mode)
|
||||||
JS_SetPropertyStr(js, hidden_env, "args", JS_NULL);
|
JS_SetPropertyStr(js, env_ref.val, "args", JS_NULL);
|
||||||
|
|
||||||
if (core_path)
|
if (core_path) {
|
||||||
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
|
tmp = JS_NewString(js, core_path);
|
||||||
JS_SetPropertyStr(js, hidden_env, "shop_path",
|
JS_SetPropertyStr(js, env_ref.val, "core_path", tmp);
|
||||||
shop_path ? JS_NewString(js, shop_path) : JS_NULL);
|
}
|
||||||
|
tmp = shop_path ? JS_NewString(js, shop_path) : JS_NULL;
|
||||||
|
JS_SetPropertyStr(js, env_ref.val, "shop_path", tmp);
|
||||||
|
|
||||||
// Stone the environment
|
// Stone the environment
|
||||||
hidden_env = JS_Stone(js, hidden_env);
|
JSValue hidden_env = JS_Stone(js, env_ref.val);
|
||||||
|
JS_DeleteGCRef(js, &env_ref);
|
||||||
|
|
||||||
// Run from binary
|
// Run from binary
|
||||||
crt->state = ACTOR_RUNNING;
|
crt->state = ACTOR_RUNNING;
|
||||||
@@ -358,6 +369,7 @@ static void print_usage(const char *prog)
|
|||||||
printf(" --core <path> Set core path directly (overrides CELL_CORE)\n");
|
printf(" --core <path> Set core path directly (overrides CELL_CORE)\n");
|
||||||
printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n");
|
printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n");
|
||||||
printf(" --dev Dev mode (shop=.cell, core=.)\n");
|
printf(" --dev Dev mode (shop=.cell, core=.)\n");
|
||||||
|
printf(" --heap <size> Initial heap size (e.g. 256MB, 1GB)\n");
|
||||||
printf(" --seed Use seed bootstrap (minimal, for regen)\n");
|
printf(" --seed Use seed bootstrap (minimal, for regen)\n");
|
||||||
printf(" --test [heap_size] Run C test suite\n");
|
printf(" --test [heap_size] Run C test suite\n");
|
||||||
printf(" -h, --help Show this help message\n");
|
printf(" -h, --help Show this help message\n");
|
||||||
@@ -392,6 +404,7 @@ int cell_init(int argc, char **argv)
|
|||||||
/* Default: run script through bootstrap pipeline */
|
/* Default: run script through bootstrap pipeline */
|
||||||
int arg_start = 1;
|
int arg_start = 1;
|
||||||
int seed_mode = 0;
|
int seed_mode = 0;
|
||||||
|
size_t heap_size = 1024 * 1024; /* 1MB default */
|
||||||
const char *shop_override = NULL;
|
const char *shop_override = NULL;
|
||||||
const char *core_override = NULL;
|
const char *core_override = NULL;
|
||||||
|
|
||||||
@@ -414,6 +427,17 @@ int cell_init(int argc, char **argv)
|
|||||||
} else if (strcmp(argv[arg_start], "--seed") == 0) {
|
} else if (strcmp(argv[arg_start], "--seed") == 0) {
|
||||||
seed_mode = 1;
|
seed_mode = 1;
|
||||||
arg_start++;
|
arg_start++;
|
||||||
|
} else if (strcmp(argv[arg_start], "--heap") == 0) {
|
||||||
|
if (arg_start + 1 >= argc) {
|
||||||
|
printf("ERROR: --heap requires a size argument (e.g. 1GB, 256MB, 65536)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *end = NULL;
|
||||||
|
heap_size = strtoull(argv[arg_start + 1], &end, 0);
|
||||||
|
if (end && (*end == 'G' || *end == 'g')) heap_size *= 1024ULL * 1024 * 1024;
|
||||||
|
else if (end && (*end == 'M' || *end == 'm')) heap_size *= 1024ULL * 1024;
|
||||||
|
else if (end && (*end == 'K' || *end == 'k')) heap_size *= 1024ULL;
|
||||||
|
arg_start += 2;
|
||||||
} else if (strcmp(argv[arg_start], "--dev") == 0) {
|
} else if (strcmp(argv[arg_start], "--dev") == 0) {
|
||||||
shop_override = ".cell";
|
shop_override = ".cell";
|
||||||
core_override = ".";
|
core_override = ".";
|
||||||
@@ -462,7 +486,7 @@ int cell_init(int argc, char **argv)
|
|||||||
free(bin_data);
|
free(bin_data);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, 1024 * 1024);
|
JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, heap_size);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
printf("Failed to create JS context\n");
|
printf("Failed to create JS context\n");
|
||||||
free(bin_data); JS_FreeRuntime(g_runtime);
|
free(bin_data); JS_FreeRuntime(g_runtime);
|
||||||
@@ -499,21 +523,31 @@ int cell_init(int argc, char **argv)
|
|||||||
|
|
||||||
JS_FreeValue(ctx, js_core_blob_use(ctx));
|
JS_FreeValue(ctx, js_core_blob_use(ctx));
|
||||||
|
|
||||||
JSValue hidden_env = JS_NewObject(ctx);
|
JSGCRef env_ref;
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "os", js_core_os_use(ctx));
|
JS_AddGCRef(ctx, &env_ref);
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
|
env_ref.val = JS_NewObject(ctx);
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "shop_path",
|
JSValue tmp;
|
||||||
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
|
tmp = js_core_os_use(ctx);
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
JS_SetPropertyStr(ctx, env_ref.val, "os", tmp);
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "json", js_core_json_use(ctx));
|
tmp = JS_NewString(ctx, core_path);
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "init", JS_NULL);
|
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
||||||
JSValue args_arr = JS_NewArray(ctx);
|
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, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
||||||
|
tmp = js_core_json_use(ctx);
|
||||||
|
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
||||||
|
JS_SetPropertyStr(ctx, env_ref.val, "init", JS_NULL);
|
||||||
|
JSGCRef args_ref;
|
||||||
|
JS_AddGCRef(ctx, &args_ref);
|
||||||
|
args_ref.val = JS_NewArray(ctx);
|
||||||
for (int i = arg_start; i < argc; i++) {
|
for (int i = arg_start; i < argc; i++) {
|
||||||
JSValue str = JS_NewString(ctx, argv[i]);
|
JSValue str = JS_NewString(ctx, argv[i]);
|
||||||
JS_ArrayPush(ctx, &args_arr, str);
|
JS_ArrayPush(ctx, &args_ref.val, str);
|
||||||
}
|
}
|
||||||
JS_SetPropertyStr(ctx, hidden_env, "args", args_arr);
|
JS_SetPropertyStr(ctx, env_ref.val, "args", args_ref.val);
|
||||||
hidden_env = JS_Stone(ctx, hidden_env);
|
JS_DeleteGCRef(ctx, &args_ref);
|
||||||
|
JSValue hidden_env = JS_Stone(ctx, env_ref.val);
|
||||||
|
JS_DeleteGCRef(ctx, &env_ref);
|
||||||
|
|
||||||
JSValue result = JS_RunMachBin(ctx, (const uint8_t *)bin_data, bin_size, hidden_env);
|
JSValue result = JS_RunMachBin(ctx, (const uint8_t *)bin_data, bin_size, hidden_env);
|
||||||
free(bin_data);
|
free(bin_data);
|
||||||
|
|||||||
@@ -154,6 +154,43 @@ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
|
|||||||
|
|
||||||
#define countof(x) (sizeof(x)/sizeof((x)[0]))
|
#define countof(x) (sizeof(x)/sizeof((x)[0]))
|
||||||
|
|
||||||
|
/* GC safety macros for C functions that allocate multiple heap objects.
|
||||||
|
Any allocation call (JS_NewObject, JS_SetPropertyStr, etc.) can trigger GC.
|
||||||
|
JS_ROOT style: explicit, use .val to access the rooted value.
|
||||||
|
JS_LOCAL style: transparent, GC updates the C local through a pointer. */
|
||||||
|
|
||||||
|
#define JS_FRAME(ctx) \
|
||||||
|
JSContext *_js_ctx = (ctx); \
|
||||||
|
JSGCRef *_js_gc_frame = JS_GetGCFrame(_js_ctx); \
|
||||||
|
JSLocalRef *_js_local_frame = JS_GetLocalFrame(_js_ctx)
|
||||||
|
|
||||||
|
#define JS_ROOT(name, init) \
|
||||||
|
JSGCRef name; \
|
||||||
|
JS_PushGCRef(_js_ctx, &name); \
|
||||||
|
name.val = (init)
|
||||||
|
|
||||||
|
#define JS_LOCAL(name, init) \
|
||||||
|
JSValue name = (init); \
|
||||||
|
JSLocalRef name##__lr; \
|
||||||
|
name##__lr.ptr = &name; \
|
||||||
|
JS_PushLocalRef(_js_ctx, &name##__lr)
|
||||||
|
|
||||||
|
#define JS_RETURN(val) do { \
|
||||||
|
JSValue _js_ret = (val); \
|
||||||
|
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
|
||||||
|
return _js_ret; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define JS_RETURN_NULL() do { \
|
||||||
|
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
|
||||||
|
return JS_NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define JS_RETURN_EX() do { \
|
||||||
|
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
|
||||||
|
return JS_EXCEPTION; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
// Common macros for property access
|
// Common macros for property access
|
||||||
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
||||||
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
||||||
|
|||||||
156
source/mach.c
156
source/mach.c
@@ -981,6 +981,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
int ret = JS_SetProperty(ctx, obj, key, val);
|
int ret = JS_SetProperty(ctx, obj, key, val);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (ret < 0) goto disrupt;
|
if (ret < 0) goto disrupt;
|
||||||
|
mach_resolve_forward(&frame->slots[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,6 +1002,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
|
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (JS_IsException(r)) goto disrupt;
|
if (JS_IsException(r)) goto disrupt;
|
||||||
|
mach_resolve_forward(&frame->slots[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1127,6 +1129,17 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
result = frame->slots[a];
|
result = frame->slots[a];
|
||||||
if (JS_IsNull(frame->caller)) goto done;
|
if (JS_IsNull(frame->caller)) goto done;
|
||||||
{
|
{
|
||||||
|
#ifdef VALIDATE_GC
|
||||||
|
const char *callee_name = "?";
|
||||||
|
const char *callee_file = "?";
|
||||||
|
{
|
||||||
|
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
|
if (callee_fn->kind == JS_FUNC_KIND_REGISTER && callee_fn->u.reg.code) {
|
||||||
|
if (callee_fn->u.reg.code->name_cstr) callee_name = callee_fn->u.reg.code->name_cstr;
|
||||||
|
if (callee_fn->u.reg.code->filename_cstr) callee_file = callee_fn->u.reg.code->filename_cstr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
||||||
frame->caller = JS_NULL;
|
frame->caller = JS_NULL;
|
||||||
frame = caller;
|
frame = caller;
|
||||||
@@ -1143,8 +1156,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
void *rp = JS_VALUE_GET_PTR(result);
|
void *rp = JS_VALUE_GET_PTR(result);
|
||||||
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
|
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
|
||||||
if (!is_ct_ptr(ctx, rp))
|
if (!is_ct_ptr(ctx, rp))
|
||||||
fprintf(stderr, "VALIDATE_GC: stale RETURN into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u\n",
|
fprintf(stderr, "VALIDATE_GC: stale RETURN into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u callee=%s (%s) caller=%s (%s)\n",
|
||||||
ret_slot, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc);
|
ret_slot, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc,
|
||||||
|
callee_name, callee_file,
|
||||||
|
code->name_cstr ? code->name_cstr : "?",
|
||||||
|
code->filename_cstr ? code->filename_cstr : "?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1180,7 +1196,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MACH_NEWARRAY: {
|
case MACH_NEWARRAY: {
|
||||||
JSValue arr = JS_NewArray(ctx);
|
JSValue arr = JS_NewArrayCap(ctx, b);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (JS_IsException(arr)) { goto disrupt; }
|
if (JS_IsException(arr)) { goto disrupt; }
|
||||||
frame->slots[a] = arr;
|
frame->slots[a] = arr;
|
||||||
@@ -1474,6 +1490,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
int ret = JS_SetProperty(ctx, obj, key, val);
|
int ret = JS_SetProperty(ctx, obj, key, val);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (ret < 0) goto disrupt;
|
if (ret < 0) goto disrupt;
|
||||||
|
mach_resolve_forward(&frame->slots[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MACH_LOAD_INDEX: {
|
case MACH_LOAD_INDEX: {
|
||||||
@@ -1492,6 +1509,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
|
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (JS_IsException(r)) goto disrupt;
|
if (JS_IsException(r)) goto disrupt;
|
||||||
|
mach_resolve_forward(&frame->slots[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MACH_LOAD_DYNAMIC: {
|
case MACH_LOAD_DYNAMIC: {
|
||||||
@@ -1526,12 +1544,13 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
}
|
}
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (ret < 0) goto disrupt;
|
if (ret < 0) goto disrupt;
|
||||||
|
mach_resolve_forward(&frame->slots[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New record */
|
/* New record */
|
||||||
case MACH_NEWRECORD: {
|
case MACH_NEWRECORD: {
|
||||||
JSValue obj = JS_NewObject(ctx);
|
JSValue obj = b > 0 ? JS_NewObjectCap(ctx, b) : JS_NewObject(ctx);
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
if (JS_IsException(obj)) goto disrupt;
|
if (JS_IsException(obj)) goto disrupt;
|
||||||
frame->slots[a] = obj;
|
frame->slots[a] = obj;
|
||||||
@@ -1617,9 +1636,15 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
if (JS_IsPtr(ret)) {
|
if (JS_IsPtr(ret)) {
|
||||||
void *rp = JS_VALUE_GET_PTR(ret);
|
void *rp = JS_VALUE_GET_PTR(ret);
|
||||||
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
|
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
|
||||||
if (!is_ct_ptr(ctx, rp))
|
if (!is_ct_ptr(ctx, rp)) {
|
||||||
fprintf(stderr, "VALIDATE_GC: stale INVOKE result into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u kind=%d\n",
|
int magic = (fn->kind == JS_FUNC_KIND_C) ? fn->u.cfunc.magic : -1;
|
||||||
b, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc - 1, fn->kind);
|
void *cfp = (fn->kind == JS_FUNC_KIND_C) ? (void *)fn->u.cfunc.c_function.generic : NULL;
|
||||||
|
fprintf(stderr, "VALIDATE_GC: stale INVOKE result into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u kind=%d magic=%d cfunc=%p caller=%s (%s)\n",
|
||||||
|
b, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc - 1, fn->kind,
|
||||||
|
magic, cfp,
|
||||||
|
code->name_cstr ? code->name_cstr : "?",
|
||||||
|
code->filename_cstr ? code->filename_cstr : "?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1983,11 +2008,9 @@ static int mcode_reg_items(cJSON *it, cJSON **out) {
|
|||||||
/* record: [1]=dest, [2]=0(const) — no line/col suffix */
|
/* record: [1]=dest, [2]=0(const) — no line/col suffix */
|
||||||
if (!strcmp(op, "record")) { ADD(1); return c; }
|
if (!strcmp(op, "record")) { ADD(1); return c; }
|
||||||
|
|
||||||
/* array: [1]=dest, [2]=count(const), [3..]=elements (no line/col suffix) */
|
/* array: [1]=dest, [2]=count(const) — elements added via separate push instrs */
|
||||||
if (!strcmp(op, "array")) {
|
if (!strcmp(op, "array")) {
|
||||||
ADD(1);
|
ADD(1);
|
||||||
int cnt = (int)cJSON_GetArrayItem(it, 2)->valuedouble;
|
|
||||||
for (int j = 0; j < cnt; j++) ADD(3 + j);
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2043,8 +2066,8 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
int pinned = 1 + nr_args;
|
int pinned = 1 + nr_args;
|
||||||
for (int i = 0; i < pinned; i++) { first_ref[i] = 0; last_ref[i] = n; }
|
for (int i = 0; i < pinned; i++) { first_ref[i] = 0; last_ref[i] = n; }
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
{ cJSON *it = instrs ? instrs->child : NULL;
|
||||||
cJSON *it = cJSON_GetArrayItem(instrs, i);
|
for (int i = 0; it; i++, it = it->next) {
|
||||||
if (!cJSON_IsArray(it)) continue;
|
if (!cJSON_IsArray(it)) continue;
|
||||||
cJSON *regs[MAX_REG_ITEMS];
|
cJSON *regs[MAX_REG_ITEMS];
|
||||||
int rc = mcode_reg_items(it, regs);
|
int rc = mcode_reg_items(it, regs);
|
||||||
@@ -2054,7 +2077,7 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
if (first_ref[s] < 0) first_ref[s] = i;
|
if (first_ref[s] < 0) first_ref[s] = i;
|
||||||
last_ref[s] = i;
|
last_ref[s] = i;
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
|
|
||||||
/* Step 1a: extend live ranges for closure-captured slots.
|
/* Step 1a: extend live ranges for closure-captured slots.
|
||||||
If a child function captures a parent slot via get/put, that slot must
|
If a child function captures a parent slot via get/put, that slot must
|
||||||
@@ -2076,8 +2099,8 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
typedef struct { const char *name; int pos; } LabelPos;
|
typedef struct { const char *name; int pos; } LabelPos;
|
||||||
int lbl_cap = 32, lbl_n = 0;
|
int lbl_cap = 32, lbl_n = 0;
|
||||||
LabelPos *lbls = sys_malloc(lbl_cap * sizeof(LabelPos));
|
LabelPos *lbls = sys_malloc(lbl_cap * sizeof(LabelPos));
|
||||||
for (int i = 0; i < n; i++) {
|
{ cJSON *it = instrs ? instrs->child : NULL;
|
||||||
cJSON *it = cJSON_GetArrayItem(instrs, i);
|
for (int i = 0; it; i++, it = it->next) {
|
||||||
if (cJSON_IsString(it)) {
|
if (cJSON_IsString(it)) {
|
||||||
if (lbl_n >= lbl_cap) {
|
if (lbl_n >= lbl_cap) {
|
||||||
lbl_cap *= 2;
|
lbl_cap *= 2;
|
||||||
@@ -2085,23 +2108,23 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
}
|
}
|
||||||
lbls[lbl_n++] = (LabelPos){it->valuestring, i};
|
lbls[lbl_n++] = (LabelPos){it->valuestring, i};
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
/* Find backward jumps and extend live ranges */
|
/* Find backward jumps and extend live ranges */
|
||||||
int changed = 1;
|
int changed = 1;
|
||||||
while (changed) {
|
while (changed) {
|
||||||
changed = 0;
|
changed = 0;
|
||||||
for (int i = 0; i < n; i++) {
|
cJSON *it = instrs ? instrs->child : NULL;
|
||||||
cJSON *it = cJSON_GetArrayItem(instrs, i);
|
for (int i = 0; it; i++, it = it->next) {
|
||||||
if (!cJSON_IsArray(it)) continue;
|
if (!cJSON_IsArray(it)) continue;
|
||||||
int sz = cJSON_GetArraySize(it);
|
int sz = cJSON_GetArraySize(it);
|
||||||
if (sz < 3) continue;
|
if (sz < 3) continue;
|
||||||
const char *op = cJSON_GetArrayItem(it, 0)->valuestring;
|
const char *op = it->child->valuestring;
|
||||||
const char *target = NULL;
|
const char *target = NULL;
|
||||||
if (!strcmp(op, "jump")) {
|
if (!strcmp(op, "jump")) {
|
||||||
target = cJSON_GetArrayItem(it, 1)->valuestring;
|
target = it->child->next->valuestring;
|
||||||
} else if (!strcmp(op, "jump_true") || !strcmp(op, "jump_false") ||
|
} else if (!strcmp(op, "jump_true") || !strcmp(op, "jump_false") ||
|
||||||
!strcmp(op, "jump_not_null")) {
|
!strcmp(op, "jump_not_null")) {
|
||||||
target = cJSON_GetArrayItem(it, 2)->valuestring;
|
target = it->child->next->next->valuestring;
|
||||||
}
|
}
|
||||||
if (!target) continue;
|
if (!target) continue;
|
||||||
/* Find label position */
|
/* Find label position */
|
||||||
@@ -2207,8 +2230,8 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Step 3: apply remap to instructions */
|
/* Step 3: apply remap to instructions */
|
||||||
for (int i = 0; i < n; i++) {
|
{ cJSON *it = instrs ? instrs->child : NULL;
|
||||||
cJSON *it = cJSON_GetArrayItem(instrs, i);
|
for (int i = 0; it; i++, it = it->next) {
|
||||||
if (!cJSON_IsArray(it)) continue;
|
if (!cJSON_IsArray(it)) continue;
|
||||||
cJSON *regs[MAX_REG_ITEMS];
|
cJSON *regs[MAX_REG_ITEMS];
|
||||||
int rc = mcode_reg_items(it, regs);
|
int rc = mcode_reg_items(it, regs);
|
||||||
@@ -2218,7 +2241,7 @@ static int *mcode_compress_regs(cJSON *fobj, int *out_old_nr_slots,
|
|||||||
cJSON_SetNumberValue(regs[j], remap[old]);
|
cJSON_SetNumberValue(regs[j], remap[old]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
|
|
||||||
/* Update nr_slots in the JSON */
|
/* Update nr_slots in the JSON */
|
||||||
cJSON_SetNumberValue(nr_slots_j, new_max);
|
cJSON_SetNumberValue(nr_slots_j, new_max);
|
||||||
@@ -2250,8 +2273,8 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
|||||||
s.flat_to_pc = sys_malloc((n + 1) * sizeof(int));
|
s.flat_to_pc = sys_malloc((n + 1) * sizeof(int));
|
||||||
s.flat_count = n;
|
s.flat_count = n;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
{ cJSON *it = instrs ? instrs->child : NULL;
|
||||||
cJSON *it = cJSON_GetArrayItem(instrs, i);
|
for (int i = 0; it; i++, it = it->next) {
|
||||||
s.flat_to_pc[i] = s.code_count;
|
s.flat_to_pc[i] = s.code_count;
|
||||||
if (cJSON_IsString(it)) {
|
if (cJSON_IsString(it)) {
|
||||||
ml_label(&s, it->valuestring);
|
ml_label(&s, it->valuestring);
|
||||||
@@ -2450,15 +2473,10 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
|||||||
}
|
}
|
||||||
/* Array/Object creation */
|
/* Array/Object creation */
|
||||||
else if (strcmp(op, "array") == 0) {
|
else if (strcmp(op, "array") == 0) {
|
||||||
int dest = A1, count = A2;
|
EM(MACH_ABC(MACH_NEWARRAY, A1, A2, 0));
|
||||||
EM(MACH_ABC(MACH_NEWARRAY, dest, 0, 0));
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
int elem = ml_int(it, 3 + j);
|
|
||||||
EM(MACH_ABC(MACH_PUSH, dest, elem, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (strcmp(op, "record") == 0) {
|
else if (strcmp(op, "record") == 0) {
|
||||||
EM(MACH_ABC(MACH_NEWRECORD, A1, 0, 0));
|
EM(MACH_ABC(MACH_NEWRECORD, A1, A2, 0));
|
||||||
}
|
}
|
||||||
/* Push/Pop */
|
/* Push/Pop */
|
||||||
else if (strcmp(op, "push") == 0) {
|
else if (strcmp(op, "push") == 0) {
|
||||||
@@ -2551,7 +2569,7 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
|||||||
/* Unknown opcode — emit NOP */
|
/* Unknown opcode — emit NOP */
|
||||||
EM(MACH_ABC(MACH_NOP, 0, 0, 0));
|
EM(MACH_ABC(MACH_NOP, 0, 0, 0));
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
/* Sentinel for flat_to_pc */
|
/* Sentinel for flat_to_pc */
|
||||||
s.flat_to_pc[n] = s.code_count;
|
s.flat_to_pc[n] = s.code_count;
|
||||||
|
|
||||||
@@ -2690,34 +2708,32 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
/* Scan main's instructions */
|
/* Scan main's instructions */
|
||||||
{
|
{
|
||||||
cJSON *main_instrs = cJSON_GetObjectItemCaseSensitive(main_obj, "instructions");
|
cJSON *main_instrs = cJSON_GetObjectItemCaseSensitive(main_obj, "instructions");
|
||||||
int mn = main_instrs ? cJSON_GetArraySize(main_instrs) : 0;
|
cJSON *it = main_instrs ? main_instrs->child : NULL;
|
||||||
for (int i = 0; i < mn; i++) {
|
for (; it; it = it->next) {
|
||||||
cJSON *it = cJSON_GetArrayItem(main_instrs, i);
|
|
||||||
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue;
|
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue;
|
||||||
const char *op = cJSON_GetArrayItem(it, 0)->valuestring;
|
const char *op = it->child->valuestring;
|
||||||
if (!strcmp(op, "function")) {
|
if (!strcmp(op, "function")) {
|
||||||
int child_idx = (int)cJSON_GetArrayItem(it, 2)->valuedouble;
|
int child_idx = (int)it->child->next->next->valuedouble;
|
||||||
if (child_idx >= 0 && child_idx < func_count)
|
if (child_idx >= 0 && child_idx < func_count)
|
||||||
parent_of[child_idx] = func_count; /* main */
|
parent_of[child_idx] = func_count; /* main */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Scan each function's instructions */
|
/* Scan each function's instructions */
|
||||||
for (int fi = 0; fi < func_count; fi++) {
|
{ cJSON *fobj = funcs_arr ? funcs_arr->child : NULL;
|
||||||
cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi);
|
for (int fi = 0; fobj; fi++, fobj = fobj->next) {
|
||||||
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
||||||
int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0;
|
cJSON *it = finstrs ? finstrs->child : NULL;
|
||||||
for (int i = 0; i < fn; i++) {
|
for (; it; it = it->next) {
|
||||||
cJSON *it = cJSON_GetArrayItem(finstrs, i);
|
|
||||||
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue;
|
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 3) continue;
|
||||||
const char *op = cJSON_GetArrayItem(it, 0)->valuestring;
|
const char *op = it->child->valuestring;
|
||||||
if (!strcmp(op, "function")) {
|
if (!strcmp(op, "function")) {
|
||||||
int child_idx = (int)cJSON_GetArrayItem(it, 2)->valuedouble;
|
int child_idx = (int)it->child->next->next->valuedouble;
|
||||||
if (child_idx >= 0 && child_idx < func_count)
|
if (child_idx >= 0 && child_idx < func_count)
|
||||||
parent_of[child_idx] = fi;
|
parent_of[child_idx] = fi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
|
|
||||||
/* Build per-function capture sets: for each function F, which of its slots
|
/* Build per-function capture sets: for each function F, which of its slots
|
||||||
are captured by descendant functions via get/put. Captured slots must
|
are captured by descendant functions via get/put. Captured slots must
|
||||||
@@ -2727,17 +2743,16 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
memset(cap_slots, 0, (func_count + 1) * sizeof(int *));
|
memset(cap_slots, 0, (func_count + 1) * sizeof(int *));
|
||||||
memset(cap_counts, 0, (func_count + 1) * sizeof(int));
|
memset(cap_counts, 0, (func_count + 1) * sizeof(int));
|
||||||
|
|
||||||
for (int fi = 0; fi < func_count; fi++) {
|
{ cJSON *fobj = funcs_arr ? funcs_arr->child : NULL;
|
||||||
cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi);
|
for (int fi = 0; fobj; fi++, fobj = fobj->next) {
|
||||||
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
||||||
int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0;
|
cJSON *it = finstrs ? finstrs->child : NULL;
|
||||||
for (int i = 0; i < fn; i++) {
|
for (; it; it = it->next) {
|
||||||
cJSON *it = cJSON_GetArrayItem(finstrs, i);
|
|
||||||
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue;
|
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue;
|
||||||
const char *op = cJSON_GetArrayItem(it, 0)->valuestring;
|
const char *op = it->child->valuestring;
|
||||||
if (strcmp(op, "get") && strcmp(op, "put")) continue;
|
if (strcmp(op, "get") && strcmp(op, "put")) continue;
|
||||||
int slot = (int)cJSON_GetArrayItem(it, 2)->valuedouble;
|
int slot = (int)it->child->next->next->valuedouble;
|
||||||
int level = (int)cJSON_GetArrayItem(it, 3)->valuedouble;
|
int level = (int)it->child->next->next->next->valuedouble;
|
||||||
/* Walk up parent chain to find the ancestor whose slot is referenced */
|
/* Walk up parent chain to find the ancestor whose slot is referenced */
|
||||||
int ancestor = fi;
|
int ancestor = fi;
|
||||||
for (int l = 0; l < level && ancestor >= 0; l++)
|
for (int l = 0; l < level && ancestor >= 0; l++)
|
||||||
@@ -2753,7 +2768,7 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
cap_slots[ancestor][cap_counts[ancestor]++] = slot;
|
cap_slots[ancestor][cap_counts[ancestor]++] = slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
|
|
||||||
/* Compress registers for functions that exceed 8-bit slot limits.
|
/* Compress registers for functions that exceed 8-bit slot limits.
|
||||||
Save remap tables so we can fix get/put parent_slot references. */
|
Save remap tables so we can fix get/put parent_slot references. */
|
||||||
@@ -2761,9 +2776,11 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
int *remap_sizes = sys_malloc((func_count + 1) * sizeof(int));
|
int *remap_sizes = sys_malloc((func_count + 1) * sizeof(int));
|
||||||
memset(remaps, 0, (func_count + 1) * sizeof(int *));
|
memset(remaps, 0, (func_count + 1) * sizeof(int *));
|
||||||
|
|
||||||
for (int i = 0; i < func_count; i++)
|
{ cJSON *fobj = funcs_arr ? funcs_arr->child : NULL;
|
||||||
remaps[i] = mcode_compress_regs(cJSON_GetArrayItem(funcs_arr, i),
|
for (int i = 0; fobj; i++, fobj = fobj->next)
|
||||||
|
remaps[i] = mcode_compress_regs(fobj,
|
||||||
&remap_sizes[i], cap_slots[i], cap_counts[i]);
|
&remap_sizes[i], cap_slots[i], cap_counts[i]);
|
||||||
|
}
|
||||||
/* main is stored at index func_count in our arrays */
|
/* main is stored at index func_count in our arrays */
|
||||||
remaps[func_count] = mcode_compress_regs(main_obj,
|
remaps[func_count] = mcode_compress_regs(main_obj,
|
||||||
&remap_sizes[func_count], cap_slots[func_count], cap_counts[func_count]);
|
&remap_sizes[func_count], cap_slots[func_count], cap_counts[func_count]);
|
||||||
@@ -2775,16 +2792,15 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
sys_free(cap_counts);
|
sys_free(cap_counts);
|
||||||
|
|
||||||
/* Fix up get/put parent_slot references using ancestor remap tables */
|
/* Fix up get/put parent_slot references using ancestor remap tables */
|
||||||
for (int fi = 0; fi < func_count; fi++) {
|
{ cJSON *fobj = funcs_arr ? funcs_arr->child : NULL;
|
||||||
cJSON *fobj = cJSON_GetArrayItem(funcs_arr, fi);
|
for (int fi = 0; fobj; fi++, fobj = fobj->next) {
|
||||||
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
cJSON *finstrs = cJSON_GetObjectItemCaseSensitive(fobj, "instructions");
|
||||||
int fn = finstrs ? cJSON_GetArraySize(finstrs) : 0;
|
cJSON *it = finstrs ? finstrs->child : NULL;
|
||||||
for (int i = 0; i < fn; i++) {
|
for (; it; it = it->next) {
|
||||||
cJSON *it = cJSON_GetArrayItem(finstrs, i);
|
|
||||||
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue;
|
if (!cJSON_IsArray(it) || cJSON_GetArraySize(it) < 4) continue;
|
||||||
const char *op = cJSON_GetArrayItem(it, 0)->valuestring;
|
const char *op = it->child->valuestring;
|
||||||
if (strcmp(op, "get") && strcmp(op, "put")) continue;
|
if (strcmp(op, "get") && strcmp(op, "put")) continue;
|
||||||
int level = (int)cJSON_GetArrayItem(it, 3)->valuedouble;
|
int level = (int)it->child->next->next->next->valuedouble;
|
||||||
/* Walk up parent chain 'level' times to find ancestor */
|
/* Walk up parent chain 'level' times to find ancestor */
|
||||||
int ancestor = fi;
|
int ancestor = fi;
|
||||||
for (int l = 0; l < level && ancestor >= 0; l++) {
|
for (int l = 0; l < level && ancestor >= 0; l++) {
|
||||||
@@ -2793,14 +2809,14 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
if (ancestor < 0) continue; /* unknown parent — leave as is */
|
if (ancestor < 0) continue; /* unknown parent — leave as is */
|
||||||
int *anc_remap = remaps[ancestor];
|
int *anc_remap = remaps[ancestor];
|
||||||
if (!anc_remap) continue; /* ancestor wasn't compressed */
|
if (!anc_remap) continue; /* ancestor wasn't compressed */
|
||||||
cJSON *slot_item = cJSON_GetArrayItem(it, 2);
|
cJSON *slot_item = it->child->next->next;
|
||||||
int old_slot = (int)slot_item->valuedouble;
|
int old_slot = (int)slot_item->valuedouble;
|
||||||
if (old_slot >= 0 && old_slot < remap_sizes[ancestor]) {
|
if (old_slot >= 0 && old_slot < remap_sizes[ancestor]) {
|
||||||
int new_slot = anc_remap[old_slot];
|
int new_slot = anc_remap[old_slot];
|
||||||
cJSON_SetNumberValue(slot_item, new_slot);
|
cJSON_SetNumberValue(slot_item, new_slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} }
|
||||||
|
|
||||||
/* Free remap tables */
|
/* Free remap tables */
|
||||||
for (int i = 0; i <= func_count; i++)
|
for (int i = 0; i <= func_count; i++)
|
||||||
@@ -2814,8 +2830,10 @@ MachCode *mach_compile_mcode(cJSON *mcode_json) {
|
|||||||
if (func_count > 0) {
|
if (func_count > 0) {
|
||||||
compiled = sys_malloc(func_count * sizeof(MachCode *));
|
compiled = sys_malloc(func_count * sizeof(MachCode *));
|
||||||
memset(compiled, 0, func_count * sizeof(MachCode *));
|
memset(compiled, 0, func_count * sizeof(MachCode *));
|
||||||
for (int i = 0; i < func_count; i++)
|
{ cJSON *fobj = funcs_arr->child;
|
||||||
compiled[i] = mcode_lower_func(cJSON_GetArrayItem(funcs_arr, i), filename);
|
for (int i = 0; fobj; i++, fobj = fobj->next)
|
||||||
|
compiled[i] = mcode_lower_func(fobj, filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile main */
|
/* Compile main */
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_actor_use(JSContext *js) {
|
JSValue js_core_actor_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js,mod,js_actor_funcs,countof(js_actor_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_actor_funcs, countof(js_actor_funcs));
|
||||||
}
|
JS_RETURN(mod.val);
|
||||||
|
}
|
||||||
|
|||||||
@@ -845,6 +845,18 @@ static inline objhdr_t *chase(JSValue v) {
|
|||||||
return oh;
|
return oh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Resolve a forward pointer in-place. After rec_resize the old record
|
||||||
|
gets a forward header; any JSValue slot still pointing at it must be
|
||||||
|
updated to follow the chain to the live copy. */
|
||||||
|
static inline void mach_resolve_forward(JSValue *slot) {
|
||||||
|
if (JS_IsPtr(*slot)) {
|
||||||
|
objhdr_t h = *(objhdr_t *)JS_VALUE_GET_PTR(*slot);
|
||||||
|
if (objhdr_type(h) == OBJ_FORWARD) {
|
||||||
|
*slot = JS_MKPTR(objhdr_fwd_ptr(h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Inline type checks — use these in the VM dispatch loop to avoid
|
/* Inline type checks — use these in the VM dispatch loop to avoid
|
||||||
function call overhead. The public API (JS_IsArray etc. in quickjs.h)
|
function call overhead. The public API (JS_IsArray etc. in quickjs.h)
|
||||||
remains non-inline for external callers; those wrappers live in runtime.c. */
|
remains non-inline for external callers; those wrappers live in runtime.c. */
|
||||||
@@ -1070,6 +1082,11 @@ struct JSContext {
|
|||||||
uint8_t *heap_end; /* end of block */
|
uint8_t *heap_end; /* end of block */
|
||||||
size_t current_block_size; /* current block size (64KB initially) */
|
size_t current_block_size; /* current block size (64KB initially) */
|
||||||
size_t next_block_size; /* doubles if <10% recovered after GC */
|
size_t next_block_size; /* doubles if <10% recovered after GC */
|
||||||
|
int gc_poor_streak; /* consecutive poor-recovery GC cycles */
|
||||||
|
|
||||||
|
/* GC stats (lightweight, always on) */
|
||||||
|
uint64_t gc_count; /* number of GC cycles */
|
||||||
|
uint64_t gc_bytes_copied; /* total bytes copied across all GCs */
|
||||||
|
|
||||||
/* Constant text pool — compilation constants */
|
/* Constant text pool — compilation constants */
|
||||||
uint8_t *ct_base; /* pool base */
|
uint8_t *ct_base; /* pool base */
|
||||||
@@ -1092,6 +1109,7 @@ struct JSContext {
|
|||||||
|
|
||||||
JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */
|
JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */
|
||||||
JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */
|
JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */
|
||||||
|
JSLocalRef *top_local_ref; /* for JS_LOCAL macro - GC updates C locals through pointers */
|
||||||
CCallRoot *c_call_root; /* stack of auto-rooted C call argv arrays */
|
CCallRoot *c_call_root; /* stack of auto-rooted C call argv arrays */
|
||||||
|
|
||||||
int class_count; /* size of class_array and class_proto */
|
int class_count; /* size of class_array and class_proto */
|
||||||
@@ -1185,7 +1203,15 @@ static int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size);
|
|||||||
|
|
||||||
/* Helper to check if a pointer is in constant text pool memory */
|
/* Helper to check if a pointer is in constant text pool memory */
|
||||||
static inline int is_ct_ptr (JSContext *ctx, void *ptr) {
|
static inline int is_ct_ptr (JSContext *ctx, void *ptr) {
|
||||||
return (uint8_t *)ptr >= ctx->ct_base && (uint8_t *)ptr < ctx->ct_end;
|
uint8_t *p = (uint8_t *)ptr;
|
||||||
|
if (p >= ctx->ct_base && p < ctx->ct_end) return 1;
|
||||||
|
/* Also check overflow pages */
|
||||||
|
CTPage *page = (CTPage *)ctx->ct_pages;
|
||||||
|
while (page) {
|
||||||
|
if (p >= page->data && p < page->data + page->size) return 1;
|
||||||
|
page = page->next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HEAP_CHECK
|
#ifdef HEAP_CHECK
|
||||||
|
|||||||
@@ -146,10 +146,22 @@ typedef struct JSGCRef {
|
|||||||
struct JSGCRef *prev;
|
struct JSGCRef *prev;
|
||||||
} JSGCRef;
|
} JSGCRef;
|
||||||
|
|
||||||
|
/* JSLocalRef - GC updates C locals through pointers (OCaml-style) */
|
||||||
|
typedef struct JSLocalRef {
|
||||||
|
JSValue *ptr;
|
||||||
|
struct JSLocalRef *prev;
|
||||||
|
} JSLocalRef;
|
||||||
|
|
||||||
/* stack of JSGCRef */
|
/* stack of JSGCRef */
|
||||||
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
|
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
|
||||||
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);
|
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);
|
||||||
|
|
||||||
|
/* JS_FRAME/JS_ROOT/JS_LOCAL helpers (for use from cell.h macros) */
|
||||||
|
JSGCRef *JS_GetGCFrame(JSContext *ctx);
|
||||||
|
JSLocalRef *JS_GetLocalFrame(JSContext *ctx);
|
||||||
|
void JS_PushLocalRef(JSContext *ctx, JSLocalRef *ref);
|
||||||
|
void JS_RestoreFrame(JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame);
|
||||||
|
|
||||||
#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)
|
#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)
|
||||||
#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)
|
#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)
|
||||||
|
|
||||||
@@ -502,9 +514,11 @@ JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_i
|
|||||||
JSValue JS_NewObjectClass (JSContext *ctx, int class_id);
|
JSValue JS_NewObjectClass (JSContext *ctx, int class_id);
|
||||||
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto);
|
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto);
|
||||||
JSValue JS_NewObject (JSContext *ctx);
|
JSValue JS_NewObject (JSContext *ctx);
|
||||||
|
JSValue JS_NewObjectCap (JSContext *ctx, uint32_t n);
|
||||||
|
|
||||||
JSValue JS_NewArray (JSContext *ctx);
|
JSValue JS_NewArray (JSContext *ctx);
|
||||||
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
|
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
|
||||||
|
JSValue JS_NewArrayCap (JSContext *ctx, uint32_t cap);
|
||||||
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
|
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
|
||||||
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
|
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
|
||||||
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
|
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
|
||||||
|
|||||||
256
source/runtime.c
256
source/runtime.c
@@ -183,6 +183,25 @@ void JS_DeleteGCRef (JSContext *ctx, JSGCRef *ref) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* JS_FRAME/JS_ROOT/JS_LOCAL helper functions */
|
||||||
|
JSGCRef *JS_GetGCFrame (JSContext *ctx) {
|
||||||
|
return ctx->top_gc_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSLocalRef *JS_GetLocalFrame (JSContext *ctx) {
|
||||||
|
return ctx->top_local_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JS_PushLocalRef (JSContext *ctx, JSLocalRef *ref) {
|
||||||
|
ref->prev = ctx->top_local_ref;
|
||||||
|
ctx->top_local_ref = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JS_RestoreFrame (JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame) {
|
||||||
|
ctx->top_gc_ref = gc_frame;
|
||||||
|
ctx->top_local_ref = local_frame;
|
||||||
|
}
|
||||||
|
|
||||||
void *ct_alloc (JSContext *ctx, size_t bytes, size_t align) {
|
void *ct_alloc (JSContext *ctx, size_t bytes, size_t align) {
|
||||||
/* Align the request */
|
/* Align the request */
|
||||||
bytes = (bytes + align - 1) & ~(align - 1);
|
bytes = (bytes + align - 1) & ~(align - 1);
|
||||||
@@ -1300,8 +1319,6 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
void *ptr = JS_VALUE_GET_PTR (v);
|
void *ptr = JS_VALUE_GET_PTR (v);
|
||||||
if (is_ct_ptr (ctx, ptr)) return v;
|
|
||||||
|
|
||||||
if (!ptr_in_range (ptr, from_base, from_end)) return v;
|
if (!ptr_in_range (ptr, from_base, from_end)) return v;
|
||||||
|
|
||||||
objhdr_t *hdr_ptr = (objhdr_t *)ptr;
|
objhdr_t *hdr_ptr = (objhdr_t *)ptr;
|
||||||
@@ -1608,6 +1625,14 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
|||||||
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
|
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy JS_LOCAL roots (update C locals through pointers) */
|
||||||
|
#ifdef DUMP_GC_DETAIL
|
||||||
|
printf(" roots: top_local_ref\n"); fflush(stdout);
|
||||||
|
#endif
|
||||||
|
for (JSLocalRef *ref = ctx->top_local_ref; ref != NULL; ref = ref->prev) {
|
||||||
|
*ref->ptr = gc_copy_value (ctx, *ref->ptr, from_base, from_end, to_base, &to_free, to_end);
|
||||||
|
}
|
||||||
|
|
||||||
/* Copy JS_AddGCRef/JS_DeleteGCRef roots */
|
/* Copy JS_AddGCRef/JS_DeleteGCRef roots */
|
||||||
#ifdef DUMP_GC_DETAIL
|
#ifdef DUMP_GC_DETAIL
|
||||||
printf(" roots: last_gc_ref\n"); fflush(stdout);
|
printf(" roots: last_gc_ref\n"); fflush(stdout);
|
||||||
@@ -1651,6 +1676,10 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
|||||||
|
|
||||||
/* Update context with new block */
|
/* Update context with new block */
|
||||||
size_t new_used = to_free - to_base;
|
size_t new_used = to_free - to_base;
|
||||||
|
|
||||||
|
/* Update GC stats */
|
||||||
|
ctx->gc_count++;
|
||||||
|
ctx->gc_bytes_copied += new_used;
|
||||||
size_t recovered = old_used > new_used ? old_used - new_used : 0;
|
size_t recovered = old_used > new_used ? old_used - new_used : 0;
|
||||||
|
|
||||||
ctx->heap_base = to_base;
|
ctx->heap_base = to_base;
|
||||||
@@ -1670,19 +1699,23 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* If <20% recovered, double next block size for future allocations
|
/* If <40% recovered, grow next block size for future allocations.
|
||||||
But only if allow_grow is set (i.e., GC was triggered due to low space) */
|
First poor recovery: double. Consecutive poor: quadruple. */
|
||||||
#ifdef DUMP_GC
|
#ifdef DUMP_GC
|
||||||
int will_grow = 0;
|
int will_grow = 0;
|
||||||
#endif
|
#endif
|
||||||
if (allow_grow && recovered > 0 && old_used > 0 && recovered < old_used / 5) {
|
if (allow_grow && recovered > 0 && old_used > 0 && recovered < old_used * 2 / 5) {
|
||||||
size_t doubled = new_size * 2;
|
size_t factor = ctx->gc_poor_streak >= 1 ? 4 : 2;
|
||||||
if (doubled <= buddy_max_block(&ctx->rt->buddy)) {
|
size_t grown = new_size * factor;
|
||||||
ctx->next_block_size = doubled;
|
if (grown <= buddy_max_block(&ctx->rt->buddy)) {
|
||||||
|
ctx->next_block_size = grown;
|
||||||
#ifdef DUMP_GC
|
#ifdef DUMP_GC
|
||||||
will_grow = 1;
|
will_grow = 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
ctx->gc_poor_streak++;
|
||||||
|
} else {
|
||||||
|
ctx->gc_poor_streak = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DUMP_GC
|
#ifdef DUMP_GC
|
||||||
@@ -1828,6 +1861,20 @@ JSContext *JS_NewContextRawWithHeapSize (JSRuntime *rt, size_t heap_size) {
|
|||||||
/* Initialize per-context execution state (moved from JSRuntime) */
|
/* Initialize per-context execution state (moved from JSRuntime) */
|
||||||
ctx->current_exception = JS_NULL;
|
ctx->current_exception = JS_NULL;
|
||||||
|
|
||||||
|
/* Initialize constant text pool (avoids overflow pages for common case) */
|
||||||
|
{
|
||||||
|
size_t ct_pool_size = 64 * 1024; /* 64KB initial CT pool */
|
||||||
|
ctx->ct_base = js_malloc_rt (ct_pool_size);
|
||||||
|
if (!ctx->ct_base) {
|
||||||
|
js_free_rt (ctx->class_array);
|
||||||
|
js_free_rt (ctx->class_proto);
|
||||||
|
js_free_rt (ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ctx->ct_free = ctx->ct_base;
|
||||||
|
ctx->ct_end = ctx->ct_base + ct_pool_size;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize constant text intern table */
|
/* Initialize constant text intern table */
|
||||||
ctx->ct_pages = NULL;
|
ctx->ct_pages = NULL;
|
||||||
ctx->ct_array = NULL;
|
ctx->ct_array = NULL;
|
||||||
@@ -1917,6 +1964,7 @@ void JS_FreeContext (JSContext *ctx) {
|
|||||||
|
|
||||||
/* Free constant text pool and intern table */
|
/* Free constant text pool and intern table */
|
||||||
ct_free_all (ctx);
|
ct_free_all (ctx);
|
||||||
|
if (ctx->ct_base) js_free_rt (ctx->ct_base);
|
||||||
js_free_rt (ctx->ct_hash);
|
js_free_rt (ctx->ct_hash);
|
||||||
js_free_rt (ctx->ct_array);
|
js_free_rt (ctx->ct_array);
|
||||||
|
|
||||||
@@ -2109,8 +2157,24 @@ static JSValue js_sub_string_val (JSContext *ctx, JSValue src, int start, int en
|
|||||||
return js_new_string8_len (ctx, buf, len);
|
return js_new_string8_len (ctx, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Heap string — fast path for short ASCII substrings (avoids heap alloc) */
|
||||||
|
JSText *p = JS_VALUE_GET_STRING (src);
|
||||||
|
if (len <= MIST_ASCII_MAX_LEN) {
|
||||||
|
char buf[MIST_ASCII_MAX_LEN];
|
||||||
|
int all_ascii = 1;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
uint32_t c = string_get (p, start + i);
|
||||||
|
if (c >= 0x80) { all_ascii = 0; break; }
|
||||||
|
buf[i] = (char)c;
|
||||||
|
}
|
||||||
|
if (all_ascii) {
|
||||||
|
JSValue imm = MIST_TryNewImmediateASCII (buf, len);
|
||||||
|
if (!JS_IsNull (imm)) return imm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Heap string — delegate to existing js_sub_string */
|
/* Heap string — delegate to existing js_sub_string */
|
||||||
return js_sub_string (ctx, JS_VALUE_GET_STRING (src), start, end);
|
return js_sub_string (ctx, p, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a new pretext (mutable JSText) with initial capacity */
|
/* Allocate a new pretext (mutable JSText) with initial capacity */
|
||||||
@@ -2619,11 +2683,51 @@ JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len) {
|
|||||||
|
|
||||||
JSValue JS_NewArray (JSContext *ctx) { return JS_NewArrayLen (ctx, 0); }
|
JSValue JS_NewArray (JSContext *ctx) { return JS_NewArrayLen (ctx, 0); }
|
||||||
|
|
||||||
|
/* Create array with pre-allocated capacity but len=0 (for push-fill patterns) */
|
||||||
|
JSValue JS_NewArrayCap (JSContext *ctx, uint32_t cap) {
|
||||||
|
if (cap == 0) cap = JS_ARRAY_INITIAL_SIZE;
|
||||||
|
size_t values_size = sizeof (JSValue) * cap;
|
||||||
|
size_t total_size = sizeof (JSArray) + values_size;
|
||||||
|
JSArray *arr = js_malloc (ctx, total_size);
|
||||||
|
if (!arr) return JS_EXCEPTION;
|
||||||
|
arr->mist_hdr = objhdr_make (cap, OBJ_ARRAY, false, false, false, false);
|
||||||
|
arr->len = 0;
|
||||||
|
for (uint32_t i = 0; i < cap; i++)
|
||||||
|
arr->values[i] = JS_NULL;
|
||||||
|
return JS_MKPTR (arr);
|
||||||
|
}
|
||||||
|
|
||||||
JSValue JS_NewObject (JSContext *ctx) {
|
JSValue JS_NewObject (JSContext *ctx) {
|
||||||
/* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
|
/* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
|
||||||
return JS_NewObjectProtoClass (ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
|
return JS_NewObjectProtoClass (ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create object with pre-allocated hash table for n properties */
|
||||||
|
JSValue JS_NewObjectCap (JSContext *ctx, uint32_t n) {
|
||||||
|
/* slot 0 is reserved, so need n+1 slots minimum.
|
||||||
|
Hash table needs ~2x entries for good load factor.
|
||||||
|
mask must be power-of-2 minus 1. */
|
||||||
|
uint32_t need = (n + 1) * 2;
|
||||||
|
uint32_t mask = JS_RECORD_INITIAL_MASK;
|
||||||
|
while (mask + 1 < need) mask = (mask << 1) | 1;
|
||||||
|
|
||||||
|
JSGCRef proto_ref;
|
||||||
|
JS_PushGCRef (ctx, &proto_ref);
|
||||||
|
proto_ref.val = ctx->class_proto[JS_CLASS_OBJECT];
|
||||||
|
|
||||||
|
JSRecord *rec = js_new_record_class (ctx, mask, JS_CLASS_OBJECT);
|
||||||
|
|
||||||
|
JSValue proto_val = proto_ref.val;
|
||||||
|
JS_PopGCRef (ctx, &proto_ref);
|
||||||
|
|
||||||
|
if (!rec) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
if (JS_IsRecord (proto_val))
|
||||||
|
rec->proto = proto_val;
|
||||||
|
|
||||||
|
return JS_MKPTR (rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Note: at least 'length' arguments will be readable in 'argv' */
|
/* Note: at least 'length' arguments will be readable in 'argv' */
|
||||||
static JSValue JS_NewCFunction3 (JSContext *ctx, JSCFunction *func, const char *name, int length, JSCFunctionEnum cproto, int magic) {
|
static JSValue JS_NewCFunction3 (JSContext *ctx, JSCFunction *func, const char *name, int length, JSCFunctionEnum cproto, int magic) {
|
||||||
@@ -4383,6 +4487,10 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
|
|||||||
ctx->trace_hook (ctx, JS_HOOK_CALL, &dbg, ctx->trace_data);
|
ctx->trace_hook (ctx, JS_HOOK_CALL, &dbg, ctx->trace_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef VALIDATE_GC
|
||||||
|
uint8_t *pre_heap_base = ctx->heap_base;
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (cproto) {
|
switch (cproto) {
|
||||||
case JS_CFUNC_generic:
|
case JS_CFUNC_generic:
|
||||||
ret_val = func.generic (ctx, this_obj, argc, arg_copy);
|
ret_val = func.generic (ctx, this_obj, argc, arg_copy);
|
||||||
@@ -4449,6 +4557,21 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
|
|||||||
abort ();
|
abort ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef VALIDATE_GC
|
||||||
|
if (ctx->heap_base != pre_heap_base && JS_IsPtr (ret_val)) {
|
||||||
|
void *rp = JS_VALUE_GET_PTR (ret_val);
|
||||||
|
if (!is_ct_ptr (ctx, rp) &&
|
||||||
|
((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free)) {
|
||||||
|
/* Note: f is stale after GC (func_obj was passed by value), so we
|
||||||
|
cannot read f->name here. Just report the pointer. */
|
||||||
|
fprintf (stderr, "VALIDATE_GC: C function returned stale ptr=%p "
|
||||||
|
"heap=[%p,%p) after GC\n", rp,
|
||||||
|
(void *)ctx->heap_base, (void *)ctx->heap_free);
|
||||||
|
fflush (stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ctx->c_call_root = root.prev;
|
ctx->c_call_root = root.prev;
|
||||||
|
|
||||||
if (unlikely (ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET))
|
if (unlikely (ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET))
|
||||||
@@ -5356,19 +5479,27 @@ static JSValue cjson_to_jsvalue (JSContext *ctx, const cJSON *item) {
|
|||||||
if (cJSON_IsString (item)) return JS_NewString (ctx, item->valuestring);
|
if (cJSON_IsString (item)) return JS_NewString (ctx, item->valuestring);
|
||||||
if (cJSON_IsArray (item)) {
|
if (cJSON_IsArray (item)) {
|
||||||
int n = cJSON_GetArraySize (item);
|
int n = cJSON_GetArraySize (item);
|
||||||
JSValue arr = JS_NewArrayLen (ctx,n);
|
JSGCRef arr_ref;
|
||||||
|
JS_AddGCRef (ctx, &arr_ref);
|
||||||
|
arr_ref.val = JS_NewArrayLen (ctx, n);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
cJSON *child = cJSON_GetArrayItem (item, i);
|
cJSON *child = cJSON_GetArrayItem (item, i);
|
||||||
JS_SetPropertyNumber (ctx, arr, i, cjson_to_jsvalue (ctx, child));
|
JS_SetPropertyNumber (ctx, arr_ref.val, i, cjson_to_jsvalue (ctx, child));
|
||||||
}
|
}
|
||||||
return arr;
|
JSValue result = arr_ref.val;
|
||||||
|
JS_DeleteGCRef (ctx, &arr_ref);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (cJSON_IsObject (item)) {
|
if (cJSON_IsObject (item)) {
|
||||||
JSValue obj = JS_NewObject (ctx);
|
JSGCRef obj_ref;
|
||||||
|
JS_AddGCRef (ctx, &obj_ref);
|
||||||
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
for (cJSON *child = item->child; child; child = child->next) {
|
for (cJSON *child = item->child; child; child = child->next) {
|
||||||
JS_SetPropertyStr (ctx, obj, child->string, cjson_to_jsvalue (ctx, child));
|
JS_SetPropertyStr (ctx, obj_ref.val, child->string, cjson_to_jsvalue (ctx, child));
|
||||||
}
|
}
|
||||||
return obj;
|
JSValue result = obj_ref.val;
|
||||||
|
JS_DeleteGCRef (ctx, &obj_ref);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
}
|
}
|
||||||
@@ -7857,8 +7988,8 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
JSValue exit_val = argc > 3 ? argv[3] : JS_NULL;
|
JSValue exit_val = argc > 3 ? argv[3] : JS_NULL;
|
||||||
|
|
||||||
JSGCRef result_ref;
|
JSGCRef result_ref;
|
||||||
JS_PushGCRef (ctx, &result_ref); /* Push first - sets val to JS_NULL */
|
JS_PushGCRef (ctx, &result_ref);
|
||||||
result_ref.val = JS_NewArray (ctx); /* Then assign */
|
result_ref.val = JS_NewArrayLen (ctx, len);
|
||||||
if (JS_IsException (result_ref.val)) {
|
if (JS_IsException (result_ref.val)) {
|
||||||
JS_PopGCRef (ctx, &result_ref);
|
JS_PopGCRef (ctx, &result_ref);
|
||||||
JS_PopGCRef (ctx, &arg1_ref);
|
JS_PopGCRef (ctx, &arg1_ref);
|
||||||
@@ -7866,17 +7997,23 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
return result_ref.val;
|
return result_ref.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int out_idx = 0;
|
||||||
|
#define MAP_STORE(val) do { \
|
||||||
|
JSArray *out = JS_VALUE_GET_ARRAY (result_ref.val); \
|
||||||
|
out->values[out_idx++] = (val); \
|
||||||
|
} while(0)
|
||||||
|
#define MAP_ERR() do { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; } while(0)
|
||||||
|
|
||||||
if (arity >= 2) {
|
if (arity >= 2) {
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
for (int i = len - 1; i >= 0; i--) {
|
for (int i = len - 1; i >= 0; i--) {
|
||||||
/* Re-chase input array each iteration */
|
|
||||||
arr = JS_VALUE_GET_ARRAY (arg0_ref.val);
|
arr = JS_VALUE_GET_ARRAY (arg0_ref.val);
|
||||||
if (i >= (int)arr->len) continue; /* array may have shrunk */
|
if (i >= (int)arr->len) continue;
|
||||||
JSValue args[2] = { arr->values[i], JS_NewInt32 (ctx, i) };
|
JSValue args[2] = { arr->values[i], JS_NewInt32 (ctx, i) };
|
||||||
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 2, args, 0);
|
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 2, args, 0);
|
||||||
if (JS_IsException (val)) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
if (JS_IsException (val)) { MAP_ERR (); }
|
||||||
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
||||||
if (js_intrinsic_array_push (ctx, &result_ref.val, val) < 0) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
MAP_STORE (val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
@@ -7884,9 +8021,9 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
if (i >= (int)arr->len) break;
|
if (i >= (int)arr->len) break;
|
||||||
JSValue args[2] = { arr->values[i], JS_NewInt32 (ctx, i) };
|
JSValue args[2] = { arr->values[i], JS_NewInt32 (ctx, i) };
|
||||||
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 2, args, 0);
|
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 2, args, 0);
|
||||||
if (JS_IsException (val)) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
if (JS_IsException (val)) { MAP_ERR (); }
|
||||||
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
||||||
if (js_intrinsic_array_push (ctx, &result_ref.val, val) < 0) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
MAP_STORE (val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -7896,9 +8033,9 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
if (i >= (int)arr->len) continue;
|
if (i >= (int)arr->len) continue;
|
||||||
JSValue item = arr->values[i];
|
JSValue item = arr->values[i];
|
||||||
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 1, &item, 0);
|
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 1, &item, 0);
|
||||||
if (JS_IsException (val)) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
if (JS_IsException (val)) { MAP_ERR (); }
|
||||||
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
||||||
if (js_intrinsic_array_push (ctx, &result_ref.val, val) < 0) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
MAP_STORE (val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
@@ -7906,12 +8043,17 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
if (i >= (int)arr->len) break;
|
if (i >= (int)arr->len) break;
|
||||||
JSValue item = arr->values[i];
|
JSValue item = arr->values[i];
|
||||||
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 1, &item, 0);
|
JSValue val = JS_CallInternal (ctx, arg1_ref.val, JS_NULL, 1, &item, 0);
|
||||||
if (JS_IsException (val)) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
if (JS_IsException (val)) { MAP_ERR (); }
|
||||||
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
if (!JS_IsNull (exit_val) && js_strict_eq (ctx, val, exit_val)) break;
|
||||||
if (js_intrinsic_array_push (ctx, &result_ref.val, val) < 0) { JS_PopGCRef (ctx, &result_ref); JS_PopGCRef (ctx, &arg1_ref); JS_PopGCRef (ctx, &arg0_ref); return JS_EXCEPTION; }
|
MAP_STORE (val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#undef MAP_STORE
|
||||||
|
#undef MAP_ERR
|
||||||
|
/* Truncate if early exit produced fewer elements */
|
||||||
|
JSArray *out = JS_VALUE_GET_ARRAY (result_ref.val);
|
||||||
|
out->len = out_idx;
|
||||||
JSValue result = result_ref.val;
|
JSValue result = result_ref.val;
|
||||||
JS_PopGCRef (ctx, &result_ref);
|
JS_PopGCRef (ctx, &result_ref);
|
||||||
JS_PopGCRef (ctx, &arg1_ref);
|
JS_PopGCRef (ctx, &arg1_ref);
|
||||||
@@ -8018,7 +8160,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
JS_PushGCRef (ctx, &arr_ref);
|
JS_PushGCRef (ctx, &arr_ref);
|
||||||
JS_PushGCRef (ctx, &str_ref);
|
JS_PushGCRef (ctx, &str_ref);
|
||||||
str_ref.val = arg;
|
str_ref.val = arg;
|
||||||
arr_ref.val = JS_NewArray (ctx);
|
arr_ref.val = JS_NewArrayLen (ctx, len);
|
||||||
if (JS_IsException (arr_ref.val)) {
|
if (JS_IsException (arr_ref.val)) {
|
||||||
JS_PopGCRef (ctx, &str_ref);
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
JS_PopGCRef (ctx, &arr_ref);
|
JS_PopGCRef (ctx, &arr_ref);
|
||||||
@@ -8031,7 +8173,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
|
|||||||
JS_PopGCRef (ctx, &arr_ref);
|
JS_PopGCRef (ctx, &arr_ref);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
JS_ArrayPush (ctx, &arr_ref.val, ch);
|
JS_SetPropertyNumber (ctx, arr_ref.val, i, ch);
|
||||||
}
|
}
|
||||||
JSValue result = arr_ref.val;
|
JSValue result = arr_ref.val;
|
||||||
JS_PopGCRef (ctx, &str_ref);
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
@@ -9636,6 +9778,22 @@ static JSValue js_mach_dump_mcode (JSContext *ctx, JSValue this_val, int argc, J
|
|||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* gc_stats() — return {count, bytes_copied, heap_size, ct_pages} and reset counters */
|
||||||
|
static JSValue js_gc_stats (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
JSValue obj = JS_NewObject (ctx);
|
||||||
|
if (JS_IsException (obj)) return obj;
|
||||||
|
JS_SetPropertyStr (ctx, obj, "count", JS_NewInt64 (ctx, (int64_t)ctx->gc_count));
|
||||||
|
JS_SetPropertyStr (ctx, obj, "bytes_copied", JS_NewInt64 (ctx, (int64_t)ctx->gc_bytes_copied));
|
||||||
|
JS_SetPropertyStr (ctx, obj, "heap_size", JS_NewInt64 (ctx, (int64_t)ctx->current_block_size));
|
||||||
|
/* Count CT overflow pages */
|
||||||
|
int ct_page_count = 0;
|
||||||
|
for (CTPage *p = (CTPage *)ctx->ct_pages; p; p = p->next) ct_page_count++;
|
||||||
|
JS_SetPropertyStr (ctx, obj, "ct_pages", JS_NewInt32 (ctx, ct_page_count));
|
||||||
|
ctx->gc_count = 0;
|
||||||
|
ctx->gc_bytes_copied = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */
|
/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */
|
||||||
static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
||||||
@@ -10128,16 +10286,13 @@ JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue
|
|||||||
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values) {
|
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values) {
|
||||||
JSGCRef arr_ref;
|
JSGCRef arr_ref;
|
||||||
JS_PushGCRef (ctx, &arr_ref);
|
JS_PushGCRef (ctx, &arr_ref);
|
||||||
arr_ref.val = JS_NewArray (ctx);
|
arr_ref.val = JS_NewArrayLen (ctx, count);
|
||||||
if (JS_IsException (arr_ref.val)) {
|
if (JS_IsException (arr_ref.val)) {
|
||||||
JS_PopGCRef (ctx, &arr_ref);
|
JS_PopGCRef (ctx, &arr_ref);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (JS_ArrayPush (ctx, &arr_ref.val, values[i]) < 0) {
|
JS_SetPropertyNumber (ctx, arr_ref.val, i, values[i]);
|
||||||
JS_PopGCRef (ctx, &arr_ref);
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
JSValue result = arr_ref.val;
|
JSValue result = arr_ref.val;
|
||||||
JS_PopGCRef (ctx, &arr_ref);
|
JS_PopGCRef (ctx, &arr_ref);
|
||||||
@@ -10731,6 +10886,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
|||||||
js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3);
|
js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3);
|
||||||
js_set_global_cfunc(ctx, "mach_dump_mcode", js_mach_dump_mcode, 3);
|
js_set_global_cfunc(ctx, "mach_dump_mcode", js_mach_dump_mcode, 3);
|
||||||
js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2);
|
js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2);
|
||||||
|
js_set_global_cfunc(ctx, "gc_stats", js_gc_stats, 0);
|
||||||
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
|
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
|
||||||
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
|
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
|
||||||
js_set_global_cfunc(ctx, "call", js_cell_call, 3);
|
js_set_global_cfunc(ctx, "call", js_cell_call, 3);
|
||||||
@@ -11009,7 +11165,7 @@ static char *js_do_nota_decode (JSContext *js, JSValue *tmp, char *nota, JSValue
|
|||||||
break;
|
break;
|
||||||
case NOTA_ARR:
|
case NOTA_ARR:
|
||||||
nota = nota_read_array (&n, nota);
|
nota = nota_read_array (&n, nota);
|
||||||
*tmp = JS_NewArray (js);
|
*tmp = JS_NewArrayLen (js, n);
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
nota = js_do_nota_decode (js, &ret2, nota, *tmp, JS_NewInt32 (js, i), reviver);
|
nota = js_do_nota_decode (js, &ret2, nota, *tmp, JS_NewInt32 (js, i), reviver);
|
||||||
JS_SetPropertyNumber (js, *tmp, i, ret2);
|
JS_SetPropertyNumber (js, *tmp, i, ret2);
|
||||||
@@ -11879,9 +12035,13 @@ static const JSCFunctionListEntry js_math_radians_funcs[]
|
|||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_core_math_radians_use (JSContext *ctx) {
|
JSValue js_core_math_radians_use (JSContext *ctx) {
|
||||||
JSValue obj = JS_NewObject (ctx);
|
JSGCRef obj_ref;
|
||||||
JS_SetPropertyFunctionList (ctx, obj, js_math_radians_funcs, countof (js_math_radians_funcs));
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
return obj;
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
|
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs));
|
||||||
|
JSValue result = obj_ref.val;
|
||||||
|
JS_PopGCRef (ctx, &obj_ref);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
@@ -11945,9 +12105,13 @@ static const JSCFunctionListEntry js_math_degrees_funcs[]
|
|||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_core_math_degrees_use (JSContext *ctx) {
|
JSValue js_core_math_degrees_use (JSContext *ctx) {
|
||||||
JSValue obj = JS_NewObject (ctx);
|
JSGCRef obj_ref;
|
||||||
JS_SetPropertyFunctionList (ctx, obj, js_math_degrees_funcs, countof (js_math_degrees_funcs));
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
return obj;
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
|
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs));
|
||||||
|
JSValue result = obj_ref.val;
|
||||||
|
JS_PopGCRef (ctx, &obj_ref);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
@@ -12010,9 +12174,13 @@ static const JSCFunctionListEntry js_math_cycles_funcs[]
|
|||||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||||
|
|
||||||
JSValue js_core_math_cycles_use (JSContext *ctx) {
|
JSValue js_core_math_cycles_use (JSContext *ctx) {
|
||||||
JSValue obj = JS_NewObject (ctx);
|
JSGCRef obj_ref;
|
||||||
JS_SetPropertyFunctionList (ctx, obj, js_math_cycles_funcs, countof (js_math_cycles_funcs));
|
JS_PushGCRef (ctx, &obj_ref);
|
||||||
return obj;
|
obj_ref.val = JS_NewObject (ctx);
|
||||||
|
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs));
|
||||||
|
JSValue result = obj_ref.val;
|
||||||
|
JS_PopGCRef (ctx, &obj_ref);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
/* Public API: get stack trace as cJSON array */
|
/* Public API: get stack trace as cJSON array */
|
||||||
cJSON *JS_GetStack(JSContext *ctx) {
|
cJSON *JS_GetStack(JSContext *ctx) {
|
||||||
|
|||||||
432
streamline.cm
432
streamline.cm
@@ -60,6 +60,26 @@ var streamline = function(ir, log) {
|
|||||||
is_record: T_RECORD
|
is_record: T_RECORD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simplify_algebra dispatch tables
|
||||||
|
var self_true_ops = {
|
||||||
|
eq_int: true, eq_float: true, eq_text: true, eq_bool: true,
|
||||||
|
is_identical: true,
|
||||||
|
le_int: true, le_float: true, le_text: true,
|
||||||
|
ge_int: true, ge_float: true, ge_text: true
|
||||||
|
}
|
||||||
|
var self_false_ops = {
|
||||||
|
ne_int: true, ne_float: true, ne_text: true, ne_bool: true,
|
||||||
|
lt_int: true, lt_float: true, lt_text: true,
|
||||||
|
gt_int: true, gt_float: true, gt_text: true
|
||||||
|
}
|
||||||
|
var no_clear_ops = {
|
||||||
|
int: true, access: true, true: true, false: true, move: true, null: true,
|
||||||
|
jump: true, jump_true: true, jump_false: true, jump_not_null: true,
|
||||||
|
return: true, disrupt: true,
|
||||||
|
store_field: true, store_index: true, store_dynamic: true,
|
||||||
|
push: true, setarg: true, invoke: true, tail_invoke: true
|
||||||
|
}
|
||||||
|
|
||||||
// --- Logging support ---
|
// --- Logging support ---
|
||||||
|
|
||||||
var ir_stats = null
|
var ir_stats = null
|
||||||
@@ -119,49 +139,30 @@ var streamline = function(ir, log) {
|
|||||||
return T_UNKNOWN
|
return T_UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// track_types reuses write_rules table; move handled specially
|
||||||
var track_types = function(slot_types, instr) {
|
var track_types = function(slot_types, instr) {
|
||||||
var op = instr[0]
|
var op = instr[0]
|
||||||
|
var rule = null
|
||||||
var src_type = null
|
var src_type = null
|
||||||
if (op == "access") {
|
var typ = null
|
||||||
slot_types[text(instr[1])] = access_value_type(instr[2])
|
if (op == "move") {
|
||||||
} else if (op == "int") {
|
src_type = slot_types[instr[2]]
|
||||||
slot_types[text(instr[1])] = T_INT
|
slot_types[instr[1]] = src_type != null ? src_type : T_UNKNOWN
|
||||||
} else if (op == "true" || op == "false") {
|
return null
|
||||||
slot_types[text(instr[1])] = T_BOOL
|
}
|
||||||
} else if (op == "null") {
|
rule = write_rules[op]
|
||||||
slot_types[text(instr[1])] = T_NULL
|
if (rule != null) {
|
||||||
} else if (op == "move") {
|
typ = rule[1]
|
||||||
src_type = slot_types[text(instr[2])]
|
if (typ == null) {
|
||||||
slot_types[text(instr[1])] = src_type != null ? src_type : T_UNKNOWN
|
typ = access_value_type(instr[2])
|
||||||
} else if (op == "concat") {
|
}
|
||||||
slot_types[text(instr[1])] = T_TEXT
|
slot_types[instr[rule[0]]] = typ
|
||||||
} else if (bool_result_ops[op] == true) {
|
|
||||||
slot_types[text(instr[1])] = T_BOOL
|
|
||||||
} else if (op == "load_field" || op == "load_index" || op == "load_dynamic") {
|
|
||||||
slot_types[text(instr[1])] = T_UNKNOWN
|
|
||||||
} else if (op == "invoke" || op == "tail_invoke") {
|
|
||||||
slot_types[text(instr[2])] = T_UNKNOWN
|
|
||||||
} else if (op == "pop" || op == "get") {
|
|
||||||
slot_types[text(instr[1])] = T_UNKNOWN
|
|
||||||
} else if (op == "array") {
|
|
||||||
slot_types[text(instr[1])] = T_ARRAY
|
|
||||||
} else if (op == "record") {
|
|
||||||
slot_types[text(instr[1])] = T_RECORD
|
|
||||||
} else if (op == "function") {
|
|
||||||
slot_types[text(instr[1])] = T_FUNCTION
|
|
||||||
} else if (op == "length") {
|
|
||||||
slot_types[text(instr[1])] = T_INT
|
|
||||||
} else if (op == "negate" || numeric_ops[op] == true) {
|
|
||||||
slot_types[text(instr[1])] = T_UNKNOWN
|
|
||||||
} else if (op == "bitnot" || op == "bitand" || op == "bitor" ||
|
|
||||||
op == "bitxor" || op == "shl" || op == "shr" || op == "ushr") {
|
|
||||||
slot_types[text(instr[1])] = T_INT
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
var slot_is = function(slot_types, slot, typ) {
|
var slot_is = function(slot_types, slot, typ) {
|
||||||
var known = slot_types[text(slot)]
|
var known = slot_types[slot]
|
||||||
if (known == null) {
|
if (known == null) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -175,24 +176,22 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var merge_backward = function(backward_types, slot, typ) {
|
var merge_backward = function(backward_types, slot, typ) {
|
||||||
var sk = null
|
|
||||||
var existing = null
|
var existing = null
|
||||||
if (!is_number(slot)) {
|
if (!is_number(slot)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
sk = text(slot)
|
existing = backward_types[slot]
|
||||||
existing = backward_types[sk]
|
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
backward_types[sk] = typ
|
backward_types[slot] = typ
|
||||||
} else if (existing != typ && existing != T_UNKNOWN) {
|
} else if (existing != typ && existing != T_UNKNOWN) {
|
||||||
if ((existing == T_INT || existing == T_FLOAT) && typ == T_NUM) {
|
if ((existing == T_INT || existing == T_FLOAT) && typ == T_NUM) {
|
||||||
// Keep more specific
|
// Keep more specific
|
||||||
} else if (existing == T_NUM && (typ == T_INT || typ == T_FLOAT)) {
|
} else if (existing == T_NUM && (typ == T_INT || typ == T_FLOAT)) {
|
||||||
backward_types[sk] = typ
|
backward_types[slot] = typ
|
||||||
} else if ((existing == T_INT && typ == T_FLOAT) || (existing == T_FLOAT && typ == T_INT)) {
|
} else if ((existing == T_INT && typ == T_FLOAT) || (existing == T_FLOAT && typ == T_INT)) {
|
||||||
backward_types[sk] = T_NUM
|
backward_types[slot] = T_NUM
|
||||||
} else {
|
} else {
|
||||||
backward_types[sk] = T_UNKNOWN
|
backward_types[slot] = T_UNKNOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -201,8 +200,8 @@ var streamline = function(ir, log) {
|
|||||||
var seed_params = function(slot_types, param_types, nr_args) {
|
var seed_params = function(slot_types, param_types, nr_args) {
|
||||||
var j = 1
|
var j = 1
|
||||||
while (j <= nr_args) {
|
while (j <= nr_args) {
|
||||||
if (param_types[text(j)] != null) {
|
if (param_types[j] != null) {
|
||||||
slot_types[text(j)] = param_types[text(j)]
|
slot_types[j] = param_types[j]
|
||||||
}
|
}
|
||||||
j = j + 1
|
j = j + 1
|
||||||
}
|
}
|
||||||
@@ -210,10 +209,11 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var seed_writes = function(slot_types, write_types) {
|
var seed_writes = function(slot_types, write_types) {
|
||||||
var keys = array(write_types)
|
|
||||||
var k = 0
|
var k = 0
|
||||||
while (k < length(keys)) {
|
while (k < length(write_types)) {
|
||||||
slot_types[keys[k]] = write_types[keys[k]]
|
if (write_types[k] != null) {
|
||||||
|
slot_types[k] = write_types[k]
|
||||||
|
}
|
||||||
k = k + 1
|
k = k + 1
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -222,7 +222,35 @@ var streamline = function(ir, log) {
|
|||||||
// =========================================================
|
// =========================================================
|
||||||
// Pass: infer_param_types — backward type inference
|
// Pass: infer_param_types — backward type inference
|
||||||
// Scans typed operators to infer immutable parameter types.
|
// Scans typed operators to infer immutable parameter types.
|
||||||
|
// Uses data-driven dispatch: each rule is [pos1, type1] or
|
||||||
|
// [pos1, type1, pos2, type2] for operand positions to merge.
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
var param_rules = {
|
||||||
|
subtract: [2, T_NUM, 3, T_NUM], multiply: [2, T_NUM, 3, T_NUM],
|
||||||
|
divide: [2, T_NUM, 3, T_NUM], modulo: [2, T_NUM, 3, T_NUM],
|
||||||
|
pow: [2, T_NUM, 3, T_NUM], negate: [2, T_NUM],
|
||||||
|
eq_int: [2, T_INT, 3, T_INT], ne_int: [2, T_INT, 3, T_INT],
|
||||||
|
lt_int: [2, T_INT, 3, T_INT], gt_int: [2, T_INT, 3, T_INT],
|
||||||
|
le_int: [2, T_INT, 3, T_INT], ge_int: [2, T_INT, 3, T_INT],
|
||||||
|
bitand: [2, T_INT, 3, T_INT], bitor: [2, T_INT, 3, T_INT],
|
||||||
|
bitxor: [2, T_INT, 3, T_INT], shl: [2, T_INT, 3, T_INT],
|
||||||
|
shr: [2, T_INT, 3, T_INT], ushr: [2, T_INT, 3, T_INT],
|
||||||
|
bitnot: [2, T_INT],
|
||||||
|
eq_float: [2, T_FLOAT, 3, T_FLOAT], ne_float: [2, T_FLOAT, 3, T_FLOAT],
|
||||||
|
lt_float: [2, T_FLOAT, 3, T_FLOAT], gt_float: [2, T_FLOAT, 3, T_FLOAT],
|
||||||
|
le_float: [2, T_FLOAT, 3, T_FLOAT], ge_float: [2, T_FLOAT, 3, T_FLOAT],
|
||||||
|
concat: [2, T_TEXT, 3, T_TEXT],
|
||||||
|
eq_text: [2, T_TEXT, 3, T_TEXT], ne_text: [2, T_TEXT, 3, T_TEXT],
|
||||||
|
lt_text: [2, T_TEXT, 3, T_TEXT], gt_text: [2, T_TEXT, 3, T_TEXT],
|
||||||
|
le_text: [2, T_TEXT, 3, T_TEXT], ge_text: [2, T_TEXT, 3, T_TEXT],
|
||||||
|
eq_bool: [2, T_BOOL, 3, T_BOOL], ne_bool: [2, T_BOOL, 3, T_BOOL],
|
||||||
|
not: [2, T_BOOL], and: [2, T_BOOL, 3, T_BOOL], or: [2, T_BOOL, 3, T_BOOL],
|
||||||
|
store_index: [1, T_ARRAY, 2, T_INT], store_field: [1, T_RECORD],
|
||||||
|
push: [1, T_ARRAY],
|
||||||
|
load_index: [2, T_ARRAY, 3, T_INT], load_field: [2, T_RECORD],
|
||||||
|
pop: [2, T_ARRAY]
|
||||||
|
}
|
||||||
|
|
||||||
var infer_param_types = function(func) {
|
var infer_param_types = function(func) {
|
||||||
var instructions = func.instructions
|
var instructions = func.instructions
|
||||||
var nr_args = func.nr_args != null ? func.nr_args : 0
|
var nr_args = func.nr_args != null ? func.nr_args : 0
|
||||||
@@ -232,76 +260,36 @@ var streamline = function(ir, log) {
|
|||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
var instr = null
|
var instr = null
|
||||||
var op = null
|
|
||||||
var bt = null
|
var bt = null
|
||||||
|
var rule = null
|
||||||
|
|
||||||
if (instructions == null || nr_args == 0) {
|
if (instructions == null || nr_args == 0) {
|
||||||
return {}
|
return array(func.nr_slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
num_instr = length(instructions)
|
num_instr = length(instructions)
|
||||||
backward_types = {}
|
backward_types = array(func.nr_slots)
|
||||||
i = 0
|
i = 0
|
||||||
while (i < num_instr) {
|
while (i < num_instr) {
|
||||||
instr = instructions[i]
|
instr = instructions[i]
|
||||||
if (is_array(instr)) {
|
if (is_array(instr)) {
|
||||||
op = instr[0]
|
rule = param_rules[instr[0]]
|
||||||
if (op == "subtract" || op == "multiply" ||
|
if (rule != null) {
|
||||||
op == "divide" || op == "modulo" || op == "pow") {
|
merge_backward(backward_types, instr[rule[0]], rule[1])
|
||||||
merge_backward(backward_types, instr[2], T_NUM)
|
if (length(rule) > 2) {
|
||||||
merge_backward(backward_types, instr[3], T_NUM)
|
merge_backward(backward_types, instr[rule[2]], rule[3])
|
||||||
} else if (op == "negate") {
|
}
|
||||||
merge_backward(backward_types, instr[2], T_NUM)
|
|
||||||
} else if (op == "eq_int" || op == "ne_int" || op == "lt_int" ||
|
|
||||||
op == "gt_int" || op == "le_int" || op == "ge_int" ||
|
|
||||||
op == "bitand" || op == "bitor" || op == "bitxor" ||
|
|
||||||
op == "shl" || op == "shr" || op == "ushr") {
|
|
||||||
merge_backward(backward_types, instr[2], T_INT)
|
|
||||||
merge_backward(backward_types, instr[3], T_INT)
|
|
||||||
} else if (op == "bitnot") {
|
|
||||||
merge_backward(backward_types, instr[2], T_INT)
|
|
||||||
} else if (op == "eq_float" || op == "ne_float" || op == "lt_float" ||
|
|
||||||
op == "gt_float" || op == "le_float" || op == "ge_float") {
|
|
||||||
merge_backward(backward_types, instr[2], T_FLOAT)
|
|
||||||
merge_backward(backward_types, instr[3], T_FLOAT)
|
|
||||||
} else if (op == "concat" ||
|
|
||||||
op == "eq_text" || op == "ne_text" || op == "lt_text" ||
|
|
||||||
op == "gt_text" || op == "le_text" || op == "ge_text") {
|
|
||||||
merge_backward(backward_types, instr[2], T_TEXT)
|
|
||||||
merge_backward(backward_types, instr[3], T_TEXT)
|
|
||||||
} else if (op == "eq_bool" || op == "ne_bool") {
|
|
||||||
merge_backward(backward_types, instr[2], T_BOOL)
|
|
||||||
merge_backward(backward_types, instr[3], T_BOOL)
|
|
||||||
} else if (op == "not") {
|
|
||||||
merge_backward(backward_types, instr[2], T_BOOL)
|
|
||||||
} else if (op == "and" || op == "or") {
|
|
||||||
merge_backward(backward_types, instr[2], T_BOOL)
|
|
||||||
merge_backward(backward_types, instr[3], T_BOOL)
|
|
||||||
} else if (op == "store_index") {
|
|
||||||
merge_backward(backward_types, instr[1], T_ARRAY)
|
|
||||||
merge_backward(backward_types, instr[2], T_INT)
|
|
||||||
} else if (op == "store_field") {
|
|
||||||
merge_backward(backward_types, instr[1], T_RECORD)
|
|
||||||
} else if (op == "push") {
|
|
||||||
merge_backward(backward_types, instr[1], T_ARRAY)
|
|
||||||
} else if (op == "load_index") {
|
|
||||||
merge_backward(backward_types, instr[2], T_ARRAY)
|
|
||||||
merge_backward(backward_types, instr[3], T_INT)
|
|
||||||
} else if (op == "load_field") {
|
|
||||||
merge_backward(backward_types, instr[2], T_RECORD)
|
|
||||||
} else if (op == "pop") {
|
|
||||||
merge_backward(backward_types, instr[2], T_ARRAY)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
param_types = {}
|
param_types = array(func.nr_slots)
|
||||||
j = 1
|
j = 1
|
||||||
while (j <= nr_args) {
|
while (j <= nr_args) {
|
||||||
bt = backward_types[text(j)]
|
bt = backward_types[j]
|
||||||
if (bt != null && bt != T_UNKNOWN) {
|
if (bt != null && bt != T_UNKNOWN) {
|
||||||
param_types[text(j)] = bt
|
param_types[j] = bt
|
||||||
}
|
}
|
||||||
j = j + 1
|
j = j + 1
|
||||||
}
|
}
|
||||||
@@ -313,114 +301,85 @@ var streamline = function(ir, log) {
|
|||||||
// Scans all instructions to find non-parameter slots where
|
// Scans all instructions to find non-parameter slots where
|
||||||
// every write produces the same type. These types persist
|
// every write produces the same type. These types persist
|
||||||
// across label join points.
|
// across label join points.
|
||||||
|
// Uses data-driven dispatch: each rule is [dest_pos, type].
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
var write_rules = {
|
||||||
|
int: [1, T_INT], true: [1, T_BOOL], false: [1, T_BOOL],
|
||||||
|
null: [1, T_NULL], access: [1, null],
|
||||||
|
array: [1, T_ARRAY], record: [1, T_RECORD],
|
||||||
|
function: [1, T_FUNCTION], length: [1, T_INT],
|
||||||
|
bitnot: [1, T_INT], bitand: [1, T_INT], bitor: [1, T_INT],
|
||||||
|
bitxor: [1, T_INT], shl: [1, T_INT], shr: [1, T_INT], ushr: [1, T_INT],
|
||||||
|
negate: [1, T_UNKNOWN], concat: [1, T_TEXT],
|
||||||
|
eq: [1, T_BOOL], ne: [1, T_BOOL], lt: [1, T_BOOL],
|
||||||
|
le: [1, T_BOOL], gt: [1, T_BOOL], ge: [1, T_BOOL], in: [1, T_BOOL],
|
||||||
|
add: [1, T_UNKNOWN], subtract: [1, T_UNKNOWN], multiply: [1, T_UNKNOWN],
|
||||||
|
divide: [1, T_UNKNOWN], modulo: [1, T_UNKNOWN], pow: [1, T_UNKNOWN],
|
||||||
|
move: [1, T_UNKNOWN], load_field: [1, T_UNKNOWN],
|
||||||
|
load_index: [1, T_UNKNOWN], load_dynamic: [1, T_UNKNOWN],
|
||||||
|
pop: [1, T_UNKNOWN], get: [1, T_UNKNOWN],
|
||||||
|
invoke: [2, T_UNKNOWN], tail_invoke: [2, T_UNKNOWN],
|
||||||
|
eq_int: [1, T_BOOL], ne_int: [1, T_BOOL], lt_int: [1, T_BOOL],
|
||||||
|
gt_int: [1, T_BOOL], le_int: [1, T_BOOL], ge_int: [1, T_BOOL],
|
||||||
|
eq_float: [1, T_BOOL], ne_float: [1, T_BOOL], lt_float: [1, T_BOOL],
|
||||||
|
gt_float: [1, T_BOOL], le_float: [1, T_BOOL], ge_float: [1, T_BOOL],
|
||||||
|
eq_text: [1, T_BOOL], ne_text: [1, T_BOOL], lt_text: [1, T_BOOL],
|
||||||
|
gt_text: [1, T_BOOL], le_text: [1, T_BOOL], ge_text: [1, T_BOOL],
|
||||||
|
eq_bool: [1, T_BOOL], ne_bool: [1, T_BOOL],
|
||||||
|
eq_tol: [1, T_BOOL], ne_tol: [1, T_BOOL],
|
||||||
|
not: [1, T_BOOL], and: [1, T_BOOL], or: [1, T_BOOL],
|
||||||
|
is_int: [1, T_BOOL], is_text: [1, T_BOOL], is_num: [1, T_BOOL],
|
||||||
|
is_bool: [1, T_BOOL], is_null: [1, T_BOOL], is_identical: [1, T_BOOL],
|
||||||
|
is_array: [1, T_BOOL], is_func: [1, T_BOOL],
|
||||||
|
is_record: [1, T_BOOL], is_stone: [1, T_BOOL]
|
||||||
|
}
|
||||||
|
|
||||||
var infer_slot_write_types = function(func) {
|
var infer_slot_write_types = function(func) {
|
||||||
var instructions = func.instructions
|
var instructions = func.instructions
|
||||||
var nr_args = func.nr_args != null ? func.nr_args : 0
|
var nr_args = func.nr_args != null ? func.nr_args : 0
|
||||||
var num_instr = 0
|
var num_instr = 0
|
||||||
var write_types = null
|
var write_types = null
|
||||||
var result = null
|
|
||||||
var keys = null
|
|
||||||
var i = 0
|
var i = 0
|
||||||
var k = 0
|
var k = 0
|
||||||
var instr = null
|
var instr = null
|
||||||
var op = null
|
|
||||||
var slot = 0
|
var slot = 0
|
||||||
var typ = null
|
var typ = null
|
||||||
var wt = null
|
var rule = null
|
||||||
|
|
||||||
if (instructions == null) {
|
if (instructions == null) {
|
||||||
return {}
|
return array(func.nr_slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
num_instr = length(instructions)
|
num_instr = length(instructions)
|
||||||
write_types = {}
|
write_types = array(func.nr_slots)
|
||||||
i = 0
|
i = 0
|
||||||
while (i < num_instr) {
|
while (i < num_instr) {
|
||||||
instr = instructions[i]
|
instr = instructions[i]
|
||||||
if (!is_array(instr)) {
|
if (is_array(instr)) {
|
||||||
i = i + 1
|
rule = write_rules[instr[0]]
|
||||||
continue
|
if (rule != null) {
|
||||||
|
slot = instr[rule[0]]
|
||||||
|
typ = rule[1]
|
||||||
|
if (typ == null) {
|
||||||
|
typ = access_value_type(instr[2])
|
||||||
|
}
|
||||||
|
if (slot > 0 && slot > nr_args) {
|
||||||
|
merge_backward(write_types, slot, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
op = instr[0]
|
|
||||||
slot = -1
|
|
||||||
typ = null
|
|
||||||
|
|
||||||
if (op == "int") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_INT
|
|
||||||
} else if (op == "true" || op == "false") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_BOOL
|
|
||||||
} else if (op == "null") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_NULL
|
|
||||||
} else if (op == "access") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = access_value_type(instr[2])
|
|
||||||
} else if (op == "array") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_ARRAY
|
|
||||||
} else if (op == "record") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_RECORD
|
|
||||||
} else if (op == "function") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_FUNCTION
|
|
||||||
} else if (op == "length") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_INT
|
|
||||||
} else if (op == "bitnot" || op == "bitand" ||
|
|
||||||
op == "bitor" || op == "bitxor" || op == "shl" ||
|
|
||||||
op == "shr" || op == "ushr") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_INT
|
|
||||||
} else if (op == "negate") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_UNKNOWN
|
|
||||||
} else if (op == "concat") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_TEXT
|
|
||||||
} else if (bool_result_ops[op] == true) {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_BOOL
|
|
||||||
} else if (op == "eq" || op == "ne" || op == "lt" ||
|
|
||||||
op == "le" || op == "gt" || op == "ge" || op == "in") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_BOOL
|
|
||||||
} else if (op == "add" || op == "subtract" || op == "multiply" ||
|
|
||||||
op == "divide" || op == "modulo" || op == "pow") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_UNKNOWN
|
|
||||||
} else if (op == "move" || op == "load_field" || op == "load_index" ||
|
|
||||||
op == "load_dynamic" || op == "pop" || op == "get") {
|
|
||||||
slot = instr[1]
|
|
||||||
typ = T_UNKNOWN
|
|
||||||
} else if (op == "invoke" || op == "tail_invoke") {
|
|
||||||
slot = instr[2]
|
|
||||||
typ = T_UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot > 0 && slot > nr_args) {
|
|
||||||
merge_backward(write_types, slot, typ != null ? typ : T_UNKNOWN)
|
|
||||||
}
|
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter to only slots with known (non-unknown) types
|
// Filter to only slots with known (non-unknown) types
|
||||||
result = {}
|
|
||||||
keys = array(write_types)
|
|
||||||
k = 0
|
k = 0
|
||||||
while (k < length(keys)) {
|
while (k < length(write_types)) {
|
||||||
wt = write_types[keys[k]]
|
if (write_types[k] == T_UNKNOWN) {
|
||||||
if (wt != null && wt != T_UNKNOWN) {
|
write_types[k] = null
|
||||||
result[keys[k]] = wt
|
|
||||||
}
|
}
|
||||||
k = k + 1
|
k = k + 1
|
||||||
}
|
}
|
||||||
return result
|
return write_types
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
@@ -431,9 +390,8 @@ var streamline = function(ir, log) {
|
|||||||
var eliminate_type_checks = function(func, param_types, write_types, log) {
|
var eliminate_type_checks = function(func, param_types, write_types, log) {
|
||||||
var instructions = func.instructions
|
var instructions = func.instructions
|
||||||
var nr_args = func.nr_args != null ? func.nr_args : 0
|
var nr_args = func.nr_args != null ? func.nr_args : 0
|
||||||
var has_params = false
|
|
||||||
var has_writes = false
|
|
||||||
var num_instr = 0
|
var num_instr = 0
|
||||||
|
var base_types = null
|
||||||
var slot_types = null
|
var slot_types = null
|
||||||
var nc = 0
|
var nc = 0
|
||||||
var i = 0
|
var i = 0
|
||||||
@@ -460,35 +418,32 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
num_instr = length(instructions)
|
num_instr = length(instructions)
|
||||||
|
|
||||||
|
// Pre-compute base types: params + write-invariant types
|
||||||
|
base_types = array(func.nr_slots)
|
||||||
j = 1
|
j = 1
|
||||||
while (j <= nr_args) {
|
while (j <= nr_args) {
|
||||||
if (param_types[text(j)] != null) {
|
if (param_types[j] != null) {
|
||||||
has_params = true
|
base_types[j] = param_types[j]
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
j = 0
|
||||||
|
while (j < length(write_types)) {
|
||||||
|
if (write_types[j] != null) {
|
||||||
|
base_types[j] = write_types[j]
|
||||||
}
|
}
|
||||||
j = j + 1
|
j = j + 1
|
||||||
}
|
}
|
||||||
has_writes = length(array(write_types)) > 0
|
|
||||||
|
|
||||||
slot_types = {}
|
slot_types = array(base_types)
|
||||||
if (has_params) {
|
|
||||||
seed_params(slot_types, param_types, nr_args)
|
|
||||||
}
|
|
||||||
if (has_writes) {
|
|
||||||
seed_writes(slot_types, write_types)
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < num_instr) {
|
while (i < num_instr) {
|
||||||
instr = instructions[i]
|
instr = instructions[i]
|
||||||
|
|
||||||
if (is_text(instr)) {
|
if (is_text(instr)) {
|
||||||
slot_types = {}
|
slot_types = array(base_types)
|
||||||
if (has_params) {
|
|
||||||
seed_params(slot_types, param_types, nr_args)
|
|
||||||
}
|
|
||||||
if (has_writes) {
|
|
||||||
seed_writes(slot_types, write_types)
|
|
||||||
}
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -525,14 +480,14 @@ var streamline = function(ir, log) {
|
|||||||
at: i,
|
at: i,
|
||||||
before: [instr, next],
|
before: [instr, next],
|
||||||
after: [instructions[i], instructions[i + 1]],
|
after: [instructions[i], instructions[i + 1]],
|
||||||
why: {slot: src, known_type: slot_types[text(src)], checked_type: checked_type}
|
why: {slot: src, known_type: slot_types[src], checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
src_known = slot_types[text(src)]
|
src_known = slot_types[src]
|
||||||
if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) {
|
if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) {
|
||||||
if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) {
|
if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
@@ -550,7 +505,7 @@ var streamline = function(ir, log) {
|
|||||||
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -569,12 +524,12 @@ var streamline = function(ir, log) {
|
|||||||
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_UNKNOWN
|
slot_types[dest] = T_UNKNOWN
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
slot_types[text(src)] = checked_type
|
slot_types[src] = checked_type
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -594,14 +549,14 @@ var streamline = function(ir, log) {
|
|||||||
at: i,
|
at: i,
|
||||||
before: [instr, next],
|
before: [instr, next],
|
||||||
after: [instructions[i], instructions[i + 1]],
|
after: [instructions[i], instructions[i + 1]],
|
||||||
why: {slot: src, known_type: slot_types[text(src)], checked_type: checked_type}
|
why: {slot: src, known_type: slot_types[src], checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
src_known = slot_types[text(src)]
|
src_known = slot_types[src]
|
||||||
if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) {
|
if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) {
|
||||||
if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) {
|
if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
@@ -619,7 +574,7 @@ var streamline = function(ir, log) {
|
|||||||
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -638,17 +593,17 @@ var streamline = function(ir, log) {
|
|||||||
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
why: {slot: src, known_type: src_known, checked_type: checked_type}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 2
|
i = i + 2
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slot_types[text(dest)] = T_BOOL
|
slot_types[dest] = T_BOOL
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -664,7 +619,7 @@ var streamline = function(ir, log) {
|
|||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_field",
|
rule: "dynamic_to_field",
|
||||||
at: i, before: old_op, after: instr[0],
|
at: i, before: old_op, after: instr[0],
|
||||||
why: {slot: instr[3], known_type: slot_types[text(instr[3])]}
|
why: {slot: instr[3], known_type: slot_types[instr[3]]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
||||||
@@ -675,11 +630,11 @@ var streamline = function(ir, log) {
|
|||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_index",
|
rule: "dynamic_to_index",
|
||||||
at: i, before: old_op, after: instr[0],
|
at: i, before: old_op, after: instr[0],
|
||||||
why: {slot: instr[3], known_type: slot_types[text(instr[3])]}
|
why: {slot: instr[3], known_type: slot_types[instr[3]]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_types[text(instr[1])] = T_UNKNOWN
|
slot_types[instr[1]] = T_UNKNOWN
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -693,7 +648,7 @@ var streamline = function(ir, log) {
|
|||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_field",
|
rule: "dynamic_to_field",
|
||||||
at: i, before: old_op, after: instr[0],
|
at: i, before: old_op, after: instr[0],
|
||||||
why: {slot: instr[3], known_type: slot_types[text(instr[3])]}
|
why: {slot: instr[3], known_type: slot_types[instr[3]]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
||||||
@@ -704,7 +659,7 @@ var streamline = function(ir, log) {
|
|||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_index",
|
rule: "dynamic_to_index",
|
||||||
at: i, before: old_op, after: instr[0],
|
at: i, before: old_op, after: instr[0],
|
||||||
why: {slot: instr[3], known_type: slot_types[text(instr[3])]}
|
why: {slot: instr[3], known_type: slot_types[instr[3]]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -745,14 +700,14 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
num_instr = length(instructions)
|
num_instr = length(instructions)
|
||||||
slot_values = {}
|
slot_values = array(func.nr_slots)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < num_instr) {
|
while (i < num_instr) {
|
||||||
instr = instructions[i]
|
instr = instructions[i]
|
||||||
|
|
||||||
if (is_text(instr)) {
|
if (is_text(instr)) {
|
||||||
slot_values = {}
|
slot_values = array(func.nr_slots)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -766,28 +721,25 @@ var streamline = function(ir, log) {
|
|||||||
|
|
||||||
// Track known constant values
|
// Track known constant values
|
||||||
if (op == "int") {
|
if (op == "int") {
|
||||||
slot_values[text(instr[1])] = instr[2]
|
slot_values[instr[1]] = instr[2]
|
||||||
} else if (op == "access" && is_number(instr[2])) {
|
} else if (op == "access" && is_number(instr[2])) {
|
||||||
slot_values[text(instr[1])] = instr[2]
|
slot_values[instr[1]] = instr[2]
|
||||||
} else if (op == "true") {
|
} else if (op == "true") {
|
||||||
slot_values[text(instr[1])] = true
|
slot_values[instr[1]] = true
|
||||||
} else if (op == "false") {
|
} else if (op == "false") {
|
||||||
slot_values[text(instr[1])] = false
|
slot_values[instr[1]] = false
|
||||||
} else if (op == "move") {
|
} else if (op == "move") {
|
||||||
sv = slot_values[text(instr[2])]
|
sv = slot_values[instr[2]]
|
||||||
if (sv != null) {
|
if (sv != null) {
|
||||||
slot_values[text(instr[1])] = sv
|
slot_values[instr[1]] = sv
|
||||||
} else {
|
} else {
|
||||||
slot_values[text(instr[1])] = null
|
slot_values[instr[1]] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same-slot comparisons
|
// Same-slot comparisons
|
||||||
if (is_number(instr[2]) && instr[2] == instr[3]) {
|
if (is_number(instr[2]) && instr[2] == instr[3]) {
|
||||||
if (op == "eq_int" || op == "eq_float" || op == "eq_text" ||
|
if (self_true_ops[op] == true) {
|
||||||
op == "eq_bool" || op == "is_identical" ||
|
|
||||||
op == "le_int" || op == "le_float" || op == "le_text" ||
|
|
||||||
op == "ge_int" || op == "ge_float" || op == "ge_text") {
|
|
||||||
instructions[i] = ["true", instr[1], instr[ilen - 2], instr[ilen - 1]]
|
instructions[i] = ["true", instr[1], instr[ilen - 2], instr[ilen - 1]]
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
@@ -797,14 +749,11 @@ var streamline = function(ir, log) {
|
|||||||
why: {op: op, slot: instr[2]}
|
why: {op: op, slot: instr[2]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_values[text(instr[1])] = true
|
slot_values[instr[1]] = true
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "ne_int" || op == "ne_float" || op == "ne_text" ||
|
if (self_false_ops[op] == true) {
|
||||||
op == "ne_bool" ||
|
|
||||||
op == "lt_int" || op == "lt_float" || op == "lt_text" ||
|
|
||||||
op == "gt_int" || op == "gt_float" || op == "gt_text") {
|
|
||||||
instructions[i] = ["false", instr[1], instr[ilen - 2], instr[ilen - 1]]
|
instructions[i] = ["false", instr[1], instr[ilen - 2], instr[ilen - 1]]
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
@@ -814,7 +763,7 @@ var streamline = function(ir, log) {
|
|||||||
why: {op: op, slot: instr[2]}
|
why: {op: op, slot: instr[2]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slot_values[text(instr[1])] = false
|
slot_values[instr[1]] = false
|
||||||
i = i + 1
|
i = i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -822,15 +771,10 @@ var streamline = function(ir, log) {
|
|||||||
|
|
||||||
// Clear value tracking for dest-producing ops (not reads-only)
|
// Clear value tracking for dest-producing ops (not reads-only)
|
||||||
if (op == "invoke" || op == "tail_invoke") {
|
if (op == "invoke" || op == "tail_invoke") {
|
||||||
slot_values[text(instr[2])] = null
|
slot_values[instr[2]] = null
|
||||||
} else if (op != "int" && op != "access" && op != "true" &&
|
} else if (no_clear_ops[op] != true) {
|
||||||
op != "false" && op != "move" && op != "null" &&
|
|
||||||
op != "jump" && op != "jump_true" && op != "jump_false" &&
|
|
||||||
op != "jump_not_null" && op != "return" && op != "disrupt" &&
|
|
||||||
op != "store_field" && op != "store_index" &&
|
|
||||||
op != "store_dynamic" && op != "push" && op != "setarg") {
|
|
||||||
if (is_number(instr[1])) {
|
if (is_number(instr[1])) {
|
||||||
slot_values[text(instr[1])] = null
|
slot_values[instr[1]] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
time.cm
3
time.cm
@@ -5,6 +5,9 @@ var now = time.now
|
|||||||
var computer_zone = time.computer_zone
|
var computer_zone = time.computer_zone
|
||||||
var computer_dst = time.computer_dst
|
var computer_dst = time.computer_dst
|
||||||
|
|
||||||
|
//delete time.now
|
||||||
|
//delete time.computer_zone
|
||||||
|
//delete time.computer_dst
|
||||||
|
|
||||||
time.second = 1
|
time.second = 1
|
||||||
time.minute = 60
|
time.minute = 60
|
||||||
|
|||||||
318
tokenize.cm
318
tokenize.cm
@@ -1,72 +1,11 @@
|
|||||||
var tokenize = function(src, filename) {
|
var tokenize = function(src, filename) {
|
||||||
var len = length(src)
|
var len = length(src)
|
||||||
var cp = array(array(src), codepoint)
|
|
||||||
|
|
||||||
var pos = 0
|
var pos = 0
|
||||||
var row = 0
|
var row = 0
|
||||||
var col = 0
|
var col = 0
|
||||||
var tokens = []
|
var tokens = []
|
||||||
|
|
||||||
// Codepoint constants
|
|
||||||
def CP_LF = 10
|
|
||||||
def CP_CR = 13
|
|
||||||
def CP_TAB = 9
|
|
||||||
def CP_SPACE = 32
|
|
||||||
def CP_BANG = 33
|
|
||||||
def CP_DQUOTE = 34
|
|
||||||
def CP_HASH = 35
|
|
||||||
def CP_DOLLAR = 36
|
|
||||||
def CP_PERCENT = 37
|
|
||||||
def CP_AMP = 38
|
|
||||||
def CP_SQUOTE = 39
|
|
||||||
def CP_LPAREN = 40
|
|
||||||
def CP_RPAREN = 41
|
|
||||||
def CP_STAR = 42
|
|
||||||
def CP_PLUS = 43
|
|
||||||
def CP_COMMA = 44
|
|
||||||
def CP_MINUS = 45
|
|
||||||
def CP_DOT = 46
|
|
||||||
def CP_SLASH = 47
|
|
||||||
def CP_0 = 48
|
|
||||||
def CP_1 = 49
|
|
||||||
def CP_7 = 55
|
|
||||||
def CP_9 = 57
|
|
||||||
def CP_COLON = 58
|
|
||||||
def CP_SEMI = 59
|
|
||||||
def CP_LT = 60
|
|
||||||
def CP_EQ = 61
|
|
||||||
def CP_GT = 62
|
|
||||||
def CP_QMARK = 63
|
|
||||||
def CP_AT = 64
|
|
||||||
def CP_A = 65
|
|
||||||
def CP_B = 66
|
|
||||||
def CP_E = 69
|
|
||||||
def CP_F = 70
|
|
||||||
def CP_O = 79
|
|
||||||
def CP_X = 88
|
|
||||||
def CP_Z = 90
|
|
||||||
def CP_LBRACKET = 91
|
|
||||||
def CP_BSLASH = 92
|
|
||||||
def CP_RBRACKET = 93
|
|
||||||
def CP_CARET = 94
|
|
||||||
def CP_UNDERSCORE = 95
|
|
||||||
def CP_BACKTICK = 96
|
|
||||||
def CP_a = 97
|
|
||||||
def CP_b = 98
|
|
||||||
def CP_e = 101
|
|
||||||
def CP_f = 102
|
|
||||||
def CP_n = 110
|
|
||||||
def CP_o = 111
|
|
||||||
def CP_r = 114
|
|
||||||
def CP_t = 116
|
|
||||||
def CP_u = 117
|
|
||||||
def CP_x = 120
|
|
||||||
def CP_z = 122
|
|
||||||
def CP_LBRACE = 123
|
|
||||||
def CP_PIPE = 124
|
|
||||||
def CP_RBRACE = 125
|
|
||||||
def CP_TILDE = 126
|
|
||||||
|
|
||||||
// Keywords lookup
|
// Keywords lookup
|
||||||
var keywords = {
|
var keywords = {
|
||||||
if: "if", in: "in", do: "do", go: "go",
|
if: "if", in: "in", do: "do", go: "go",
|
||||||
@@ -78,21 +17,27 @@ var tokenize = function(src, filename) {
|
|||||||
disruption: "disruption"
|
disruption: "disruption"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var escape_map = {
|
||||||
|
n: "\n", t: "\t", r: "\r", "\\": "\\",
|
||||||
|
"'": "'", "\"": "\"", "`": "`",
|
||||||
|
"0": character(0)
|
||||||
|
}
|
||||||
|
|
||||||
var pk = function() {
|
var pk = function() {
|
||||||
if (pos >= len) return -1
|
if (pos >= len) return null
|
||||||
return cp[pos]
|
return src[pos]
|
||||||
}
|
}
|
||||||
|
|
||||||
var pk_at = function(n) {
|
var pk_at = function(n) {
|
||||||
var idx = pos + n
|
var idx = pos + n
|
||||||
if (idx >= len) return -1
|
if (idx >= len) return null
|
||||||
return cp[idx]
|
return src[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
var adv = function() {
|
var adv = function() {
|
||||||
var c = cp[pos]
|
var c = src[pos]
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
if (c == CP_LF) {
|
if (c == "\n") {
|
||||||
row = row + 1
|
row = row + 1
|
||||||
col = 0
|
col = 0
|
||||||
} else {
|
} else {
|
||||||
@@ -102,17 +47,17 @@ var tokenize = function(src, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var is_digit = function(c) {
|
var is_digit = function(c) {
|
||||||
return c >= CP_0 && c <= CP_9
|
return c >= "0" && c <= "9"
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_hex = function(c) {
|
var is_hex = function(c) {
|
||||||
return (c >= CP_0 && c <= CP_9) || (c >= CP_a && c <= CP_f) || (c >= CP_A && c <= CP_F)
|
return (c >= "0" && c <= "9") || (c >= "a" && c <= "f") || (c >= "A" && c <= "F")
|
||||||
}
|
}
|
||||||
|
|
||||||
var hex_val = function(c) {
|
var hex_val = function(c) {
|
||||||
if (c >= CP_0 && c <= CP_9) return c - CP_0
|
if (c >= "0" && c <= "9") return codepoint(c) - codepoint("0")
|
||||||
if (c >= CP_a && c <= CP_f) return c - CP_a + 10
|
if (c >= "a" && c <= "f") return codepoint(c) - codepoint("a") + 10
|
||||||
if (c >= CP_A && c <= CP_F) return c - CP_A + 10
|
if (c >= "A" && c <= "F") return codepoint(c) - codepoint("A") + 10
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +72,7 @@ var tokenize = function(src, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var is_alpha = function(c) {
|
var is_alpha = function(c) {
|
||||||
return (c >= CP_a && c <= CP_z) || (c >= CP_A && c <= CP_Z)
|
return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z")
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_alnum = function(c) {
|
var is_alnum = function(c) {
|
||||||
@@ -135,41 +80,36 @@ var tokenize = function(src, filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var is_ident_start = function(c) {
|
var is_ident_start = function(c) {
|
||||||
return is_alpha(c) || c == CP_UNDERSCORE || c == CP_DOLLAR
|
return is_alpha(c) || c == "_" || c == "$"
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_ident_char = function(c) {
|
var is_ident_char = function(c) {
|
||||||
return is_alnum(c) || c == CP_UNDERSCORE || c == CP_DOLLAR || c == CP_QMARK || c == CP_BANG
|
return is_alnum(c) || c == "_" || c == "$" || c == "?" || c == "!"
|
||||||
}
|
}
|
||||||
|
|
||||||
var substr = function(start, end) {
|
var substr = function(start, end) {
|
||||||
return text(src, start, end)
|
return text(src, start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
var read_string = function(quote_cp) {
|
var read_string = function(quote) {
|
||||||
var start = pos
|
var start = pos
|
||||||
var start_row = row
|
var start_row = row
|
||||||
var start_col = col
|
var start_col = col
|
||||||
var parts = []
|
var parts = []
|
||||||
var run_start = 0
|
var run_start = 0
|
||||||
var esc = 0
|
var esc = null
|
||||||
|
var esc_val = null
|
||||||
adv() // skip opening quote
|
adv() // skip opening quote
|
||||||
run_start = pos
|
run_start = pos
|
||||||
while (pos < len && pk() != quote_cp) {
|
while (pos < len && pk() != quote) {
|
||||||
if (pk() == CP_BSLASH) {
|
if (pk() == "\\") {
|
||||||
if (pos > run_start) push(parts, text(src, run_start, pos))
|
if (pos > run_start) push(parts, text(src, run_start, pos))
|
||||||
adv()
|
adv()
|
||||||
esc = adv()
|
esc = adv()
|
||||||
if (esc == CP_n) { push(parts, "\n") }
|
esc_val = escape_map[esc]
|
||||||
else if (esc == CP_t) { push(parts, "\t") }
|
if (esc_val != null) { push(parts, esc_val) }
|
||||||
else if (esc == CP_r) { push(parts, "\r") }
|
else if (esc == "u") { push(parts, read_unicode_escape()) }
|
||||||
else if (esc == CP_BSLASH) { push(parts, "\\") }
|
else { push(parts, esc) }
|
||||||
else if (esc == CP_SQUOTE) { push(parts, "'") }
|
|
||||||
else if (esc == CP_DQUOTE) { push(parts, "\"") }
|
|
||||||
else if (esc == CP_0) { push(parts, character(0)) }
|
|
||||||
else if (esc == CP_BACKTICK) { push(parts, "`") }
|
|
||||||
else if (esc == CP_u) { push(parts, read_unicode_escape()) }
|
|
||||||
else { push(parts, character(esc)) }
|
|
||||||
run_start = pos
|
run_start = pos
|
||||||
} else {
|
} else {
|
||||||
adv()
|
adv()
|
||||||
@@ -192,33 +132,33 @@ var tokenize = function(src, filename) {
|
|||||||
var parts = []
|
var parts = []
|
||||||
var run_start = 0
|
var run_start = 0
|
||||||
var depth = 0
|
var depth = 0
|
||||||
var tc = 0
|
var tc = null
|
||||||
var q = 0
|
var q = null
|
||||||
var interp_start = 0
|
var interp_start = 0
|
||||||
adv() // skip opening backtick
|
adv() // skip opening backtick
|
||||||
run_start = pos
|
run_start = pos
|
||||||
while (pos < len && pk() != CP_BACKTICK) {
|
while (pos < len && pk() != "`") {
|
||||||
if (pk() == CP_BSLASH && pos + 1 < len) {
|
if (pk() == "\\" && pos + 1 < len) {
|
||||||
if (pos > run_start) push(parts, text(src, run_start, pos))
|
if (pos > run_start) push(parts, text(src, run_start, pos))
|
||||||
push(parts, text(src, pos, pos + 2))
|
push(parts, text(src, pos, pos + 2))
|
||||||
adv(); adv()
|
adv(); adv()
|
||||||
run_start = pos
|
run_start = pos
|
||||||
} else if (pk() == CP_DOLLAR && pos + 1 < len && pk_at(1) == CP_LBRACE) {
|
} else if (pk() == "$" && pos + 1 < len && pk_at(1) == "{") {
|
||||||
if (pos > run_start) push(parts, text(src, run_start, pos))
|
if (pos > run_start) push(parts, text(src, run_start, pos))
|
||||||
interp_start = pos
|
interp_start = pos
|
||||||
adv(); adv() // $ {
|
adv(); adv() // $ {
|
||||||
depth = 1
|
depth = 1
|
||||||
while (pos < len && depth > 0) {
|
while (pos < len && depth > 0) {
|
||||||
tc = pk()
|
tc = pk()
|
||||||
if (tc == CP_LBRACE) { depth = depth + 1; adv() }
|
if (tc == "{") { depth = depth + 1; adv() }
|
||||||
else if (tc == CP_RBRACE) {
|
else if (tc == "}") {
|
||||||
depth = depth - 1
|
depth = depth - 1
|
||||||
adv()
|
adv()
|
||||||
}
|
}
|
||||||
else if (tc == CP_SQUOTE || tc == CP_DQUOTE || tc == CP_BACKTICK) {
|
else if (tc == "'" || tc == "\"" || tc == "`") {
|
||||||
q = adv()
|
q = adv()
|
||||||
while (pos < len && pk() != q) {
|
while (pos < len && pk() != q) {
|
||||||
if (pk() == CP_BSLASH && pos + 1 < len) adv()
|
if (pk() == "\\" && pos + 1 < len) adv()
|
||||||
adv()
|
adv()
|
||||||
}
|
}
|
||||||
if (pos < len) adv()
|
if (pos < len) adv()
|
||||||
@@ -245,24 +185,24 @@ var tokenize = function(src, filename) {
|
|||||||
var start_row = row
|
var start_row = row
|
||||||
var start_col = col
|
var start_col = col
|
||||||
var raw = ""
|
var raw = ""
|
||||||
if (pk() == CP_0 && (pk_at(1) == CP_x || pk_at(1) == CP_X)) {
|
if (pk() == "0" && (pk_at(1) == "x" || pk_at(1) == "X")) {
|
||||||
adv(); adv()
|
adv(); adv()
|
||||||
while (pos < len && (is_hex(pk()) || pk() == CP_UNDERSCORE)) adv()
|
while (pos < len && (is_hex(pk()) || pk() == "_")) adv()
|
||||||
} else if (pk() == CP_0 && (pk_at(1) == CP_b || pk_at(1) == CP_B)) {
|
} else if (pk() == "0" && (pk_at(1) == "b" || pk_at(1) == "B")) {
|
||||||
adv(); adv()
|
adv(); adv()
|
||||||
while (pos < len && (pk() == CP_0 || pk() == CP_1 || pk() == CP_UNDERSCORE)) adv()
|
while (pos < len && (pk() == "0" || pk() == "1" || pk() == "_")) adv()
|
||||||
} else if (pk() == CP_0 && (pk_at(1) == CP_o || pk_at(1) == CP_O)) {
|
} else if (pk() == "0" && (pk_at(1) == "o" || pk_at(1) == "O")) {
|
||||||
adv(); adv()
|
adv(); adv()
|
||||||
while (pos < len && pk() >= CP_0 && pk() <= CP_7) adv()
|
while (pos < len && pk() >= "0" && pk() <= "7") adv()
|
||||||
} else {
|
} else {
|
||||||
while (pos < len && (is_digit(pk()) || pk() == CP_UNDERSCORE)) adv()
|
while (pos < len && (is_digit(pk()) || pk() == "_")) adv()
|
||||||
if (pos < len && pk() == CP_DOT) {
|
if (pos < len && pk() == ".") {
|
||||||
adv()
|
adv()
|
||||||
while (pos < len && (is_digit(pk()) || pk() == CP_UNDERSCORE)) adv()
|
while (pos < len && (is_digit(pk()) || pk() == "_")) adv()
|
||||||
}
|
}
|
||||||
if (pos < len && (pk() == CP_e || pk() == CP_E)) {
|
if (pos < len && (pk() == "e" || pk() == "E")) {
|
||||||
adv()
|
adv()
|
||||||
if (pos < len && (pk() == CP_PLUS || pk() == CP_MINUS)) adv()
|
if (pos < len && (pk() == "+" || pk() == "-")) adv()
|
||||||
while (pos < len && is_digit(pk())) adv()
|
while (pos < len && is_digit(pk())) adv()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,12 +245,12 @@ var tokenize = function(src, filename) {
|
|||||||
var start_row = row
|
var start_row = row
|
||||||
var start_col = col
|
var start_col = col
|
||||||
var raw = ""
|
var raw = ""
|
||||||
if (pk_at(1) == CP_SLASH) {
|
if (pk_at(1) == "/") {
|
||||||
while (pos < len && pk() != CP_LF && pk() != CP_CR) adv()
|
while (pos < len && pk() != "\n" && pk() != "\r") adv()
|
||||||
} else {
|
} else {
|
||||||
adv(); adv() // skip /*
|
adv(); adv() // skip /*
|
||||||
while (pos < len) {
|
while (pos < len) {
|
||||||
if (pk() == CP_STAR && pk_at(1) == CP_SLASH) {
|
if (pk() == "*" && pk_at(1) == "/") {
|
||||||
adv(); adv()
|
adv(); adv()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -359,144 +299,144 @@ var tokenize = function(src, filename) {
|
|||||||
var start_row = 0
|
var start_row = 0
|
||||||
var start_col = 0
|
var start_col = 0
|
||||||
var raw = ""
|
var raw = ""
|
||||||
if (c == -1) return false
|
if (c == null) return false
|
||||||
|
|
||||||
if (c == CP_LF) {
|
if (c == "\n") {
|
||||||
start = pos; start_row = row; start_col = col
|
start = pos; start_row = row; start_col = col
|
||||||
adv()
|
adv()
|
||||||
push(tokens, { kind: "newline", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: "\n" })
|
push(tokens, { kind: "newline", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: "\n" })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (c == CP_CR) {
|
if (c == "\r") {
|
||||||
start = pos; start_row = row; start_col = col
|
start = pos; start_row = row; start_col = col
|
||||||
adv()
|
adv()
|
||||||
if (pos < len && pk() == CP_LF) adv()
|
if (pos < len && pk() == "\n") adv()
|
||||||
push(tokens, { kind: "newline", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: "\n" })
|
push(tokens, { kind: "newline", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: "\n" })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (c == CP_SPACE || c == CP_TAB) {
|
if (c == " " || c == "\t") {
|
||||||
start = pos; start_row = row; start_col = col
|
start = pos; start_row = row; start_col = col
|
||||||
while (pos < len && (pk() == CP_SPACE || pk() == CP_TAB)) adv()
|
while (pos < len && (pk() == " " || pk() == "\t")) adv()
|
||||||
raw = substr(start, pos)
|
raw = substr(start, pos)
|
||||||
push(tokens, { kind: "space", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: raw })
|
push(tokens, { kind: "space", at: start, from_row: start_row, from_column: start_col, to_row: row, to_column: col, value: raw })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (c == CP_SQUOTE || c == CP_DQUOTE) { read_string(c); return true }
|
if (c == "'" || c == "\"") { read_string(c); return true }
|
||||||
if (c == CP_BACKTICK) { read_template(); return true }
|
if (c == "`") { read_template(); return true }
|
||||||
if (is_digit(c)) { read_number(); return true }
|
if (is_digit(c)) { read_number(); return true }
|
||||||
if (c == CP_DOT && is_digit(pk_at(1))) { read_number(); return true }
|
if (c == "." && is_digit(pk_at(1))) { read_number(); return true }
|
||||||
if (is_ident_start(c)) { read_name(); return true }
|
if (is_ident_start(c)) { read_name(); return true }
|
||||||
if (c == CP_SLASH) {
|
if (c == "/") {
|
||||||
if (pk_at(1) == CP_SLASH || pk_at(1) == CP_STAR) { read_comment(); return true }
|
if (pk_at(1) == "/" || pk_at(1) == "*") { read_comment(); return true }
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("/=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("/=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("/", 1); return true
|
emit_op("/", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_STAR) {
|
if (c == "*") {
|
||||||
if (pk_at(1) == CP_STAR) {
|
if (pk_at(1) == "*") {
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("**=", 3); return true }
|
if (pk_at(2) == "=") { emit_op("**=", 3); return true }
|
||||||
emit_op("**", 2); return true
|
emit_op("**", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("*=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("*=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("*", 1); return true
|
emit_op("*", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_PERCENT) {
|
if (c == "%") {
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("%=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("%=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("%", 1); return true
|
emit_op("%", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_PLUS) {
|
if (c == "+") {
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("+=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("+=", 2); return true }
|
||||||
if (pk_at(1) == CP_PLUS) { emit_op("++", 2); return true }
|
if (pk_at(1) == "+") { emit_op("++", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("+", 1); return true
|
emit_op("+", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_MINUS) {
|
if (c == "-") {
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("-=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("-=", 2); return true }
|
||||||
if (pk_at(1) == CP_MINUS) { emit_op("--", 2); return true }
|
if (pk_at(1) == "-") { emit_op("--", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("-", 1); return true
|
emit_op("-", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_LT) {
|
if (c == "<") {
|
||||||
if (pk_at(1) == CP_EQ && pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(1) == "=" && pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("<=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("<=", 2); return true }
|
||||||
if (pk_at(1) == CP_LT) {
|
if (pk_at(1) == "<") {
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("<<=", 3); return true }
|
if (pk_at(2) == "=") { emit_op("<<=", 3); return true }
|
||||||
emit_op("<<", 2); return true
|
emit_op("<<", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("<", 1); return true
|
emit_op("<", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_GT) {
|
if (c == ">") {
|
||||||
if (pk_at(1) == CP_EQ && pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(1) == "=" && pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(1) == CP_EQ) { emit_op(">=", 2); return true }
|
if (pk_at(1) == "=") { emit_op(">=", 2); return true }
|
||||||
if (pk_at(1) == CP_GT) {
|
if (pk_at(1) == ">") {
|
||||||
if (pk_at(2) == CP_GT) {
|
if (pk_at(2) == ">") {
|
||||||
if (pk_at(3) == CP_BANG) { emit_ident(4); return true }
|
if (pk_at(3) == "!") { emit_ident(4); return true }
|
||||||
if (pk_at(3) == CP_EQ) { emit_op(">>>=", 4); return true }
|
if (pk_at(3) == "=") { emit_op(">>>=", 4); return true }
|
||||||
emit_op(">>>", 3); return true
|
emit_op(">>>", 3); return true
|
||||||
}
|
}
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op(">>=", 3); return true }
|
if (pk_at(2) == "=") { emit_op(">>=", 3); return true }
|
||||||
emit_op(">>", 2); return true
|
emit_op(">>", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op(">", 1); return true
|
emit_op(">", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_EQ) {
|
if (c == "=") {
|
||||||
if (pk_at(1) == CP_EQ) {
|
if (pk_at(1) == "=") {
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("===", 3); return true }
|
if (pk_at(2) == "=") { emit_op("===", 3); return true }
|
||||||
emit_op("==", 2); return true
|
emit_op("==", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_GT) { emit_op("=>", 2); return true }
|
if (pk_at(1) == ">") { emit_op("=>", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("=", 1); return true
|
emit_op("=", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_BANG) {
|
if (c == "!") {
|
||||||
if (pk_at(1) == CP_EQ) {
|
if (pk_at(1) == "=") {
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("!==", 3); return true }
|
if (pk_at(2) == "=") { emit_op("!==", 3); return true }
|
||||||
emit_op("!=", 2); return true
|
emit_op("!=", 2); return true
|
||||||
}
|
}
|
||||||
emit_op("!", 1); return true
|
emit_op("!", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_AMP) {
|
if (c == "&") {
|
||||||
if (pk_at(1) == CP_AMP) {
|
if (pk_at(1) == "&") {
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("&&=", 3); return true }
|
if (pk_at(2) == "=") { emit_op("&&=", 3); return true }
|
||||||
emit_op("&&", 2); return true
|
emit_op("&&", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("&=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("&=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("&", 1); return true
|
emit_op("&", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_PIPE) {
|
if (c == "|") {
|
||||||
if (pk_at(1) == CP_PIPE) {
|
if (pk_at(1) == "|") {
|
||||||
if (pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
if (pk_at(2) == CP_EQ) { emit_op("||=", 3); return true }
|
if (pk_at(2) == "=") { emit_op("||=", 3); return true }
|
||||||
emit_op("||", 2); return true
|
emit_op("||", 2); return true
|
||||||
}
|
}
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("|=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("|=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("|", 1); return true
|
emit_op("|", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_CARET) {
|
if (c == "^") {
|
||||||
if (pk_at(1) == CP_EQ) { emit_op("^=", 2); return true }
|
if (pk_at(1) == "=") { emit_op("^=", 2); return true }
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("^", 1); return true
|
emit_op("^", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_LBRACKET) {
|
if (c == "[") {
|
||||||
if (pk_at(1) == CP_RBRACKET && pk_at(2) == CP_BANG) { emit_ident(3); return true }
|
if (pk_at(1) == "]" && pk_at(2) == "!") { emit_ident(3); return true }
|
||||||
emit_op("[", 1); return true
|
emit_op("[", 1); return true
|
||||||
}
|
}
|
||||||
if (c == CP_TILDE) {
|
if (c == "~") {
|
||||||
if (pk_at(1) == CP_BANG) { emit_ident(2); return true }
|
if (pk_at(1) == "!") { emit_ident(2); return true }
|
||||||
emit_op("~", 1); return true
|
emit_op("~", 1); return true
|
||||||
}
|
}
|
||||||
emit_op(character(c), 1)
|
emit_op(c, 1)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +448,7 @@ var tokenize = function(src, filename) {
|
|||||||
// EOF token
|
// EOF token
|
||||||
push(tokens, { kind: "eof", at: pos, from_row: row, from_column: col, to_row: row, to_column: col })
|
push(tokens, { kind: "eof", at: pos, from_row: row, from_column: col, to_row: row, to_column: col })
|
||||||
|
|
||||||
return {filename: filename, tokens: tokens, cp: cp}
|
return {filename: filename, tokens: tokens}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenize
|
return tokenize
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ static const JSCFunctionListEntry js_wildstar_funcs[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_core_wildstar_use(JSContext *js) {
|
JSValue js_core_wildstar_use(JSContext *js) {
|
||||||
JSValue mod = JS_NewObject(js);
|
JS_FRAME(js);
|
||||||
JS_SetPropertyFunctionList(js, mod, js_wildstar_funcs, countof(js_wildstar_funcs));
|
JS_ROOT(mod, JS_NewObject(js));
|
||||||
return mod;
|
JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs));
|
||||||
|
JS_RETURN(mod.val);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user