Merge remote-tracking branch 'origin/master'
This commit is contained in:
86
bench_arith.ce
Normal file
86
bench_arith.ce
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// bench_arith.ce — arithmetic and number crunching benchmark
|
||||||
|
// Tests: integer add/mul, float ops, loop counter overhead, conditionals
|
||||||
|
|
||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
def iterations = 2000000
|
||||||
|
|
||||||
|
// 1. Integer sum in tight loop
|
||||||
|
function bench_int_sum() {
|
||||||
|
var i = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
s = s + i
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Integer multiply + mod (sieve-like)
|
||||||
|
function bench_int_mul_mod() {
|
||||||
|
var i = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
s = s + (i * 7 % 1000)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Float math — accumulate with division
|
||||||
|
function bench_float_arith() {
|
||||||
|
var i = 0
|
||||||
|
var s = 0.5
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
s = s + 1.0 / i
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Nested loop with branch (fizzbuzz-like counter)
|
||||||
|
function bench_branch() {
|
||||||
|
var i = 0
|
||||||
|
var fizz = 0
|
||||||
|
var buzz = 0
|
||||||
|
var fizzbuzz = 0
|
||||||
|
for (i = 1; i <= iterations; i++) {
|
||||||
|
if (i % 15 == 0) {
|
||||||
|
fizzbuzz = fizzbuzz + 1
|
||||||
|
} else if (i % 3 == 0) {
|
||||||
|
fizz = fizz + 1
|
||||||
|
} else if (i % 5 == 0) {
|
||||||
|
buzz = buzz + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fizz + buzz + fizzbuzz
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Nested loop (small inner)
|
||||||
|
function bench_nested() {
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var s = 0
|
||||||
|
def outer = 5000
|
||||||
|
def inner = 5000
|
||||||
|
for (i = 0; i < outer; i++) {
|
||||||
|
for (j = 0; j < inner; j++) {
|
||||||
|
s = s + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run each and print timing
|
||||||
|
function run(name, fn) {
|
||||||
|
var start = time.number()
|
||||||
|
var result = fn()
|
||||||
|
var elapsed = time.number() - start
|
||||||
|
var ms = whole(elapsed * 100000) / 100
|
||||||
|
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("=== Arithmetic Benchmark ===")
|
||||||
|
log.console(` iterations: ${iterations}`)
|
||||||
|
run("int_sum ", bench_int_sum)
|
||||||
|
run("int_mul_mod ", bench_int_mul_mod)
|
||||||
|
run("float_arith ", bench_float_arith)
|
||||||
|
run("branch ", bench_branch)
|
||||||
|
run("nested_loop ", bench_nested)
|
||||||
67
bench_arith.js
Normal file
67
bench_arith.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// bench_arith.js — arithmetic and number crunching benchmark (QuickJS)
|
||||||
|
|
||||||
|
const iterations = 2000000;
|
||||||
|
|
||||||
|
function bench_int_sum() {
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
s = s + i;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_int_mul_mod() {
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 1; i < iterations; i++) {
|
||||||
|
s = s + (i * 7 % 1000);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_float_arith() {
|
||||||
|
let s = 0.5;
|
||||||
|
for (let i = 1; i < iterations; i++) {
|
||||||
|
s = s + 1.0 / i;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_branch() {
|
||||||
|
let fizz = 0, buzz = 0, fizzbuzz = 0;
|
||||||
|
for (let i = 1; i <= iterations; i++) {
|
||||||
|
if (i % 15 === 0) {
|
||||||
|
fizzbuzz = fizzbuzz + 1;
|
||||||
|
} else if (i % 3 === 0) {
|
||||||
|
fizz = fizz + 1;
|
||||||
|
} else if (i % 5 === 0) {
|
||||||
|
buzz = buzz + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fizz + buzz + fizzbuzz;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_nested() {
|
||||||
|
let s = 0;
|
||||||
|
const outer = 5000, inner = 5000;
|
||||||
|
for (let i = 0; i < outer; i++) {
|
||||||
|
for (let j = 0; j < inner; j++) {
|
||||||
|
s = s + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
const start = performance.now();
|
||||||
|
const result = fn();
|
||||||
|
const elapsed = performance.now() - start;
|
||||||
|
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("=== Arithmetic Benchmark ===");
|
||||||
|
console.log(` iterations: ${iterations}`);
|
||||||
|
run("int_sum ", bench_int_sum);
|
||||||
|
run("int_mul_mod ", bench_int_mul_mod);
|
||||||
|
run("float_arith ", bench_float_arith);
|
||||||
|
run("branch ", bench_branch);
|
||||||
|
run("nested_loop ", bench_nested);
|
||||||
68
bench_arith.lua
Normal file
68
bench_arith.lua
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
-- bench_arith.lua — arithmetic and number crunching benchmark (Lua)
|
||||||
|
|
||||||
|
local iterations = 2000000
|
||||||
|
local clock = os.clock
|
||||||
|
|
||||||
|
local function bench_int_sum()
|
||||||
|
local s = 0
|
||||||
|
for i = 0, iterations - 1 do
|
||||||
|
s = s + i
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_int_mul_mod()
|
||||||
|
local s = 0
|
||||||
|
for i = 1, iterations - 1 do
|
||||||
|
s = s + (i * 7 % 1000)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_float_arith()
|
||||||
|
local s = 0.5
|
||||||
|
for i = 1, iterations - 1 do
|
||||||
|
s = s + 1.0 / i
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_branch()
|
||||||
|
local fizz, buzz, fizzbuzz = 0, 0, 0
|
||||||
|
for i = 1, iterations do
|
||||||
|
if i % 15 == 0 then
|
||||||
|
fizzbuzz = fizzbuzz + 1
|
||||||
|
elseif i % 3 == 0 then
|
||||||
|
fizz = fizz + 1
|
||||||
|
elseif i % 5 == 0 then
|
||||||
|
buzz = buzz + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return fizz + buzz + fizzbuzz
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_nested()
|
||||||
|
local s = 0
|
||||||
|
local outer, inner = 5000, 5000
|
||||||
|
for i = 0, outer - 1 do
|
||||||
|
for j = 0, inner - 1 do
|
||||||
|
s = s + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(name, fn)
|
||||||
|
local start = clock()
|
||||||
|
local result = fn()
|
||||||
|
local elapsed = (clock() - start) * 1000
|
||||||
|
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||||
|
end
|
||||||
|
|
||||||
|
print("=== Arithmetic Benchmark ===")
|
||||||
|
print(string.format(" iterations: %d", iterations))
|
||||||
|
run("int_sum ", bench_int_sum)
|
||||||
|
run("int_mul_mod ", bench_int_mul_mod)
|
||||||
|
run("float_arith ", bench_float_arith)
|
||||||
|
run("branch ", bench_branch)
|
||||||
|
run("nested_loop ", bench_nested)
|
||||||
113
bench_array.ce
Normal file
113
bench_array.ce
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// bench_array.ce — array operation benchmark
|
||||||
|
// Tests: sequential access, push/build, index write, sum reduction, sort
|
||||||
|
|
||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
def size = 100000
|
||||||
|
|
||||||
|
// 1. Build array with push
|
||||||
|
function bench_push() {
|
||||||
|
var a = []
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
a[] = i
|
||||||
|
}
|
||||||
|
return length(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Index write into preallocated array
|
||||||
|
function bench_index_write() {
|
||||||
|
var a = array(size, 0)
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
a[i] = i
|
||||||
|
}
|
||||||
|
return a[size - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Sequential read and sum
|
||||||
|
function bench_seq_read() {
|
||||||
|
var a = array(size, 0)
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
a[i] = i
|
||||||
|
}
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
s = s + a[i]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Reverse array in-place
|
||||||
|
function bench_reverse() {
|
||||||
|
var a = array(size, 0)
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
a[i] = i
|
||||||
|
}
|
||||||
|
var lo = 0
|
||||||
|
var hi = size - 1
|
||||||
|
var tmp = 0
|
||||||
|
while (lo < hi) {
|
||||||
|
tmp = a[lo]
|
||||||
|
a[lo] = a[hi]
|
||||||
|
a[hi] = tmp
|
||||||
|
lo = lo + 1
|
||||||
|
hi = hi - 1
|
||||||
|
}
|
||||||
|
return a[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Nested array access (matrix-like, 300x300)
|
||||||
|
function bench_matrix() {
|
||||||
|
def n = 300
|
||||||
|
var mat = array(n, null)
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
mat[i] = array(n, 0)
|
||||||
|
for (j = 0; j < n; j++) {
|
||||||
|
mat[i][j] = i * n + j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sum diagonal
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
s = s + mat[i][i]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. filter-like: count evens
|
||||||
|
function bench_filter_count() {
|
||||||
|
var a = array(size, 0)
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
a[i] = i
|
||||||
|
}
|
||||||
|
var count = 0
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (a[i] % 2 == 0) {
|
||||||
|
count = count + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
var start = time.number()
|
||||||
|
var result = fn()
|
||||||
|
var elapsed = time.number() - start
|
||||||
|
var ms = whole(elapsed * 100000) / 100
|
||||||
|
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("=== Array Benchmark ===")
|
||||||
|
log.console(` size: ${size}`)
|
||||||
|
run("push ", bench_push)
|
||||||
|
run("index_write ", bench_index_write)
|
||||||
|
run("seq_read_sum ", bench_seq_read)
|
||||||
|
run("reverse ", bench_reverse)
|
||||||
|
run("matrix_300 ", bench_matrix)
|
||||||
|
run("filter_count ", bench_filter_count)
|
||||||
93
bench_array.js
Normal file
93
bench_array.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// bench_array.js — array operation benchmark (QuickJS)
|
||||||
|
|
||||||
|
const size = 100000;
|
||||||
|
|
||||||
|
function bench_push() {
|
||||||
|
let a = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
a.push(i);
|
||||||
|
}
|
||||||
|
return a.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_index_write() {
|
||||||
|
let a = new Array(size).fill(0);
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
a[i] = i;
|
||||||
|
}
|
||||||
|
return a[size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_seq_read() {
|
||||||
|
let a = new Array(size).fill(0);
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
a[i] = i;
|
||||||
|
}
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
s = s + a[i];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_reverse() {
|
||||||
|
let a = new Array(size).fill(0);
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
a[i] = i;
|
||||||
|
}
|
||||||
|
let lo = 0, hi = size - 1, tmp;
|
||||||
|
while (lo < hi) {
|
||||||
|
tmp = a[lo];
|
||||||
|
a[lo] = a[hi];
|
||||||
|
a[hi] = tmp;
|
||||||
|
lo = lo + 1;
|
||||||
|
hi = hi - 1;
|
||||||
|
}
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_matrix() {
|
||||||
|
const n = 300;
|
||||||
|
let mat = new Array(n);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
mat[i] = new Array(n).fill(0);
|
||||||
|
for (let j = 0; j < n; j++) {
|
||||||
|
mat[i][j] = i * n + j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
s = s + mat[i][i];
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_filter_count() {
|
||||||
|
let a = new Array(size).fill(0);
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
a[i] = i;
|
||||||
|
}
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
if (a[i] % 2 === 0) {
|
||||||
|
count = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
const start = performance.now();
|
||||||
|
const result = fn();
|
||||||
|
const elapsed = performance.now() - start;
|
||||||
|
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("=== Array Benchmark ===");
|
||||||
|
console.log(` size: ${size}`);
|
||||||
|
run("push ", bench_push);
|
||||||
|
run("index_write ", bench_index_write);
|
||||||
|
run("seq_read_sum ", bench_seq_read);
|
||||||
|
run("reverse ", bench_reverse);
|
||||||
|
run("matrix_300 ", bench_matrix);
|
||||||
|
run("filter_count ", bench_filter_count);
|
||||||
93
bench_array.lua
Normal file
93
bench_array.lua
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
-- bench_array.lua — array operation benchmark (Lua)
|
||||||
|
|
||||||
|
local size = 100000
|
||||||
|
local clock = os.clock
|
||||||
|
|
||||||
|
local function bench_push()
|
||||||
|
local a = {}
|
||||||
|
for i = 0, size - 1 do
|
||||||
|
a[#a + 1] = i
|
||||||
|
end
|
||||||
|
return #a
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_index_write()
|
||||||
|
local a = {}
|
||||||
|
for i = 1, size do a[i] = 0 end
|
||||||
|
for i = 1, size do
|
||||||
|
a[i] = i - 1
|
||||||
|
end
|
||||||
|
return a[size]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_seq_read()
|
||||||
|
local a = {}
|
||||||
|
for i = 1, size do
|
||||||
|
a[i] = i - 1
|
||||||
|
end
|
||||||
|
local s = 0
|
||||||
|
for i = 1, size do
|
||||||
|
s = s + a[i]
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_reverse()
|
||||||
|
local a = {}
|
||||||
|
for i = 1, size do
|
||||||
|
a[i] = i - 1
|
||||||
|
end
|
||||||
|
local lo, hi = 1, size
|
||||||
|
while lo < hi do
|
||||||
|
a[lo], a[hi] = a[hi], a[lo]
|
||||||
|
lo = lo + 1
|
||||||
|
hi = hi - 1
|
||||||
|
end
|
||||||
|
return a[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_matrix()
|
||||||
|
local n = 300
|
||||||
|
local mat = {}
|
||||||
|
for i = 1, n do
|
||||||
|
mat[i] = {}
|
||||||
|
for j = 1, n do
|
||||||
|
mat[i][j] = (i - 1) * n + (j - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local s = 0
|
||||||
|
for i = 1, n do
|
||||||
|
s = s + mat[i][i]
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_filter_count()
|
||||||
|
local a = {}
|
||||||
|
for i = 1, size do
|
||||||
|
a[i] = i - 1
|
||||||
|
end
|
||||||
|
local count = 0
|
||||||
|
for i = 1, size do
|
||||||
|
if a[i] % 2 == 0 then
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(name, fn)
|
||||||
|
local start = clock()
|
||||||
|
local result = fn()
|
||||||
|
local elapsed = (clock() - start) * 1000
|
||||||
|
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||||
|
end
|
||||||
|
|
||||||
|
print("=== Array Benchmark ===")
|
||||||
|
print(string.format(" size: %d", size))
|
||||||
|
run("push ", bench_push)
|
||||||
|
run("index_write ", bench_index_write)
|
||||||
|
run("seq_read_sum ", bench_seq_read)
|
||||||
|
run("reverse ", bench_reverse)
|
||||||
|
run("matrix_300 ", bench_matrix)
|
||||||
|
run("filter_count ", bench_filter_count)
|
||||||
21
bench_fib.ce
Normal file
21
bench_fib.ce
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
function fib(n) {
|
||||||
|
if (n < 2) {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return fib(n - 1) + fib(n - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
var start = time.number()
|
||||||
|
var result = fn()
|
||||||
|
var elapsed = time.number() - start
|
||||||
|
var ms = whole(elapsed * 100000) / 100
|
||||||
|
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("=== Cell fib ===")
|
||||||
|
run("fib(25)", function() { return fib(25) })
|
||||||
|
run("fib(30)", function() { return fib(30) })
|
||||||
|
run("fib(35)", function() { return fib(35) })
|
||||||
118
bench_object.ce
Normal file
118
bench_object.ce
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// bench_object.ce — object/record and string benchmark
|
||||||
|
// Tests: property read/write, string concat, string interpolation, method-like dispatch
|
||||||
|
|
||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
def iterations = 200000
|
||||||
|
|
||||||
|
// 1. Record create + property write
|
||||||
|
function bench_record_create() {
|
||||||
|
var i = 0
|
||||||
|
var r = null
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
r = {x: i, y: i + 1, z: i + 2}
|
||||||
|
}
|
||||||
|
return r.z
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Property read in loop
|
||||||
|
function bench_prop_read() {
|
||||||
|
var obj = {x: 10, y: 20, z: 30, w: 40}
|
||||||
|
var i = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
s = s + obj.x + obj.y + obj.z + obj.w
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Dynamic property access (computed keys)
|
||||||
|
function bench_dynamic_prop() {
|
||||||
|
var obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||||
|
var keys = ["a", "b", "c", "d", "e"]
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
for (j = 0; j < 5; j++) {
|
||||||
|
s = s + obj[keys[j]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. String concatenation
|
||||||
|
function bench_string_concat() {
|
||||||
|
var i = 0
|
||||||
|
var s = ""
|
||||||
|
def n = 10000
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
s = s + "x"
|
||||||
|
}
|
||||||
|
return length(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. String interpolation
|
||||||
|
function bench_interpolation() {
|
||||||
|
var i = 0
|
||||||
|
var s = ""
|
||||||
|
def n = 50000
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
s = `item_${i}`
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Prototype chain / method-like call
|
||||||
|
function make_point(x, y) {
|
||||||
|
return {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
sum: function(self) {
|
||||||
|
return self.x + self.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_method_call() {
|
||||||
|
var p = make_point(3, 4)
|
||||||
|
var i = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < iterations; i++) {
|
||||||
|
s = s + p.sum(p)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Function call overhead (simple recursion depth)
|
||||||
|
function fib(n) {
|
||||||
|
if (n <= 1) return n
|
||||||
|
return fib(n - 1) + fib(n - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_fncall() {
|
||||||
|
var i = 0
|
||||||
|
var s = 0
|
||||||
|
for (i = 0; i < 20; i++) {
|
||||||
|
s = s + fib(25)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
var start = time.number()
|
||||||
|
var result = fn()
|
||||||
|
var elapsed = time.number() - start
|
||||||
|
var ms = whole(elapsed * 100000) / 100
|
||||||
|
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("=== Object / String / Call Benchmark ===")
|
||||||
|
log.console(` iterations: ${iterations}`)
|
||||||
|
run("record_create ", bench_record_create)
|
||||||
|
run("prop_read ", bench_prop_read)
|
||||||
|
run("dynamic_prop ", bench_dynamic_prop)
|
||||||
|
run("string_concat ", bench_string_concat)
|
||||||
|
run("interpolation ", bench_interpolation)
|
||||||
|
run("method_call ", bench_method_call)
|
||||||
|
run("fncall_fib25 ", bench_fncall)
|
||||||
99
bench_object.js
Normal file
99
bench_object.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// bench_object.js — object/string/call benchmark (QuickJS)
|
||||||
|
|
||||||
|
const iterations = 200000;
|
||||||
|
|
||||||
|
function bench_record_create() {
|
||||||
|
let r;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
r = {x: i, y: i + 1, z: i + 2};
|
||||||
|
}
|
||||||
|
return r.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_prop_read() {
|
||||||
|
const obj = {x: 10, y: 20, z: 30, w: 40};
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
s = s + obj.x + obj.y + obj.z + obj.w;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_dynamic_prop() {
|
||||||
|
const obj = {a: 1, b: 2, c: 3, d: 4, e: 5};
|
||||||
|
const keys = ["a", "b", "c", "d", "e"];
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
for (let j = 0; j < 5; j++) {
|
||||||
|
s = s + obj[keys[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_string_concat() {
|
||||||
|
let s = "";
|
||||||
|
const n = 10000;
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
s = s + "x";
|
||||||
|
}
|
||||||
|
return s.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_interpolation() {
|
||||||
|
let s = "";
|
||||||
|
const n = 50000;
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
s = `item_${i}`;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_point(x, y) {
|
||||||
|
return {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
sum: function(self) {
|
||||||
|
return self.x + self.y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_method_call() {
|
||||||
|
const p = make_point(3, 4);
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
s = s + p.sum(p);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fib(n) {
|
||||||
|
if (n <= 1) return n;
|
||||||
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bench_fncall() {
|
||||||
|
let s = 0;
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
s = s + fib(25);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(name, fn) {
|
||||||
|
const start = performance.now();
|
||||||
|
const result = fn();
|
||||||
|
const elapsed = performance.now() - start;
|
||||||
|
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("=== Object / String / Call Benchmark ===");
|
||||||
|
console.log(` iterations: ${iterations}`);
|
||||||
|
run("record_create ", bench_record_create);
|
||||||
|
run("prop_read ", bench_prop_read);
|
||||||
|
run("dynamic_prop ", bench_dynamic_prop);
|
||||||
|
run("string_concat ", bench_string_concat);
|
||||||
|
run("interpolation ", bench_interpolation);
|
||||||
|
run("method_call ", bench_method_call);
|
||||||
|
run("fncall_fib25 ", bench_fncall);
|
||||||
101
bench_object.lua
Normal file
101
bench_object.lua
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
-- bench_object.lua — object/string/call benchmark (Lua)
|
||||||
|
|
||||||
|
local iterations = 200000
|
||||||
|
local clock = os.clock
|
||||||
|
|
||||||
|
local function bench_record_create()
|
||||||
|
local r
|
||||||
|
for i = 0, iterations - 1 do
|
||||||
|
r = {x = i, y = i + 1, z = i + 2}
|
||||||
|
end
|
||||||
|
return r.z
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_prop_read()
|
||||||
|
local obj = {x = 10, y = 20, z = 30, w = 40}
|
||||||
|
local s = 0
|
||||||
|
for i = 0, iterations - 1 do
|
||||||
|
s = s + obj.x + obj.y + obj.z + obj.w
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_dynamic_prop()
|
||||||
|
local obj = {a = 1, b = 2, c = 3, d = 4, e = 5}
|
||||||
|
local keys = {"a", "b", "c", "d", "e"}
|
||||||
|
local s = 0
|
||||||
|
for i = 0, iterations - 1 do
|
||||||
|
for j = 1, 5 do
|
||||||
|
s = s + obj[keys[j]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_string_concat()
|
||||||
|
local parts = {}
|
||||||
|
local n = 10000
|
||||||
|
for i = 1, n do
|
||||||
|
parts[i] = "x"
|
||||||
|
end
|
||||||
|
local s = table.concat(parts)
|
||||||
|
return #s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_interpolation()
|
||||||
|
local s = ""
|
||||||
|
local n = 50000
|
||||||
|
for i = 0, n - 1 do
|
||||||
|
s = string.format("item_%d", i)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_point(x, y)
|
||||||
|
return {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
sum = function(self)
|
||||||
|
return self.x + self.y
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_method_call()
|
||||||
|
local p = make_point(3, 4)
|
||||||
|
local s = 0
|
||||||
|
for i = 0, iterations - 1 do
|
||||||
|
s = s + p.sum(p)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fib(n)
|
||||||
|
if n <= 1 then return n end
|
||||||
|
return fib(n - 1) + fib(n - 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bench_fncall()
|
||||||
|
local s = 0
|
||||||
|
for i = 0, 19 do
|
||||||
|
s = s + fib(25)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run(name, fn)
|
||||||
|
local start = clock()
|
||||||
|
local result = fn()
|
||||||
|
local elapsed = (clock() - start) * 1000
|
||||||
|
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||||
|
end
|
||||||
|
|
||||||
|
print("=== Object / String / Call Benchmark ===")
|
||||||
|
print(string.format(" iterations: %d", iterations))
|
||||||
|
run("record_create ", bench_record_create)
|
||||||
|
run("prop_read ", bench_prop_read)
|
||||||
|
run("dynamic_prop ", bench_dynamic_prop)
|
||||||
|
run("string_concat ", bench_string_concat)
|
||||||
|
run("interpolation ", bench_interpolation)
|
||||||
|
run("method_call ", bench_method_call)
|
||||||
|
run("fncall_fib25 ", bench_fncall)
|
||||||
302
benches/micro_core.cm
Normal file
302
benches/micro_core.cm
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
// micro_core.cm — direct microbenchmarks for core ops
|
||||||
|
|
||||||
|
function blackhole(sink, x) {
|
||||||
|
return (sink + (x | 0)) | 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_obj_xy(x, y) {
|
||||||
|
return {x: x, y: y}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_obj_yx(x, y) {
|
||||||
|
// Different insertion order to force a different shape
|
||||||
|
return {y: y, x: x}
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_packed_array(n) {
|
||||||
|
var a = []
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) push(a, i)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_holey_array(n) {
|
||||||
|
var a = []
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i += 2) a[i] = i
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loop_empty: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) {}
|
||||||
|
return blackhole(sink, n)
|
||||||
|
},
|
||||||
|
|
||||||
|
i32_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = (x + 3) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
f64_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1.0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = x + 3.14159
|
||||||
|
return blackhole(sink, x | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
mixed_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = x + 0.25
|
||||||
|
return blackhole(sink, x | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
bit_ops: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0x12345678
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = ((x << 5) ^ (x >>> 3)) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
overflow_path: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0x70000000
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = (x + 0x10000000) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
call_direct: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var f = function(a) { return (a + 1) | 0 }
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = f(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
call_indirect: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var f = function(a) { return (a + 1) | 0 }
|
||||||
|
var g = f
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = g(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
call_closure: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var make_adder = function(k) {
|
||||||
|
return function(a) { return (a + k) | 0 }
|
||||||
|
}
|
||||||
|
var add3 = make_adder(3)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = add3(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_read_packed: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(1024)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = (x + a[i & 1023]) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_write_packed: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(1024)
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) a[i & 1023] = i
|
||||||
|
return blackhole(sink, a[17] | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_read_holey: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_holey_array(2048)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
var v = null
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
v = a[(i & 2047)]
|
||||||
|
if (v) x = (x + v) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_push_steady: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
var a = null
|
||||||
|
for (j = 0; j < n; j++) {
|
||||||
|
a = []
|
||||||
|
for (i = 0; i < 256; i++) push(a, i)
|
||||||
|
x = (x + length(a)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_indexed_sum: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(1024)
|
||||||
|
var x = 0
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
for (j = 0; j < n; j++) {
|
||||||
|
x = 0
|
||||||
|
for (i = 0; i < 1024; i++) {
|
||||||
|
x = (x + a[i]) | 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_read_mono: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var o = make_obj_xy(1, 2)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = (x + o.x) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_read_poly_2: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_obj_xy(1, 2)
|
||||||
|
var b = make_obj_yx(1, 2)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
var o = null
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
o = (i & 1) == 0 ? a : b
|
||||||
|
x = (x + o.x) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_read_poly_4: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var shapes = [
|
||||||
|
{x: 1, y: 2},
|
||||||
|
{y: 2, x: 1},
|
||||||
|
{x: 1, z: 3, y: 2},
|
||||||
|
{w: 0, x: 1, y: 2}
|
||||||
|
]
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
x = (x + shapes[i & 3].x) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
string_concat_small: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
var s = null
|
||||||
|
for (j = 0; j < n; j++) {
|
||||||
|
s = ""
|
||||||
|
for (i = 0; i < 16; i++) s = s + "x"
|
||||||
|
x = (x + length(s)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
string_concat_medium: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
var s = null
|
||||||
|
for (j = 0; j < n; j++) {
|
||||||
|
s = ""
|
||||||
|
for (i = 0; i < 100; i++) s = s + "abcdefghij"
|
||||||
|
x = (x + length(s)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
string_slice: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var base = "the quick brown fox jumps over the lazy dog"
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
var s = null
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
s = text(base, i % 10, i % 10 + 10)
|
||||||
|
x = (x + length(s)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
guard_hot_number: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) x = x + 1
|
||||||
|
return blackhole(sink, x | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
guard_mixed_types: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var vals = [1, "a", 2, "b", 3, "c", 4, "d"]
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (is_number(vals[i & 7])) x = (x + vals[i & 7]) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
reduce_sum: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(256)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
x = (x + reduce(a, function(acc, v) { return acc + v }, 0)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
filter_evens: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(256)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
x = (x + length(filter(a, function(v) { return v % 2 == 0 }))) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
arrfor_sum: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(256)
|
||||||
|
var x = 0
|
||||||
|
var i = 0
|
||||||
|
var sum = 0
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
sum = 0
|
||||||
|
arrfor(a, function(v) { sum += v })
|
||||||
|
x = (x + sum) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,22 +95,9 @@
|
|||||||
"nr_close_slots": 0,
|
"nr_close_slots": 0,
|
||||||
"instructions": [
|
"instructions": [
|
||||||
["move", 2, 1, 14, 14],
|
["move", 2, 1, 14, 14],
|
||||||
[
|
["is_blob", 3, 1, 15, 16],
|
||||||
"access",
|
|
||||||
3,
|
|
||||||
{
|
|
||||||
"name": "is_blob",
|
|
||||||
"kind": "name",
|
|
||||||
"make": "intrinsic"
|
|
||||||
},
|
|
||||||
15,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
["frame", 4, 3, 1, 15, 8],
|
|
||||||
["setarg", 4, 1, 1, 15, 8],
|
|
||||||
["invoke", 4, 3, 15, 8],
|
|
||||||
"_nop_bl_1",
|
"_nop_bl_1",
|
||||||
["jump_true", 3, "if_else_6", 15, 8],
|
["jump_true", 3, "if_else_6", 15, 16],
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
3,
|
3,
|
||||||
@@ -199,7 +186,7 @@
|
|||||||
"_nop_ur_1",
|
"_nop_ur_1",
|
||||||
"_nop_ur_2"
|
"_nop_ur_2"
|
||||||
],
|
],
|
||||||
"_write_types": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "null", "text", "array", null, null, null, "text", null, null, null, null],
|
"_write_types": [null, null, null, "bool", null, null, null, null, null, null, null, null, null, null, null, null, null, "null", "text", "array", null, null, null, "text", null, null, null, null],
|
||||||
"name": "content_hash",
|
"name": "content_hash",
|
||||||
"filename": ".cell/packages/core/internal/bootstrap.cm",
|
"filename": ".cell/packages/core/internal/bootstrap.cm",
|
||||||
"nr_args": 1
|
"nr_args": 1
|
||||||
@@ -222,7 +209,7 @@
|
|||||||
8
|
8
|
||||||
],
|
],
|
||||||
"_nop_bl_1",
|
"_nop_bl_1",
|
||||||
["jump_true", 2, "if_else_10", 20, 8],
|
["wary_true", 2, "if_else_10", 20, 8],
|
||||||
["null", 2, 20, 26],
|
["null", 2, 20, 26],
|
||||||
["return", 2, 20, 26],
|
["return", 2, 20, 26],
|
||||||
"_nop_ur_1",
|
"_nop_ur_1",
|
||||||
@@ -345,7 +332,7 @@
|
|||||||
8
|
8
|
||||||
],
|
],
|
||||||
"_nop_bl_1",
|
"_nop_bl_1",
|
||||||
["jump_true", 1, "if_else_18", 25, 8],
|
["wary_true", 1, "if_else_18", 25, 8],
|
||||||
["null", 1, 25, 26],
|
["null", 1, 25, 26],
|
||||||
["return", 1, 25, 26],
|
["return", 1, 25, 26],
|
||||||
"_nop_ur_1",
|
"_nop_ur_1",
|
||||||
@@ -427,7 +414,7 @@
|
|||||||
["invoke", 5, 3, 27, 8],
|
["invoke", 5, 3, 27, 8],
|
||||||
"call_done_26",
|
"call_done_26",
|
||||||
"_nop_bl_2",
|
"_nop_bl_2",
|
||||||
["jump_true", 3, "if_else_23", 27, 8],
|
["wary_true", 3, "if_else_23", 27, 8],
|
||||||
["get", 2, 11, 1, 27, 24],
|
["get", 2, 11, 1, 27, 24],
|
||||||
["is_proxy", 3, 2, 27, 24],
|
["is_proxy", 3, 2, 27, 24],
|
||||||
["jump_false", 3, "record_path_27", 27, 24],
|
["jump_false", 3, "record_path_27", 27, 24],
|
||||||
@@ -628,7 +615,7 @@
|
|||||||
["invoke", 8, 6, 36, 8],
|
["invoke", 8, 6, 36, 8],
|
||||||
"call_done_41",
|
"call_done_41",
|
||||||
"_nop_bl_1",
|
"_nop_bl_1",
|
||||||
["jump_true", 6, "if_else_38", 36, 8],
|
["wary_true", 6, "if_else_38", 36, 8],
|
||||||
["access", 5, "error: missing seed: ", 37, 14],
|
["access", 5, "error: missing seed: ", 37, 14],
|
||||||
"_nop_tc_7",
|
"_nop_tc_7",
|
||||||
"_nop_tc_8",
|
"_nop_tc_8",
|
||||||
@@ -1026,30 +1013,11 @@
|
|||||||
"call_done_63",
|
"call_done_63",
|
||||||
"if_end_58",
|
"if_end_58",
|
||||||
["access", 3, 1, 66, 17],
|
["access", 3, 1, 66, 17],
|
||||||
"_nop_tc_1",
|
|
||||||
"_nop_tc_2",
|
|
||||||
"_nop_tc_3",
|
|
||||||
"_nop_tc_4",
|
|
||||||
["add", 5, 5, 3, 66, 17],
|
["add", 5, 5, 3, 66, 17],
|
||||||
["jump", "num_done_65", 66, 17],
|
|
||||||
"num_err_64",
|
|
||||||
"_nop_ucfg_1",
|
|
||||||
"_nop_ucfg_2",
|
|
||||||
"_nop_ucfg_3",
|
|
||||||
"_nop_ucfg_4",
|
|
||||||
"_nop_ucfg_5",
|
|
||||||
"_nop_ucfg_6",
|
|
||||||
"_nop_ucfg_7",
|
|
||||||
"_nop_ucfg_8",
|
|
||||||
"_nop_ucfg_9",
|
|
||||||
"_nop_ucfg_10",
|
|
||||||
"_nop_ucfg_11",
|
|
||||||
"_nop_ucfg_12",
|
|
||||||
"num_done_65",
|
|
||||||
["jump", "while_start_55", 66, 17],
|
["jump", "while_start_55", 66, 17],
|
||||||
"while_end_56",
|
"while_end_56",
|
||||||
["disrupt", 68, 5],
|
["disrupt", 68, 5],
|
||||||
"_nop_ucfg_13",
|
"_nop_ucfg_1",
|
||||||
"if_else_53",
|
"if_else_53",
|
||||||
"if_end_54",
|
"if_end_54",
|
||||||
["get", 3, 15, 1, 70, 10],
|
["get", 3, 15, 1, 70, 10],
|
||||||
@@ -1060,7 +1028,7 @@
|
|||||||
"_nop_ur_1",
|
"_nop_ur_1",
|
||||||
"_nop_ur_2"
|
"_nop_ur_2"
|
||||||
],
|
],
|
||||||
"_write_types": [null, null, null, "int", null, null, "bool", null, null, null, null, null, null, null, null, null, null, null, "null", "bool", "bool", null, "int", "int", "bool", null, "int", "bool", null, null, null, null, "null", "bool", "bool", null, "null", "bool", null, null, null, null, null, null, null, null, "array", null, "text", null, null, null, null, null, "null", "text", "array", null, null, null, "array", null, "text", null, null, null, null, null, "null", "text", "array", null, null, null, "int", null, null, null, null, null, null, null, null, null, null, null, null, null],
|
"_write_types": [null, null, null, "int", null, null, "bool", null, null, null, null, null, null, null, null, null, null, null, "null", "bool", "bool", null, "int", "int", "bool", null, "int", "bool", null, null, null, null, "null", "bool", "bool", null, "null", "bool", null, null, null, null, null, null, null, null, "array", null, "text", null, null, null, null, null, "null", "text", "array", null, null, null, "array", null, "text", null, null, null, null, null, "null", "text", "array", null, null, null, "int", null, null, null, null],
|
||||||
"name": "analyze",
|
"name": "analyze",
|
||||||
"filename": ".cell/packages/core/internal/bootstrap.cm",
|
"filename": ".cell/packages/core/internal/bootstrap.cm",
|
||||||
"nr_args": 2
|
"nr_args": 2
|
||||||
@@ -1078,7 +1046,7 @@
|
|||||||
"instructions": [
|
"instructions": [
|
||||||
["get", 3, 11, 1, 74, 21],
|
["get", 3, 11, 1, 74, 21],
|
||||||
["is_proxy", 4, 3, 74, 21],
|
["is_proxy", 4, 3, 74, 21],
|
||||||
["jump_false", 4, "record_path_66", 74, 21],
|
["jump_false", 4, "record_path_64", 74, 21],
|
||||||
["null", 4, 74, 21],
|
["null", 4, 74, 21],
|
||||||
["access", 5, "slurp", 74, 21],
|
["access", 5, "slurp", 74, 21],
|
||||||
["array", 6, 0, 74, 21],
|
["array", 6, 0, 74, 21],
|
||||||
@@ -1089,14 +1057,14 @@
|
|||||||
["setarg", 7, 1, 5, 74, 21],
|
["setarg", 7, 1, 5, 74, 21],
|
||||||
["setarg", 7, 2, 6, 74, 21],
|
["setarg", 7, 2, 6, 74, 21],
|
||||||
["invoke", 7, 4, 74, 21],
|
["invoke", 7, 4, 74, 21],
|
||||||
["jump", "call_done_67", 74, 21],
|
["jump", "call_done_65", 74, 21],
|
||||||
"record_path_66",
|
"record_path_64",
|
||||||
["load_field", 5, 3, "slurp", 74, 21],
|
["load_field", 5, 3, "slurp", 74, 21],
|
||||||
["frame", 6, 5, 1, 74, 21],
|
["frame", 6, 5, 1, 74, 21],
|
||||||
["setarg", 6, 0, 3, 74, 21],
|
["setarg", 6, 0, 3, 74, 21],
|
||||||
["setarg", 6, 1, 2, 74, 21],
|
["setarg", 6, 1, 2, 74, 21],
|
||||||
["invoke", 6, 4, 74, 21],
|
["invoke", 6, 4, 74, 21],
|
||||||
"call_done_67",
|
"call_done_65",
|
||||||
["move", 3, 4, 74, 21],
|
["move", 3, 4, 74, 21],
|
||||||
["get", 5, 4, 1, 75, 14],
|
["get", 5, 4, 1, 75, 14],
|
||||||
["frame", 6, 5, 1, 75, 14],
|
["frame", 6, 5, 1, 75, 14],
|
||||||
@@ -1113,10 +1081,10 @@
|
|||||||
["null", 8, 79, 20],
|
["null", 8, 79, 20],
|
||||||
["null", 9, 80, 19],
|
["null", 9, 80, 19],
|
||||||
["move", 10, 4, 81, 7],
|
["move", 10, 4, 81, 7],
|
||||||
["jump_false", 4, "and_end_70", 81, 7],
|
["wary_false", 4, "and_end_68", 81, 7],
|
||||||
["get", 4, 11, 1, 81, 17],
|
["get", 4, 11, 1, 81, 17],
|
||||||
["is_proxy", 11, 4, 81, 17],
|
["is_proxy", 11, 4, 81, 17],
|
||||||
["jump_false", 11, "record_path_71", 81, 17],
|
["jump_false", 11, "record_path_69", 81, 17],
|
||||||
["null", 11, 81, 17],
|
["null", 11, 81, 17],
|
||||||
["access", 12, "is_file", 81, 17],
|
["access", 12, "is_file", 81, 17],
|
||||||
["array", 13, 0, 81, 17],
|
["array", 13, 0, 81, 17],
|
||||||
@@ -1127,22 +1095,22 @@
|
|||||||
["setarg", 14, 1, 12, 81, 17],
|
["setarg", 14, 1, 12, 81, 17],
|
||||||
["setarg", 14, 2, 13, 81, 17],
|
["setarg", 14, 2, 13, 81, 17],
|
||||||
["invoke", 14, 11, 81, 17],
|
["invoke", 14, 11, 81, 17],
|
||||||
["jump", "call_done_72", 81, 17],
|
["jump", "call_done_70", 81, 17],
|
||||||
"record_path_71",
|
"record_path_69",
|
||||||
["load_field", 12, 4, "is_file", 81, 17],
|
["load_field", 12, 4, "is_file", 81, 17],
|
||||||
["frame", 13, 12, 1, 81, 17],
|
["frame", 13, 12, 1, 81, 17],
|
||||||
["setarg", 13, 0, 4, 81, 17],
|
["setarg", 13, 0, 4, 81, 17],
|
||||||
["setarg", 13, 1, 5, 81, 17],
|
["setarg", 13, 1, 5, 81, 17],
|
||||||
["invoke", 13, 11, 81, 17],
|
["invoke", 13, 11, 81, 17],
|
||||||
"call_done_72",
|
"call_done_70",
|
||||||
["move", 10, 11, 81, 17],
|
["move", 10, 11, 81, 17],
|
||||||
"and_end_70",
|
"and_end_68",
|
||||||
["jump_false", 10, "if_else_68", 81, 17],
|
["wary_false", 10, "if_else_66", 81, 17],
|
||||||
["null", 4, 81, 37],
|
["null", 4, 81, 37],
|
||||||
["return", 4, 81, 37],
|
["return", 4, 81, 37],
|
||||||
"_nop_ur_1",
|
"_nop_ur_1",
|
||||||
"if_else_68",
|
"if_else_66",
|
||||||
"if_end_69",
|
"if_end_67",
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
4,
|
4,
|
||||||
@@ -1174,7 +1142,7 @@
|
|||||||
["move", 7, 3, 83, 14],
|
["move", 7, 3, 83, 14],
|
||||||
["get", 3, 12, 1, 84, 16],
|
["get", 3, 12, 1, 84, 16],
|
||||||
["is_proxy", 4, 3, 84, 16],
|
["is_proxy", 4, 3, 84, 16],
|
||||||
["jump_false", 4, "record_path_73", 84, 16],
|
["jump_false", 4, "record_path_71", 84, 16],
|
||||||
["null", 4, 84, 16],
|
["null", 4, 84, 16],
|
||||||
["access", 6, "encode", 84, 16],
|
["access", 6, "encode", 84, 16],
|
||||||
["array", 10, 0, 84, 16],
|
["array", 10, 0, 84, 16],
|
||||||
@@ -1185,14 +1153,14 @@
|
|||||||
["setarg", 11, 1, 6, 84, 16],
|
["setarg", 11, 1, 6, 84, 16],
|
||||||
["setarg", 11, 2, 10, 84, 16],
|
["setarg", 11, 2, 10, 84, 16],
|
||||||
["invoke", 11, 4, 84, 16],
|
["invoke", 11, 4, 84, 16],
|
||||||
["jump", "call_done_74", 84, 16],
|
["jump", "call_done_72", 84, 16],
|
||||||
"record_path_73",
|
"record_path_71",
|
||||||
["load_field", 6, 3, "encode", 84, 16],
|
["load_field", 6, 3, "encode", 84, 16],
|
||||||
["frame", 10, 6, 1, 84, 16],
|
["frame", 10, 6, 1, 84, 16],
|
||||||
["setarg", 10, 0, 3, 84, 16],
|
["setarg", 10, 0, 3, 84, 16],
|
||||||
["setarg", 10, 1, 7, 84, 16],
|
["setarg", 10, 1, 7, 84, 16],
|
||||||
["invoke", 10, 4, 84, 16],
|
["invoke", 10, 4, 84, 16],
|
||||||
"call_done_74",
|
"call_done_72",
|
||||||
["move", 8, 4, 84, 16],
|
["move", 8, 4, 84, 16],
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
@@ -1210,13 +1178,13 @@
|
|||||||
["setarg", 6, 2, 4, 85, 15],
|
["setarg", 6, 2, 4, 85, 15],
|
||||||
["invoke", 6, 3, 85, 15],
|
["invoke", 6, 3, 85, 15],
|
||||||
["move", 9, 3, 85, 15],
|
["move", 9, 3, 85, 15],
|
||||||
["jump_false", 5, "if_else_75", 86, 7],
|
["wary_false", 5, "if_else_73", 86, 7],
|
||||||
["get", 3, 6, 1, 87, 5],
|
["get", 3, 6, 1, 87, 5],
|
||||||
["frame", 4, 3, 0, 87, 5],
|
["frame", 4, 3, 0, 87, 5],
|
||||||
["invoke", 4, 3, 87, 5],
|
["invoke", 4, 3, 87, 5],
|
||||||
["get", 3, 11, 1, 88, 5],
|
["get", 3, 11, 1, 88, 5],
|
||||||
["is_proxy", 4, 3, 88, 5],
|
["is_proxy", 4, 3, 88, 5],
|
||||||
["jump_false", 4, "record_path_77", 88, 5],
|
["jump_false", 4, "record_path_75", 88, 5],
|
||||||
["null", 4, 88, 5],
|
["null", 4, 88, 5],
|
||||||
["access", 6, "slurpwrite", 88, 5],
|
["access", 6, "slurpwrite", 88, 5],
|
||||||
["array", 7, 0, 88, 5],
|
["array", 7, 0, 88, 5],
|
||||||
@@ -1228,18 +1196,18 @@
|
|||||||
["setarg", 8, 1, 6, 88, 5],
|
["setarg", 8, 1, 6, 88, 5],
|
||||||
["setarg", 8, 2, 7, 88, 5],
|
["setarg", 8, 2, 7, 88, 5],
|
||||||
["invoke", 8, 4, 88, 5],
|
["invoke", 8, 4, 88, 5],
|
||||||
["jump", "call_done_78", 88, 5],
|
["jump", "call_done_76", 88, 5],
|
||||||
"record_path_77",
|
"record_path_75",
|
||||||
["load_field", 6, 3, "slurpwrite", 88, 5],
|
["load_field", 6, 3, "slurpwrite", 88, 5],
|
||||||
["frame", 7, 6, 2, 88, 5],
|
["frame", 7, 6, 2, 88, 5],
|
||||||
["setarg", 7, 0, 3, 88, 5],
|
["setarg", 7, 0, 3, 88, 5],
|
||||||
["setarg", 7, 1, 5, 88, 5],
|
["setarg", 7, 1, 5, 88, 5],
|
||||||
["setarg", 7, 2, 9, 88, 5],
|
["setarg", 7, 2, 9, 88, 5],
|
||||||
["invoke", 7, 4, 88, 5],
|
["invoke", 7, 4, 88, 5],
|
||||||
"call_done_78",
|
"call_done_76",
|
||||||
["jump", "if_end_76", 88, 5],
|
["jump", "if_end_74", 88, 5],
|
||||||
"if_else_75",
|
"if_else_73",
|
||||||
"if_end_76",
|
"if_end_74",
|
||||||
["null", 3, 88, 5],
|
["null", 3, 88, 5],
|
||||||
["return", 3, 88, 5]
|
["return", 3, 88, 5]
|
||||||
],
|
],
|
||||||
@@ -1369,10 +1337,10 @@
|
|||||||
["move", 1, 22, 99, 26],
|
["move", 1, 22, 99, 26],
|
||||||
["access", 17, 0, 101, 10],
|
["access", 17, 0, 101, 10],
|
||||||
["null", 18, 102, 13],
|
["null", 18, 102, 13],
|
||||||
"while_start_79",
|
"while_start_77",
|
||||||
["length", 19, 1, 103, 20],
|
["length", 19, 1, 103, 20],
|
||||||
["lt", 20, 17, 19, 103, 20],
|
["lt", 20, 17, 19, 103, 20],
|
||||||
["jump_false", 20, "while_end_80", 103, 20],
|
["jump_false", 20, "while_end_78", 103, 20],
|
||||||
["load_index", 19, 1, 17, 104, 22],
|
["load_index", 19, 1, 17, 104, 22],
|
||||||
["move", 18, 19, 104, 22],
|
["move", 18, 19, 104, 22],
|
||||||
["load_field", 20, 19, "name", 105, 21],
|
["load_field", 20, 19, "name", 105, 21],
|
||||||
@@ -1389,19 +1357,19 @@
|
|||||||
],
|
],
|
||||||
["access", 21, "/", 105, 45],
|
["access", 21, "/", 105, 45],
|
||||||
["is_text", 22, 19, 105, 45],
|
["is_text", 22, 19, 105, 45],
|
||||||
["jump_false", 22, "add_cn_82", 105, 45],
|
["jump_false", 22, "add_cn_80", 105, 45],
|
||||||
"_nop_tc_1",
|
"_nop_tc_1",
|
||||||
"_nop_tc_2",
|
"_nop_tc_2",
|
||||||
["concat", 23, 19, 21, 105, 45],
|
["concat", 23, 19, 21, 105, 45],
|
||||||
["jump", "add_done_81", 105, 45],
|
["jump", "add_done_79", 105, 45],
|
||||||
"add_cn_82",
|
"add_cn_80",
|
||||||
["is_num", 22, 19, 105, 45],
|
["is_num", 22, 19, 105, 45],
|
||||||
["jump_false", 22, "add_err_83", 105, 45],
|
["jump_false", 22, "add_err_81", 105, 45],
|
||||||
"_nop_tc_3",
|
"_nop_tc_3",
|
||||||
"_nop_dj_1",
|
"_nop_dj_1",
|
||||||
"_nop_ucfg_1",
|
"_nop_ucfg_1",
|
||||||
"_nop_ucfg_2",
|
"_nop_ucfg_2",
|
||||||
"add_err_83",
|
"add_err_81",
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
19,
|
19,
|
||||||
@@ -1426,22 +1394,22 @@
|
|||||||
["setarg", 22, 2, 24, 105, 45],
|
["setarg", 22, 2, 24, 105, 45],
|
||||||
["invoke", 22, 19, 105, 45],
|
["invoke", 22, 19, 105, 45],
|
||||||
["disrupt", 105, 45],
|
["disrupt", 105, 45],
|
||||||
"add_done_81",
|
"add_done_79",
|
||||||
["load_field", 19, 18, "path", 105, 51],
|
["load_field", 19, 18, "path", 105, 51],
|
||||||
"_nop_tc_1",
|
"_nop_tc_1",
|
||||||
"_nop_tc_2",
|
"_nop_tc_2",
|
||||||
["is_text", 21, 19, 105, 51],
|
["is_text", 21, 19, 105, 51],
|
||||||
["jump_false", 21, "add_cn_85", 105, 51],
|
["jump_false", 21, "add_cn_83", 105, 51],
|
||||||
["concat", 21, 23, 19, 105, 51],
|
["concat", 21, 23, 19, 105, 51],
|
||||||
["jump", "add_done_84", 105, 51],
|
["jump", "add_done_82", 105, 51],
|
||||||
"add_cn_85",
|
"add_cn_83",
|
||||||
"_nop_tc_3",
|
"_nop_tc_3",
|
||||||
["jump", "add_err_86", 105, 51],
|
["jump", "add_err_84", 105, 51],
|
||||||
"_nop_ucfg_1",
|
"_nop_ucfg_1",
|
||||||
"_nop_ucfg_2",
|
"_nop_ucfg_2",
|
||||||
"_nop_ucfg_3",
|
"_nop_ucfg_3",
|
||||||
"_nop_ucfg_4",
|
"_nop_ucfg_4",
|
||||||
"add_err_86",
|
"add_err_84",
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
19,
|
19,
|
||||||
@@ -1466,35 +1434,16 @@
|
|||||||
["setarg", 23, 2, 24, 105, 51],
|
["setarg", 23, 2, 24, 105, 51],
|
||||||
["invoke", 23, 19, 105, 51],
|
["invoke", 23, 19, 105, 51],
|
||||||
["disrupt", 105, 51],
|
["disrupt", 105, 51],
|
||||||
"add_done_84",
|
"add_done_82",
|
||||||
["frame", 19, 9, 2, 105, 3],
|
["frame", 19, 9, 2, 105, 3],
|
||||||
["setarg", 19, 1, 20, 105, 3],
|
["setarg", 19, 1, 20, 105, 3],
|
||||||
["stone_text", 21],
|
["stone_text", 21],
|
||||||
["setarg", 19, 2, 21, 105, 3],
|
["setarg", 19, 2, 21, 105, 3],
|
||||||
["invoke", 19, 20, 105, 3],
|
["invoke", 19, 20, 105, 3],
|
||||||
["access", 19, 1, 106, 13],
|
["access", 19, 1, 106, 13],
|
||||||
"_nop_tc_4",
|
|
||||||
"_nop_tc_5",
|
|
||||||
"_nop_tc_6",
|
|
||||||
"_nop_tc_7",
|
|
||||||
["add", 17, 17, 19, 106, 13],
|
["add", 17, 17, 19, 106, 13],
|
||||||
["jump", "num_done_88", 106, 13],
|
["jump", "while_start_77", 106, 13],
|
||||||
"num_err_87",
|
"while_end_78",
|
||||||
"_nop_ucfg_3",
|
|
||||||
"_nop_ucfg_4",
|
|
||||||
"_nop_ucfg_5",
|
|
||||||
"_nop_ucfg_6",
|
|
||||||
"_nop_ucfg_7",
|
|
||||||
"_nop_ucfg_8",
|
|
||||||
"_nop_ucfg_9",
|
|
||||||
"_nop_ucfg_10",
|
|
||||||
"_nop_ucfg_11",
|
|
||||||
"_nop_ucfg_12",
|
|
||||||
"_nop_ucfg_13",
|
|
||||||
"_nop_ucfg_14",
|
|
||||||
"num_done_88",
|
|
||||||
["jump", "while_start_79", 106, 13],
|
|
||||||
"while_end_80",
|
|
||||||
["access", 1, "bootstrap: cache seeded\n", 108, 10],
|
["access", 1, "bootstrap: cache seeded\n", 108, 10],
|
||||||
[
|
[
|
||||||
"access",
|
"access",
|
||||||
@@ -1508,7 +1457,7 @@
|
|||||||
1
|
1
|
||||||
],
|
],
|
||||||
["is_proxy", 17, 9, 108, 1],
|
["is_proxy", 17, 9, 108, 1],
|
||||||
["jump_false", 17, "record_path_89", 108, 1],
|
["jump_false", 17, "record_path_85", 108, 1],
|
||||||
["null", 17, 108, 1],
|
["null", 17, 108, 1],
|
||||||
["access", 18, "print", 108, 1],
|
["access", 18, "print", 108, 1],
|
||||||
["array", 19, 0, 108, 1],
|
["array", 19, 0, 108, 1],
|
||||||
@@ -1520,18 +1469,18 @@
|
|||||||
["setarg", 20, 1, 18, 108, 1],
|
["setarg", 20, 1, 18, 108, 1],
|
||||||
["setarg", 20, 2, 19, 108, 1],
|
["setarg", 20, 2, 19, 108, 1],
|
||||||
["invoke", 20, 17, 108, 1],
|
["invoke", 20, 17, 108, 1],
|
||||||
["jump", "call_done_90", 108, 1],
|
["jump", "call_done_86", 108, 1],
|
||||||
"record_path_89",
|
"record_path_85",
|
||||||
["load_field", 18, 9, "print", 108, 1],
|
["load_field", 18, 9, "print", 108, 1],
|
||||||
["frame", 19, 18, 1, 108, 1],
|
["frame", 19, 18, 1, 108, 1],
|
||||||
["setarg", 19, 0, 9, 108, 1],
|
["setarg", 19, 0, 9, 108, 1],
|
||||||
["stone_text", 1],
|
["stone_text", 1],
|
||||||
["setarg", 19, 1, 1, 108, 1],
|
["setarg", 19, 1, 1, 108, 1],
|
||||||
["invoke", 19, 17, 108, 1],
|
["invoke", 19, 17, 108, 1],
|
||||||
"call_done_90",
|
"call_done_86",
|
||||||
["return", 17, 108, 1]
|
["return", 17, 108, 1]
|
||||||
],
|
],
|
||||||
"_write_types": [null, "function", "function", "function", null, "function", null, null, null, null, null, null, null, null, "function", "int", "function", "function", null, "array", "function", "function", "function", "function", "function", "function", "function", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "array", "int", "bool", null, null, null, "text", "text", "bool", null, null, "text", "text", "array", null, null, "null", null, null, "bool", "bool", null, "text", "text", "array", null, null, "null", null, null, "int", null, null, null, null, null, null, null, null, null, "text", null, null, null, "null", "text", "array", null, null, null],
|
"_write_types": [null, "function", "function", "function", null, "function", null, null, null, null, null, null, null, null, "function", "int", "function", "function", null, "array", "function", "function", "function", "function", "function", "function", "function", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "text", null, null, "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "record", "text", "text", "array", "int", "bool", null, null, null, "text", "text", "bool", null, null, "text", "text", "array", null, null, "null", null, null, "bool", "bool", null, "text", "text", "array", null, null, "null", null, null, "int", "text", null, null, null, "null", "text", "array", null, null, null],
|
||||||
"nr_args": 0
|
"nr_args": 0
|
||||||
},
|
},
|
||||||
"name": ".cell/packages/core/internal/bootstrap.cm",
|
"name": ".cell/packages/core/internal/bootstrap.cm",
|
||||||
|
|||||||
4786
boot/fold.cm.mcode
4786
boot/fold.cm.mcode
File diff suppressed because one or more lines are too long
25586
boot/mcode.cm.mcode
25586
boot/mcode.cm.mcode
File diff suppressed because one or more lines are too long
9732
boot/parse.cm.mcode
9732
boot/parse.cm.mcode
File diff suppressed because one or more lines are too long
27739
boot/streamline.cm.mcode
27739
boot/streamline.cm.mcode
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -319,7 +319,8 @@ JSC_SCALL(os_system,
|
|||||||
|
|
||||||
JSC_CCALL(os_exit,
|
JSC_CCALL(os_exit,
|
||||||
int code = 0;
|
int code = 0;
|
||||||
if (argc > 0) JS_ToInt32(js, &code, argv[0]);
|
if (argc > 0 && !JS_IsNull(argv[0]))
|
||||||
|
JS_ToInt32(js, &code, argv[0]);
|
||||||
exit(code);
|
exit(code);
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,33 @@ var os = use('internal/os')
|
|||||||
var link = use('link')
|
var link = use('link')
|
||||||
|
|
||||||
// These come from env (via core_extras in engine.cm):
|
// These come from env (via core_extras in engine.cm):
|
||||||
// analyze, run_ast_fn, core_json, use_cache, shop_path, actor_api, runtime_env,
|
// analyze, run_ast_fn, core_json, use_cache, core_path, shop_path, actor_api,
|
||||||
// content_hash, cache_path, ensure_build_dir
|
// runtime_env, content_hash, cache_path, ensure_build_dir
|
||||||
var shop_json = core_json
|
var shop_json = core_json
|
||||||
var global_shop_path = shop_path
|
var global_shop_path = shop_path
|
||||||
var my$_ = actor_api
|
var my$_ = actor_api
|
||||||
|
|
||||||
var core = "core"
|
var core = "core"
|
||||||
|
|
||||||
|
// Compiler fingerprint: hash of all compiler source files so that any compiler
|
||||||
|
// change invalidates the entire build cache. Folded into hash_path().
|
||||||
|
var compiler_fingerprint = (function() {
|
||||||
|
var files = [
|
||||||
|
"tokenize", "parse", "fold", "mcode", "streamline",
|
||||||
|
"qbe", "qbe_emit", "ir_stats"
|
||||||
|
]
|
||||||
|
var combined = ""
|
||||||
|
var i = 0
|
||||||
|
var path = null
|
||||||
|
while (i < length(files)) {
|
||||||
|
path = core_path + '/' + files[i] + '.cm'
|
||||||
|
if (fd.is_file(path))
|
||||||
|
combined = combined + text(fd.slurp(path))
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return content_hash(stone(blob(combined)))
|
||||||
|
})()
|
||||||
|
|
||||||
// Make a package name safe for use in C identifiers.
|
// Make a package name safe for use in C identifiers.
|
||||||
// Replaces /, ., -, @ with _ so the result is a valid C identifier fragment.
|
// Replaces /, ., -, @ with _ so the result is a valid C identifier fragment.
|
||||||
function safe_c_name(name) {
|
function safe_c_name(name) {
|
||||||
@@ -43,7 +62,7 @@ function put_into_cache(content, obj)
|
|||||||
function hash_path(content, salt)
|
function hash_path(content, salt)
|
||||||
{
|
{
|
||||||
var s = salt || 'mach'
|
var s = salt || 'mach'
|
||||||
return global_shop_path + '/build/' + content_hash(stone(blob(text(content) + '\n' + s)))
|
return global_shop_path + '/build/' + content_hash(stone(blob(text(content) + '\n' + s + '\n' + compiler_fingerprint)))
|
||||||
}
|
}
|
||||||
|
|
||||||
var Shop = {}
|
var Shop = {}
|
||||||
@@ -456,6 +475,15 @@ Shop.extract_commit_hash = function(pkg, response) {
|
|||||||
var open_dls = {}
|
var open_dls = {}
|
||||||
var package_dylibs = {} // pkg -> [{file, symbol, dylib}, ...]
|
var package_dylibs = {} // pkg -> [{file, symbol, dylib}, ...]
|
||||||
|
|
||||||
|
function open_dylib_cached(path) {
|
||||||
|
var handle = open_dls[path]
|
||||||
|
if (handle) return handle
|
||||||
|
handle = os.dylib_open(path)
|
||||||
|
if (!handle) return null
|
||||||
|
open_dls[path] = handle
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
|
||||||
// Host target detection for native dylib resolution
|
// Host target detection for native dylib resolution
|
||||||
function detect_host_target() {
|
function detect_host_target() {
|
||||||
var platform = os.platform()
|
var platform = os.platform()
|
||||||
@@ -492,7 +520,7 @@ function try_native_mod_dylib(pkg, stem) {
|
|||||||
if (!fd.is_file(build_path)) return null
|
if (!fd.is_file(build_path)) return null
|
||||||
|
|
||||||
log.shop('native dylib cache hit: ' + stem)
|
log.shop('native dylib cache hit: ' + stem)
|
||||||
var handle = os.dylib_open(build_path)
|
var handle = open_dylib_cached(build_path)
|
||||||
if (!handle) return null
|
if (!handle) return null
|
||||||
var sym = Shop.c_symbol_for_file(pkg, stem)
|
var sym = Shop.c_symbol_for_file(pkg, stem)
|
||||||
return {_native: true, _handle: handle, _sym: sym}
|
return {_native: true, _handle: handle, _sym: sym}
|
||||||
@@ -1250,8 +1278,17 @@ Shop.is_loaded = function is_loaded(path, package_context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a use function bound to a specific package context
|
// Create a use function bound to a specific package context
|
||||||
function make_use_fn(pkg) {
|
function make_use_fn(pkg, force_native) {
|
||||||
return function(path) {
|
return function(path) {
|
||||||
|
var _native = null
|
||||||
|
if (force_native && !native_mode) {
|
||||||
|
_native = function() {
|
||||||
|
return Shop.use_native(path, pkg)
|
||||||
|
} disruption {
|
||||||
|
return Shop.use(path, pkg)
|
||||||
|
}
|
||||||
|
return _native()
|
||||||
|
}
|
||||||
return Shop.use(path, pkg)
|
return Shop.use(path, pkg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1282,7 +1319,7 @@ function execute_module(info)
|
|||||||
inject = Shop.script_inject_for(file_info)
|
inject = Shop.script_inject_for(file_info)
|
||||||
env = inject_env(inject)
|
env = inject_env(inject)
|
||||||
pkg = file_info.package
|
pkg = file_info.package
|
||||||
env.use = make_use_fn(pkg)
|
env.use = make_use_fn(pkg, true)
|
||||||
env = stone(env)
|
env = stone(env)
|
||||||
used = os.native_module_load_named(
|
used = os.native_module_load_named(
|
||||||
mod_resolve.symbol._handle, mod_resolve.symbol._sym, env)
|
mod_resolve.symbol._handle, mod_resolve.symbol._sym, env)
|
||||||
@@ -2186,7 +2223,7 @@ Shop.load_as_dylib = function(path, pkg) {
|
|||||||
if (!file_info) file_info = Shop.file_info(file_path)
|
if (!file_info) file_info = Shop.file_info(file_path)
|
||||||
inject = Shop.script_inject_for(file_info)
|
inject = Shop.script_inject_for(file_info)
|
||||||
env = inject_env(inject)
|
env = inject_env(inject)
|
||||||
env.use = make_use_fn(real_pkg)
|
env.use = make_use_fn(real_pkg, true)
|
||||||
env = stone(env)
|
env = stone(env)
|
||||||
return os.native_module_load_named(result._handle, result._sym, env)
|
return os.native_module_load_named(result._handle, result._sym, env)
|
||||||
}
|
}
|
||||||
@@ -2233,18 +2270,45 @@ Shop.parse_package = function(locator) {
|
|||||||
|
|
||||||
Shop.use_native = function(path, package_context) {
|
Shop.use_native = function(path, package_context) {
|
||||||
var src_path = path
|
var src_path = path
|
||||||
if (!starts_with(path, '/'))
|
var locator = null
|
||||||
|
var lookup = null
|
||||||
|
var cache_key = null
|
||||||
|
var cfg = null
|
||||||
|
var old_native = null
|
||||||
|
if (!starts_with(path, '/') && !fd.is_file(path)) {
|
||||||
|
lookup = ends_with(path, '.cm') ? path : path + '.cm'
|
||||||
|
locator = resolve_locator(lookup, package_context)
|
||||||
|
if (!locator) { print('Module not found: ' + path); disrupt }
|
||||||
|
src_path = locator.path
|
||||||
|
} else if (!starts_with(path, '/')) {
|
||||||
src_path = fd.realpath(path)
|
src_path = fd.realpath(path)
|
||||||
|
}
|
||||||
if (!fd.is_file(src_path)) { log.error('File not found: ' + path); disrupt }
|
if (!fd.is_file(src_path)) { log.error('File not found: ' + path); disrupt }
|
||||||
|
|
||||||
var file_info = Shop.file_info(src_path)
|
var file_info = Shop.file_info(src_path)
|
||||||
var pkg = file_info.package || package_context
|
var pkg = file_info.package || (locator ? locator.pkg : package_context)
|
||||||
|
var sym_stem = fd.basename(src_path)
|
||||||
|
var pkg_dir = null
|
||||||
|
cache_key = 'native:' + text(pkg || '') + ':' + src_path
|
||||||
|
if (use_cache[cache_key]) return use_cache[cache_key]
|
||||||
|
|
||||||
var sym_name = null
|
var sym_name = null
|
||||||
if (pkg)
|
if (pkg) {
|
||||||
sym_name = Shop.c_symbol_for_file(pkg, fd.basename(src_path))
|
pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
|
||||||
|
if (starts_with(src_path, pkg_dir + '/')) {
|
||||||
|
sym_stem = text(src_path, length(pkg_dir) + 1)
|
||||||
|
}
|
||||||
|
sym_name = Shop.c_symbol_for_file(pkg, sym_stem)
|
||||||
|
}
|
||||||
|
|
||||||
var build = Shop.use('build', 'core')
|
var build = use_cache['core/build'] || use_cache['build']
|
||||||
|
if (!build) {
|
||||||
|
cfg = Shop.load_config()
|
||||||
|
old_native = cfg.policy.native
|
||||||
|
cfg.policy.native = false
|
||||||
|
build = Shop.use('build', 'core')
|
||||||
|
cfg.policy.native = old_native
|
||||||
|
}
|
||||||
var dylib_path = build.compile_native(src_path, null, null, pkg)
|
var dylib_path = build.compile_native(src_path, null, null, pkg)
|
||||||
|
|
||||||
var handle = os.dylib_open(dylib_path)
|
var handle = os.dylib_open(dylib_path)
|
||||||
@@ -2253,12 +2317,16 @@ Shop.use_native = function(path, package_context) {
|
|||||||
// Build env with runtime functions and capabilities
|
// Build env with runtime functions and capabilities
|
||||||
var inject = Shop.script_inject_for(file_info)
|
var inject = Shop.script_inject_for(file_info)
|
||||||
var env = inject_env(inject)
|
var env = inject_env(inject)
|
||||||
env.use = make_use_fn(pkg)
|
env.use = make_use_fn(pkg, true)
|
||||||
env = stone(env)
|
env = stone(env)
|
||||||
|
|
||||||
|
var loaded = null
|
||||||
if (sym_name)
|
if (sym_name)
|
||||||
return os.native_module_load_named(handle, sym_name, env)
|
loaded = os.native_module_load_named(handle, sym_name, env)
|
||||||
return os.native_module_load(handle, env)
|
else
|
||||||
|
loaded = os.native_module_load(handle, env)
|
||||||
|
use_cache[cache_key] = loaded
|
||||||
|
return loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
return Shop
|
return Shop
|
||||||
|
|||||||
452
mcode.cm
452
mcode.cm
@@ -38,6 +38,11 @@ var mcode = function(ast) {
|
|||||||
is_array: "is_array", is_function: "is_func", is_object: "is_record",
|
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_stone: "is_stone", is_integer: "is_int", is_text: "is_text",
|
||||||
is_number: "is_num", is_logical: "is_bool", is_null: "is_null",
|
is_number: "is_num", is_logical: "is_bool", is_null: "is_null",
|
||||||
|
is_blob: "is_blob", is_data: "is_data",
|
||||||
|
is_true: "is_true", is_false: "is_false", is_fit: "is_fit",
|
||||||
|
is_character: "is_char", is_digit: "is_digit", is_letter: "is_letter",
|
||||||
|
is_lower: "is_lower", is_upper: "is_upper", is_whitespace: "is_ws",
|
||||||
|
is_actor: "is_actor",
|
||||||
length: "length"
|
length: "length"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,6 +878,78 @@ var mcode = function(ast) {
|
|||||||
var inline_every = true
|
var inline_every = true
|
||||||
var inline_some = true
|
var inline_some = true
|
||||||
var inline_reduce = true
|
var inline_reduce = true
|
||||||
|
var inline_map = true
|
||||||
|
var inline_find = true
|
||||||
|
|
||||||
|
// --- Helper: emit arity-dispatched callback invocation ---
|
||||||
|
// ctx = {fn, fn_arity, result, null_s, frame, zero, one, az, ao, prefix}
|
||||||
|
// args = [slot_for_arg1, slot_for_arg2] — data args (not this)
|
||||||
|
// max_args = 1 or 2 — how many data args to support
|
||||||
|
var emit_arity_call = function(ctx, args, max_args) {
|
||||||
|
var call_one = gen_label(ctx.prefix + "_c1")
|
||||||
|
var call_two = gen_label(ctx.prefix + "_c2")
|
||||||
|
var call_done = gen_label(ctx.prefix + "_cd")
|
||||||
|
emit_3("eq", ctx.az, ctx.fn_arity, ctx.zero)
|
||||||
|
emit_jump_cond("jump_false", ctx.az, call_one)
|
||||||
|
emit_3("frame", ctx.frame, ctx.fn, 0)
|
||||||
|
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||||
|
emit_2("invoke", ctx.frame, ctx.result)
|
||||||
|
emit_jump(call_done)
|
||||||
|
emit_label(call_one)
|
||||||
|
if (max_args >= 2) {
|
||||||
|
emit_3("eq", ctx.ao, ctx.fn_arity, ctx.one)
|
||||||
|
emit_jump_cond("jump_false", ctx.ao, call_two)
|
||||||
|
}
|
||||||
|
emit_3("frame", ctx.frame, ctx.fn, 1)
|
||||||
|
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||||
|
emit_3("setarg", ctx.frame, 1, args[0])
|
||||||
|
emit_2("invoke", ctx.frame, ctx.result)
|
||||||
|
if (max_args < 2) {
|
||||||
|
emit_label(call_done)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
emit_jump(call_done)
|
||||||
|
emit_label(call_two)
|
||||||
|
emit_3("frame", ctx.frame, ctx.fn, 2)
|
||||||
|
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||||
|
emit_3("setarg", ctx.frame, 1, args[0])
|
||||||
|
emit_3("setarg", ctx.frame, 2, args[1])
|
||||||
|
emit_2("invoke", ctx.frame, ctx.result)
|
||||||
|
emit_label(call_done)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helper: forward loop scaffolding ---
|
||||||
|
// L = {arr, len, i, check, item, one, loop_label, done_label}
|
||||||
|
// body_fn(L) — called between element load and increment
|
||||||
|
var emit_forward_loop = function(L, body_fn) {
|
||||||
|
emit_2("int", L.i, 0)
|
||||||
|
emit_label(L.loop_label)
|
||||||
|
emit_3("lt", L.check, L.i, L.len)
|
||||||
|
emit_jump_cond("jump_false", L.check, L.done_label)
|
||||||
|
emit_3("load_index", L.item, L.arr, L.i)
|
||||||
|
body_fn(L)
|
||||||
|
emit_3("add", L.i, L.i, L.one)
|
||||||
|
emit_jump(L.loop_label)
|
||||||
|
emit_label(L.done_label)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helper: reverse loop scaffolding ---
|
||||||
|
var emit_reverse_loop = function(L, body_fn) {
|
||||||
|
var zero = alloc_slot()
|
||||||
|
emit_2("int", zero, 0)
|
||||||
|
emit_3("subtract", L.i, L.len, L.one)
|
||||||
|
emit_label(L.loop_label)
|
||||||
|
emit_3("ge", L.check, L.i, zero)
|
||||||
|
emit_jump_cond("jump_false", L.check, L.done_label)
|
||||||
|
emit_3("load_index", L.item, L.arr, L.i)
|
||||||
|
body_fn(L)
|
||||||
|
emit_3("subtract", L.i, L.i, L.one)
|
||||||
|
emit_jump(L.loop_label)
|
||||||
|
emit_label(L.done_label)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// --- Helper: emit a reduce loop body ---
|
// --- Helper: emit a reduce loop body ---
|
||||||
// r = {acc, i, arr, fn, len, fn_arity}; emits loop updating acc in-place.
|
// r = {acc, i, arr, fn, len, fn_arity}; emits loop updating acc in-place.
|
||||||
@@ -889,13 +966,12 @@ var mcode = function(ast) {
|
|||||||
var null_s = alloc_slot()
|
var null_s = alloc_slot()
|
||||||
var one = alloc_slot()
|
var one = alloc_slot()
|
||||||
var zero = alloc_slot()
|
var zero = alloc_slot()
|
||||||
var arity_is_zero = alloc_slot()
|
var az = alloc_slot()
|
||||||
var arity_is_one = alloc_slot()
|
var ao = alloc_slot()
|
||||||
var f = alloc_slot()
|
var f = alloc_slot()
|
||||||
var loop_label = gen_label("reduce_loop")
|
var loop_label = gen_label("reduce_loop")
|
||||||
var call_one_label = gen_label("reduce_call_one")
|
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: acc, null_s: null_s,
|
||||||
var call_two_label = gen_label("reduce_call_two")
|
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "reduce"}
|
||||||
var call_done_label = gen_label("reduce_call_done")
|
|
||||||
emit_2("int", one, 1)
|
emit_2("int", one, 1)
|
||||||
emit_2("int", zero, 0)
|
emit_2("int", zero, 0)
|
||||||
emit_1("null", null_s)
|
emit_1("null", null_s)
|
||||||
@@ -907,27 +983,7 @@ var mcode = function(ast) {
|
|||||||
}
|
}
|
||||||
emit_jump_cond("jump_false", check, done_label)
|
emit_jump_cond("jump_false", check, done_label)
|
||||||
emit_3("load_index", item, arr_slot, i)
|
emit_3("load_index", item, arr_slot, i)
|
||||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
emit_arity_call(ctx, [acc, item], 2)
|
||||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
|
||||||
emit_3("frame", f, fn_slot, 0)
|
|
||||||
emit_3("setarg", f, 0, null_s)
|
|
||||||
emit_2("invoke", f, acc)
|
|
||||||
emit_jump(call_done_label)
|
|
||||||
emit_label(call_one_label)
|
|
||||||
emit_3("eq", arity_is_one, fn_arity, one)
|
|
||||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
|
||||||
emit_3("frame", f, fn_slot, 1)
|
|
||||||
emit_3("setarg", f, 0, null_s)
|
|
||||||
emit_3("setarg", f, 1, acc)
|
|
||||||
emit_2("invoke", f, acc)
|
|
||||||
emit_jump(call_done_label)
|
|
||||||
emit_label(call_two_label)
|
|
||||||
emit_3("frame", f, fn_slot, 2)
|
|
||||||
emit_3("setarg", f, 0, null_s)
|
|
||||||
emit_3("setarg", f, 1, acc)
|
|
||||||
emit_3("setarg", f, 2, item)
|
|
||||||
emit_2("invoke", f, acc)
|
|
||||||
emit_label(call_done_label)
|
|
||||||
if (forward) {
|
if (forward) {
|
||||||
emit_3("add", i, i, one)
|
emit_3("add", i, i, one)
|
||||||
} else {
|
} else {
|
||||||
@@ -936,60 +992,63 @@ var mcode = function(ast) {
|
|||||||
emit_jump(loop_label)
|
emit_jump(loop_label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Inline expansion: arrfor(arr, fn) ---
|
// --- Inline expansion: arrfor(arr, fn[, rev[, exit]]) ---
|
||||||
var expand_inline_arrfor = function(dest, arr_slot, fn_slot) {
|
var expand_inline_arrfor = function(dest, args, nargs) {
|
||||||
|
var arr_slot = args.arr
|
||||||
|
var fn_slot = args.fn
|
||||||
var len = alloc_slot()
|
var len = alloc_slot()
|
||||||
var i = alloc_slot()
|
var i = alloc_slot()
|
||||||
var check = alloc_slot()
|
var check = alloc_slot()
|
||||||
var item = alloc_slot()
|
var item = alloc_slot()
|
||||||
var fn_arity = alloc_slot()
|
var fn_arity = alloc_slot()
|
||||||
var arity_is_zero = alloc_slot()
|
var az = alloc_slot()
|
||||||
var arity_is_one = alloc_slot()
|
var ao = alloc_slot()
|
||||||
var null_s = alloc_slot()
|
var null_s = alloc_slot()
|
||||||
var zero = alloc_slot()
|
var zero = alloc_slot()
|
||||||
var one = alloc_slot()
|
var one = alloc_slot()
|
||||||
var f = alloc_slot()
|
var f = alloc_slot()
|
||||||
var discard = alloc_slot()
|
var val = alloc_slot()
|
||||||
var loop_label = gen_label("arrfor_loop")
|
var eq_check = alloc_slot()
|
||||||
var done_label = gen_label("arrfor_done")
|
var early_exit = gen_label("arrfor_exit")
|
||||||
var call_one_label = gen_label("arrfor_call_one")
|
var done_final = gen_label("arrfor_final")
|
||||||
var call_two_label = gen_label("arrfor_call_two")
|
var rev_label = gen_label("arrfor_rev")
|
||||||
var call_done_label = gen_label("arrfor_call_done")
|
var final_label = gen_label("arrfor_fwd_done")
|
||||||
|
var fwd_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("arrfor_fwd"), done_label: gen_label("arrfor_fwd_d")}
|
||||||
|
var rev_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("arrfor_rev_l"), done_label: gen_label("arrfor_rev_d")}
|
||||||
|
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||||
|
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "arrfor"}
|
||||||
|
var body_fn = function(L) {
|
||||||
|
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||||
|
if (nargs >= 4 && args.exit >= 0) {
|
||||||
|
emit_3("eq", eq_check, val, args.exit)
|
||||||
|
emit_jump_cond("jump_true", eq_check, early_exit)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
emit_2("length", len, arr_slot)
|
emit_2("length", len, arr_slot)
|
||||||
emit_2("int", i, 0)
|
|
||||||
emit_2("int", zero, 0)
|
emit_2("int", zero, 0)
|
||||||
emit_2("int", one, 1)
|
emit_2("int", one, 1)
|
||||||
emit_1("null", null_s)
|
emit_1("null", null_s)
|
||||||
emit_2("length", fn_arity, fn_slot)
|
emit_2("length", fn_arity, fn_slot)
|
||||||
emit_label(loop_label)
|
if (nargs <= 2) {
|
||||||
emit_3("lt", check, i, len)
|
emit_forward_loop(fwd_L, body_fn)
|
||||||
emit_jump_cond("jump_false", check, done_label)
|
} else {
|
||||||
emit_3("load_index", item, arr_slot, i)
|
emit_jump_cond("wary_true", args.rev, rev_label)
|
||||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
emit_forward_loop(fwd_L, body_fn)
|
||||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
emit_jump(final_label)
|
||||||
emit_3("frame", f, fn_slot, 0)
|
emit_label(rev_label)
|
||||||
emit_3("setarg", f, 0, null_s)
|
emit_reverse_loop(rev_L, body_fn)
|
||||||
emit_2("invoke", f, discard)
|
emit_label(final_label)
|
||||||
emit_jump(call_done_label)
|
}
|
||||||
emit_label(call_one_label)
|
|
||||||
emit_3("eq", arity_is_one, fn_arity, one)
|
|
||||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
|
||||||
emit_3("frame", f, fn_slot, 1)
|
|
||||||
emit_3("setarg", f, 0, null_s)
|
|
||||||
emit_3("setarg", f, 1, item)
|
|
||||||
emit_2("invoke", f, discard)
|
|
||||||
emit_jump(call_done_label)
|
|
||||||
emit_label(call_two_label)
|
|
||||||
emit_3("frame", f, fn_slot, 2)
|
|
||||||
emit_3("setarg", f, 0, null_s)
|
|
||||||
emit_3("setarg", f, 1, item)
|
|
||||||
emit_3("setarg", f, 2, i)
|
|
||||||
emit_2("invoke", f, discard)
|
|
||||||
emit_label(call_done_label)
|
|
||||||
emit_3("add", i, i, one)
|
|
||||||
emit_jump(loop_label)
|
|
||||||
emit_label(done_label)
|
|
||||||
emit_1("null", dest)
|
emit_1("null", dest)
|
||||||
|
emit_jump(done_final)
|
||||||
|
if (nargs >= 4 && args.exit >= 0) {
|
||||||
|
emit_label(early_exit)
|
||||||
|
emit_2("move", dest, val)
|
||||||
|
}
|
||||||
|
emit_label(done_final)
|
||||||
return dest
|
return dest
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,7 +1093,7 @@ var mcode = function(ast) {
|
|||||||
emit_3("setarg", f, 1, item)
|
emit_3("setarg", f, 1, item)
|
||||||
emit_2("invoke", f, val)
|
emit_2("invoke", f, val)
|
||||||
emit_label(call_done_label)
|
emit_label(call_done_label)
|
||||||
emit_jump_cond("jump_false", val, ret_false)
|
emit_jump_cond("wary_false", val, ret_false)
|
||||||
emit_3("add", i, i, one)
|
emit_3("add", i, i, one)
|
||||||
emit_jump(loop_label)
|
emit_jump(loop_label)
|
||||||
emit_label(ret_true)
|
emit_label(ret_true)
|
||||||
@@ -1087,7 +1146,7 @@ var mcode = function(ast) {
|
|||||||
emit_3("setarg", f, 1, item)
|
emit_3("setarg", f, 1, item)
|
||||||
emit_2("invoke", f, val)
|
emit_2("invoke", f, val)
|
||||||
emit_label(call_done_label)
|
emit_label(call_done_label)
|
||||||
emit_jump_cond("jump_true", val, ret_true)
|
emit_jump_cond("wary_true", val, ret_true)
|
||||||
emit_3("add", i, i, one)
|
emit_3("add", i, i, one)
|
||||||
emit_jump(loop_label)
|
emit_jump(loop_label)
|
||||||
emit_label(ret_true)
|
emit_label(ret_true)
|
||||||
@@ -1101,6 +1160,159 @@ var mcode = function(ast) {
|
|||||||
|
|
||||||
// --- Inline expansion: filter(arr, fn) ---
|
// --- Inline expansion: filter(arr, fn) ---
|
||||||
var expand_inline_filter = function(dest, arr_slot, fn_slot) {
|
var expand_inline_filter = function(dest, arr_slot, fn_slot) {
|
||||||
|
var result = alloc_slot()
|
||||||
|
var len = alloc_slot()
|
||||||
|
var i = alloc_slot()
|
||||||
|
var check = alloc_slot()
|
||||||
|
var item = alloc_slot()
|
||||||
|
var fn_arity = alloc_slot()
|
||||||
|
var az = alloc_slot()
|
||||||
|
var ao = alloc_slot()
|
||||||
|
var null_s = alloc_slot()
|
||||||
|
var zero = alloc_slot()
|
||||||
|
var one = alloc_slot()
|
||||||
|
var f = alloc_slot()
|
||||||
|
var val = alloc_slot()
|
||||||
|
var skip = gen_label("filter_skip")
|
||||||
|
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||||
|
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "filter"}
|
||||||
|
var L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("filter_loop"), done_label: gen_label("filter_done")}
|
||||||
|
add_instr(["array", result, 0])
|
||||||
|
emit_2("length", len, arr_slot)
|
||||||
|
emit_2("int", zero, 0)
|
||||||
|
emit_2("int", one, 1)
|
||||||
|
emit_1("null", null_s)
|
||||||
|
emit_2("length", fn_arity, fn_slot)
|
||||||
|
emit_forward_loop(L, function(L) {
|
||||||
|
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||||
|
emit_jump_cond("wary_false", val, skip)
|
||||||
|
emit_2("push", result, L.item)
|
||||||
|
emit_label(skip)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
emit_2("move", dest, result)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inline expansion: find(arr, target[, rev[, from]]) ---
|
||||||
|
var expand_inline_find = function(dest, args, nargs) {
|
||||||
|
var arr_slot = args.arr
|
||||||
|
var target = args.target
|
||||||
|
var len = alloc_slot()
|
||||||
|
var i = alloc_slot()
|
||||||
|
var check = alloc_slot()
|
||||||
|
var item = alloc_slot()
|
||||||
|
var fn_arity = alloc_slot()
|
||||||
|
var az = alloc_slot()
|
||||||
|
var ao = alloc_slot()
|
||||||
|
var null_s = alloc_slot()
|
||||||
|
var zero = alloc_slot()
|
||||||
|
var one = alloc_slot()
|
||||||
|
var f = alloc_slot()
|
||||||
|
var val = alloc_slot()
|
||||||
|
var is_fn = alloc_slot()
|
||||||
|
var eq_check = alloc_slot()
|
||||||
|
var fn_mode_label = gen_label("find_fn")
|
||||||
|
var found_label = gen_label("find_found")
|
||||||
|
var not_found_label = gen_label("find_nf")
|
||||||
|
var final_label = gen_label("find_final")
|
||||||
|
var vrev = gen_label("find_vrev")
|
||||||
|
var vdone = gen_label("find_vdone")
|
||||||
|
var frev = gen_label("find_frev")
|
||||||
|
var fdone = gen_label("find_fdone")
|
||||||
|
var vL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("find_vl"), done_label: gen_label("find_vd")}
|
||||||
|
var vrL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("find_vrl"), done_label: gen_label("find_vrd")}
|
||||||
|
var fL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("find_fl"), done_label: gen_label("find_fd")}
|
||||||
|
var ffL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("find_ffl"), done_label: gen_label("find_ffd")}
|
||||||
|
var frL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||||
|
loop_label: gen_label("find_frl"), done_label: gen_label("find_frd")}
|
||||||
|
var ctx = {fn: target, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||||
|
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "find"}
|
||||||
|
var val_body = function(L) {
|
||||||
|
emit_3("eq", eq_check, L.item, target)
|
||||||
|
emit_jump_cond("jump_true", eq_check, found_label)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var fn_body = function(L) {
|
||||||
|
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||||
|
emit_jump_cond("wary_true", val, found_label)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
emit_2("length", len, arr_slot)
|
||||||
|
emit_2("int", zero, 0)
|
||||||
|
emit_2("int", one, 1)
|
||||||
|
emit_1("null", null_s)
|
||||||
|
emit_2("is_func", is_fn, target)
|
||||||
|
emit_jump_cond("jump_true", is_fn, fn_mode_label)
|
||||||
|
// === Value mode ===
|
||||||
|
if (nargs <= 2) {
|
||||||
|
emit_forward_loop(vL, val_body)
|
||||||
|
} else {
|
||||||
|
emit_jump_cond("wary_true", args.rev, vrev)
|
||||||
|
if (nargs >= 4 && args.from >= 0) {
|
||||||
|
emit_2("move", i, args.from)
|
||||||
|
}
|
||||||
|
if (nargs >= 4 && args.from >= 0) {
|
||||||
|
emit_label(vL.loop_label)
|
||||||
|
emit_3("lt", vL.check, vL.i, vL.len)
|
||||||
|
emit_jump_cond("jump_false", vL.check, vL.done_label)
|
||||||
|
emit_3("load_index", vL.item, vL.arr, vL.i)
|
||||||
|
val_body(vL)
|
||||||
|
emit_3("add", vL.i, vL.i, vL.one)
|
||||||
|
emit_jump(vL.loop_label)
|
||||||
|
emit_label(vL.done_label)
|
||||||
|
} else {
|
||||||
|
emit_forward_loop(vL, val_body)
|
||||||
|
}
|
||||||
|
emit_jump(vdone)
|
||||||
|
emit_label(vrev)
|
||||||
|
emit_reverse_loop(vrL, val_body)
|
||||||
|
emit_label(vdone)
|
||||||
|
}
|
||||||
|
emit_jump(not_found_label)
|
||||||
|
// === Function mode ===
|
||||||
|
emit_label(fn_mode_label)
|
||||||
|
emit_2("length", fn_arity, target)
|
||||||
|
if (nargs <= 2) {
|
||||||
|
emit_forward_loop(fL, fn_body)
|
||||||
|
} else {
|
||||||
|
emit_jump_cond("wary_true", args.rev, frev)
|
||||||
|
if (nargs >= 4 && args.from >= 0) {
|
||||||
|
emit_2("move", i, args.from)
|
||||||
|
}
|
||||||
|
if (nargs >= 4 && args.from >= 0) {
|
||||||
|
emit_label(ffL.loop_label)
|
||||||
|
emit_3("lt", ffL.check, ffL.i, ffL.len)
|
||||||
|
emit_jump_cond("jump_false", ffL.check, ffL.done_label)
|
||||||
|
emit_3("load_index", ffL.item, ffL.arr, ffL.i)
|
||||||
|
fn_body(ffL)
|
||||||
|
emit_3("add", ffL.i, ffL.i, ffL.one)
|
||||||
|
emit_jump(ffL.loop_label)
|
||||||
|
emit_label(ffL.done_label)
|
||||||
|
} else {
|
||||||
|
emit_forward_loop(ffL, fn_body)
|
||||||
|
}
|
||||||
|
emit_jump(fdone)
|
||||||
|
emit_label(frev)
|
||||||
|
emit_reverse_loop(frL, fn_body)
|
||||||
|
emit_label(fdone)
|
||||||
|
}
|
||||||
|
emit_label(not_found_label)
|
||||||
|
emit_1("null", dest)
|
||||||
|
emit_jump(final_label)
|
||||||
|
emit_label(found_label)
|
||||||
|
emit_2("move", dest, i)
|
||||||
|
emit_label(final_label)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inline expansion: array(arr, fn) → map ---
|
||||||
|
var expand_inline_map = function(dest, arr_slot, fn_slot) {
|
||||||
var result = alloc_slot()
|
var result = alloc_slot()
|
||||||
var len = alloc_slot()
|
var len = alloc_slot()
|
||||||
var i = alloc_slot()
|
var i = alloc_slot()
|
||||||
@@ -1114,12 +1326,11 @@ var mcode = function(ast) {
|
|||||||
var one = alloc_slot()
|
var one = alloc_slot()
|
||||||
var f = alloc_slot()
|
var f = alloc_slot()
|
||||||
var val = alloc_slot()
|
var val = alloc_slot()
|
||||||
var loop_label = gen_label("filter_loop")
|
var loop_label = gen_label("map_loop")
|
||||||
var call_one_label = gen_label("filter_call_one")
|
var call_one_label = gen_label("map_call_one")
|
||||||
var call_two_label = gen_label("filter_call_two")
|
var call_two_label = gen_label("map_call_two")
|
||||||
var call_done_label = gen_label("filter_call_done")
|
var call_done_label = gen_label("map_call_done")
|
||||||
var skip_label = gen_label("filter_skip")
|
var done_label = gen_label("map_done")
|
||||||
var done_label = gen_label("filter_done")
|
|
||||||
add_instr(["array", result, 0])
|
add_instr(["array", result, 0])
|
||||||
emit_2("length", len, arr_slot)
|
emit_2("length", len, arr_slot)
|
||||||
emit_2("int", i, 0)
|
emit_2("int", i, 0)
|
||||||
@@ -1152,9 +1363,37 @@ var mcode = function(ast) {
|
|||||||
emit_3("setarg", f, 2, i)
|
emit_3("setarg", f, 2, i)
|
||||||
emit_2("invoke", f, val)
|
emit_2("invoke", f, val)
|
||||||
emit_label(call_done_label)
|
emit_label(call_done_label)
|
||||||
emit_jump_cond("jump_false", val, skip_label)
|
emit_2("push", result, val)
|
||||||
emit_2("push", result, item)
|
emit_3("add", i, i, one)
|
||||||
emit_label(skip_label)
|
emit_jump(loop_label)
|
||||||
|
emit_label(done_label)
|
||||||
|
emit_2("move", dest, result)
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Inline expansion: array(arr, intrinsic_op) → map with direct opcode ---
|
||||||
|
var expand_inline_map_intrinsic = function(dest, arr_slot, opcode) {
|
||||||
|
var result = alloc_slot()
|
||||||
|
var len = alloc_slot()
|
||||||
|
var i = alloc_slot()
|
||||||
|
var check = alloc_slot()
|
||||||
|
var item = alloc_slot()
|
||||||
|
var val = alloc_slot()
|
||||||
|
var zero = alloc_slot()
|
||||||
|
var one = alloc_slot()
|
||||||
|
var loop_label = gen_label("mapi_loop")
|
||||||
|
var done_label = gen_label("mapi_done")
|
||||||
|
add_instr(["array", result, 0])
|
||||||
|
emit_2("length", len, arr_slot)
|
||||||
|
emit_2("int", i, 0)
|
||||||
|
emit_2("int", zero, 0)
|
||||||
|
emit_2("int", one, 1)
|
||||||
|
emit_label(loop_label)
|
||||||
|
emit_3("lt", check, i, len)
|
||||||
|
emit_jump_cond("jump_false", check, done_label)
|
||||||
|
emit_3("load_index", item, arr_slot, i)
|
||||||
|
emit_2(opcode, val, item)
|
||||||
|
emit_2("push", result, val)
|
||||||
emit_3("add", i, i, one)
|
emit_3("add", i, i, one)
|
||||||
emit_jump(loop_label)
|
emit_jump(loop_label)
|
||||||
emit_label(done_label)
|
emit_label(done_label)
|
||||||
@@ -1246,7 +1485,7 @@ var mcode = function(ast) {
|
|||||||
// No initial
|
// No initial
|
||||||
emit_3("lt", check, zero, len)
|
emit_3("lt", check, zero, len)
|
||||||
emit_jump_cond("jump_false", check, null_label)
|
emit_jump_cond("jump_false", check, null_label)
|
||||||
emit_jump_cond("jump_true", rev_slot, no_init_rev)
|
emit_jump_cond("wary_true", rev_slot, no_init_rev)
|
||||||
// No initial, forward
|
// No initial, forward
|
||||||
emit_3("load_index", acc, arr_slot, zero)
|
emit_3("load_index", acc, arr_slot, zero)
|
||||||
emit_2("move", i, one)
|
emit_2("move", i, one)
|
||||||
@@ -1268,7 +1507,7 @@ var mcode = function(ast) {
|
|||||||
emit_jump(final_label)
|
emit_jump(final_label)
|
||||||
// Has initial
|
// Has initial
|
||||||
emit_label(has_init)
|
emit_label(has_init)
|
||||||
emit_jump_cond("jump_true", rev_slot, init_rev)
|
emit_jump_cond("wary_true", rev_slot, init_rev)
|
||||||
// Has initial, forward
|
// Has initial, forward
|
||||||
emit_2("move", acc, init_slot)
|
emit_2("move", acc, init_slot)
|
||||||
emit_2("int", i, 0)
|
emit_2("int", i, 0)
|
||||||
@@ -1315,7 +1554,7 @@ var mcode = function(ast) {
|
|||||||
left_slot = gen_expr(left, -1)
|
left_slot = gen_expr(left, -1)
|
||||||
dest = alloc_slot()
|
dest = alloc_slot()
|
||||||
emit_2("move", dest, left_slot)
|
emit_2("move", dest, left_slot)
|
||||||
emit_jump_cond("jump_false", dest, end_label)
|
emit_jump_cond("wary_false", dest, end_label)
|
||||||
right_slot = gen_expr(right, -1)
|
right_slot = gen_expr(right, -1)
|
||||||
emit_2("move", dest, right_slot)
|
emit_2("move", dest, right_slot)
|
||||||
emit_label(end_label)
|
emit_label(end_label)
|
||||||
@@ -1327,7 +1566,7 @@ var mcode = function(ast) {
|
|||||||
left_slot = gen_expr(left, -1)
|
left_slot = gen_expr(left, -1)
|
||||||
dest = alloc_slot()
|
dest = alloc_slot()
|
||||||
emit_2("move", dest, left_slot)
|
emit_2("move", dest, left_slot)
|
||||||
emit_jump_cond("jump_true", dest, end_label)
|
emit_jump_cond("wary_true", dest, end_label)
|
||||||
right_slot = gen_expr(right, -1)
|
right_slot = gen_expr(right, -1)
|
||||||
emit_2("move", dest, right_slot)
|
emit_2("move", dest, right_slot)
|
||||||
emit_label(end_label)
|
emit_label(end_label)
|
||||||
@@ -1896,12 +2135,22 @@ var mcode = function(ast) {
|
|||||||
emit_label(guard_done)
|
emit_label(guard_done)
|
||||||
return a1
|
return a1
|
||||||
}
|
}
|
||||||
// Callback intrinsics → inline mcode loops
|
// apply(fn, arr) → direct opcode
|
||||||
if (nargs == 2 && fname == "arrfor" && inline_arrfor) {
|
if (nargs == 2 && fname == "apply") {
|
||||||
a0 = gen_expr(args_list[0], -1)
|
a0 = gen_expr(args_list[0], -1)
|
||||||
a1 = gen_expr(args_list[1], -1)
|
a1 = gen_expr(args_list[1], -1)
|
||||||
d = alloc_slot()
|
d = alloc_slot()
|
||||||
return expand_inline_arrfor(d, a0, a1)
|
emit_3("apply", d, a0, a1)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
// Callback intrinsics → inline mcode loops
|
||||||
|
if (fname == "arrfor" && nargs >= 2 && nargs <= 4 && inline_arrfor) {
|
||||||
|
a0 = gen_expr(args_list[0], -1)
|
||||||
|
a1 = gen_expr(args_list[1], -1)
|
||||||
|
a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1
|
||||||
|
a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1
|
||||||
|
d = alloc_slot()
|
||||||
|
return expand_inline_arrfor(d, {arr: a0, fn: a1, rev: a2, exit: a3}, nargs)
|
||||||
}
|
}
|
||||||
if (nargs == 2 && fname == "every" && inline_every) {
|
if (nargs == 2 && fname == "every" && inline_every) {
|
||||||
a0 = gen_expr(args_list[0], -1)
|
a0 = gen_expr(args_list[0], -1)
|
||||||
@@ -1921,6 +2170,14 @@ var mcode = function(ast) {
|
|||||||
d = alloc_slot()
|
d = alloc_slot()
|
||||||
return expand_inline_filter(d, a0, a1)
|
return expand_inline_filter(d, a0, a1)
|
||||||
}
|
}
|
||||||
|
if (fname == "find" && nargs >= 2 && nargs <= 4 && inline_find) {
|
||||||
|
a0 = gen_expr(args_list[0], -1)
|
||||||
|
a1 = gen_expr(args_list[1], -1)
|
||||||
|
a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1
|
||||||
|
a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1
|
||||||
|
d = alloc_slot()
|
||||||
|
return expand_inline_find(d, {arr: a0, target: a1, rev: a2, from: a3}, nargs)
|
||||||
|
}
|
||||||
if (fname == "reduce" && nargs >= 2 && nargs <= 4 && inline_reduce) {
|
if (fname == "reduce" && nargs >= 2 && nargs <= 4 && inline_reduce) {
|
||||||
a0 = gen_expr(args_list[0], -1)
|
a0 = gen_expr(args_list[0], -1)
|
||||||
a1 = gen_expr(args_list[1], -1)
|
a1 = gen_expr(args_list[1], -1)
|
||||||
@@ -1929,6 +2186,25 @@ var mcode = function(ast) {
|
|||||||
d = alloc_slot()
|
d = alloc_slot()
|
||||||
return expand_inline_reduce(d, {arr: a0, fn: a1, init: a2, rev: a3}, nargs)
|
return expand_inline_reduce(d, {arr: a0, fn: a1, init: a2, rev: a3}, nargs)
|
||||||
}
|
}
|
||||||
|
// array(arr, fn) → inline map expansion
|
||||||
|
// Skip when first arg is a number literal (that's array(N, fn) — creation, not map)
|
||||||
|
if (nargs == 2 && fname == "array" && inline_map
|
||||||
|
&& args_list[0].kind != "number") {
|
||||||
|
// Specialized: array(arr, known_sensory_intrinsic) → direct opcode loop
|
||||||
|
if (args_list[1].kind == "name" && args_list[1].intrinsic == true
|
||||||
|
&& sensory_ops[args_list[1].name] != null) {
|
||||||
|
a0 = gen_expr(args_list[0], -1)
|
||||||
|
d = alloc_slot()
|
||||||
|
return expand_inline_map_intrinsic(d, a0, sensory_ops[args_list[1].name])
|
||||||
|
}
|
||||||
|
// General: array(arr, fn_literal) → map loop with arity dispatch
|
||||||
|
if (args_list[1].kind == "function") {
|
||||||
|
a0 = gen_expr(args_list[0], -1)
|
||||||
|
a1 = gen_expr(args_list[1], -1)
|
||||||
|
d = alloc_slot()
|
||||||
|
return expand_inline_map(d, a0, a1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect arg slots
|
// Collect arg slots
|
||||||
@@ -2085,7 +2361,7 @@ var mcode = function(ast) {
|
|||||||
else_label = gen_label("tern_else")
|
else_label = gen_label("tern_else")
|
||||||
end_label = gen_label("tern_end")
|
end_label = gen_label("tern_end")
|
||||||
cond_slot = gen_expr(cond, -1)
|
cond_slot = gen_expr(cond, -1)
|
||||||
emit_jump_cond("jump_false", cond_slot, else_label)
|
emit_jump_cond("wary_false", cond_slot, else_label)
|
||||||
dest = alloc_slot()
|
dest = alloc_slot()
|
||||||
then_slot = gen_expr(then_expr, -1)
|
then_slot = gen_expr(then_expr, -1)
|
||||||
emit_2("move", dest, then_slot)
|
emit_2("move", dest, then_slot)
|
||||||
@@ -2310,7 +2586,7 @@ var mcode = function(ast) {
|
|||||||
else_label = gen_label("if_else")
|
else_label = gen_label("if_else")
|
||||||
end_label = gen_label("if_end")
|
end_label = gen_label("if_end")
|
||||||
cond_slot = gen_expr(cond, -1)
|
cond_slot = gen_expr(cond, -1)
|
||||||
emit_jump_cond("jump_false", cond_slot, else_label)
|
emit_jump_cond("wary_false", cond_slot, else_label)
|
||||||
_i = 0
|
_i = 0
|
||||||
while (_i < length(then_stmts)) {
|
while (_i < length(then_stmts)) {
|
||||||
gen_statement(then_stmts[_i])
|
gen_statement(then_stmts[_i])
|
||||||
@@ -2351,7 +2627,7 @@ var mcode = function(ast) {
|
|||||||
}
|
}
|
||||||
emit_label(start_label)
|
emit_label(start_label)
|
||||||
cond_slot = gen_expr(cond, -1)
|
cond_slot = gen_expr(cond, -1)
|
||||||
emit_jump_cond("jump_false", cond_slot, end_label)
|
emit_jump_cond("wary_false", cond_slot, end_label)
|
||||||
_i = 0
|
_i = 0
|
||||||
while (_i < length(stmts)) {
|
while (_i < length(stmts)) {
|
||||||
gen_statement(stmts[_i])
|
gen_statement(stmts[_i])
|
||||||
@@ -2386,7 +2662,7 @@ var mcode = function(ast) {
|
|||||||
}
|
}
|
||||||
emit_label(cond_label)
|
emit_label(cond_label)
|
||||||
cond_slot = gen_expr(cond, -1)
|
cond_slot = gen_expr(cond, -1)
|
||||||
emit_jump_cond("jump_true", cond_slot, start_label)
|
emit_jump_cond("wary_true", cond_slot, start_label)
|
||||||
emit_label(end_label)
|
emit_label(end_label)
|
||||||
s_loop_break = old_break
|
s_loop_break = old_break
|
||||||
s_loop_continue = old_continue
|
s_loop_continue = old_continue
|
||||||
@@ -2420,7 +2696,7 @@ var mcode = function(ast) {
|
|||||||
emit_label(start_label)
|
emit_label(start_label)
|
||||||
if (test != null) {
|
if (test != null) {
|
||||||
test_slot = gen_expr(test, -1)
|
test_slot = gen_expr(test, -1)
|
||||||
emit_jump_cond("jump_false", test_slot, end_label)
|
emit_jump_cond("wary_false", test_slot, end_label)
|
||||||
}
|
}
|
||||||
_i = 0
|
_i = 0
|
||||||
while (_i < length(stmts)) {
|
while (_i < length(stmts)) {
|
||||||
|
|||||||
2086
qbe_emit.cm
2086
qbe_emit.cm
File diff suppressed because it is too large
Load Diff
350
source/mach.c
350
source/mach.c
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "quickjs-internal.h"
|
#include "quickjs-internal.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Mach VM instruction definitions (private to mach.c)
|
Mach VM instruction definitions (private to mach.c)
|
||||||
@@ -88,7 +89,7 @@
|
|||||||
[P] IS_IDENTICAL, IS_INT, IS_NUM, IS_TEXT, IS_BOOL, IS_NULL
|
[P] IS_IDENTICAL, IS_INT, IS_NUM, IS_TEXT, IS_BOOL, IS_NULL
|
||||||
[P] IS_ARRAY, IS_FUNC, IS_RECORD, IS_STONE, IS_PROXY
|
[P] IS_ARRAY, IS_FUNC, IS_RECORD, IS_STONE, IS_PROXY
|
||||||
[P] NOT, AND, OR, BITNOT, BITAND, BITOR, BITXOR
|
[P] NOT, AND, OR, BITNOT, BITAND, BITOR, BITXOR
|
||||||
[P] JMP, JMPTRUE, JMPFALSE, JMPNULL, JMPNOTNULL
|
[P] JMP, JMPTRUE, JMPFALSE, JMPNULL, JMPNOTNULL, WARYTRUE, WARYFALSE, JMPEMPTY
|
||||||
[P] RETURN, RETNIL, SETARG, GETUP, SETUP, DISRUPT, THROW
|
[P] RETURN, RETNIL, SETARG, GETUP, SETUP, DISRUPT, THROW
|
||||||
[P] LENGTH (array + imm-ASCII fast path only; text/blob fallback is [G])
|
[P] LENGTH (array + imm-ASCII fast path only; text/blob fallback is [G])
|
||||||
[N] EQ_TEXT..GE_TEXT (js_string_compare_value — no allocation)
|
[N] EQ_TEXT..GE_TEXT (js_string_compare_value — no allocation)
|
||||||
@@ -269,6 +270,22 @@ typedef enum MachOpcode {
|
|||||||
MACH_IS_STONE, /* R(A) = is_stone(R(B)) */
|
MACH_IS_STONE, /* R(A) = is_stone(R(B)) */
|
||||||
MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */
|
MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */
|
||||||
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
|
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
|
||||||
|
MACH_IS_BLOB, /* R(A) = is_blob(R(B)) */
|
||||||
|
MACH_IS_DATA, /* R(A) = is_data(R(B)) — plain record, not array/func/blob */
|
||||||
|
MACH_IS_TRUE, /* R(A) = (R(B) === true) */
|
||||||
|
MACH_IS_FALSE, /* R(A) = (R(B) === false) */
|
||||||
|
MACH_IS_FIT, /* R(A) = is_fit(R(B)) — safe integer */
|
||||||
|
MACH_IS_CHAR, /* R(A) = is_character(R(B)) — single char text */
|
||||||
|
MACH_IS_DIGIT, /* R(A) = is_digit(R(B)) */
|
||||||
|
MACH_IS_LETTER, /* R(A) = is_letter(R(B)) */
|
||||||
|
MACH_IS_LOWER, /* R(A) = is_lower(R(B)) */
|
||||||
|
MACH_IS_UPPER, /* R(A) = is_upper(R(B)) */
|
||||||
|
MACH_IS_WS, /* R(A) = is_whitespace(R(B)) */
|
||||||
|
MACH_IS_ACTOR, /* R(A) = is_actor(R(B)) — has actor_sym property */
|
||||||
|
MACH_APPLY, /* R(A) = apply(R(B), R(C)) — call fn with args from array (ABC) */
|
||||||
|
MACH_WARYTRUE, /* if toBool(R(A)): pc += sBx — coercing (iAsBx) */
|
||||||
|
MACH_WARYFALSE, /* if !toBool(R(A)): pc += sBx — coercing (iAsBx) */
|
||||||
|
MACH_JMPEMPTY, /* if R(A)==empty_text: pc += sBx (iAsBx) */
|
||||||
|
|
||||||
MACH_OP_COUNT
|
MACH_OP_COUNT
|
||||||
} MachOpcode;
|
} MachOpcode;
|
||||||
@@ -381,6 +398,22 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
|||||||
[MACH_IS_STONE] = "is_stone",
|
[MACH_IS_STONE] = "is_stone",
|
||||||
[MACH_LENGTH] = "length",
|
[MACH_LENGTH] = "length",
|
||||||
[MACH_IS_PROXY] = "is_proxy",
|
[MACH_IS_PROXY] = "is_proxy",
|
||||||
|
[MACH_IS_BLOB] = "is_blob",
|
||||||
|
[MACH_IS_DATA] = "is_data",
|
||||||
|
[MACH_IS_TRUE] = "is_true",
|
||||||
|
[MACH_IS_FALSE] = "is_false",
|
||||||
|
[MACH_IS_FIT] = "is_fit",
|
||||||
|
[MACH_IS_CHAR] = "is_char",
|
||||||
|
[MACH_IS_DIGIT] = "is_digit",
|
||||||
|
[MACH_IS_LETTER] = "is_letter",
|
||||||
|
[MACH_IS_LOWER] = "is_lower",
|
||||||
|
[MACH_IS_UPPER] = "is_upper",
|
||||||
|
[MACH_IS_WS] = "is_ws",
|
||||||
|
[MACH_IS_ACTOR] = "is_actor",
|
||||||
|
[MACH_APPLY] = "apply",
|
||||||
|
[MACH_WARYTRUE] = "wary_true",
|
||||||
|
[MACH_WARYFALSE] = "wary_false",
|
||||||
|
[MACH_JMPEMPTY] = "jump_empty",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---- Compile-time constant pool entry ---- */
|
/* ---- Compile-time constant pool entry ---- */
|
||||||
@@ -1396,6 +1429,14 @@ vm_dispatch:
|
|||||||
DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC),
|
DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC),
|
||||||
DT(MACH_IS_RECORD), DT(MACH_IS_STONE),
|
DT(MACH_IS_RECORD), DT(MACH_IS_STONE),
|
||||||
DT(MACH_LENGTH), DT(MACH_IS_PROXY),
|
DT(MACH_LENGTH), DT(MACH_IS_PROXY),
|
||||||
|
DT(MACH_IS_BLOB), DT(MACH_IS_DATA),
|
||||||
|
DT(MACH_IS_TRUE), DT(MACH_IS_FALSE),
|
||||||
|
DT(MACH_IS_FIT), DT(MACH_IS_CHAR),
|
||||||
|
DT(MACH_IS_DIGIT), DT(MACH_IS_LETTER),
|
||||||
|
DT(MACH_IS_LOWER), DT(MACH_IS_UPPER),
|
||||||
|
DT(MACH_IS_WS), DT(MACH_IS_ACTOR),
|
||||||
|
DT(MACH_APPLY),
|
||||||
|
DT(MACH_WARYTRUE), DT(MACH_WARYFALSE), DT(MACH_JMPEMPTY),
|
||||||
};
|
};
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#undef DT
|
#undef DT
|
||||||
@@ -1988,21 +2029,12 @@ vm_dispatch:
|
|||||||
int depth = b;
|
int depth = b;
|
||||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||||
if (!target) {
|
assert(depth > 0);
|
||||||
fprintf(stderr, "GETUP: NULL outer_frame at depth 0! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
|
assert(target != NULL);
|
||||||
pc-1, a, depth, c, code->nr_slots, instr);
|
|
||||||
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame");
|
|
||||||
goto disrupt;
|
|
||||||
}
|
|
||||||
for (int d = 1; d < depth; d++) {
|
for (int d = 1; d < depth; d++) {
|
||||||
fn = JS_VALUE_GET_FUNCTION(target->function);
|
fn = JS_VALUE_GET_FUNCTION(target->function);
|
||||||
JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||||
if (!next) {
|
assert(next != NULL);
|
||||||
fprintf(stderr, "GETUP: NULL outer_frame at depth %d! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
|
|
||||||
d, pc-1, a, depth, c, code->nr_slots, instr);
|
|
||||||
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame at depth %d", d);
|
|
||||||
goto disrupt;
|
|
||||||
}
|
|
||||||
target = next;
|
target = next;
|
||||||
}
|
}
|
||||||
stone_mutable_text(target->slots[c]);
|
stone_mutable_text(target->slots[c]);
|
||||||
@@ -2015,22 +2047,14 @@ vm_dispatch:
|
|||||||
int depth = b;
|
int depth = b;
|
||||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||||
|
assert(depth > 0);
|
||||||
|
assert(target != NULL);
|
||||||
for (int d = 1; d < depth; d++) {
|
for (int d = 1; d < depth; d++) {
|
||||||
fn = JS_VALUE_GET_FUNCTION(target->function);
|
fn = JS_VALUE_GET_FUNCTION(target->function);
|
||||||
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||||
|
assert(target != NULL);
|
||||||
}
|
}
|
||||||
{
|
assert((unsigned)c < objhdr_cap56(target->header));
|
||||||
uint64_t tcap = objhdr_cap56(target->header);
|
|
||||||
if ((unsigned)c >= tcap) {
|
|
||||||
fprintf(stderr, "MACH_SETUP OOB: slot=%d >= target_cap=%llu depth=%d "
|
|
||||||
"cur_fn=%s (%s) pc=%u\n",
|
|
||||||
c, (unsigned long long)tcap, depth,
|
|
||||||
code->name_cstr ? code->name_cstr : "?",
|
|
||||||
code->filename_cstr ? code->filename_cstr : "?", pc - 1);
|
|
||||||
fflush(stderr);
|
|
||||||
VM_BREAK();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target->slots[c] = frame->slots[a];
|
target->slots[c] = frame->slots[a];
|
||||||
VM_BREAK();
|
VM_BREAK();
|
||||||
}
|
}
|
||||||
@@ -2055,12 +2079,7 @@ vm_dispatch:
|
|||||||
}
|
}
|
||||||
|
|
||||||
VM_CASE(MACH_JMPTRUE): {
|
VM_CASE(MACH_JMPTRUE): {
|
||||||
JSValue v = frame->slots[a];
|
if (frame->slots[a] == JS_TRUE) {
|
||||||
int cond;
|
|
||||||
if (v == JS_TRUE) cond = 1;
|
|
||||||
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
|
|
||||||
else cond = JS_ToBool(ctx, v);
|
|
||||||
if (cond) {
|
|
||||||
int offset = MACH_GET_sBx(instr);
|
int offset = MACH_GET_sBx(instr);
|
||||||
pc = (uint32_t)((int32_t)pc + offset);
|
pc = (uint32_t)((int32_t)pc + offset);
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
@@ -2081,12 +2100,7 @@ vm_dispatch:
|
|||||||
}
|
}
|
||||||
|
|
||||||
VM_CASE(MACH_JMPFALSE): {
|
VM_CASE(MACH_JMPFALSE): {
|
||||||
JSValue v = frame->slots[a];
|
if (frame->slots[a] == JS_FALSE) {
|
||||||
int cond;
|
|
||||||
if (v == JS_TRUE) cond = 1;
|
|
||||||
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
|
|
||||||
else cond = JS_ToBool(ctx, v);
|
|
||||||
if (!cond) {
|
|
||||||
int offset = MACH_GET_sBx(instr);
|
int offset = MACH_GET_sBx(instr);
|
||||||
pc = (uint32_t)((int32_t)pc + offset);
|
pc = (uint32_t)((int32_t)pc + offset);
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
@@ -2386,6 +2400,166 @@ vm_dispatch:
|
|||||||
frame->slots[a] = JS_NewBool(ctx, is_proxy);
|
frame->slots[a] = JS_NewBool(ctx, is_proxy);
|
||||||
VM_BREAK();
|
VM_BREAK();
|
||||||
}
|
}
|
||||||
|
VM_CASE(MACH_IS_BLOB):
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, mist_is_blob(frame->slots[b]));
|
||||||
|
VM_BREAK();
|
||||||
|
VM_CASE(MACH_IS_DATA): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (mist_is_gc_object(v) && !mist_is_array(v)
|
||||||
|
&& !mist_is_function(v) && !mist_is_blob(v))
|
||||||
|
result = 1;
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_TRUE):
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_TRUE);
|
||||||
|
VM_BREAK();
|
||||||
|
VM_CASE(MACH_IS_FALSE):
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_FALSE);
|
||||||
|
VM_BREAK();
|
||||||
|
VM_CASE(MACH_IS_FIT): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (JS_IsInt(v)) {
|
||||||
|
result = 1;
|
||||||
|
} else if (JS_IsShortFloat(v)) {
|
||||||
|
double d = JS_VALUE_GET_FLOAT64(v);
|
||||||
|
result = (isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0);
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_CHAR): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v))
|
||||||
|
result = (MIST_GetImmediateASCIILen(v) == 1);
|
||||||
|
else if (mist_is_text(v))
|
||||||
|
result = (js_string_value_len(v) == 1);
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_DIGIT): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||||
|
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||||
|
result = (ch >= '0' && ch <= '9');
|
||||||
|
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||||
|
uint32_t ch = js_string_value_get(v, 0);
|
||||||
|
result = (ch >= '0' && ch <= '9');
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_LETTER): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||||
|
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||||
|
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||||
|
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||||
|
uint32_t ch = js_string_value_get(v, 0);
|
||||||
|
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_LOWER): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||||
|
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||||
|
result = (ch >= 'a' && ch <= 'z');
|
||||||
|
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||||
|
uint32_t ch = js_string_value_get(v, 0);
|
||||||
|
result = (ch >= 'a' && ch <= 'z');
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_UPPER): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||||
|
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||||
|
result = (ch >= 'A' && ch <= 'Z');
|
||||||
|
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||||
|
uint32_t ch = js_string_value_get(v, 0);
|
||||||
|
result = (ch >= 'A' && ch <= 'Z');
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_WS): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||||
|
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||||
|
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||||
|
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||||
|
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||||
|
uint32_t ch = js_string_value_get(v, 0);
|
||||||
|
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||||
|
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_IS_ACTOR): {
|
||||||
|
JSValue v = frame->slots[b];
|
||||||
|
int result = 0;
|
||||||
|
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym)) {
|
||||||
|
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
}
|
||||||
|
frame->slots[a] = JS_NewBool(ctx, result);
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
VM_CASE(MACH_APPLY): {
|
||||||
|
/* A=dest, B=fn, C=arr_or_val */
|
||||||
|
JSValue fn_val = frame->slots[b];
|
||||||
|
JSValue arg_val = frame->slots[c];
|
||||||
|
if (!mist_is_function(fn_val)) {
|
||||||
|
frame->slots[a] = fn_val;
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||||
|
JSValue ret;
|
||||||
|
ctx->reg_current_frame = frame_ref.val;
|
||||||
|
ctx->current_register_pc = pc > 0 ? pc - 1 : 0;
|
||||||
|
ctx->vm_call_depth++;
|
||||||
|
if (!mist_is_array(arg_val)) {
|
||||||
|
/* Non-array: use as single argument */
|
||||||
|
if (!mach_check_call_arity(ctx, fn, 1)) {
|
||||||
|
ctx->vm_call_depth--;
|
||||||
|
goto disrupt;
|
||||||
|
}
|
||||||
|
ret = JS_CallInternal(ctx, fn_val, JS_NULL, 1, &arg_val, 0);
|
||||||
|
} else {
|
||||||
|
JSArray *arr = JS_VALUE_GET_ARRAY(arg_val);
|
||||||
|
int len = arr->len;
|
||||||
|
if (!mach_check_call_arity(ctx, fn, len)) {
|
||||||
|
ctx->vm_call_depth--;
|
||||||
|
goto disrupt;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
ret = JS_CallInternal(ctx, fn_val, JS_NULL, 0, NULL, 0);
|
||||||
|
} else {
|
||||||
|
JSValue *args = alloca(sizeof(JSValue) * len);
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
args[i] = arr->values[i];
|
||||||
|
ret = JS_CallInternal(ctx, fn_val, JS_NULL, len, args, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->vm_call_depth--;
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
ctx->reg_current_frame = JS_NULL;
|
||||||
|
if (JS_IsException(ret)) goto disrupt;
|
||||||
|
frame->slots[a] = ret;
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
/* Logical */
|
/* Logical */
|
||||||
VM_CASE(MACH_NOT): {
|
VM_CASE(MACH_NOT): {
|
||||||
int bval = JS_ToBool(ctx, frame->slots[b]);
|
int bval = JS_ToBool(ctx, frame->slots[b]);
|
||||||
@@ -2695,6 +2869,67 @@ vm_dispatch:
|
|||||||
VM_BREAK();
|
VM_BREAK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wary jumps — coerce via JS_ToBool (old JMPTRUE/JMPFALSE behavior) */
|
||||||
|
VM_CASE(MACH_WARYTRUE): {
|
||||||
|
JSValue v = frame->slots[a];
|
||||||
|
int cond;
|
||||||
|
if (v == JS_TRUE) cond = 1;
|
||||||
|
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
|
||||||
|
else cond = JS_ToBool(ctx, v);
|
||||||
|
if (cond) {
|
||||||
|
int offset = MACH_GET_sBx(instr);
|
||||||
|
pc = (uint32_t)((int32_t)pc + offset);
|
||||||
|
if (offset < 0) {
|
||||||
|
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
|
||||||
|
if (pf == 2) {
|
||||||
|
result = JS_RaiseDisrupt(ctx, "interrupted");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (pf == 1) {
|
||||||
|
if (ctx->vm_call_depth > 0)
|
||||||
|
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
|
||||||
|
else
|
||||||
|
goto suspend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_CASE(MACH_WARYFALSE): {
|
||||||
|
JSValue v = frame->slots[a];
|
||||||
|
int cond;
|
||||||
|
if (v == JS_TRUE) cond = 1;
|
||||||
|
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
|
||||||
|
else cond = JS_ToBool(ctx, v);
|
||||||
|
if (!cond) {
|
||||||
|
int offset = MACH_GET_sBx(instr);
|
||||||
|
pc = (uint32_t)((int32_t)pc + offset);
|
||||||
|
if (offset < 0) {
|
||||||
|
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
|
||||||
|
if (pf == 2) {
|
||||||
|
result = JS_RaiseDisrupt(ctx, "interrupted");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (pf == 1) {
|
||||||
|
if (ctx->vm_call_depth > 0)
|
||||||
|
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
|
||||||
|
else
|
||||||
|
goto suspend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_CASE(MACH_JMPEMPTY): {
|
||||||
|
if (frame->slots[a] == JS_EMPTY_TEXT) {
|
||||||
|
int offset = MACH_GET_sBx(instr);
|
||||||
|
pc = (uint32_t)((int32_t)pc + offset);
|
||||||
|
}
|
||||||
|
VM_BREAK();
|
||||||
|
}
|
||||||
|
|
||||||
/* Disrupt (mcode alias) */
|
/* Disrupt (mcode alias) */
|
||||||
VM_CASE(MACH_DISRUPT):
|
VM_CASE(MACH_DISRUPT):
|
||||||
goto disrupt;
|
goto disrupt;
|
||||||
@@ -3031,6 +3266,19 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
|||||||
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
|
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
|
||||||
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
|
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
|
||||||
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
|
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
|
||||||
|
else if (strcmp(op, "is_blob") == 0) { AB2(MACH_IS_BLOB); }
|
||||||
|
else if (strcmp(op, "is_data") == 0) { AB2(MACH_IS_DATA); }
|
||||||
|
else if (strcmp(op, "is_true") == 0) { AB2(MACH_IS_TRUE); }
|
||||||
|
else if (strcmp(op, "is_false") == 0) { AB2(MACH_IS_FALSE); }
|
||||||
|
else if (strcmp(op, "is_fit") == 0) { AB2(MACH_IS_FIT); }
|
||||||
|
else if (strcmp(op, "is_char") == 0) { AB2(MACH_IS_CHAR); }
|
||||||
|
else if (strcmp(op, "is_digit") == 0) { AB2(MACH_IS_DIGIT); }
|
||||||
|
else if (strcmp(op, "is_letter") == 0) { AB2(MACH_IS_LETTER); }
|
||||||
|
else if (strcmp(op, "is_lower") == 0) { AB2(MACH_IS_LOWER); }
|
||||||
|
else if (strcmp(op, "is_upper") == 0) { AB2(MACH_IS_UPPER); }
|
||||||
|
else if (strcmp(op, "is_ws") == 0) { AB2(MACH_IS_WS); }
|
||||||
|
else if (strcmp(op, "is_actor") == 0) { AB2(MACH_IS_ACTOR); }
|
||||||
|
else if (strcmp(op, "apply") == 0) { ABC3(MACH_APPLY); }
|
||||||
/* Logical */
|
/* Logical */
|
||||||
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
|
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
|
||||||
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }
|
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }
|
||||||
@@ -3181,6 +3429,34 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
|||||||
EM(MACH_AsBx(MACH_JMPNOTNULL, reg, 0));
|
EM(MACH_AsBx(MACH_JMPNOTNULL, reg, 0));
|
||||||
ml_patch(&s, pc_now, lbl, 0, reg);
|
ml_patch(&s, pc_now, lbl, 0, reg);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(op, "jump_null") == 0) {
|
||||||
|
int reg = A1;
|
||||||
|
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
|
||||||
|
int pc_now = s.code_count;
|
||||||
|
EM(MACH_AsBx(MACH_JMPNULL, reg, 0));
|
||||||
|
ml_patch(&s, pc_now, lbl, 0, reg);
|
||||||
|
}
|
||||||
|
else if (strcmp(op, "wary_true") == 0) {
|
||||||
|
int reg = A1;
|
||||||
|
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
|
||||||
|
int pc_now = s.code_count;
|
||||||
|
EM(MACH_AsBx(MACH_WARYTRUE, reg, 0));
|
||||||
|
ml_patch(&s, pc_now, lbl, 0, reg);
|
||||||
|
}
|
||||||
|
else if (strcmp(op, "wary_false") == 0) {
|
||||||
|
int reg = A1;
|
||||||
|
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
|
||||||
|
int pc_now = s.code_count;
|
||||||
|
EM(MACH_AsBx(MACH_WARYFALSE, reg, 0));
|
||||||
|
ml_patch(&s, pc_now, lbl, 0, reg);
|
||||||
|
}
|
||||||
|
else if (strcmp(op, "jump_empty") == 0) {
|
||||||
|
int reg = A1;
|
||||||
|
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
|
||||||
|
int pc_now = s.code_count;
|
||||||
|
EM(MACH_AsBx(MACH_JMPEMPTY, reg, 0));
|
||||||
|
ml_patch(&s, pc_now, lbl, 0, reg);
|
||||||
|
}
|
||||||
/* Return / error */
|
/* Return / error */
|
||||||
else if (strcmp(op, "return") == 0) {
|
else if (strcmp(op, "return") == 0) {
|
||||||
EM(MACH_ABC(MACH_RETURN, A1, 0, 0));
|
EM(MACH_ABC(MACH_RETURN, A1, 0, 0));
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ JSValue qbe_new_float64(JSContext *ctx, double d) {
|
|||||||
return __JS_NewFloat64(ctx, 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,
|
||||||
@@ -31,128 +27,89 @@ enum {
|
|||||||
QBE_CMP_GE = 5
|
QBE_CMP_GE = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ============================================================
|
/* Generic comparison helper matching MACH eq/ne/lt/le/gt/ge semantics. */
|
||||||
Float binary arithmetic
|
JSValue cell_rt_cmp(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||||
============================================================ */
|
if (JS_VALUE_IS_BOTH_INT(a, b)) {
|
||||||
|
int32_t ia = JS_VALUE_GET_INT(a);
|
||||||
static inline JSValue qbe_float_binop(JSContext *ctx, JSValue a, JSValue b,
|
int32_t ib = JS_VALUE_GET_INT(b);
|
||||||
double (*op)(double, double)) {
|
switch (op) {
|
||||||
double da, db;
|
case QBE_CMP_EQ: return JS_NewBool(ctx, ia == ib);
|
||||||
JS_ToFloat64(ctx, &da, a);
|
case QBE_CMP_NE: return JS_NewBool(ctx, ia != ib);
|
||||||
JS_ToFloat64(ctx, &db, b);
|
case QBE_CMP_LT: return JS_NewBool(ctx, ia < ib);
|
||||||
double r = op(da, db);
|
case QBE_CMP_LE: return JS_NewBool(ctx, ia <= ib);
|
||||||
if (!isfinite(r))
|
case QBE_CMP_GT: return JS_NewBool(ctx, ia > ib);
|
||||||
return JS_NULL;
|
case QBE_CMP_GE: return JS_NewBool(ctx, ia >= ib);
|
||||||
return JS_NewFloat64(ctx, r);
|
default: return JS_NewBool(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double op_add(double a, double b) { return a + b; }
|
|
||||||
static double op_sub(double a, double b) { return a - b; }
|
|
||||||
static double op_mul(double a, double b) { return a * b; }
|
|
||||||
|
|
||||||
JSValue qbe_float_add(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
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_RaiseDisrupt(ctx, "cannot add incompatible types");
|
|
||||||
return JS_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_sub(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
return qbe_float_binop(ctx, a, b, op_sub);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_mul(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
return qbe_float_binop(ctx, a, b, op_mul);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_div(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
double da, db;
|
|
||||||
JS_ToFloat64(ctx, &da, a);
|
|
||||||
JS_ToFloat64(ctx, &db, b);
|
|
||||||
if (db == 0.0)
|
|
||||||
return JS_NULL;
|
|
||||||
double r = da / db;
|
|
||||||
if (!isfinite(r))
|
|
||||||
return JS_NULL;
|
|
||||||
return JS_NewFloat64(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_mod(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
double da, db;
|
|
||||||
JS_ToFloat64(ctx, &da, a);
|
|
||||||
JS_ToFloat64(ctx, &db, b);
|
|
||||||
if (db == 0.0)
|
|
||||||
return JS_NULL;
|
|
||||||
double r = fmod(da, db);
|
|
||||||
if (!isfinite(r))
|
|
||||||
return JS_NULL;
|
|
||||||
return JS_NewFloat64(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_pow(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
double da, db;
|
|
||||||
JS_ToFloat64(ctx, &da, a);
|
|
||||||
JS_ToFloat64(ctx, &db, b);
|
|
||||||
double r = pow(da, db);
|
|
||||||
if (!isfinite(r) && isfinite(da) && isfinite(db))
|
|
||||||
return JS_NULL;
|
|
||||||
return JS_NewFloat64(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================
|
|
||||||
Float unary ops
|
|
||||||
============================================================ */
|
|
||||||
|
|
||||||
JSValue qbe_float_neg(JSContext *ctx, JSValue v) {
|
|
||||||
double d;
|
|
||||||
JS_ToFloat64(ctx, &d, v);
|
|
||||||
return JS_NewFloat64(ctx, -d);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_inc(JSContext *ctx, JSValue v) {
|
|
||||||
double d;
|
|
||||||
JS_ToFloat64(ctx, &d, v);
|
|
||||||
return JS_NewFloat64(ctx, d + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_float_dec(JSContext *ctx, JSValue v) {
|
|
||||||
double d;
|
|
||||||
JS_ToFloat64(ctx, &d, v);
|
|
||||||
return JS_NewFloat64(ctx, d - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================
|
|
||||||
Float comparison — returns 0 or 1 for QBE branching
|
|
||||||
============================================================ */
|
|
||||||
|
|
||||||
int qbe_float_cmp(JSContext *ctx, int op, JSValue a, JSValue b) {
|
|
||||||
double da, db;
|
|
||||||
JS_ToFloat64(ctx, &da, a);
|
|
||||||
JS_ToFloat64(ctx, &db, b);
|
|
||||||
switch (op) {
|
|
||||||
case QBE_CMP_EQ: return da == db;
|
|
||||||
case QBE_CMP_NE: return da != db;
|
|
||||||
case QBE_CMP_LT: return da < db;
|
|
||||||
case QBE_CMP_LE: return da <= db;
|
|
||||||
case QBE_CMP_GT: return da > db;
|
|
||||||
case QBE_CMP_GE: return da >= db;
|
|
||||||
default: return 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================
|
/* Fast path: identity after chasing forward pointers (matches MACH). */
|
||||||
Boolean conversion wrapper
|
{
|
||||||
============================================================ */
|
JSValue ca = JS_IsPtr(a) ? JS_MKPTR(chase(a)) : a;
|
||||||
|
JSValue cb = JS_IsPtr(b) ? JS_MKPTR(chase(b)) : b;
|
||||||
|
if (ca == cb) {
|
||||||
|
if (op == QBE_CMP_EQ || op == QBE_CMP_LE || op == QBE_CMP_GE)
|
||||||
|
return JS_TRUE;
|
||||||
|
if (op == QBE_CMP_NE)
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int qbe_to_bool(JSContext *ctx, JSValue v) {
|
if (JS_IsNumber(a) && JS_IsNumber(b)) {
|
||||||
return JS_ToBool(ctx, v);
|
double da, db;
|
||||||
|
JS_ToFloat64(ctx, &da, a);
|
||||||
|
JS_ToFloat64(ctx, &db, b);
|
||||||
|
switch (op) {
|
||||||
|
case QBE_CMP_EQ: return JS_NewBool(ctx, da == db);
|
||||||
|
case QBE_CMP_NE: return JS_NewBool(ctx, da != db);
|
||||||
|
case QBE_CMP_LT: return JS_NewBool(ctx, da < db);
|
||||||
|
case QBE_CMP_LE: return JS_NewBool(ctx, da <= db);
|
||||||
|
case QBE_CMP_GT: return JS_NewBool(ctx, da > db);
|
||||||
|
case QBE_CMP_GE: return JS_NewBool(ctx, da >= db);
|
||||||
|
default: return JS_NewBool(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mist_is_text(a) && mist_is_text(b)) {
|
||||||
|
int cmp = js_string_compare_value(ctx, a, b, FALSE);
|
||||||
|
switch (op) {
|
||||||
|
case QBE_CMP_EQ: return JS_NewBool(ctx, cmp == 0);
|
||||||
|
case QBE_CMP_NE: return JS_NewBool(ctx, cmp != 0);
|
||||||
|
case QBE_CMP_LT: return JS_NewBool(ctx, cmp < 0);
|
||||||
|
case QBE_CMP_LE: return JS_NewBool(ctx, cmp <= 0);
|
||||||
|
case QBE_CMP_GT: return JS_NewBool(ctx, cmp > 0);
|
||||||
|
case QBE_CMP_GE: return JS_NewBool(ctx, cmp >= 0);
|
||||||
|
default: return JS_NewBool(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JS_IsNull(a) && JS_IsNull(b)) {
|
||||||
|
if (op == QBE_CMP_EQ || op == QBE_CMP_LE || op == QBE_CMP_GE)
|
||||||
|
return JS_TRUE;
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JS_IsBool(a) && JS_IsBool(b)) {
|
||||||
|
int ba = JS_VALUE_GET_BOOL(a);
|
||||||
|
int bb = JS_VALUE_GET_BOOL(b);
|
||||||
|
switch (op) {
|
||||||
|
case QBE_CMP_EQ: return JS_NewBool(ctx, ba == bb);
|
||||||
|
case QBE_CMP_NE: return JS_NewBool(ctx, ba != bb);
|
||||||
|
case QBE_CMP_LT: return JS_NewBool(ctx, ba < bb);
|
||||||
|
case QBE_CMP_LE: return JS_NewBool(ctx, ba <= bb);
|
||||||
|
case QBE_CMP_GT: return JS_NewBool(ctx, ba > bb);
|
||||||
|
case QBE_CMP_GE: return JS_NewBool(ctx, ba >= bb);
|
||||||
|
default: return JS_NewBool(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == QBE_CMP_EQ)
|
||||||
|
return JS_NewBool(ctx, 0);
|
||||||
|
if (op == QBE_CMP_NE)
|
||||||
|
return JS_NewBool(ctx, 1);
|
||||||
|
|
||||||
|
JS_RaiseDisrupt(ctx, "cannot compare: operands must be same type");
|
||||||
|
return JS_EXCEPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -190,29 +147,33 @@ JSValue qbe_bitwise_xor(JSContext *ctx, JSValue a, JSValue b) {
|
|||||||
return JS_NewInt32(ctx, ia ^ ib);
|
return JS_NewInt32(ctx, ia ^ ib);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* Concat helper matching MACH_CONCAT semantics exactly. */
|
||||||
Shift ops on floats (convert to int32, shift, re-tag)
|
JSValue cell_rt_concat(JSContext *ctx, JSValue left, JSValue right, int self_assign) {
|
||||||
============================================================ */
|
if (self_assign) {
|
||||||
|
if (JS_IsPtr(left)) {
|
||||||
|
JSText *s = (JSText *)chase(left);
|
||||||
|
int slen = (int)s->length;
|
||||||
|
int rlen = js_string_value_len(right);
|
||||||
|
int cap = (int)objhdr_cap56(s->hdr);
|
||||||
|
if (objhdr_type(s->hdr) == OBJ_TEXT
|
||||||
|
&& !(s->hdr & OBJHDR_S_MASK)
|
||||||
|
&& slen + rlen <= cap) {
|
||||||
|
for (int i = 0; i < rlen; i++)
|
||||||
|
string_put(s, slen + i, js_string_value_get(right, i));
|
||||||
|
s->length = slen + rlen;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSValue res = JS_ConcatStringGrow(ctx, left, right);
|
||||||
|
if (JS_IsException(res))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
JSValue qbe_shift_shl(JSContext *ctx, JSValue a, JSValue b) {
|
JSValue res = JS_ConcatString(ctx, left, right);
|
||||||
int32_t ia, ib;
|
if (JS_IsException(res))
|
||||||
JS_ToInt32(ctx, &ia, a);
|
return JS_EXCEPTION;
|
||||||
JS_ToInt32(ctx, &ib, b);
|
return res;
|
||||||
return JS_NewInt32(ctx, ia << (ib & 31));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_shift_sar(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
int32_t ia, ib;
|
|
||||||
JS_ToInt32(ctx, &ia, a);
|
|
||||||
JS_ToInt32(ctx, &ib, b);
|
|
||||||
return JS_NewInt32(ctx, ia >> (ib & 31));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
int32_t ia, ib;
|
|
||||||
JS_ToInt32(ctx, &ia, a);
|
|
||||||
JS_ToInt32(ctx, &ib, b);
|
|
||||||
return JS_NewInt32(ctx, (uint32_t)ia >> (ib & 31));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -381,13 +342,6 @@ static JSValue cell_rt_load_field_key(JSContext *ctx, JSValue obj, JSValue key)
|
|||||||
return JS_GetProperty(ctx, obj, key);
|
return JS_GetProperty(ctx, obj, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
|
|
||||||
JSValue key = aot_key_from_cstr(ctx, name);
|
|
||||||
if (JS_IsException(key))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
return cell_rt_load_field_key(ctx, obj, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||||
if (JS_IsException(key))
|
if (JS_IsException(key))
|
||||||
@@ -395,29 +349,12 @@ JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
|||||||
return cell_rt_load_field_key(ctx, obj, key);
|
return cell_rt_load_field_key(ctx, obj, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Like cell_rt_load_field but without the function guard.
|
|
||||||
Used by load_dynamic when the key happens to be a static string. */
|
|
||||||
JSValue cell_rt_load_prop_str(JSContext *ctx, JSValue obj, const char *name) {
|
|
||||||
JSValue key = aot_key_from_cstr(ctx, name);
|
|
||||||
if (JS_IsException(key))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
return JS_GetProperty(ctx, obj, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cell_rt_store_field_key(JSContext *ctx, JSValue val, JSValue obj,
|
static int cell_rt_store_field_key(JSContext *ctx, JSValue val, JSValue obj,
|
||||||
JSValue key) {
|
JSValue key) {
|
||||||
int ret = JS_SetProperty(ctx, obj, key, val);
|
int ret = JS_SetProperty(ctx, obj, key, val);
|
||||||
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
|
|
||||||
const char *name) {
|
|
||||||
JSValue key = aot_key_from_cstr(ctx, name);
|
|
||||||
if (JS_IsException(key))
|
|
||||||
return 0;
|
|
||||||
return cell_rt_store_field_key(ctx, val, obj, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
int cell_rt_store_field_lit(JSContext *ctx, JSValue val, JSValue obj,
|
int cell_rt_store_field_lit(JSContext *ctx, JSValue val, JSValue obj,
|
||||||
int64_t lit_idx) {
|
int64_t lit_idx) {
|
||||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||||
@@ -455,25 +392,6 @@ int cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
|
|
||||||
if (JS_IsInt(idx))
|
|
||||||
return JS_GetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx));
|
|
||||||
return JS_GetProperty(ctx, arr, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
int cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
|
|
||||||
JSValue idx) {
|
|
||||||
int ret = 0;
|
|
||||||
JSValue nr = JS_NULL;
|
|
||||||
if (JS_IsInt(idx))
|
|
||||||
nr = JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
|
|
||||||
else
|
|
||||||
ret = JS_SetProperty(ctx, arr, idx, val);
|
|
||||||
if (JS_IsInt(idx))
|
|
||||||
return JS_IsException(nr) ? 0 : 1;
|
|
||||||
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Intrinsic/global lookup --- */
|
/* --- Intrinsic/global lookup --- */
|
||||||
|
|
||||||
void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
||||||
@@ -546,52 +464,6 @@ JSValue cell_rt_get_intrinsic_lit(JSContext *ctx, int64_t lit_idx) {
|
|||||||
return cell_rt_get_intrinsic_key(ctx, key);
|
return cell_rt_get_intrinsic_key(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Closure access ---
|
|
||||||
Walk the outer_frame chain on JSFunction (JS_FUNC_KIND_NATIVE).
|
|
||||||
The frame's function field links to the JSFunction, whose
|
|
||||||
u.native.outer_frame points to the enclosing frame.
|
|
||||||
GC traces outer_frame naturally — no registry needed. */
|
|
||||||
|
|
||||||
/* Get the outer frame's slots from a frame pointer.
|
|
||||||
The frame's function must be JS_FUNC_KIND_NATIVE. */
|
|
||||||
static JSValue *get_outer_frame_slots(JSValue *fp) {
|
|
||||||
/* fp points to frame->slots[0]; frame header is before it */
|
|
||||||
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
|
|
||||||
if (JS_IsNull(frame->function))
|
|
||||||
return NULL;
|
|
||||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
|
||||||
if (fn->kind != JS_FUNC_KIND_NATIVE)
|
|
||||||
return NULL;
|
|
||||||
JSValue outer = fn->u.cell.outer_frame;
|
|
||||||
if (JS_IsNull(outer))
|
|
||||||
return NULL;
|
|
||||||
JSFrameRegister *outer_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(outer);
|
|
||||||
return (JSValue *)outer_frame->slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_get_closure(JSContext *ctx, void *fp, int64_t depth,
|
|
||||||
int64_t slot) {
|
|
||||||
(void)ctx;
|
|
||||||
JSValue *frame = (JSValue *)fp;
|
|
||||||
for (int64_t d = 0; d < depth; d++) {
|
|
||||||
frame = get_outer_frame_slots(frame);
|
|
||||||
if (!frame)
|
|
||||||
return JS_NULL;
|
|
||||||
}
|
|
||||||
return frame[slot];
|
|
||||||
}
|
|
||||||
|
|
||||||
void cell_rt_put_closure(JSContext *ctx, void *fp, JSValue val, int64_t depth,
|
|
||||||
int64_t slot) {
|
|
||||||
(void)ctx;
|
|
||||||
JSValue *frame = (JSValue *)fp;
|
|
||||||
for (int64_t d = 0; d < depth; d++) {
|
|
||||||
frame = get_outer_frame_slots(frame);
|
|
||||||
if (!frame) return;
|
|
||||||
}
|
|
||||||
frame[slot] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- GC-managed AOT frame stack ---
|
/* --- GC-managed AOT frame stack ---
|
||||||
Each native dispatch loop pushes a GC ref so the GC can find and
|
Each native dispatch loop pushes a GC ref so the GC can find and
|
||||||
update the current frame pointer when it moves objects.
|
update the current frame pointer when it moves objects.
|
||||||
@@ -740,18 +612,6 @@ typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
|
|||||||
to call another function (instead of recursing via C stack).
|
to call another function (instead of recursing via C stack).
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
/* Poll pause state on taken backward jumps (AOT backedges).
|
|
||||||
MACH can suspend/resume a register VM frame at pc granularity; native AOT
|
|
||||||
does not currently have an equivalent resume point, so we acknowledge timer
|
|
||||||
pauses by clearing pause_flag and continuing the current turn. */
|
|
||||||
int cell_rt_check_backedge(JSContext *ctx) {
|
|
||||||
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
|
|
||||||
if (pf >= 1) {
|
|
||||||
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cell_rt_signal_call(JSContext *ctx, void *fp, int64_t frame_slot) {
|
void cell_rt_signal_call(JSContext *ctx, void *fp, int64_t frame_slot) {
|
||||||
NativeRTState *st = native_state(ctx);
|
NativeRTState *st = native_state(ctx);
|
||||||
if (!st) return;
|
if (!st) return;
|
||||||
@@ -779,6 +639,46 @@ static int cell_check_call_arity(JSContext *ctx, JSFunction *fn, int argc) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSValue cell_rt_apply(JSContext *ctx, JSValue fn_val, JSValue arg_val) {
|
||||||
|
if (!mist_is_function(fn_val))
|
||||||
|
return fn_val;
|
||||||
|
|
||||||
|
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||||
|
if (!mist_is_array(arg_val)) {
|
||||||
|
if (!cell_check_call_arity(ctx, fn, 1))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
return JS_CallInternal(ctx, fn_val, JS_NULL, 1, &arg_val, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSArray *arr = JS_VALUE_GET_ARRAY(arg_val);
|
||||||
|
int len = arr->len;
|
||||||
|
if (!cell_check_call_arity(ctx, fn, len))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
if (len == 0)
|
||||||
|
return JS_CallInternal(ctx, fn_val, JS_NULL, 0, NULL, 0);
|
||||||
|
|
||||||
|
JSValue args[len];
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
args[i] = arr->values[i];
|
||||||
|
return JS_CallInternal(ctx, fn_val, JS_NULL, len, args, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cell_copy_args_0_4(JSValue *fp, JSValue *argv, int copy) {
|
||||||
|
/* fp[0] is `this`; copy args into fp[1..4] */
|
||||||
|
switch (copy) {
|
||||||
|
case 4: fp[4] = argv[3];
|
||||||
|
case 3: fp[3] = argv[2];
|
||||||
|
case 2: fp[2] = argv[1];
|
||||||
|
case 1: fp[1] = argv[0];
|
||||||
|
case 0: break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cell_sync_dl_from_native_fn(NativeRTState *st, JSFunction *fn) {
|
||||||
|
st->current_dl_handle = JS_VALUE_GET_CODE(fn->u.cell.code)->u.native.dl_handle;
|
||||||
|
}
|
||||||
|
|
||||||
/* Entry point called from JS_CallInternal / JS_Call / MACH_INVOKE
|
/* Entry point called from JS_CallInternal / JS_Call / MACH_INVOKE
|
||||||
for JS_FUNC_KIND_NATIVE functions. */
|
for JS_FUNC_KIND_NATIVE functions. */
|
||||||
JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||||
@@ -823,8 +723,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
fp[0] = this_obj;
|
fp[0] = this_obj;
|
||||||
int copy = (argc < arity) ? argc : arity;
|
int copy = (argc < arity) ? argc : arity;
|
||||||
if (copy < 0) copy = argc; /* variadic: copy all */
|
if (copy < 0) copy = argc; /* variadic: copy all */
|
||||||
for (int i = 0; i < copy && i < nr_slots - 1; i++)
|
if (copy > nr_slots - 1)
|
||||||
fp[1 + i] = argv[i];
|
copy = nr_slots - 1;
|
||||||
|
if (unlikely(copy > 4)) {
|
||||||
|
JS_RaiseDisrupt(ctx, "native calls support at most 4 arguments");
|
||||||
|
RETURN_DISPATCH(JS_EXCEPTION);
|
||||||
|
}
|
||||||
|
if (copy > 0 && argv)
|
||||||
|
cell_copy_args_0_4(fp, argv, copy);
|
||||||
|
|
||||||
/* Link function to frame for closure access */
|
/* Link function to frame for closure access */
|
||||||
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
|
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
|
||||||
@@ -873,17 +779,29 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
|
|
||||||
if (!JS_IsFunction(callee_fn_val)) {
|
if (!JS_IsFunction(callee_fn_val)) {
|
||||||
JS_RaiseDisrupt(ctx, "not a function");
|
JS_RaiseDisrupt(ctx, "not a function");
|
||||||
|
{
|
||||||
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||||
|
int ret_slot = ret_info & 0xFFFF;
|
||||||
|
if (ret_slot != 0xFFFF)
|
||||||
|
fp[ret_slot] = JS_EXCEPTION;
|
||||||
|
}
|
||||||
/* Resume caller with exception pending */
|
/* Resume caller with exception pending */
|
||||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, exc_fn);
|
||||||
JS_PopGCRef(ctx, &callee_ref);
|
JS_PopGCRef(ctx, &callee_ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(callee_fn_val);
|
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(callee_fn_val);
|
||||||
if (!cell_check_call_arity(ctx, callee_fn, callee_argc)) {
|
if (!cell_check_call_arity(ctx, callee_fn, callee_argc)) {
|
||||||
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||||
|
int ret_slot = ret_info & 0xFFFF;
|
||||||
|
if (ret_slot != 0xFFFF)
|
||||||
|
fp[ret_slot] = JS_EXCEPTION;
|
||||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, exc_fn);
|
||||||
JS_PopGCRef(ctx, &callee_ref);
|
JS_PopGCRef(ctx, &callee_ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -911,6 +829,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
||||||
fp = (JSValue *)frame->slots;
|
fp = (JSValue *)frame->slots;
|
||||||
fn = callee_ptr;
|
fn = callee_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, callee_fn);
|
||||||
} else {
|
} else {
|
||||||
/* Regular call: link caller and push prepared callee frame. */
|
/* Regular call: link caller and push prepared callee frame. */
|
||||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||||
@@ -932,12 +851,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
fp = (JSValue *)frame->slots;
|
fp = (JSValue *)frame->slots;
|
||||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, exc_fn);
|
||||||
JS_PopGCRef(ctx, &callee_ref);
|
JS_PopGCRef(ctx, &callee_ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
|
||||||
fp = (JSValue *)frame->slots;
|
fp = (JSValue *)frame->slots;
|
||||||
fn = callee_ptr;
|
fn = callee_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, callee_fn);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Non-native callee (C function, register VM, etc.) —
|
/* Non-native callee (C function, register VM, etc.) —
|
||||||
@@ -969,6 +890,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
Just resume it — it will detect JS_EXCEPTION in the return slot. */
|
Just resume it — it will detect JS_EXCEPTION in the return slot. */
|
||||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, exc_fn);
|
||||||
JS_PopGCRef(ctx, &callee_ref);
|
JS_PopGCRef(ctx, &callee_ref);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1000,6 +922,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
/* Resume caller */
|
/* Resume caller */
|
||||||
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(caller_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(caller_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, caller_fn);
|
||||||
} else {
|
} else {
|
||||||
/* Regular call: store result and resume current function */
|
/* Regular call: store result and resume current function */
|
||||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||||
@@ -1009,6 +932,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
/* fn stays the same — we resume the same function at next segment */
|
/* fn stays the same — we resume the same function at next segment */
|
||||||
JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(cur_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(cur_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, cur_fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JS_PopGCRef(ctx, &callee_ref);
|
JS_PopGCRef(ctx, &callee_ref);
|
||||||
@@ -1042,6 +966,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
|
|
||||||
JSFunction *exc_caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *exc_caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_caller_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(exc_caller_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, exc_caller_fn);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1066,6 +991,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
|||||||
|
|
||||||
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||||
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(caller_fn))->u.native.fn_ptr;
|
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(FN_READ_CODE(caller_fn))->u.native.fn_ptr;
|
||||||
|
cell_sync_dl_from_native_fn(st, caller_fn);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1159,56 +1085,10 @@ JSValue cell_rt_frame(JSContext *ctx, JSValue fn, int64_t nargs) {
|
|||||||
return JS_MKPTR(new_frame);
|
return JS_MKPTR(new_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cell_rt_setarg(JSValue frame_val, int64_t idx, JSValue val) {
|
|
||||||
if (frame_val == JS_EXCEPTION || frame_val == JS_NULL) return;
|
|
||||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
||||||
fr->slots[idx] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cell_rt_invoke — still used for non-dispatch-loop paths (e.g. old code) */
|
|
||||||
JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
|
|
||||||
if (frame_val == JS_EXCEPTION) return JS_EXCEPTION;
|
|
||||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
|
||||||
int c_argc = JS_VALUE_GET_INT(fr->address);
|
|
||||||
if (c_argc < 0) c_argc = 0;
|
|
||||||
JSValue fn_val = fr->function;
|
|
||||||
|
|
||||||
if (!JS_IsFunction(fn_val)) {
|
|
||||||
JS_RaiseDisrupt(ctx, "not a function");
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
|
||||||
JSValue result;
|
|
||||||
if (!cell_check_call_arity(ctx, fn, c_argc))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
|
|
||||||
if (fn->kind == JS_FUNC_KIND_C) {
|
|
||||||
result = js_call_c_function(ctx, fn_val, fr->slots[0], c_argc, &fr->slots[1]);
|
|
||||||
} else if (fn->kind == JS_FUNC_KIND_NATIVE) {
|
|
||||||
result = cell_native_dispatch(ctx, fn_val, fr->slots[0], c_argc, &fr->slots[1]);
|
|
||||||
} else {
|
|
||||||
JSValue args[c_argc > 0 ? c_argc : 1];
|
|
||||||
for (int i = 0; i < c_argc; i++)
|
|
||||||
args[i] = fr->slots[i + 1];
|
|
||||||
result = JS_CallInternal(ctx, fn_val, fr->slots[0], c_argc, args, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JS_IsException(result))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
if (JS_HasException(ctx))
|
|
||||||
JS_GetException(ctx);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_goframe(JSContext *ctx, JSValue fn, int64_t nargs) {
|
JSValue cell_rt_goframe(JSContext *ctx, JSValue fn, int64_t nargs) {
|
||||||
return cell_rt_frame(ctx, fn, nargs);
|
return cell_rt_frame(ctx, fn, nargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue cell_rt_goinvoke(JSContext *ctx, JSValue frame_val) {
|
|
||||||
return cell_rt_invoke(ctx, frame_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Array push/pop --- */
|
/* --- Array push/pop --- */
|
||||||
|
|
||||||
JSValue cell_rt_push(JSContext *ctx, JSValue arr, JSValue val) {
|
JSValue cell_rt_push(JSContext *ctx, JSValue arr, JSValue val) {
|
||||||
@@ -1236,13 +1116,6 @@ static JSValue cell_rt_delete_key(JSContext *ctx, JSValue obj, JSValue key) {
|
|||||||
return JS_NewBool(ctx, ret >= 0);
|
return JS_NewBool(ctx, ret >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue cell_rt_delete_str(JSContext *ctx, JSValue obj, const char *name) {
|
|
||||||
JSValue key = aot_key_from_cstr(ctx, name);
|
|
||||||
if (JS_IsException(key))
|
|
||||||
return JS_EXCEPTION;
|
|
||||||
return cell_rt_delete_key(ctx, obj, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||||
if (JS_IsException(key))
|
if (JS_IsException(key))
|
||||||
@@ -1250,49 +1123,6 @@ JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
|||||||
return cell_rt_delete_key(ctx, obj, key);
|
return cell_rt_delete_key(ctx, obj, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Typeof --- */
|
|
||||||
|
|
||||||
JSValue cell_rt_typeof(JSContext *ctx, JSValue val) {
|
|
||||||
if (JS_IsNull(val)) return JS_NewString(ctx, "null");
|
|
||||||
if (JS_IsInt(val) || JS_IsNumber(val)) return JS_NewString(ctx, "number");
|
|
||||||
if (JS_IsBool(val)) return JS_NewString(ctx, "logical");
|
|
||||||
if (JS_IsText(val)) return JS_NewString(ctx, "text");
|
|
||||||
if (JS_IsFunction(val)) return JS_NewString(ctx, "function");
|
|
||||||
if (JS_IsArray(val)) return JS_NewString(ctx, "array");
|
|
||||||
if (JS_IsRecord(val)) return JS_NewString(ctx, "object");
|
|
||||||
return JS_NewString(ctx, "unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Text comparison stubs (called from QBE type-dispatch branches) --- */
|
|
||||||
|
|
||||||
JSValue cell_rt_lt_text(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
const char *sa = JS_ToCString(ctx, a);
|
|
||||||
const char *sb = JS_ToCString(ctx, b);
|
|
||||||
int r = (sa && sb) ? strcmp(sa, sb) < 0 : 0;
|
|
||||||
return JS_NewBool(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_gt_text(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
const char *sa = JS_ToCString(ctx, a);
|
|
||||||
const char *sb = JS_ToCString(ctx, b);
|
|
||||||
int r = (sa && sb) ? strcmp(sa, sb) > 0 : 0;
|
|
||||||
return JS_NewBool(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_le_text(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
const char *sa = JS_ToCString(ctx, a);
|
|
||||||
const char *sb = JS_ToCString(ctx, b);
|
|
||||||
int r = (sa && sb) ? strcmp(sa, sb) <= 0 : 0;
|
|
||||||
return JS_NewBool(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_ge_text(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
const char *sa = JS_ToCString(ctx, a);
|
|
||||||
const char *sb = JS_ToCString(ctx, b);
|
|
||||||
int r = (sa && sb) ? strcmp(sa, sb) >= 0 : 0;
|
|
||||||
return JS_NewBool(ctx, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cell_rt_tol_eq_inner(JSContext *ctx, JSValue a, JSValue b,
|
static int cell_rt_tol_eq_inner(JSContext *ctx, JSValue a, JSValue b,
|
||||||
JSValue tol) {
|
JSValue tol) {
|
||||||
if (JS_IsNumber(a) && JS_IsNumber(b) && JS_IsNumber(tol)) {
|
if (JS_IsNumber(a) && JS_IsNumber(b) && JS_IsNumber(tol)) {
|
||||||
@@ -1326,31 +1156,11 @@ JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b, JSValue tol) {
|
|||||||
return JS_NewBool(ctx, !cell_rt_tol_eq_inner(ctx, a, b, tol));
|
return JS_NewBool(ctx, !cell_rt_tol_eq_inner(ctx, a, b, tol));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Type check: is_proxy (function with arity 2) --- */
|
int cell_rt_is_actor(JSContext *ctx, JSValue v) {
|
||||||
|
int result = 0;
|
||||||
int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym))
|
||||||
(void)ctx;
|
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
|
||||||
if (!JS_IsFunction(v)) return 0;
|
return result;
|
||||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
|
|
||||||
return fn->length == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Identity check (chases forwarding pointers) --- */
|
|
||||||
|
|
||||||
JSValue cell_rt_is_identical(JSContext *ctx, JSValue a, JSValue b) {
|
|
||||||
if (JS_IsPtr(a)) a = JS_MKPTR(chase(a));
|
|
||||||
if (JS_IsPtr(b)) b = JS_MKPTR(chase(b));
|
|
||||||
return JS_NewBool(ctx, a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Short-circuit and/or (non-allocating) --- */
|
|
||||||
|
|
||||||
JSValue cell_rt_and(JSContext *ctx, JSValue left, JSValue right) {
|
|
||||||
return JS_ToBool(ctx, left) ? right : left;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSValue cell_rt_or(JSContext *ctx, JSValue left, JSValue right) {
|
|
||||||
return JS_ToBool(ctx, left) ? left : right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Exception checking ---
|
/* --- Exception checking ---
|
||||||
|
|||||||
104
source/runtime.c
104
source/runtime.c
@@ -149,8 +149,10 @@ int JS_IsPretext (JSValue v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
|
JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||||
if (ref == ctx->top_gc_ref)
|
if (ref == ctx->top_gc_ref) {
|
||||||
return &ref->val; /* already at top — loop re-entry; just update val */
|
fprintf(stderr, "[warn] JS_PushGCRef duplicate top ref (non-fatal)\n");
|
||||||
|
return &ref->val;
|
||||||
|
}
|
||||||
ref->prev = ctx->top_gc_ref;
|
ref->prev = ctx->top_gc_ref;
|
||||||
ctx->top_gc_ref = ref;
|
ctx->top_gc_ref = ref;
|
||||||
ref->val = JS_NULL;
|
ref->val = JS_NULL;
|
||||||
@@ -202,7 +204,10 @@ JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSValue *JS_AddGCRef (JSContext *ctx, JSGCRef *ref) {
|
JSValue *JS_AddGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||||
assert(ref != ctx->last_gc_ref && "JS_AddGCRef: same address added twice — cycle in GC ref list");
|
if (ref == ctx->last_gc_ref) {
|
||||||
|
fprintf(stderr, "[warn] JS_AddGCRef duplicate tail ref (non-fatal)\n");
|
||||||
|
return &ref->val;
|
||||||
|
}
|
||||||
ref->prev = ctx->last_gc_ref;
|
ref->prev = ctx->last_gc_ref;
|
||||||
ctx->last_gc_ref = ref;
|
ctx->last_gc_ref = ref;
|
||||||
ref->val = JS_NULL;
|
ref->val = JS_NULL;
|
||||||
@@ -9441,15 +9446,23 @@ static JSValue js_cell_fn_apply (JSContext *ctx, JSValue this_val, int argc, JSV
|
|||||||
if (argc < 1) return JS_NULL;
|
if (argc < 1) return JS_NULL;
|
||||||
if (!JS_IsFunction (argv[0])) return argv[0];
|
if (!JS_IsFunction (argv[0])) return argv[0];
|
||||||
|
|
||||||
|
JSFunction *fn = JS_VALUE_GET_FUNCTION (argv[0]);
|
||||||
|
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
|
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
|
||||||
|
|
||||||
if (!JS_IsArray (argv[1]))
|
if (!JS_IsArray (argv[1])) {
|
||||||
|
if (fn->length >= 0 && 1 > fn->length)
|
||||||
|
return JS_RaiseDisrupt (ctx, "too many arguments for apply: expected %d, got 1", fn->length);
|
||||||
return JS_CallInternal (ctx, argv[0], JS_NULL, 1, &argv[1], 0);
|
return JS_CallInternal (ctx, argv[0], JS_NULL, 1, &argv[1], 0);
|
||||||
|
}
|
||||||
|
|
||||||
JSArray *arr = JS_VALUE_GET_ARRAY (argv[1]);
|
JSArray *arr = JS_VALUE_GET_ARRAY (argv[1]);
|
||||||
int len = arr->len;
|
int len = arr->len;
|
||||||
|
|
||||||
|
if (fn->length >= 0 && len > fn->length)
|
||||||
|
return JS_RaiseDisrupt (ctx, "too many arguments for apply: expected %d, got %d", fn->length, len);
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
|
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
|
||||||
|
|
||||||
@@ -10531,6 +10544,8 @@ static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue
|
|||||||
if (!JS_IsArray (obj)) return JS_NULL;
|
if (!JS_IsArray (obj)) return JS_NULL;
|
||||||
|
|
||||||
JSArray *arr = JS_VALUE_GET_ARRAY (obj);
|
JSArray *arr = JS_VALUE_GET_ARRAY (obj);
|
||||||
|
if (objhdr_s (arr->mist_hdr))
|
||||||
|
return JS_RaiseDisrupt (ctx, "cannot pop from a stoned array");
|
||||||
|
|
||||||
if (arr->len == 0) return JS_NULL;
|
if (arr->len == 0) return JS_NULL;
|
||||||
|
|
||||||
@@ -11398,6 +11413,79 @@ static JSValue js_cell_is_letter (JSContext *ctx, JSValue this_val, int argc, JS
|
|||||||
return JS_NewBool (ctx, (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
|
return JS_NewBool (ctx, (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* is_true(val) - check if value is exactly true */
|
||||||
|
static JSValue js_cell_is_true (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
return JS_NewBool (ctx, argv[0] == JS_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_false(val) - check if value is exactly false */
|
||||||
|
static JSValue js_cell_is_false (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
return JS_NewBool (ctx, argv[0] == JS_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_fit(val) - check if value is a safe integer (int32 or float with integer value <= 2^53) */
|
||||||
|
static JSValue js_cell_is_fit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (JS_IsInt (val)) return JS_TRUE;
|
||||||
|
if (JS_IsShortFloat (val)) {
|
||||||
|
double d = JS_VALUE_GET_FLOAT64 (val);
|
||||||
|
return JS_NewBool (ctx, isfinite (d) && trunc (d) == d && fabs (d) <= 9007199254740992.0);
|
||||||
|
}
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_character(val) - check if value is a single character text */
|
||||||
|
static JSValue js_cell_is_character (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (!JS_IsText (val)) return JS_FALSE;
|
||||||
|
return JS_NewBool (ctx, js_string_value_len (val) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_digit(val) - check if value is a single digit character */
|
||||||
|
static JSValue js_cell_is_digit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (!JS_IsText (val)) return JS_FALSE;
|
||||||
|
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||||
|
uint32_t c = js_string_value_get (val, 0);
|
||||||
|
return JS_NewBool (ctx, c >= '0' && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_lower(val) - check if value is a single lowercase letter */
|
||||||
|
static JSValue js_cell_is_lower (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (!JS_IsText (val)) return JS_FALSE;
|
||||||
|
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||||
|
uint32_t c = js_string_value_get (val, 0);
|
||||||
|
return JS_NewBool (ctx, c >= 'a' && c <= 'z');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_upper(val) - check if value is a single uppercase letter */
|
||||||
|
static JSValue js_cell_is_upper (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (!JS_IsText (val)) return JS_FALSE;
|
||||||
|
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||||
|
uint32_t c = js_string_value_get (val, 0);
|
||||||
|
return JS_NewBool (ctx, c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is_whitespace(val) - check if value is a single whitespace character */
|
||||||
|
static JSValue js_cell_is_whitespace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
|
if (argc < 1) return JS_FALSE;
|
||||||
|
JSValue val = argv[0];
|
||||||
|
if (!JS_IsText (val)) return JS_FALSE;
|
||||||
|
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||||
|
uint32_t c = js_string_value_get (val, 0);
|
||||||
|
return JS_NewBool (ctx, c == ' ' || c == '\t' || c == '\n'
|
||||||
|
|| c == '\r' || c == '\f' || c == '\v');
|
||||||
|
}
|
||||||
|
|
||||||
/* is_proto(val, master) - check if val has master in prototype chain */
|
/* is_proto(val, master) - check if val has master in prototype chain */
|
||||||
static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
if (argc < 2) return JS_FALSE;
|
if (argc < 2) return JS_FALSE;
|
||||||
@@ -11565,6 +11653,14 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
|||||||
js_set_global_cfunc(ctx, "is_text", js_cell_is_text, 1);
|
js_set_global_cfunc(ctx, "is_text", js_cell_is_text, 1);
|
||||||
js_set_global_cfunc(ctx, "is_proto", js_cell_is_proto, 2);
|
js_set_global_cfunc(ctx, "is_proto", js_cell_is_proto, 2);
|
||||||
js_set_global_cfunc(ctx, "is_letter", js_cell_is_letter, 1);
|
js_set_global_cfunc(ctx, "is_letter", js_cell_is_letter, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_true", js_cell_is_true, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_false", js_cell_is_false, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_fit", js_cell_is_fit, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_character", js_cell_is_character, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_digit", js_cell_is_digit, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_lower", js_cell_is_lower, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_upper", js_cell_is_upper, 1);
|
||||||
|
js_set_global_cfunc(ctx, "is_whitespace", js_cell_is_whitespace, 1);
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
js_set_global_cfunc(ctx, "apply", js_cell_fn_apply, 2);
|
js_set_global_cfunc(ctx, "apply", js_cell_fn_apply, 2);
|
||||||
|
|||||||
648
streamline.cm
648
streamline.cm
@@ -46,13 +46,17 @@ var streamline = function(ir, log) {
|
|||||||
not: true, and: true, or: true,
|
not: true, and: true, or: true,
|
||||||
is_int: true, is_text: true, is_num: true,
|
is_int: true, is_text: true, is_num: true,
|
||||||
is_bool: true, is_null: true, is_identical: true,
|
is_bool: true, is_null: true, is_identical: true,
|
||||||
is_array: true, is_func: true, is_record: true, is_stone: true
|
is_array: true, is_func: true, is_record: true, is_stone: true,
|
||||||
|
is_blob: true, is_data: true,
|
||||||
|
is_true: true, is_false: true, is_fit: true,
|
||||||
|
is_char: true, is_digit: true, is_letter: true,
|
||||||
|
is_lower: true, is_upper: true, is_ws: true, is_actor: true
|
||||||
}
|
}
|
||||||
var type_check_map = {
|
var type_check_map = {
|
||||||
is_int: T_INT, is_text: T_TEXT, is_num: T_NUM,
|
is_int: T_INT, is_text: T_TEXT, is_num: T_NUM,
|
||||||
is_bool: T_BOOL, is_null: T_NULL,
|
is_bool: T_BOOL, is_null: T_NULL,
|
||||||
is_array: T_ARRAY, is_func: T_FUNCTION,
|
is_array: T_ARRAY, is_func: T_FUNCTION,
|
||||||
is_record: T_RECORD
|
is_record: T_RECORD, is_blob: T_BLOB
|
||||||
}
|
}
|
||||||
|
|
||||||
// simplify_algebra dispatch tables
|
// simplify_algebra dispatch tables
|
||||||
@@ -65,12 +69,19 @@ var streamline = function(ir, log) {
|
|||||||
var no_clear_ops = {
|
var no_clear_ops = {
|
||||||
int: true, access: true, true: true, false: true, move: true, null: true,
|
int: true, access: true, true: true, false: true, move: true, null: true,
|
||||||
jump: true, jump_true: true, jump_false: true, jump_not_null: true,
|
jump: true, jump_true: true, jump_false: true, jump_not_null: true,
|
||||||
|
wary_true: true, wary_false: true, jump_null: true, jump_empty: true,
|
||||||
return: true, disrupt: true,
|
return: true, disrupt: true,
|
||||||
store_field: true, store_index: true, store_dynamic: true,
|
store_field: true, store_index: true, store_dynamic: true,
|
||||||
push: true, setarg: true, invoke: true, tail_invoke: true,
|
push: true, setarg: true, invoke: true, tail_invoke: true,
|
||||||
stone_text: true
|
stone_text: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var is_cond_jump = function(op) {
|
||||||
|
return op == "jump_true" || op == "jump_false" || op == "jump_not_null"
|
||||||
|
|| op == "wary_true" || op == "wary_false"
|
||||||
|
|| op == "jump_null" || op == "jump_empty"
|
||||||
|
}
|
||||||
|
|
||||||
// --- Logging support ---
|
// --- Logging support ---
|
||||||
|
|
||||||
var ir_stats = null
|
var ir_stats = null
|
||||||
@@ -148,6 +159,21 @@ var streamline = function(ir, log) {
|
|||||||
slot_types[instr[1]] = src_type != null ? src_type : T_UNKNOWN
|
slot_types[instr[1]] = src_type != null ? src_type : T_UNKNOWN
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
if (op == "load_index") {
|
||||||
|
slot_types[instr[2]] = T_ARRAY
|
||||||
|
slot_types[instr[3]] = T_INT
|
||||||
|
} else if (op == "store_index") {
|
||||||
|
slot_types[instr[1]] = T_ARRAY
|
||||||
|
slot_types[instr[3]] = T_INT
|
||||||
|
} else if (op == "load_field") {
|
||||||
|
slot_types[instr[2]] = T_RECORD
|
||||||
|
} else if (op == "store_field") {
|
||||||
|
slot_types[instr[1]] = T_RECORD
|
||||||
|
} else if (op == "push") {
|
||||||
|
slot_types[instr[1]] = T_ARRAY
|
||||||
|
} else if (op == "pop") {
|
||||||
|
slot_types[instr[2]] = T_ARRAY
|
||||||
|
}
|
||||||
rule = write_rules[op]
|
rule = write_rules[op]
|
||||||
if (rule != null) {
|
if (rule != null) {
|
||||||
typ = rule[1]
|
typ = rule[1]
|
||||||
@@ -359,7 +385,12 @@ var streamline = function(ir, log) {
|
|||||||
is_int: [1, T_BOOL], is_text: [1, T_BOOL], is_num: [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_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_array: [1, T_BOOL], is_func: [1, T_BOOL],
|
||||||
is_record: [1, T_BOOL], is_stone: [1, T_BOOL]
|
is_record: [1, T_BOOL], is_stone: [1, T_BOOL],
|
||||||
|
is_blob: [1, T_BOOL], is_data: [1, T_BOOL],
|
||||||
|
is_true: [1, T_BOOL], is_false: [1, T_BOOL], is_fit: [1, T_BOOL],
|
||||||
|
is_char: [1, T_BOOL], is_digit: [1, T_BOOL], is_letter: [1, T_BOOL],
|
||||||
|
is_lower: [1, T_BOOL], is_upper: [1, T_BOOL], is_ws: [1, T_BOOL],
|
||||||
|
is_actor: [1, T_BOOL]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Known intrinsic return types for invoke result inference.
|
// Known intrinsic return types for invoke result inference.
|
||||||
@@ -367,7 +398,12 @@ var streamline = function(ir, log) {
|
|||||||
abs: T_NUM, floor: T_NUM, ceiling: T_NUM,
|
abs: T_NUM, floor: T_NUM, ceiling: T_NUM,
|
||||||
round: T_NUM, trunc: T_NUM, fraction: T_NUM,
|
round: T_NUM, trunc: T_NUM, fraction: T_NUM,
|
||||||
integer: T_NUM, whole: T_NUM, sign: T_NUM,
|
integer: T_NUM, whole: T_NUM, sign: T_NUM,
|
||||||
max: T_NUM, min: T_NUM, remainder: T_NUM, modulo: T_NUM
|
max: T_NUM, min: T_NUM, remainder: T_NUM, modulo: T_NUM,
|
||||||
|
is_integer: T_BOOL, is_text: T_BOOL, is_number: T_BOOL,
|
||||||
|
is_null: T_BOOL, is_array: T_BOOL, is_function: T_BOOL,
|
||||||
|
is_object: T_BOOL, is_logical: T_BOOL, is_stone: T_BOOL,
|
||||||
|
is_blob: T_BOOL, starts_with: T_BOOL, ends_with: T_BOOL,
|
||||||
|
some: T_BOOL, every: T_BOOL
|
||||||
}
|
}
|
||||||
|
|
||||||
var narrow_arith_type = function(write_types, param_types, instr, typ) {
|
var narrow_arith_type = function(write_types, param_types, instr, typ) {
|
||||||
@@ -667,7 +703,49 @@ var streamline = function(ir, log) {
|
|||||||
if (is_array(next)) {
|
if (is_array(next)) {
|
||||||
next_op = next[0]
|
next_op = next[0]
|
||||||
|
|
||||||
if (next_op == "jump_false" && next[1] == dest) {
|
// is_null + jump fusion: replace with jump_null / jump_not_null
|
||||||
|
if (op == "is_null" && (next_op == "jump_true" || next_op == "wary_true") && next[1] == dest) {
|
||||||
|
jlen = length(next)
|
||||||
|
nc = nc + 1
|
||||||
|
instructions[i] = "_nop_tc_" + text(nc)
|
||||||
|
instructions[i + 1] = ["jump_null", src, next[2], next[jlen - 2], next[jlen - 1]]
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite",
|
||||||
|
pass: "eliminate_type_checks",
|
||||||
|
rule: "is_null_jump_fusion",
|
||||||
|
at: i,
|
||||||
|
before: [instr, next],
|
||||||
|
after: [instructions[i], instructions[i + 1]],
|
||||||
|
why: {slot: src, fused_to: "jump_null"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slot_types[dest] = T_BOOL
|
||||||
|
i = i + 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (op == "is_null" && (next_op == "jump_false" || next_op == "wary_false") && next[1] == dest) {
|
||||||
|
jlen = length(next)
|
||||||
|
nc = nc + 1
|
||||||
|
instructions[i] = "_nop_tc_" + text(nc)
|
||||||
|
instructions[i + 1] = ["jump_not_null", src, next[2], next[jlen - 2], next[jlen - 1]]
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite",
|
||||||
|
pass: "eliminate_type_checks",
|
||||||
|
rule: "is_null_jump_fusion",
|
||||||
|
at: i,
|
||||||
|
before: [instr, next],
|
||||||
|
after: [instructions[i], instructions[i + 1]],
|
||||||
|
why: {slot: src, fused_to: "jump_not_null"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slot_types[dest] = T_BOOL
|
||||||
|
i = i + 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((next_op == "jump_false" || next_op == "wary_false") && next[1] == dest) {
|
||||||
target_label = next[2]
|
target_label = next[2]
|
||||||
if (slot_is(slot_types, src, checked_type)) {
|
if (slot_is(slot_types, src, checked_type)) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
@@ -743,7 +821,7 @@ var streamline = function(ir, log) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_op == "jump_true" && next[1] == dest) {
|
if ((next_op == "jump_true" || next_op == "wary_true") && next[1] == dest) {
|
||||||
target_label = next[2]
|
target_label = next[2]
|
||||||
if (slot_is(slot_types, src, checked_type)) {
|
if (slot_is(slot_types, src, checked_type)) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
@@ -826,26 +904,32 @@ var streamline = function(ir, log) {
|
|||||||
// Dynamic access reduction
|
// Dynamic access reduction
|
||||||
if (op == "load_dynamic") {
|
if (op == "load_dynamic") {
|
||||||
old_op = op
|
old_op = op
|
||||||
if (slot_is(slot_types, instr[3], T_TEXT)) {
|
if (slot_is(slot_types, instr[2], T_RECORD) && slot_is(slot_types, instr[3], T_TEXT)) {
|
||||||
instr[0] = "load_field"
|
instr[0] = "load_field"
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite",
|
event: "rewrite",
|
||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_field",
|
rule: "dynamic_record_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[instr[3]]}
|
why: {
|
||||||
|
object_slot: instr[2], object_type: slot_types[instr[2]],
|
||||||
|
key_slot: instr[3], key_type: slot_types[instr[3]]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
} else if (slot_is(slot_types, instr[2], T_ARRAY) && slot_is(slot_types, instr[3], T_INT)) {
|
||||||
instr[0] = "load_index"
|
instr[0] = "load_index"
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite",
|
event: "rewrite",
|
||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_index",
|
rule: "dynamic_array_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[instr[3]]}
|
why: {
|
||||||
|
object_slot: instr[2], object_type: slot_types[instr[2]],
|
||||||
|
key_slot: instr[3], key_type: slot_types[instr[3]]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -855,26 +939,32 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
if (op == "store_dynamic") {
|
if (op == "store_dynamic") {
|
||||||
old_op = op
|
old_op = op
|
||||||
if (slot_is(slot_types, instr[3], T_TEXT)) {
|
if (slot_is(slot_types, instr[1], T_RECORD) && slot_is(slot_types, instr[3], T_TEXT)) {
|
||||||
instr[0] = "store_field"
|
instr[0] = "store_field"
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite",
|
event: "rewrite",
|
||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_field",
|
rule: "dynamic_record_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[instr[3]]}
|
why: {
|
||||||
|
object_slot: instr[1], object_type: slot_types[instr[1]],
|
||||||
|
key_slot: instr[3], key_type: slot_types[instr[3]]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (slot_is(slot_types, instr[3], T_INT)) {
|
} else if (slot_is(slot_types, instr[1], T_ARRAY) && slot_is(slot_types, instr[3], T_INT)) {
|
||||||
instr[0] = "store_index"
|
instr[0] = "store_index"
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite",
|
event: "rewrite",
|
||||||
pass: "eliminate_type_checks",
|
pass: "eliminate_type_checks",
|
||||||
rule: "dynamic_to_index",
|
rule: "dynamic_array_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[instr[3]]}
|
why: {
|
||||||
|
object_slot: instr[1], object_type: slot_types[instr[1]],
|
||||||
|
key_slot: instr[3], key_type: slot_types[instr[3]]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -882,6 +972,32 @@ var streamline = function(ir, log) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wary-to-certain: if slot type is T_BOOL, downgrade wary to certain jump
|
||||||
|
if (op == "wary_true" && slot_is(slot_types, instr[1], T_BOOL)) {
|
||||||
|
instr[0] = "jump_true"
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite",
|
||||||
|
pass: "eliminate_type_checks",
|
||||||
|
rule: "wary_to_certain",
|
||||||
|
at: i, before: "wary_true", after: "jump_true",
|
||||||
|
why: {slot: instr[1], known_type: T_BOOL}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op == "wary_false" && slot_is(slot_types, instr[1], T_BOOL)) {
|
||||||
|
instr[0] = "jump_false"
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite",
|
||||||
|
pass: "eliminate_type_checks",
|
||||||
|
rule: "wary_to_certain",
|
||||||
|
at: i, before: "wary_false", after: "jump_false",
|
||||||
|
why: {slot: instr[1], known_type: T_BOOL}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
track_types(slot_types, instr)
|
track_types(slot_types, instr)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
@@ -1039,11 +1155,12 @@ var streamline = function(ir, log) {
|
|||||||
next_op = next[0]
|
next_op = next[0]
|
||||||
nlen = length(next)
|
nlen = length(next)
|
||||||
|
|
||||||
// not d, x; jump_false d, label → jump_true x, label
|
// not d, x; jump_false d, label → wary_true x, label
|
||||||
|
// (removing `not` removes coercion, so result must be wary)
|
||||||
if (next_op == "jump_false" && next[1] == instr[1]) {
|
if (next_op == "jump_false" && next[1] == instr[1]) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
instructions[i] = "_nop_bl_" + text(nc)
|
instructions[i] = "_nop_bl_" + text(nc)
|
||||||
instructions[i + 1] = ["jump_true", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
instructions[i + 1] = ["wary_true", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite", pass: "simplify_booleans",
|
event: "rewrite", pass: "simplify_booleans",
|
||||||
@@ -1056,11 +1173,11 @@ var streamline = function(ir, log) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// not d, x; jump_true d, label → jump_false x, label
|
// not d, x; jump_true d, label → wary_false x, label
|
||||||
if (next_op == "jump_true" && next[1] == instr[1]) {
|
if (next_op == "jump_true" && next[1] == instr[1]) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
instructions[i] = "_nop_bl_" + text(nc)
|
instructions[i] = "_nop_bl_" + text(nc)
|
||||||
instructions[i + 1] = ["jump_false", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
instructions[i + 1] = ["wary_false", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
||||||
if (events != null) {
|
if (events != null) {
|
||||||
events[] = {
|
events[] = {
|
||||||
event: "rewrite", pass: "simplify_booleans",
|
event: "rewrite", pass: "simplify_booleans",
|
||||||
@@ -1073,6 +1190,40 @@ var streamline = function(ir, log) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// not d, x; wary_false d, label → wary_true x, label
|
||||||
|
if (next_op == "wary_false" && next[1] == instr[1]) {
|
||||||
|
nc = nc + 1
|
||||||
|
instructions[i] = "_nop_bl_" + text(nc)
|
||||||
|
instructions[i + 1] = ["wary_true", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite", pass: "simplify_booleans",
|
||||||
|
rule: "not_wary_false_fusion", at: i,
|
||||||
|
before: [instr, next],
|
||||||
|
after: [instructions[i], instructions[i + 1]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// not d, x; wary_true d, label → wary_false x, label
|
||||||
|
if (next_op == "wary_true" && next[1] == instr[1]) {
|
||||||
|
nc = nc + 1
|
||||||
|
instructions[i] = "_nop_bl_" + text(nc)
|
||||||
|
instructions[i + 1] = ["wary_false", instr[2], next[2], next[nlen - 2], next[nlen - 1]]
|
||||||
|
if (events != null) {
|
||||||
|
events[] = {
|
||||||
|
event: "rewrite", pass: "simplify_booleans",
|
||||||
|
rule: "not_wary_true_fusion", at: i,
|
||||||
|
before: [instr, next],
|
||||||
|
after: [instructions[i], instructions[i + 1]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// not d1, x; not d2, d1 → move d2, x
|
// not d1, x; not d2, d1 → move d2, x
|
||||||
if (next_op == "not" && next[2] == instr[1]) {
|
if (next_op == "not" && next[2] == instr[1]) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
@@ -1160,7 +1311,7 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Control flow with a read at position 1: substitute then clear
|
// Control flow with a read at position 1: substitute then clear
|
||||||
if (op == "return" || op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
|
if (op == "return" || is_cond_jump(op)) {
|
||||||
actual = copies[text(instr[1])]
|
actual = copies[text(instr[1])]
|
||||||
if (actual != null) {
|
if (actual != null) {
|
||||||
instr[1] = actual
|
instr[1] = actual
|
||||||
@@ -1342,7 +1493,7 @@ var streamline = function(ir, log) {
|
|||||||
op = instr[0]
|
op = instr[0]
|
||||||
if (op == "jump") {
|
if (op == "jump") {
|
||||||
target = instr[1]
|
target = instr[1]
|
||||||
} else if (op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
|
} else if (is_cond_jump(op)) {
|
||||||
target = instr[2]
|
target = instr[2]
|
||||||
}
|
}
|
||||||
if (target != null && is_text(target)) {
|
if (target != null && is_text(target)) {
|
||||||
@@ -1549,7 +1700,7 @@ var streamline = function(ir, log) {
|
|||||||
if (is_number(tgt)) stack[] = tgt
|
if (is_number(tgt)) stack[] = tgt
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
|
if (is_cond_jump(op)) {
|
||||||
tgt = label_map[instr[2]]
|
tgt = label_map[instr[2]]
|
||||||
if (is_number(tgt)) stack[] = tgt
|
if (is_number(tgt)) stack[] = tgt
|
||||||
stack[] = idx + 1
|
stack[] = idx + 1
|
||||||
@@ -1654,6 +1805,7 @@ var streamline = function(ir, log) {
|
|||||||
frame: [1, 2], goframe: [1, 2],
|
frame: [1, 2], goframe: [1, 2],
|
||||||
jump: [], disrupt: [],
|
jump: [], disrupt: [],
|
||||||
jump_true: [1], jump_false: [1], jump_not_null: [1],
|
jump_true: [1], jump_false: [1], jump_not_null: [1],
|
||||||
|
wary_true: [1], wary_false: [1], jump_null: [1], jump_empty: [1],
|
||||||
return: [1],
|
return: [1],
|
||||||
stone_text: [1]
|
stone_text: [1]
|
||||||
}
|
}
|
||||||
@@ -1684,6 +1836,7 @@ var streamline = function(ir, log) {
|
|||||||
setarg: [], store_field: [], store_index: [], store_dynamic: [],
|
setarg: [], store_field: [], store_index: [], store_dynamic: [],
|
||||||
push: [], set_var: [], stone_text: [],
|
push: [], set_var: [], stone_text: [],
|
||||||
jump: [], jump_true: [], jump_false: [], jump_not_null: [],
|
jump: [], jump_true: [], jump_false: [], jump_not_null: [],
|
||||||
|
wary_true: [], wary_false: [], jump_null: [], jump_empty: [],
|
||||||
return: [], disrupt: []
|
return: [], disrupt: []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1697,6 +1850,7 @@ var streamline = function(ir, log) {
|
|||||||
store_dynamic: [1, 2, 3],
|
store_dynamic: [1, 2, 3],
|
||||||
push: [1, 2], set_var: [1], stone_text: [1],
|
push: [1, 2], set_var: [1], stone_text: [1],
|
||||||
jump: [], jump_true: [1], jump_false: [1], jump_not_null: [1],
|
jump: [], jump_true: [1], jump_false: [1], jump_not_null: [1],
|
||||||
|
wary_true: [1], wary_false: [1], jump_null: [1], jump_empty: [1],
|
||||||
return: [1], disrupt: []
|
return: [1], disrupt: []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1834,7 +1988,7 @@ var streamline = function(ir, log) {
|
|||||||
target = null
|
target = null
|
||||||
if (op == "jump") {
|
if (op == "jump") {
|
||||||
target = instr[1]
|
target = instr[1]
|
||||||
} else if (op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
|
} else if (is_cond_jump(op)) {
|
||||||
target = instr[2]
|
target = instr[2]
|
||||||
}
|
}
|
||||||
if (target == null || !is_text(target)) {
|
if (target == null || !is_text(target)) {
|
||||||
@@ -2555,6 +2709,390 @@ var streamline = function(ir, log) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Sensory function IR synthesis for callback inlining
|
||||||
|
// =========================================================
|
||||||
|
var sensory_opcodes = {
|
||||||
|
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",
|
||||||
|
is_blob: "is_blob", is_data: "is_data",
|
||||||
|
is_true: "is_true", is_false: "is_false", is_fit: "is_fit",
|
||||||
|
is_character: "is_char", is_digit: "is_digit", is_letter: "is_letter",
|
||||||
|
is_lower: "is_lower", is_upper: "is_upper", is_whitespace: "is_ws",
|
||||||
|
is_actor: "is_actor", length: "length"
|
||||||
|
}
|
||||||
|
|
||||||
|
var make_sensory_ir = function(name) {
|
||||||
|
var opcode = sensory_opcodes[name]
|
||||||
|
if (opcode == null) return null
|
||||||
|
return {
|
||||||
|
name: name, nr_args: 1, nr_close_slots: 0, nr_slots: 3,
|
||||||
|
instructions: [[opcode, 2, 1, 0, 0], ["return", 2, 0, 0]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Inline eligibility check
|
||||||
|
// =========================================================
|
||||||
|
var prefer_inline_set = {
|
||||||
|
filter: true, every: true, some: true, arrfor: true,
|
||||||
|
reduce: true, array: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structural eligibility: closures, get/put, disruption, nested functions
|
||||||
|
var can_inline_structural = function(callee_func) {
|
||||||
|
var instrs = null
|
||||||
|
var i = 0
|
||||||
|
var instr = null
|
||||||
|
if (callee_func.nr_close_slots > 0) return false
|
||||||
|
instrs = callee_func.instructions
|
||||||
|
if (instrs == null) return false
|
||||||
|
i = 0
|
||||||
|
while (i < length(instrs)) {
|
||||||
|
instr = instrs[i]
|
||||||
|
if (is_array(instr)) {
|
||||||
|
if (instr[0] == "get" || instr[0] == "put") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Reject if function creates child functions (closures may capture
|
||||||
|
// from the inlined frame, breaking get/put slot references)
|
||||||
|
if (instr[0] == "function") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
if (callee_func.disruption_pc != null && callee_func.disruption_pc > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size eligibility: instruction count check
|
||||||
|
var can_inline_size = function(callee_func, is_prefer) {
|
||||||
|
var instrs = callee_func.instructions
|
||||||
|
var count = 0
|
||||||
|
var i = 0
|
||||||
|
var limit = 0
|
||||||
|
if (instrs == null) return false
|
||||||
|
i = 0
|
||||||
|
while (i < length(instrs)) {
|
||||||
|
if (is_array(instrs[i])) count = count + 1
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
limit = is_prefer ? 200 : 40
|
||||||
|
return count <= limit
|
||||||
|
}
|
||||||
|
|
||||||
|
var can_inline = function(callee_func, is_prefer) {
|
||||||
|
if (!can_inline_structural(callee_func)) return false
|
||||||
|
return can_inline_size(callee_func, is_prefer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Pass: inline_calls — inline same-module + sensory functions
|
||||||
|
// =========================================================
|
||||||
|
var inline_counter = 0
|
||||||
|
|
||||||
|
var inline_calls = function(func, ir, log) {
|
||||||
|
var instructions = func.instructions
|
||||||
|
var num_instr = 0
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var k = 0
|
||||||
|
var instr = null
|
||||||
|
var op = null
|
||||||
|
var changed = false
|
||||||
|
var inline_count = 0
|
||||||
|
var max_inlines = 20
|
||||||
|
var slot_to_func_idx = {}
|
||||||
|
var slot_to_intrinsic = {}
|
||||||
|
var callee_slot = 0
|
||||||
|
var frame_slot = 0
|
||||||
|
var argc = 0
|
||||||
|
var result_slot = 0
|
||||||
|
var call_start = 0
|
||||||
|
var call_end = 0
|
||||||
|
var arg_slots = null
|
||||||
|
var callee_func = null
|
||||||
|
var is_prefer = false
|
||||||
|
var base = 0
|
||||||
|
var remap = null
|
||||||
|
var cinstr = null
|
||||||
|
var cop = null
|
||||||
|
var new_instr = null
|
||||||
|
var refs = null
|
||||||
|
var label_prefix = null
|
||||||
|
var cont_label = null
|
||||||
|
var spliced = null
|
||||||
|
var before = null
|
||||||
|
var after = null
|
||||||
|
var inlined_body = null
|
||||||
|
var fi = null
|
||||||
|
var intrinsic_name = null
|
||||||
|
var is_single_use = false
|
||||||
|
var ref_count = 0
|
||||||
|
var ri = 0
|
||||||
|
|
||||||
|
if (instructions == null) return false
|
||||||
|
num_instr = length(instructions)
|
||||||
|
if (num_instr == 0) return false
|
||||||
|
|
||||||
|
// Build resolution maps
|
||||||
|
i = 0
|
||||||
|
while (i < num_instr) {
|
||||||
|
instr = instructions[i]
|
||||||
|
if (is_array(instr)) {
|
||||||
|
op = instr[0]
|
||||||
|
if (op == "function") {
|
||||||
|
slot_to_func_idx[text(instr[1])] = instr[2]
|
||||||
|
} else if (op == "access" && is_object(instr[2]) && instr[2].make == "intrinsic") {
|
||||||
|
slot_to_intrinsic[text(instr[1])] = instr[2].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for frame/setarg/invoke sequences and inline
|
||||||
|
i = 0
|
||||||
|
while (i < length(instructions)) {
|
||||||
|
instr = instructions[i]
|
||||||
|
if (!is_array(instr) || instr[0] != "frame") {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (inline_count >= max_inlines) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_slot = instr[1]
|
||||||
|
callee_slot = instr[2]
|
||||||
|
argc = instr[3]
|
||||||
|
call_start = i
|
||||||
|
|
||||||
|
// Collect setarg and find invoke
|
||||||
|
arg_slots = array(argc + 1, -1)
|
||||||
|
j = i + 1
|
||||||
|
call_end = -1
|
||||||
|
while (j < length(instructions)) {
|
||||||
|
instr = instructions[j]
|
||||||
|
if (!is_array(instr)) {
|
||||||
|
j = j + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
op = instr[0]
|
||||||
|
if (op == "setarg" && instr[1] == frame_slot) {
|
||||||
|
arg_slots[instr[2]] = instr[3]
|
||||||
|
} else if ((op == "invoke" || op == "tail_invoke") && instr[1] == frame_slot) {
|
||||||
|
result_slot = instr[2]
|
||||||
|
call_end = j
|
||||||
|
j = j + 1
|
||||||
|
break
|
||||||
|
} else if (op == "frame" || op == "goframe") {
|
||||||
|
// Another frame before invoke — abort this pattern
|
||||||
|
break
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (call_end < 0) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve callee
|
||||||
|
callee_func = null
|
||||||
|
is_prefer = false
|
||||||
|
|
||||||
|
fi = slot_to_func_idx[text(callee_slot)]
|
||||||
|
if (fi != null && ir.functions != null && fi >= 0 && fi < length(ir.functions)) {
|
||||||
|
callee_func = ir.functions[fi]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callee_func == null) {
|
||||||
|
intrinsic_name = slot_to_intrinsic[text(callee_slot)]
|
||||||
|
if (intrinsic_name != null) {
|
||||||
|
if (sensory_opcodes[intrinsic_name] != null) {
|
||||||
|
callee_func = make_sensory_ir(intrinsic_name)
|
||||||
|
}
|
||||||
|
if (callee_func != null) {
|
||||||
|
is_prefer = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callee_func == null) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if callee is a single-use function literal — skip size limit
|
||||||
|
is_single_use = false
|
||||||
|
if (fi != null) {
|
||||||
|
ref_count = 0
|
||||||
|
ri = 0
|
||||||
|
while (ri < length(instructions)) {
|
||||||
|
if (is_array(instructions[ri])) {
|
||||||
|
// Count frame instructions that use this slot as callee (position 2)
|
||||||
|
if (instructions[ri][0] == "frame" && instructions[ri][2] == callee_slot) {
|
||||||
|
ref_count = ref_count + 1
|
||||||
|
}
|
||||||
|
// Also count setarg where slot is passed as value (position 3)
|
||||||
|
if (instructions[ri][0] == "setarg" && instructions[ri][3] == callee_slot) {
|
||||||
|
ref_count = ref_count + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ri = ri + 1
|
||||||
|
}
|
||||||
|
if (ref_count <= 1) is_single_use = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check eligibility
|
||||||
|
if (!can_inline_structural(callee_func)) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!is_single_use && !can_inline_size(callee_func, is_prefer)) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slot remapping
|
||||||
|
base = func.nr_slots
|
||||||
|
func.nr_slots = func.nr_slots + callee_func.nr_slots
|
||||||
|
remap = array(callee_func.nr_slots, -1)
|
||||||
|
|
||||||
|
// Slot 0 (this) → arg_slots[0] if provided, else allocate a null slot
|
||||||
|
if (length(arg_slots) > 0 && arg_slots[0] >= 0) {
|
||||||
|
remap[0] = arg_slots[0]
|
||||||
|
} else {
|
||||||
|
remap[0] = base
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params 1..nr_args → corresponding arg_slots
|
||||||
|
j = 1
|
||||||
|
while (j <= callee_func.nr_args) {
|
||||||
|
if (j < length(arg_slots) && arg_slots[j] >= 0) {
|
||||||
|
remap[j] = arg_slots[j]
|
||||||
|
} else {
|
||||||
|
remap[j] = base + j
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporaries → fresh slots
|
||||||
|
j = callee_func.nr_args + 1
|
||||||
|
while (j < callee_func.nr_slots) {
|
||||||
|
remap[j] = base + j
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate unique label prefix
|
||||||
|
inline_counter = inline_counter + 1
|
||||||
|
label_prefix = "_inl" + text(inline_counter) + "_"
|
||||||
|
cont_label = label_prefix + "cont"
|
||||||
|
|
||||||
|
// Build inlined body with remapping
|
||||||
|
// Unmapped params (e.g. caller passes 1 arg to a 2-param function)
|
||||||
|
// must be explicitly nulled. compress_slots may merge the fresh
|
||||||
|
// slot (base+j) with a previously-live slot that holds a non-null
|
||||||
|
// value, so the default-param jump_not_null preamble would skip
|
||||||
|
// the default assignment and use a stale value instead.
|
||||||
|
inlined_body = []
|
||||||
|
j = 0
|
||||||
|
while (j <= callee_func.nr_args) {
|
||||||
|
if (!(j < length(arg_slots) && arg_slots[j] >= 0)) {
|
||||||
|
inlined_body[] = ["null", remap[j], 0, 0]
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
k = 0
|
||||||
|
while (k < length(callee_func.instructions)) {
|
||||||
|
cinstr = callee_func.instructions[k]
|
||||||
|
|
||||||
|
// Labels (strings that aren't nop markers)
|
||||||
|
if (is_text(cinstr)) {
|
||||||
|
if (starts_with(cinstr, "_nop_")) {
|
||||||
|
inlined_body[] = cinstr
|
||||||
|
} else {
|
||||||
|
inlined_body[] = label_prefix + cinstr
|
||||||
|
}
|
||||||
|
k = k + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array(cinstr)) {
|
||||||
|
inlined_body[] = cinstr
|
||||||
|
k = k + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cop = cinstr[0]
|
||||||
|
|
||||||
|
// Handle return → move + jump to continuation
|
||||||
|
if (cop == "return") {
|
||||||
|
new_instr = ["move", result_slot, remap[cinstr[1]], cinstr[2], cinstr[3]]
|
||||||
|
inlined_body[] = new_instr
|
||||||
|
inlined_body[] = ["jump", cont_label, cinstr[2], cinstr[3]]
|
||||||
|
k = k + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone and remap the instruction
|
||||||
|
new_instr = array(cinstr)
|
||||||
|
refs = get_slot_refs(cinstr)
|
||||||
|
j = 0
|
||||||
|
while (j < length(refs)) {
|
||||||
|
if (new_instr[refs[j]] >= 0 && new_instr[refs[j]] < length(remap)) {
|
||||||
|
new_instr[refs[j]] = remap[new_instr[refs[j]]]
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remap labels in jump instructions
|
||||||
|
if (cop == "jump" && is_text(cinstr[1]) && !starts_with(cinstr[1], "_nop_")) {
|
||||||
|
new_instr[1] = label_prefix + cinstr[1]
|
||||||
|
} else if (is_cond_jump(cop)
|
||||||
|
&& is_text(cinstr[2]) && !starts_with(cinstr[2], "_nop_")) {
|
||||||
|
new_instr[2] = label_prefix + cinstr[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip function instructions (don't inline nested function definitions)
|
||||||
|
if (cop == "function") {
|
||||||
|
// Keep the instruction but don't remap func_id — it still refers to ir.functions
|
||||||
|
// Only remap slot position 1 (the destination slot)
|
||||||
|
new_instr = array(cinstr)
|
||||||
|
if (cinstr[1] >= 0 && cinstr[1] < length(remap)) {
|
||||||
|
new_instr[1] = remap[cinstr[1]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inlined_body[] = new_instr
|
||||||
|
k = k + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add continuation label
|
||||||
|
inlined_body[] = cont_label
|
||||||
|
|
||||||
|
// Splice: replace instructions[call_start..call_end] with inlined_body
|
||||||
|
before = array(instructions, 0, call_start)
|
||||||
|
after = array(instructions, call_end + 1, length(instructions))
|
||||||
|
spliced = array(before, inlined_body)
|
||||||
|
instructions = array(spliced, after)
|
||||||
|
func.instructions = instructions
|
||||||
|
|
||||||
|
changed = true
|
||||||
|
inline_count = inline_count + 1
|
||||||
|
|
||||||
|
// Continue scanning from after the inlined body
|
||||||
|
i = call_start + length(inlined_body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// Compose all passes
|
// Compose all passes
|
||||||
// =========================================================
|
// =========================================================
|
||||||
@@ -2657,13 +3195,12 @@ var streamline = function(ir, log) {
|
|||||||
ir._diagnostics = []
|
ir._diagnostics = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process main function
|
// Phase 1: Optimize all functions (bottom-up, existing behavior)
|
||||||
if (ir.main != null) {
|
if (ir.main != null) {
|
||||||
optimize_function(ir.main, log)
|
optimize_function(ir.main, log)
|
||||||
insert_stone_text(ir.main, log)
|
insert_stone_text(ir.main, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all sub-functions (resolve closure types from parent first)
|
|
||||||
var fi = 0
|
var fi = 0
|
||||||
if (ir.functions != null) {
|
if (ir.functions != null) {
|
||||||
fi = 0
|
fi = 0
|
||||||
@@ -2675,7 +3212,60 @@ var streamline = function(ir, log) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress slots across all functions (must run after per-function passes)
|
// Phase 2: Inline pass
|
||||||
|
var changed_main = false
|
||||||
|
var changed_fns = null
|
||||||
|
if (ir.main != null) {
|
||||||
|
changed_main = inline_calls(ir.main, ir, log)
|
||||||
|
}
|
||||||
|
if (ir.functions != null) {
|
||||||
|
changed_fns = array(length(ir.functions), false)
|
||||||
|
fi = 0
|
||||||
|
while (fi < length(ir.functions)) {
|
||||||
|
changed_fns[fi] = inline_calls(ir.functions[fi], ir, log)
|
||||||
|
fi = fi + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: Re-optimize inlined functions
|
||||||
|
if (changed_main) {
|
||||||
|
optimize_function(ir.main, log)
|
||||||
|
insert_stone_text(ir.main, log)
|
||||||
|
}
|
||||||
|
if (ir.functions != null) {
|
||||||
|
fi = 0
|
||||||
|
while (fi < length(ir.functions)) {
|
||||||
|
if (changed_fns != null && changed_fns[fi]) {
|
||||||
|
optimize_function(ir.functions[fi], log)
|
||||||
|
insert_stone_text(ir.functions[fi], log)
|
||||||
|
}
|
||||||
|
fi = fi + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 4: Cascade — second inline round (callbacks inside inlined bodies)
|
||||||
|
if (changed_main) {
|
||||||
|
changed_main = inline_calls(ir.main, ir, log)
|
||||||
|
if (changed_main) {
|
||||||
|
optimize_function(ir.main, log)
|
||||||
|
insert_stone_text(ir.main, log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ir.functions != null) {
|
||||||
|
fi = 0
|
||||||
|
while (fi < length(ir.functions)) {
|
||||||
|
if (changed_fns != null && changed_fns[fi]) {
|
||||||
|
changed_fns[fi] = inline_calls(ir.functions[fi], ir, log)
|
||||||
|
if (changed_fns[fi]) {
|
||||||
|
optimize_function(ir.functions[fi], log)
|
||||||
|
insert_stone_text(ir.functions[fi], log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fi = fi + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 5: Compress slots across all functions (must run after per-function passes)
|
||||||
compress_slots(ir)
|
compress_slots(ir)
|
||||||
|
|
||||||
// Expose DEF/USE functions via log if requested
|
// Expose DEF/USE functions via log if requested
|
||||||
|
|||||||
434
vm_suite.ce
434
vm_suite.ce
@@ -981,6 +981,99 @@ run("is_proto", function() {
|
|||||||
if (!is_proto(b, a)) fail("is_proto failed on meme")
|
if (!is_proto(b, a)) fail("is_proto failed on meme")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
run("is_data", function() {
|
||||||
|
if (!is_data({})) fail("is_data {} should be true")
|
||||||
|
if (is_data([])) fail("is_data [] should be false")
|
||||||
|
if (is_data(42)) fail("is_data number should be false")
|
||||||
|
if (is_data("hello")) fail("is_data string should be false")
|
||||||
|
if (is_data(null)) fail("is_data null should be false")
|
||||||
|
if (is_data(true)) fail("is_data bool should be false")
|
||||||
|
if (is_data(function(){})) fail("is_data function should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_true", function() {
|
||||||
|
if (!is_true(true)) fail("is_true true should be true")
|
||||||
|
if (is_true(false)) fail("is_true false should be false")
|
||||||
|
if (is_true(1)) fail("is_true 1 should be false")
|
||||||
|
if (is_true("true")) fail("is_true string should be false")
|
||||||
|
if (is_true(null)) fail("is_true null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_false", function() {
|
||||||
|
if (!is_false(false)) fail("is_false false should be true")
|
||||||
|
if (is_false(true)) fail("is_false true should be false")
|
||||||
|
if (is_false(0)) fail("is_false 0 should be false")
|
||||||
|
if (is_false("")) fail("is_false empty string should be false")
|
||||||
|
if (is_false(null)) fail("is_false null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_fit", function() {
|
||||||
|
if (!is_fit(0)) fail("is_fit 0 should be true")
|
||||||
|
if (!is_fit(42)) fail("is_fit 42 should be true")
|
||||||
|
if (!is_fit(-100)) fail("is_fit -100 should be true")
|
||||||
|
if (!is_fit(3.0)) fail("is_fit 3.0 should be true")
|
||||||
|
if (is_fit(3.5)) fail("is_fit 3.5 should be false")
|
||||||
|
if (is_fit("42")) fail("is_fit string should be false")
|
||||||
|
if (is_fit(null)) fail("is_fit null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_character", function() {
|
||||||
|
if (!is_character("a")) fail("is_character a should be true")
|
||||||
|
if (!is_character("Z")) fail("is_character Z should be true")
|
||||||
|
if (!is_character("5")) fail("is_character 5 should be true")
|
||||||
|
if (!is_character(" ")) fail("is_character space should be true")
|
||||||
|
if (is_character("ab")) fail("is_character ab should be false")
|
||||||
|
if (is_character("")) fail("is_character empty should be false")
|
||||||
|
if (is_character(42)) fail("is_character number should be false")
|
||||||
|
if (is_character(null)) fail("is_character null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_digit", function() {
|
||||||
|
if (!is_digit("0")) fail("is_digit 0 should be true")
|
||||||
|
if (!is_digit("5")) fail("is_digit 5 should be true")
|
||||||
|
if (!is_digit("9")) fail("is_digit 9 should be true")
|
||||||
|
if (is_digit("a")) fail("is_digit a should be false")
|
||||||
|
if (is_digit("55")) fail("is_digit 55 should be false")
|
||||||
|
if (is_digit(5)) fail("is_digit number should be false")
|
||||||
|
if (is_digit(null)) fail("is_digit null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_letter", function() {
|
||||||
|
if (!is_letter("a")) fail("is_letter a should be true")
|
||||||
|
if (!is_letter("Z")) fail("is_letter Z should be true")
|
||||||
|
if (is_letter("5")) fail("is_letter 5 should be false")
|
||||||
|
if (is_letter("ab")) fail("is_letter ab should be false")
|
||||||
|
if (is_letter(42)) fail("is_letter number should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_lower", function() {
|
||||||
|
if (!is_lower("a")) fail("is_lower a should be true")
|
||||||
|
if (!is_lower("z")) fail("is_lower z should be true")
|
||||||
|
if (is_lower("A")) fail("is_lower A should be false")
|
||||||
|
if (is_lower("5")) fail("is_lower 5 should be false")
|
||||||
|
if (is_lower("ab")) fail("is_lower ab should be false")
|
||||||
|
if (is_lower(42)) fail("is_lower number should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_upper", function() {
|
||||||
|
if (!is_upper("A")) fail("is_upper A should be true")
|
||||||
|
if (!is_upper("Z")) fail("is_upper Z should be true")
|
||||||
|
if (is_upper("a")) fail("is_upper a should be false")
|
||||||
|
if (is_upper("5")) fail("is_upper 5 should be false")
|
||||||
|
if (is_upper("AB")) fail("is_upper AB should be false")
|
||||||
|
if (is_upper(42)) fail("is_upper number should be false")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("is_whitespace", function() {
|
||||||
|
if (!is_whitespace(" ")) fail("is_whitespace space should be true")
|
||||||
|
if (!is_whitespace("\t")) fail("is_whitespace tab should be true")
|
||||||
|
if (!is_whitespace("\n")) fail("is_whitespace newline should be true")
|
||||||
|
if (is_whitespace("a")) fail("is_whitespace a should be false")
|
||||||
|
if (is_whitespace(" ")) fail("is_whitespace two spaces should be false")
|
||||||
|
if (is_whitespace(42)) fail("is_whitespace number should be false")
|
||||||
|
if (is_whitespace(null)) fail("is_whitespace null should be false")
|
||||||
|
})
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// GLOBAL FUNCTIONS - LENGTH
|
// GLOBAL FUNCTIONS - LENGTH
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -3409,6 +3502,166 @@ run("array map with exit", function() {
|
|||||||
if (length(result) != 5) fail("array map with exit length unexpected")
|
if (length(result) != 5) fail("array map with exit length unexpected")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
run("inline map intrinsic is_data", function() {
|
||||||
|
var items = [{}, [], "hello", 42, true]
|
||||||
|
var result = array(items, is_data)
|
||||||
|
if (result[0] != true) fail("is_data {} should be true")
|
||||||
|
if (result[1] != false) fail("is_data [] should be false")
|
||||||
|
if (result[2] != false) fail("is_data string should be false")
|
||||||
|
if (result[3] != false) fail("is_data number should be false")
|
||||||
|
if (result[4] != false) fail("is_data bool should be false")
|
||||||
|
if (length(result) != 5) fail("result length should be 5")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline map intrinsic is_number", function() {
|
||||||
|
var items = [1, "two", 3.14, null, true]
|
||||||
|
var result = array(items, is_number)
|
||||||
|
if (result[0] != true) fail("1 should be number")
|
||||||
|
if (result[1] != false) fail("'two' should not be number")
|
||||||
|
if (result[2] != true) fail("3.14 should be number")
|
||||||
|
if (result[3] != false) fail("null should not be number")
|
||||||
|
if (result[4] != false) fail("true should not be number")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline map intrinsic is_text", function() {
|
||||||
|
var items = ["hello", 42, "", null]
|
||||||
|
var result = array(items, is_text)
|
||||||
|
if (result[0] != true) fail("'hello' should be text")
|
||||||
|
if (result[1] != false) fail("42 should not be text")
|
||||||
|
if (result[2] != true) fail("'' should be text")
|
||||||
|
if (result[3] != false) fail("null should not be text")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline map intrinsic is_digit", function() {
|
||||||
|
var chars = array("a5B2 ")
|
||||||
|
var result = array(chars, is_digit)
|
||||||
|
if (result[0] != false) fail("a should not be digit")
|
||||||
|
if (result[1] != true) fail("5 should be digit")
|
||||||
|
if (result[2] != false) fail("B should not be digit")
|
||||||
|
if (result[3] != true) fail("2 should be digit")
|
||||||
|
if (result[4] != false) fail("space should not be digit")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline map lambda", function() {
|
||||||
|
var arr = [10, 20, 30]
|
||||||
|
var result = array(arr, function(x) { return x + 1 })
|
||||||
|
if (result[0] != 11) fail("10+1 should be 11")
|
||||||
|
if (result[1] != 21) fail("20+1 should be 21")
|
||||||
|
if (result[2] != 31) fail("30+1 should be 31")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline map empty array", function() {
|
||||||
|
var result = array([], is_number)
|
||||||
|
if (length(result) != 0) fail("map of empty should be empty")
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// NUMERIC INTRINSIC CALLBACK INLINING
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
var mymap = function(arr, fn) {
|
||||||
|
var result = array(length(arr))
|
||||||
|
var i = 0
|
||||||
|
while (i < length(arr)) {
|
||||||
|
result[i] = fn(arr[i])
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
var myfold = function(arr, fn) {
|
||||||
|
var acc = arr[0]
|
||||||
|
var i = 1
|
||||||
|
while (i < length(arr)) {
|
||||||
|
acc = fn(acc, arr[i])
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
run("inline callback abs", function() {
|
||||||
|
var result = mymap([-3, 5, -1.5, 0], function(a) { return abs(a) })
|
||||||
|
assert_eq(result[0], 3, "abs(-3)")
|
||||||
|
assert_eq(result[1], 5, "abs(5)")
|
||||||
|
assert_eq(result[2], 1.5, "abs(-1.5)")
|
||||||
|
assert_eq(result[3], 0, "abs(0)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback neg", function() {
|
||||||
|
var result = mymap([3, -5, 0, 1.5], function(a) { return neg(a) })
|
||||||
|
assert_eq(result[0], -3, "neg(3)")
|
||||||
|
assert_eq(result[1], 5, "neg(-5)")
|
||||||
|
assert_eq(result[2], 0, "neg(0)")
|
||||||
|
assert_eq(result[3], -1.5, "neg(1.5)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback sign", function() {
|
||||||
|
var result = mymap([-7, 0, 42, -0.5], function(a) { return sign(a) })
|
||||||
|
assert_eq(result[0], -1, "sign(-7)")
|
||||||
|
assert_eq(result[1], 0, "sign(0)")
|
||||||
|
assert_eq(result[2], 1, "sign(42)")
|
||||||
|
assert_eq(result[3], -1, "sign(-0.5)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback fraction", function() {
|
||||||
|
var result = mymap([3.75, -2.5, 5.0], function(a) { return fraction(a) })
|
||||||
|
assert_eq(result[0], 0.75, "fraction(3.75)")
|
||||||
|
assert_eq(result[1], -0.5, "fraction(-2.5)")
|
||||||
|
assert_eq(result[2], 0, "fraction(5.0)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback floor", function() {
|
||||||
|
var result = mymap([3.7, -2.3, 5.0, 1.9], function(a) { return floor(a) })
|
||||||
|
assert_eq(result[0], 3, "floor(3.7)")
|
||||||
|
assert_eq(result[1], -3, "floor(-2.3)")
|
||||||
|
assert_eq(result[2], 5, "floor(5.0)")
|
||||||
|
assert_eq(result[3], 1, "floor(1.9)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback ceiling", function() {
|
||||||
|
var result = mymap([3.2, -2.7, 5.0, 1.1], function(a) { return ceiling(a) })
|
||||||
|
assert_eq(result[0], 4, "ceiling(3.2)")
|
||||||
|
assert_eq(result[1], -2, "ceiling(-2.7)")
|
||||||
|
assert_eq(result[2], 5, "ceiling(5.0)")
|
||||||
|
assert_eq(result[3], 2, "ceiling(1.1)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback round", function() {
|
||||||
|
var result = mymap([3.5, -2.5, 5.0, 1.4], function(a) { return round(a) })
|
||||||
|
assert_eq(result[0], 4, "round(3.5)")
|
||||||
|
assert_eq(result[1], -3, "round(-2.5)")
|
||||||
|
assert_eq(result[2], 5, "round(5.0)")
|
||||||
|
assert_eq(result[3], 1, "round(1.4)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback trunc", function() {
|
||||||
|
var result = mymap([3.7, -2.3, 5.0, -1.9], function(a) { return trunc(a) })
|
||||||
|
assert_eq(result[0], 3, "trunc(3.7)")
|
||||||
|
assert_eq(result[1], -2, "trunc(-2.3)")
|
||||||
|
assert_eq(result[2], 5, "trunc(5.0)")
|
||||||
|
assert_eq(result[3], -1, "trunc(-1.9)")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback max", function() {
|
||||||
|
var result = myfold([3, 7, 2, 9, 1], function(a, b) { return max(a, b) })
|
||||||
|
assert_eq(result, 9, "max reduce")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback min", function() {
|
||||||
|
var result = myfold([3, 7, 2, 9, 1], function(a, b) { return min(a, b) })
|
||||||
|
assert_eq(result, 1, "min reduce")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback modulo", function() {
|
||||||
|
var result = myfold([17, 5], function(a, b) { return modulo(a, b) })
|
||||||
|
assert_eq(result, 2, "modulo")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("inline callback remainder", function() {
|
||||||
|
var result = myfold([-17, 5], function(a, b) { return remainder(a, b) })
|
||||||
|
assert_eq(result, -2, "remainder")
|
||||||
|
})
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// STRING METHOD EDGE CASES
|
// STRING METHOD EDGE CASES
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -5729,6 +5982,187 @@ run("gc closure - factory pattern survives gc", function() {
|
|||||||
assert_eq(b.say(), "hello bob", "second factory closure")
|
assert_eq(b.say(), "hello bob", "second factory closure")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INLINE LOOP EXPANSION TESTS
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// --- filter inline expansion ---
|
||||||
|
|
||||||
|
run("filter inline - integer predicate", function() {
|
||||||
|
var result = filter([0, 1.25, 2, 3.5, 4, 5.75], is_integer)
|
||||||
|
assert_eq(length(result), 3, "filter integer count")
|
||||||
|
assert_eq(result[0], 0, "filter integer [0]")
|
||||||
|
assert_eq(result[1], 2, "filter integer [1]")
|
||||||
|
assert_eq(result[2], 4, "filter integer [2]")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("filter inline - all pass", function() {
|
||||||
|
var result = filter([1, 2, 3], function(x) { return true })
|
||||||
|
assert_eq(length(result), 3, "filter all pass length")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("filter inline - none pass", function() {
|
||||||
|
var result = filter([1, 2, 3], function(x) { return false })
|
||||||
|
assert_eq(length(result), 0, "filter none pass length")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("filter inline - empty", function() {
|
||||||
|
var result = filter([], is_integer)
|
||||||
|
assert_eq(length(result), 0, "filter empty length")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("filter inline - with index", function() {
|
||||||
|
var result = filter([10, 20, 30], function(e, i) { return i > 0 })
|
||||||
|
assert_eq(length(result), 2, "filter index length")
|
||||||
|
assert_eq(result[0], 20, "filter index [0]")
|
||||||
|
assert_eq(result[1], 30, "filter index [1]")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("filter inline - large callback", function() {
|
||||||
|
var result = filter([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(x) {
|
||||||
|
var a = x * 2
|
||||||
|
var b = a + 1
|
||||||
|
var c = b * 3
|
||||||
|
var d = c - a
|
||||||
|
return d > 20
|
||||||
|
})
|
||||||
|
if (length(result) < 1) fail("filter large callback should return elements")
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- find inline expansion ---
|
||||||
|
|
||||||
|
run("find inline - function forward", function() {
|
||||||
|
var idx = find([1, 2, 3], function(x) { return x == 2 })
|
||||||
|
assert_eq(idx, 1, "find fn forward")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - function not found", function() {
|
||||||
|
var idx = find([1, 2, 3], function(x) { return x == 99 })
|
||||||
|
assert_eq(idx, null, "find fn not found")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - value forward", function() {
|
||||||
|
var idx = find([10, 20, 30], 20)
|
||||||
|
assert_eq(idx, 1, "find value forward")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - value not found", function() {
|
||||||
|
var idx = find([10, 20, 30], 99)
|
||||||
|
assert_eq(idx, null, "find value not found")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - reverse", function() {
|
||||||
|
var idx = find([1, 2, 1, 2], function(x) { return x == 1 }, true)
|
||||||
|
assert_eq(idx, 2, "find reverse")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - from", function() {
|
||||||
|
var idx = find([1, 2, 3, 2], function(x) { return x == 2 }, false, 2)
|
||||||
|
assert_eq(idx, 3, "find from")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - empty", function() {
|
||||||
|
var idx = find([], 1)
|
||||||
|
assert_eq(idx, null, "find empty")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - value reverse", function() {
|
||||||
|
var idx = find([10, 20, 30, 20], 20, true)
|
||||||
|
assert_eq(idx, 3, "find value reverse")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - value with from", function() {
|
||||||
|
var idx = find([10, 20, 30, 20], 20, false, 2)
|
||||||
|
assert_eq(idx, 3, "find value with from")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("find inline - with index callback", function() {
|
||||||
|
var idx = find(["a", "b", "c"], (x, i) => i == 2)
|
||||||
|
assert_eq(idx, 2, "find index callback")
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- arrfor inline expansion ---
|
||||||
|
|
||||||
|
run("arrfor inline - basic sum", function() {
|
||||||
|
var sum = 0
|
||||||
|
arrfor([1, 2, 3, 4, 5], function(x) { sum = sum + x })
|
||||||
|
assert_eq(sum, 15, "arrfor basic sum")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("arrfor inline - reverse", function() {
|
||||||
|
var order = []
|
||||||
|
arrfor([1, 2, 3], function(x) { order[] = x }, true)
|
||||||
|
assert_eq(order[0], 3, "arrfor reverse [0]")
|
||||||
|
assert_eq(order[1], 2, "arrfor reverse [1]")
|
||||||
|
assert_eq(order[2], 1, "arrfor reverse [2]")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("arrfor inline - exit", function() {
|
||||||
|
var result = arrfor([1, 2, 3, 4, 5], function(x) {
|
||||||
|
if (x > 3) return true
|
||||||
|
return null
|
||||||
|
}, false, true)
|
||||||
|
assert_eq(result, true, "arrfor exit")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("arrfor inline - no exit returns null", function() {
|
||||||
|
var result = arrfor([1, 2, 3], function(x) { })
|
||||||
|
assert_eq(result, null, "arrfor no exit null")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("arrfor inline - with index", function() {
|
||||||
|
var indices = []
|
||||||
|
arrfor([10, 20, 30], (x, i) => { indices[] = i })
|
||||||
|
assert_eq(indices[0], 0, "arrfor index [0]")
|
||||||
|
assert_eq(indices[1], 1, "arrfor index [1]")
|
||||||
|
assert_eq(indices[2], 2, "arrfor index [2]")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("arrfor inline - reverse with index", function() {
|
||||||
|
var items = []
|
||||||
|
arrfor(["a", "b", "c"], function(x, i) { items[] = text(i) + x }, true)
|
||||||
|
assert_eq(items[0], "2c", "arrfor rev index [0]")
|
||||||
|
assert_eq(items[1], "1b", "arrfor rev index [1]")
|
||||||
|
assert_eq(items[2], "0a", "arrfor rev index [2]")
|
||||||
|
})
|
||||||
|
|
||||||
|
// --- reduce inline expansion ---
|
||||||
|
|
||||||
|
run("reduce inline - no initial forward", function() {
|
||||||
|
var result = reduce([1, 2, 3, 4, 5, 6, 7, 8, 9], function(a, b) { return a + b })
|
||||||
|
assert_eq(result, 45, "reduce sum 1-9")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - single element", function() {
|
||||||
|
var result = reduce([42], function(a, b) { return a + b })
|
||||||
|
assert_eq(result, 42, "reduce single")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - empty", function() {
|
||||||
|
var result = reduce([], function(a, b) { return a + b })
|
||||||
|
assert_eq(result, null, "reduce empty")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - with initial", function() {
|
||||||
|
var result = reduce([1, 2, 3], function(a, b) { return a + b }, 10)
|
||||||
|
assert_eq(result, 16, "reduce with initial")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - with initial empty", function() {
|
||||||
|
var result = reduce([], function(a, b) { return a + b }, 99)
|
||||||
|
assert_eq(result, 99, "reduce initial empty")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - reverse", function() {
|
||||||
|
var result = reduce([1, 2, 3], function(a, b) { return a - b }, 0, true)
|
||||||
|
assert_eq(result, -6, "reduce reverse")
|
||||||
|
})
|
||||||
|
|
||||||
|
run("reduce inline - intrinsic callback", function() {
|
||||||
|
var result = reduce([3, 7, 2, 9, 1], max)
|
||||||
|
assert_eq(result, 9, "reduce max")
|
||||||
|
})
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// SUMMARY
|
// SUMMARY
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user