8 Commits

Author SHA1 Message Date
John Alanbrook
ba78eb632f initial attempt 2025-06-14 17:59:33 -05:00
John Alanbrook
fa12281ab9 add benchmarks 2025-06-14 17:03:49 -05:00
John Alanbrook
38a52fcb73 add tilemap 2025-06-13 20:48:33 -05:00
John Alanbrook
95a95e55e3 more details on cell 2025-06-13 20:42:53 -05:00
John Alanbrook
fc978a5766 add cell doc 2025-06-11 16:35:46 -05:00
John Alanbrook
9082ee2c47 move quickjs into source 2025-06-11 01:18:26 -05:00
John Alanbrook
b8ad8431f4 optimize 2025-06-11 01:13:58 -05:00
John Alanbrook
1c2b8228fe add num 2025-06-10 04:33:15 -05:00
54 changed files with 78208 additions and 652 deletions

View File

@@ -1,5 +1,5 @@
debug: FORCE
meson setup build_dbg -Dbuildtype=debug
meson setup build_dbg -Dbuildtype=debugoptimized
meson install --only-changed -C build_dbg
fast: FORCE

43
benchmarks/binarytree.ce Normal file
View File

@@ -0,0 +1,43 @@
function mainThread() {
var maxDepth = Math.max(6, Number(arg[0] || 16));
var stretchDepth = maxDepth + 1;
var check = itemCheck(bottomUpTree(stretchDepth));
log.console(`stretch tree of depth ${stretchDepth}\t check: ${check}`);
var longLivedTree = bottomUpTree(maxDepth);
for (let depth = 4; depth <= maxDepth; depth += 2) {
var iterations = 1 << maxDepth - depth + 4;
work(iterations, depth);
}
log.console(`long lived tree of depth ${maxDepth}\t check: ${itemCheck(longLivedTree)}`);
}
function work(iterations, depth) {
let check = 0;
for (let i = 0; i < iterations; i++)
check += itemCheck(bottomUpTree(depth));
log.console(`${iterations}\t trees of depth ${depth}\t check: ${check}`);
}
function TreeNode(left, right) {
return {left, right};
}
function itemCheck(node) {
if (node.left === null)
return 1;
return 1 + itemCheck(node.left) + itemCheck(node.right);
}
function bottomUpTree(depth) {
return depth > 0
? new TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
: new TreeNode(null, null);
}
mainThread()
$_.stop()

View File

@@ -0,0 +1,24 @@
var blob = use('blob')
function eratosthenes (n) {
var sieve = new blob(n, true)
var sqrtN = Math.trunc(Math.sqrt(n));
for (i = 2; i <= sqrtN; i++)
if (sieve.read_logical(i))
for (j = i * i; j <= n; j += i)
sieve.write_bit(j, false);
return sieve;
}
var sieve = eratosthenes(10000000);
stone(sieve)
var c = 0
for (var i = 0; i < sieve.length; i++)
if (sieve.read_logical(i)) c++
log.console(c)
$_.stop()

58
benchmarks/fannkuch.ce Normal file
View File

@@ -0,0 +1,58 @@
function fannkuch(n) {
var perm1 = [n]
for (let i = 0; i < n; i++) perm1[i] = i
var perm = [n]
var count = [n]
var f = 0, flips = 0, nperm = 0, checksum = 0
var i, k, r
r = n
while (r > 0) {
i = 0
while (r != 1) { count[r-1] = r; r -= 1 }
while (i < n) { perm[i] = perm1[i]; i += 1 }
// Count flips and update max and checksum
f = 0
k = perm[0]
while (k != 0) {
i = 0
while (2*i < k) {
let t = perm[i]; perm[i] = perm[k-i]; perm[k-i] = t
i += 1
}
k = perm[0]
f += 1
}
if (f > flips) flips = f
if ((nperm & 0x1) == 0) checksum += f; else checksum -= f
// Use incremental change to generate another permutation
var more = true
while (more) {
if (r == n) {
log.console( checksum )
return flips
}
let p0 = perm1[0]
i = 0
while (i < r) {
let j = i + 1
perm1[i] = perm1[j]
i = j
}
perm1[r] = p0
count[r] -= 1
if (count[r] > 0) more = false; else r += 1
}
nperm += 1
}
return flips;
}
var n = arg[0] || 10
log.console(`Pfannkuchen(${n}) = ${fannkuch(n)}`)
$_.stop()

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Run hyperfine with parameter lists
# This will create a cross-product of all libraries × all scenarios
hyperfine \
--warmup 3 \
--runs 20 \
-i \
--export-csv wota_vs_nota_vs_json.csv \
--export-json wota_vs_nota_vs_json.json \
--export-markdown wota_vs_nota_vs_json.md \
--parameter-list lib wota,nota,json \
--parameter-list scen empty,integers,floats,strings,objects,nested,large_array \
'cell benchmarks/wota_nota_json {lib} {scen}'
echo "Benchmark complete! Results saved to:"
echo " - wota_vs_nota_vs_json.csv"
echo " - wota_vs_nota_vs_json.json"
echo " - wota_vs_nota_vs_json.md"

395
benchmarks/js_perf.ce Normal file
View File

@@ -0,0 +1,395 @@
var time = use('time')
////////////////////////////////////////////////////////////////////////////////
// JavaScript Performance Benchmark Suite
// Tests core JS operations: property access, function calls, arithmetic, etc.
////////////////////////////////////////////////////////////////////////////////
// Test configurations
const iterations = {
simple: 10000000,
medium: 1000000,
complex: 100000
};
////////////////////////////////////////////////////////////////////////////////
// Utility: measureTime(fn) => how long fn() takes in seconds
////////////////////////////////////////////////////////////////////////////////
function measureTime(fn) {
var start = time.number();
fn();
var end = time.number();
return (end - start);
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Property Access
////////////////////////////////////////////////////////////////////////////////
function benchPropertyAccess() {
var obj = {
a: 1, b: 2, c: 3, d: 4, e: 5,
nested: { x: 10, y: 20, z: 30 }
};
var readTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.simple; i++) {
sum += obj.a + obj.b + obj.c + obj.d + obj.e;
sum += obj.nested.x + obj.nested.y + obj.nested.z;
}
});
var writeTime = measureTime(function() {
for (var i = 0; i < iterations.simple; i++) {
obj.a = i;
obj.b = i + 1;
obj.c = i + 2;
obj.nested.x = i * 2;
obj.nested.y = i * 3;
}
});
return { readTime: readTime, writeTime: writeTime };
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Function Calls
////////////////////////////////////////////////////////////////////////////////
function benchFunctionCalls() {
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
function complexCalc(a, b, c) { return (a + b) * c / 2; }
var obj = {
method: function(x) { return x * 2; },
nested: {
deepMethod: function(x, y) { return x + y; }
}
};
var simpleCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = add(i, 1);
result = multiply(result, 2);
}
});
var methodCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = obj.method(i);
result = obj.nested.deepMethod(result, i);
}
});
var complexCallTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.medium; i++) {
result = complexCalc(i, i + 1, i + 2);
}
});
return {
simpleCallTime: simpleCallTime,
methodCallTime: methodCallTime,
complexCallTime: complexCallTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Array Operations
////////////////////////////////////////////////////////////////////////////////
function benchArrayOps() {
var pushTime = measureTime(function() {
var arr = [];
for (var i = 0; i < iterations.medium; i++) {
arr.push(i);
}
});
var arr = [];
for (var i = 0; i < 10000; i++) arr.push(i);
var accessTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.medium; i++) {
sum += arr[i % 10000];
}
});
var iterateTime = measureTime(function() {
var sum = 0;
for (var j = 0; j < 1000; j++) {
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
}
});
return {
pushTime: pushTime,
accessTime: accessTime,
iterateTime: iterateTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Object Creation
////////////////////////////////////////////////////////////////////////////////
function benchObjectCreation() {
var literalTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var obj = { x: i, y: i * 2, z: i * 3 };
}
});
function Point(x, y) {
this.x = x;
this.y = y;
}
var constructorTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var p = new Point(i, i * 2);
}
});
var protoObj = {
x: 0,
y: 0,
move: function(dx, dy) {
this.x += dx;
this.y += dy;
}
};
var prototypeTime = measureTime(function() {
for (var i = 0; i < iterations.medium; i++) {
var obj = Object.create(protoObj);
obj.x = i;
obj.y = i * 2;
}
});
return {
literalTime: literalTime,
constructorTime: constructorTime,
prototypeTime: prototypeTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: String Operations
////////////////////////////////////////////////////////////////////////////////
function benchStringOps() {
var concatTime = measureTime(function() {
var str = "";
for (var i = 0; i < iterations.complex; i++) {
str = "test" + i + "value";
}
});
var strings = [];
for (var i = 0; i < 1000; i++) {
strings.push("string" + i);
}
var joinTime = measureTime(function() {
for (var i = 0; i < iterations.complex; i++) {
var result = strings.join(",");
}
});
var splitTime = measureTime(function() {
var str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p";
for (var i = 0; i < iterations.medium; i++) {
var parts = str.split(",");
}
});
return {
concatTime: concatTime,
joinTime: joinTime,
splitTime: splitTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Arithmetic Operations
////////////////////////////////////////////////////////////////////////////////
function benchArithmetic() {
var intMathTime = measureTime(function() {
var result = 1;
for (var i = 0; i < iterations.simple; i++) {
result = ((result + i) * 2 - 1) / 3;
result = result % 1000 + 1;
}
});
var floatMathTime = measureTime(function() {
var result = 1.5;
for (var i = 0; i < iterations.simple; i++) {
result = Math.sin(result) + Math.cos(i * 0.01);
result = Math.sqrt(Math.abs(result)) + 0.1;
}
});
var bitwiseTime = measureTime(function() {
var result = 0;
for (var i = 0; i < iterations.simple; i++) {
result = (result ^ i) & 0xFFFF;
result = (result << 1) | (result >> 15);
}
});
return {
intMathTime: intMathTime,
floatMathTime: floatMathTime,
bitwiseTime: bitwiseTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Benchmark: Closure Operations
////////////////////////////////////////////////////////////////////////////////
function benchClosures() {
function makeAdder(x) {
return function(y) { return x + y; };
}
var closureCreateTime = measureTime(function() {
var funcs = [];
for (var i = 0; i < iterations.medium; i++) {
funcs.push(makeAdder(i));
}
});
var adders = [];
for (var i = 0; i < 1000; i++) {
adders.push(makeAdder(i));
}
var closureCallTime = measureTime(function() {
var sum = 0;
for (var i = 0; i < iterations.medium; i++) {
sum += adders[i % 1000](i);
}
});
return {
closureCreateTime: closureCreateTime,
closureCallTime: closureCallTime
};
}
////////////////////////////////////////////////////////////////////////////////
// Main benchmark runner
////////////////////////////////////////////////////////////////////////////////
log.console("JavaScript Performance Benchmark");
log.console("================================\n");
// Property Access
log.console("BENCHMARK: Property Access");
var propResults = benchPropertyAccess();
log.console(" Read time: " + propResults.readTime.toFixed(3) + "s => " +
(iterations.simple / propResults.readTime).toFixed(1) + " reads/sec [" +
(propResults.readTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Write time: " + propResults.writeTime.toFixed(3) + "s => " +
(iterations.simple / propResults.writeTime).toFixed(1) + " writes/sec [" +
(propResults.writeTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Function Calls
log.console("BENCHMARK: Function Calls");
var funcResults = benchFunctionCalls();
log.console(" Simple calls: " + funcResults.simpleCallTime.toFixed(3) + "s => " +
(iterations.simple / funcResults.simpleCallTime).toFixed(1) + " calls/sec [" +
(funcResults.simpleCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Method calls: " + funcResults.methodCallTime.toFixed(3) + "s => " +
(iterations.simple / funcResults.methodCallTime).toFixed(1) + " calls/sec [" +
(funcResults.methodCallTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Complex calls: " + funcResults.complexCallTime.toFixed(3) + "s => " +
(iterations.medium / funcResults.complexCallTime).toFixed(1) + " calls/sec [" +
(funcResults.complexCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Array Operations
log.console("BENCHMARK: Array Operations");
var arrayResults = benchArrayOps();
log.console(" Push: " + arrayResults.pushTime.toFixed(3) + "s => " +
(iterations.medium / arrayResults.pushTime).toFixed(1) + " pushes/sec [" +
(arrayResults.pushTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Access: " + arrayResults.accessTime.toFixed(3) + "s => " +
(iterations.medium / arrayResults.accessTime).toFixed(1) + " accesses/sec [" +
(arrayResults.accessTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Iterate: " + arrayResults.iterateTime.toFixed(3) + "s => " +
(1000 / arrayResults.iterateTime).toFixed(1) + " full iterations/sec");
log.console("");
// Object Creation
log.console("BENCHMARK: Object Creation");
var objResults = benchObjectCreation();
log.console(" Literal: " + objResults.literalTime.toFixed(3) + "s => " +
(iterations.medium / objResults.literalTime).toFixed(1) + " creates/sec [" +
(objResults.literalTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Constructor: " + objResults.constructorTime.toFixed(3) + "s => " +
(iterations.medium / objResults.constructorTime).toFixed(1) + " creates/sec [" +
(objResults.constructorTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Prototype: " + objResults.prototypeTime.toFixed(3) + "s => " +
(iterations.medium / objResults.prototypeTime).toFixed(1) + " creates/sec [" +
(objResults.prototypeTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// String Operations
log.console("BENCHMARK: String Operations");
var strResults = benchStringOps();
log.console(" Concat: " + strResults.concatTime.toFixed(3) + "s => " +
(iterations.complex / strResults.concatTime).toFixed(1) + " concats/sec [" +
(strResults.concatTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
log.console(" Join: " + strResults.joinTime.toFixed(3) + "s => " +
(iterations.complex / strResults.joinTime).toFixed(1) + " joins/sec [" +
(strResults.joinTime / iterations.complex * 1e9).toFixed(1) + " ns/op]");
log.console(" Split: " + strResults.splitTime.toFixed(3) + "s => " +
(iterations.medium / strResults.splitTime).toFixed(1) + " splits/sec [" +
(strResults.splitTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Arithmetic Operations
log.console("BENCHMARK: Arithmetic Operations");
var mathResults = benchArithmetic();
log.console(" Integer math: " + mathResults.intMathTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.intMathTime).toFixed(1) + " ops/sec [" +
(mathResults.intMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Float math: " + mathResults.floatMathTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.floatMathTime).toFixed(1) + " ops/sec [" +
(mathResults.floatMathTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console(" Bitwise: " + mathResults.bitwiseTime.toFixed(3) + "s => " +
(iterations.simple / mathResults.bitwiseTime).toFixed(1) + " ops/sec [" +
(mathResults.bitwiseTime / iterations.simple * 1e9).toFixed(1) + " ns/op]");
log.console("");
// Closures
log.console("BENCHMARK: Closures");
var closureResults = benchClosures();
log.console(" Create: " + closureResults.closureCreateTime.toFixed(3) + "s => " +
(iterations.medium / closureResults.closureCreateTime).toFixed(1) + " creates/sec [" +
(closureResults.closureCreateTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console(" Call: " + closureResults.closureCallTime.toFixed(3) + "s => " +
(iterations.medium / closureResults.closureCallTime).toFixed(1) + " calls/sec [" +
(closureResults.closureCallTime / iterations.medium * 1e9).toFixed(1) + " ns/op]");
log.console("");
log.console("---------------------------------------------------------");
log.console("Benchmark complete.\n");
$_.stop()

40
benchmarks/mandelbrot.ce Normal file
View File

@@ -0,0 +1,40 @@
var blob = use('blob')
var iter = 50, limit = 2.0;
var zr, zi, cr, ci, tr, ti;
var h = Number(arg[0]) || 500
var w = h
log.console(`P4\n${w} ${h}`);
for (let y = 0; y < h; ++y) {
// Create a blob for the row - we need w bits
var row = new blob(w);
for (let x = 0; x < w; ++x) {
zr = zi = tr = ti = 0;
cr = 2 * x / w - 1.5;
ci = 2 * y / h - 1;
for (let i = 0; i < iter && (tr + ti <= limit * limit); ++i) {
zi = 2 * zr * zi + ci;
zr = tr - ti + cr;
tr = zr * zr;
ti = zi * zi;
}
// Write a 1 bit if inside the set, 0 if outside
if (tr + ti <= limit * limit)
row.write_bit(1);
else
row.write_bit(0);
}
// Convert the blob to stone (immutable) to prepare for output
stone(row)
// Output the blob data as raw bytes
log.console(text(row, 'b'));
}
$_.stop()

11
benchmarks/montecarlo.ce Normal file
View File

@@ -0,0 +1,11 @@
var N = 10000000;
var num = 0;
for (var i = 0; i < N; i ++) {
var x = 2 * $_.random();
var y = $_.random();
if (y < Math.sin(x * x))
num++;
}
log.console(2 * num / N);
$_.stop()

161
benchmarks/nbody.ce Normal file
View File

@@ -0,0 +1,161 @@
var PI = Math.PI;
var SOLAR_MASS = 4 * PI * PI;
var DAYS_PER_YEAR = 365.24;
function Body(x, y, z, vx, vy, vz, mass) {
this.x = x;
this.y = y;
this.z = z;
this.vx = vx;
this.vy = vy;
this.vz = vz;
this.mass = mass;
}
function Jupiter() {
return new Body(
4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01,
1.66007664274403694e-03 * DAYS_PER_YEAR,
7.69901118419740425e-03 * DAYS_PER_YEAR,
-6.90460016972063023e-05 * DAYS_PER_YEAR,
9.54791938424326609e-04 * SOLAR_MASS
);
}
function Saturn() {
return new Body(
8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01,
-2.76742510726862411e-03 * DAYS_PER_YEAR,
4.99852801234917238e-03 * DAYS_PER_YEAR,
2.30417297573763929e-05 * DAYS_PER_YEAR,
2.85885980666130812e-04 * SOLAR_MASS
);
}
function Uranus() {
return new Body(
1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01,
2.96460137564761618e-03 * DAYS_PER_YEAR,
2.37847173959480950e-03 * DAYS_PER_YEAR,
-2.96589568540237556e-05 * DAYS_PER_YEAR,
4.36624404335156298e-05 * SOLAR_MASS
);
}
function Neptune() {
return new Body(
1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01,
2.68067772490389322e-03 * DAYS_PER_YEAR,
1.62824170038242295e-03 * DAYS_PER_YEAR,
-9.51592254519715870e-05 * DAYS_PER_YEAR,
5.15138902046611451e-05 * SOLAR_MASS
);
}
function Sun() {
return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS);
}
var bodies = Array(Sun(), Jupiter(), Saturn(), Uranus(), Neptune());
function offsetMomentum() {
let px = 0;
let py = 0;
let pz = 0;
var size = bodies.length;
for (let i = 0; i < size; i++) {
var body = bodies[i];
var mass = body.mass;
px += body.vx * mass;
py += body.vy * mass;
pz += body.vz * mass;
}
var body = bodies[0];
body.vx = -px / SOLAR_MASS;
body.vy = -py / SOLAR_MASS;
body.vz = -pz / SOLAR_MASS;
}
function advance(dt) {
var size = bodies.length;
for (let i = 0; i < size; i++) {
var bodyi = bodies[i];
let vxi = bodyi.vx;
let vyi = bodyi.vy;
let vzi = bodyi.vz;
for (let j = i + 1; j < size; j++) {
var bodyj = bodies[j];
var dx = bodyi.x - bodyj.x;
var dy = bodyi.y - bodyj.y;
var dz = bodyi.z - bodyj.z;
var d2 = dx * dx + dy * dy + dz * dz;
var mag = dt / (d2 * Math.sqrt(d2));
var massj = bodyj.mass;
vxi -= dx * massj * mag;
vyi -= dy * massj * mag;
vzi -= dz * massj * mag;
var massi = bodyi.mass;
bodyj.vx += dx * massi * mag;
bodyj.vy += dy * massi * mag;
bodyj.vz += dz * massi * mag;
}
bodyi.vx = vxi;
bodyi.vy = vyi;
bodyi.vz = vzi;
}
for (let i = 0; i < size; i++) {
var body = bodies[i];
body.x += dt * body.vx;
body.y += dt * body.vy;
body.z += dt * body.vz;
}
}
function energy() {
let e = 0;
var size = bodies.length;
for (let i = 0; i < size; i++) {
var bodyi = bodies[i];
e += 0.5 * bodyi.mass * ( bodyi.vx * bodyi.vx +
bodyi.vy * bodyi.vy + bodyi.vz * bodyi.vz );
for (let j = i + 1; j < size; j++) {
var bodyj = bodies[j];
var dx = bodyi.x - bodyj.x;
var dy = bodyi.y - bodyj.y;
var dz = bodyi.z - bodyj.z;
var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
e -= (bodyi.mass * bodyj.mass) / distance;
}
}
return e;
}
var n = arg[0] || 1000000
offsetMomentum();
log.console(`n = ${n}`)
log.console(energy().toFixed(9))
for (let i = 0; i < n; i++)
advance(0.01);
log.console(energy().toFixed(9))
$_.stop()

View File

@@ -0,0 +1,50 @@
function A(i,j) {
return 1/((i+j)*(i+j+1)/2+i+1);
}
function Au(u,v) {
for (var i=0; i<u.length; ++i) {
var t = 0;
for (var j=0; j<u.length; ++j)
t += A(i,j) * u[j];
v[i] = t;
}
}
function Atu(u,v) {
for (var i=0; i<u.length; ++i) {
var t = 0;
for (var j=0; j<u.length; ++j)
t += A(j,i) * u[j];
v[i] = t;
}
}
function AtAu(u,v,w) {
Au(u,w);
Atu(w,v);
}
function spectralnorm(n) {
var i, u=[], v=[], w=[], vv=0, vBv=0;
for (i=0; i<n; ++i)
u[i] = 1; v[i] = w[i] = 0;
for (i=0; i<10; ++i) {
AtAu(u,v,w);
AtAu(v,u,w);
}
for (i=0; i<n; ++i) {
vBv += u[i]*v[i];
vv += v[i]*v[i];
}
return Math.sqrt(vBv/vv);
}
log.console(spectralnorm(arg[0]).toFixed(9));
$_.stop()

View File

@@ -2,7 +2,7 @@
// benchmark_wota_nota_json.js
//
// Usage in QuickJS:
// qjs benchmark_wota_nota_json.js
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
//
// Ensure wota, nota, json, and os are all available, e.g.:
var wota = use('wota');
@@ -12,34 +12,43 @@
var os = use('os');
//
// Parse command line arguments
if (arg.length !== 2) {
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
$_.stop()
}
var lib_name = arg[0];
var scenario_name = arg[1];
////////////////////////////////////////////////////////////////////////////////
// 1. Setup "libraries" array to easily switch among Wota, Nota, and JSON
// 1. Setup "libraries" array to easily switch among wota, nota, and json
////////////////////////////////////////////////////////////////////////////////
const libraries = [
{
name: "Wota",
name: "wota",
encode: wota.encode,
decode: wota.decode,
// Wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
getSize(encoded) {
return encoded.length;
}
},
{
name: "Nota",
name: "nota",
encode: nota.encode,
decode: nota.decode,
// Nota also produces an ArrayBuffer:
// nota also produces an ArrayBuffer:
getSize(encoded) {
return encoded.length;
}
},
{
name: "JSON",
name: "json",
encode: json.encode,
decode: json.decode,
// JSON produces a JS string. We'll measure its UTF-16 code unit length
// json produces a JS string. We'll measure its UTF-16 code unit length
// as a rough "size". Alternatively, you could convert to UTF-8 for
// a more accurate byte size. Here we just use `string.length`.
getSize(encodedStr) {
@@ -55,27 +64,27 @@ const libraries = [
const benchmarks = [
{
name: "Empty object",
name: "empty",
data: [{}, {}, {}, {}],
iterations: 10000
},
{
name: "Small Integers",
name: "integers",
data: [0, 42, -1, 2023],
iterations: 100000
},
{
name: "Floating point",
name: "floats",
data: [0.1, 1e-50, 3.14159265359],
iterations: 100000
},
{
name: "Strings (short, emoji)",
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
name: "strings",
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
iterations: 100000
},
{
name: "Small Objects",
name: "objects",
data: [
{ a:1, b:2.2, c:"3", d:false },
{ x:42, y:null, z:"test" }
@@ -83,12 +92,12 @@ const benchmarks = [
iterations: 50000
},
{
name: "Nested Arrays",
name: "nested",
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
iterations: 50000
},
{
name: "Large Array (1k integers)",
name: "large_array",
data: [ Array.from({length:1000}, (_, i) => i) ],
iterations: 1000
},
@@ -109,7 +118,7 @@ function measureTime(fn) {
// 4. For each library, we run each benchmark scenario and measure:
// - Encoding time (seconds)
// - Decoding time (seconds)
// - Total encoded size (bytes or code units for JSON)
// - Total encoded size (bytes or code units for json)
//
////////////////////////////////////////////////////////////////////////////////
@@ -153,33 +162,43 @@ function runBenchmarkForLibrary(lib, bench) {
}
////////////////////////////////////////////////////////////////////////////////
// 5. Main driver: run across all benchmarks, for each library.
// 5. Main driver: run only the specified library and scenario
////////////////////////////////////////////////////////////////////////////////
log.console("Benchmark: Wota vs Nota vs JSON");
log.console("================================\n");
// Find the requested library and scenario
var lib = libraries.find(l => l.name === lib_name);
var bench = benchmarks.find(b => b.name === scenario_name);
for (let bench of benchmarks) {
log.console(`SCENARIO: ${bench.name}`);
log.console(` Data length: ${bench.data.length} | Iterations: ${bench.iterations}\n`);
for (let lib of libraries) {
let { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
// We'll compute total operations = bench.iterations * bench.data.length
let totalOps = bench.iterations * bench.data.length;
let encOpsPerSec = (totalOps / encodeTime).toFixed(1);
let decOpsPerSec = (totalOps / decodeTime).toFixed(1);
log.console(` ${lib.name}:`);
log.console(` Encode time: ${encodeTime.toFixed(3)}s => ${encOpsPerSec} encodes/sec [${(encodeTime/bench.iterations)*1000000000} ns/try]`);
log.console(` Decode time: ${decodeTime.toFixed(3)}s => ${decOpsPerSec} decodes/sec [${(decodeTime/bench.iterations)*1000000000}/try]`);
log.console(` Total size: ${totalSize} bytes (or code units for JSON)`);
log.console("");
}
log.console("---------------------------------------------------------\n");
if (!lib) {
log.console('Unknown library:', lib_name);
log.console('Available libraries:', libraries.map(l => l.name).join(', '));
$_.stop()
}
log.console("Benchmark complete.\n");
if (!bench) {
log.console('Unknown scenario:', scenario_name);
log.console('Available scenarios:', benchmarks.map(b => b.name).join(', '));
$_.stop()
}
os.exit()
// Run the benchmark for this library/scenario combination
var { encodeTime, decodeTime, totalSize } = runBenchmarkForLibrary(lib, bench);
// Output json for easy parsing by hyperfine or other tools
var totalOps = bench.iterations * bench.data.length;
var result = {
lib: lib_name,
scenario: scenario_name,
encodeTime: encodeTime,
decodeTime: decodeTime,
totalSize: totalSize,
totalOps: totalOps,
encodeOpsPerSec: totalOps / encodeTime,
decodeOpsPerSec: totalOps / decodeTime,
encodeNsPerOp: (encodeTime / totalOps) * 1e9,
decodeNsPerOp: (decodeTime / totalOps) * 1e9
};
log.console(json.encode(result));
$_.stop()

122
cell.md Normal file
View File

@@ -0,0 +1,122 @@
JAVASCRIPT VISION
CELLSCRIPT
Javascript to its core. Objects. What does the language need? It can be quite small, I think. The key is, ANYTHING that we want to be fast and JIT'd, must be present. So, record lookups. These are actually quicker in a jit'd language that have them as a feature. Most things should be libraries. Blobs need to be in the runtime.
The purpose of this is to be a great language for passing messages. So it should be fast at creating records first and foremost, and finding items on them. So it needs first class, jitt'd records.
Finally, it needs to use less memory. Deleting a bunch of this stuff should make that simpler.
What is present?
Objects, prototypes, numbers, arrays, strings, true, false, null.
Things to do:
merge typeof and instanceof. Misty has array? stone? number? etc; it needs to be generic. 5 is number returns true.
No new operator. It's the same idea though: simply instead of 'var guy = new sprite({x,y})' you would say 'var guy = sprite({x,y})', and sprite would simply be a function written to return a sprite object.
One number type. Dec64. Numeric stack can be added in later: a bigint library, for example, built inside cell.
Simplify the property attributes stuff. It is simple: objects have text keys and whatever values. Objects can also have objects as values. These work like symbols. You can share them, if desired. No well known symbols exist to eliminate that much misdirection. Obejcts basically work like private keys. If you serialize an object, objects that are keys are not serialized; only textual keys are. You can do something about it with a json() method that is invoked, if you desire. You cannot retrieve
var works like let; use var instead of let
no const
Function closures and _ => all work the same and close over the 'this' variable
Totally delete modules, coroutines, generators, proxy .. this deletes a lot of the big switch statement
Add the 'go' statement for tail calls
Add the 'do' statement
Implementation detail: separate out arrays and objects. They are not the same. Objects no longer need to track if they're fast arrays or not. They're not. Arrays are. Always.
Add the functional proxy idea. Log will be implemented through that.
Remove ===; it's just == now, and !=.
Remove 'continue'; now, break handles both. For a do statement, label it, and break to that label; so
var x = 0
do loop {
x++
if (x < 5) break loop // goes back to loop
break // exits loop
}
rename instanceof to 'is'
remove undefined; all are 'null' now
remove 'delete'; to remove a field, assign it to null
remove with
Remove Object. New records have a prototype of nothing. There are no more 'type prototypes' at all.
Arrays are their own type
Remove property descriptors. Properties are always settable, unless the object as a whole is stone. Stone is an object property instead of a shape property.
Syntax stuff .. would like to invoke functions without (). This can effectively simulate a "getter". Make ? and all other characters usable for names. No reserve words, which are endlessly irritating.
----
This will all actually come about gradually. Add a few things at a time, fix up code that did not adhere. For a lot of this, no new functions will even need to be written; it's a matter of not calling certain functions that are no longer relevant, or calling different functions when required.
## Benchmarks to implement
### general speed
binarytrees
coro-prime-sieve
edigits
fannkuch-redux
fasta
http-server
json serialize/deserialize
knucleotide
lru
mandelbrot
merkletrees
nbody
nsieve
pidigits
regex-redux
secp256k1
spectral-norm
### function calling and recursion stress - test goto
naive recursive fibonacci [fib(35) or fib(40)]
tak
ackermann
### numeric
sieve of eratosthenes [10^7 bits]
spectral norm [5500 x 5500 matrix]
n-body sim [50 000 - 100 000 steps]
mandelbrot [1600x1200 image, max iter = 50]
### memory & gc torture
binary trees [depth 18 (~500 000 nodes)]
richards task scheduler
fannkuch redux [n=11 or 12]
### dynamic object & property access
deltablue constraint solver
splay tree [256k nodes]
json, wota, nota decode->encode [use 2MB example]
### string / regex kernels
regex-DNA
fasta
word-frequency
### concurrency/message passing
ping-pong [two actors exhange a small record N times, 1M messages end to end]
chameneos [mating color swap game w/ randezvous]
For all, track memory and time.

View File

@@ -115,11 +115,20 @@ endif
if host_machine.system() == 'linux'
deps += cc.find_library('asound', required:true)
deps += [dependency('x11'), dependency('xi'), dependency('xcursor'), dependency('egl'), dependency('gl')]
deps += cc.find_library('blas', required:true)
deps += cc.find_library('lapack', required:true)
endif
if host_machine.system() == 'windows'
deps += cc.find_library('d3d11')
deps += cc.find_library('ws2_32', required:true)
# For Windows, you may need to install OpenBLAS or Intel MKL
# and adjust these library names accordingly
deps += cc.find_library('openblas', required:false)
if not cc.find_library('openblas', required:false).found()
deps += cc.find_library('blas', required:false)
deps += cc.find_library('lapack', required:false)
endif
deps += cc.find_library('dbghelp')
deps += cc.find_library('winmm')
deps += cc.find_library('setupapi')
@@ -152,23 +161,6 @@ else
endif
endif
quickjs_opts = []
quickjs_opts += 'default_library=static'
# Enable leak detection for non-release builds
if get_option('buildtype') != 'release'
quickjs_opts += 'leaks=true'
endif
# Try to find system-installed quickjs first
quickjs_dep = dependency('quickjs', static: true, required: false)
if not quickjs_dep.found()
message('⚙ System quickjs not found, building subproject...')
deps += dependency('quickjs', static:true, default_options:quickjs_opts)
else
deps += quickjs_dep
endif
# Try to find system-installed qjs-layout first
qjs_layout_dep = dependency('qjs-layout', static: true, required: false)
if not qjs_layout_dep.found()
@@ -204,6 +196,7 @@ else
endif
deps += dependency('threads')
deps += dependency('mimalloc')
# Try to find system-installed chipmunk first
chipmunk_dep = dependency('chipmunk', static: true, required: false)
@@ -225,7 +218,7 @@ if host_machine.system() != 'emscripten'
endif
src += 'qjs_enet.c'
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true']
tracy_opts = ['fibers=true', 'no_exit=true', 'libunwind_backtrace=true', 'on_demand=true']
add_project_arguments('-DTRACY_ENABLE', language:['c','cpp'])
# Try to find system-installed tracy first
@@ -295,7 +288,7 @@ src += [
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c'
]
# quirc src
src += [
@@ -303,6 +296,9 @@ src += [
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
]
# quickjs src
src += ['quickjs.c', 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
imsrc = [
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',

View File

@@ -7,62 +7,22 @@ A collection of 2D drawing functions that create drawing command lists.
These are pure functions that return plain JavaScript objects representing
drawing operations. No rendering or actor communication happens here.
`
// Create a new command list
draw.list = function() {
var commands = []
return {
// Add a command to this list
push: function(cmd) {
commands.push(cmd)
},
// Get all commands
get: function() {
return commands
},
// Clear all commands
clear: function() {
commands = []
},
// Get command count
length: function() {
return commands.length
}
}
}
// Default command list for convenience
var current_list = draw.list()
// Set the current list
draw.set_list = function(list) {
current_list = list
}
// Get current list
draw.get_list = function() {
return current_list
}
var current_list = []
// Clear current list
draw.clear = function() {
current_list.clear()
current_list.length = 0
}
// Get commands from current list
draw.get_commands = function() {
return current_list.get()
return current_list
}
// Helper to add a command
function add_command(type, data) {
var cmd = {cmd: type}
Object.assign(cmd, data)
current_list.push(cmd)
data.cmd = type
current_list.push(data)
}
// Default geometry definitions
@@ -115,13 +75,6 @@ draw.point = function(pos, size, opt = {}, material) {
material: material
})
}
draw.point[cell.DOC] = `
:param pos: A 2D position ([x, y]) where the point should be drawn.
:param size: The size of the point.
:param opt: Optional geometry properties.
:param material: Material/styling information (color, shaders, etc.)
:return: None
`
draw.ellipse = function(pos, radii, def, material) {
var opt = def ? {...ellipse_def, ...def} : ellipse_def
@@ -184,22 +137,20 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info
})
}
draw.image = function image(image, rect, rotation = 0, anchor = [0,0], shear = [0,0], info = {}, material) {
draw.image = function image(image, rect, rotation, anchor, shear, info, material) {
if (!rect) throw Error('Need rectangle to render image.')
if (!image) throw Error('Need an image to render.')
if (!('x' in rect && 'y' in rect)) throw Error('Must provide X and Y for image.')
info = Object.assign({}, image_info, info);
add_command("draw_image", {
image: image,
rect: rect,
rotation: rotation,
anchor: anchor,
shear: shear,
info: info,
material: material
image,
rect,
rotation,
anchor,
shear,
info,
material,
})
}

View File

@@ -27,14 +27,13 @@ graphics.Image = function(surfaceData) {
this[GPU] = undefined;
this[LOADING] = false;
this[LASTUSE] = time.number();
this.rect = {x:0, y:0, width:1, height:1};
this.rect = {x:0, y:0, width:surfaceData.width, height:surfaceData.height};
}
// Define getters and methods on the prototype
Object.defineProperties(graphics.Image.prototype, {
gpu: {
get: function() {
this[LASTUSE] = time.number();
if (!this[GPU] && !this[LOADING]) {
this[LOADING] = true;
var self = this;
@@ -67,9 +66,6 @@ Object.defineProperties(graphics.Image.prototype, {
cpu: {
get: function() {
this[LASTUSE] = time.number();
// Note: Reading texture back from GPU requires async operation
// For now, return the CPU data if available
return this[CPU]
}
},
@@ -253,7 +249,7 @@ graphics.texture = function texture(path) {
if (typeof path !== 'string')
throw new Error('need a string for graphics.texture')
var id = path.split(':')[0]
var id = path //.split(':')[0]
if (cache.has(id)) return cache.get(id)
var ipath = res.find_image(id)

View File

@@ -3,11 +3,21 @@ var io = use('io');
var transform = use('transform');
var rasterize = use('rasterize');
var time = use('time')
var tilemap = use('tilemap')
// Frame timing variables
var frame_times = []
var frame_time_index = 0
var max_frame_samples = 60
var frame_start_time = 0
var average_frame_time = 0
var game = args[0]
var video
var cnf = use('accio/config')
$_.start(e => {
if (e.type !== 'greet') return
video = e.actor
@@ -17,73 +27,79 @@ $_.start(e => {
if (gameactor) return
gameactor = e.actor
$_.couple(gameactor)
loop()
}, args[0])
start_pipeline()
}, args[0], $_)
})
}, 'prosperon/sdl_video', {
title: "Prosperon",
width:500,
height:500
})
log.console('starting...')
var input = use('input')
}, 'prosperon/sdl_video', cnf)
var geometry = use('geometry')
function worldToScreenRect({x,y,width,height}, camera, winW, winH) {
var bl = worldToScreenPoint([x,y], camera, winW, winH)
var tr = worldToScreenPoint([x+width, y+height], camera, winW, winH)
function updateCameraMatrix(camera, winW, winH) {
// world→NDC
const sx = 1 / camera.size[0];
const sy = 1 / camera.size[1];
const ox = camera.pos[0] - camera.size[0] * camera.anchor[0];
const oy = camera.pos[1] - camera.size[1] * camera.anchor[1];
// NDC→pixels
const vx = camera.viewport.x * winW;
const vy = camera.viewport.y * winH;
const vw = camera.viewport.width * winW;
const vh = camera.viewport.height * winH;
// final “mat” coefficients
// [ a 0 c ]
// [ 0 e f ]
// [ 0 0 1 ]
camera.a = sx * vw;
camera.c = vx - camera.a * ox;
camera.e = -sy * vh;
camera.f = vy + vh + sy * vh * oy;
// and store the inverses so we can go back cheaply
camera.ia = 1 / camera.a;
camera.ic = -camera.c * camera.ia;
camera.ie = 1 / camera.e;
camera.if = -camera.f * camera.ie;
}
//---- forward transform ----
function worldToScreenPoint(pos, camera) {
return {
x: Math.min(bl.x, tr.x),
y: Math.min(bl.y, tr.y),
width: Math.abs(tr.x - bl.x),
height: Math.abs(tr.y - bl.y)
}
x: camera.a * pos[0] + camera.c,
y: camera.e * pos[1] + camera.f
};
}
function worldToScreenPoint([wx, wy], camera, winW, winH) {
// 1) worldwindow origin (bottomleft)
const worldX0 = camera.pos[0] - camera.size[0] * camera.anchor[0];
const worldY0 = camera.pos[1] - camera.size[1] * camera.anchor[1];
// 2) normalized device coords [0..1]
const ndcX = (wx - worldX0) / camera.size[0];
const ndcY = (wy - worldY0) / camera.size[1];
// 3) map into pixelspace via the fractional viewport
const px = camera.viewport.x * winW
+ ndcX * (camera.viewport.width * winW);
const py = camera.viewport.y * winH
+ (1 - ndcY) * (camera.viewport.height * winH);
return [ px, py ];
//---- inverse transform ----
function screenToWorldPoint(pos, camera) {
return {
x: camera.ia * pos[0] + camera.ic,
y: camera.ie * pos[1] + camera.if
};
}
function screenToWorldPoint([px, py], camera, winW, winH) {
// 1) undo pixel→NDC within the cameras viewport
const ndcX = (px - camera.viewport.x * winW)
/ (camera.viewport.width * winW)
const ndcY = 1 - (py - camera.viewport.y * winH)
/ (camera.viewport.height * winH)
//---- rectangle (two corner) ----
function worldToScreenRect(rect, camera) {
// map bottom-left and top-right
const x1 = camera.a * rect.x + camera.c;
const y1 = camera.e * rect.y + camera.f;
const x2 = camera.a * (rect.x + rect.width) + camera.c;
const y2 = camera.e * (rect.y + rect.height) + camera.f;
// 2) compute the worldwindow origin (bottomleft)
const worldX0 = camera.pos[0]
- camera.size[0] * camera.anchor[0]
const worldY0 = camera.pos[1]
- camera.size[1] * camera.anchor[1]
// 3) map NDC back to world coords
return [
ndcX * camera.size[0] + worldX0,
ndcY * camera.size[1] + worldY0
]
// pick mins and abs deltas
const x0 = x1 < x2 ? x1 : x2;
const y0 = y1 < y2 ? y1 : y2;
return {
x: x0,
y: y0,
width: x2 > x1 ? x2 - x1 : x1 - x2,
height: y2 > y1 ? y2 - y1 : y1 - y2
};
}
var camera = {
size: [500,500],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
size: [640,480],//{width:500,height:500}, // pixel size the camera "sees", like its resolution
pos: [250,250],//{x:0,y:0}, // where it is
fov:50,
near_z:0,
@@ -104,10 +120,15 @@ var gameactor
var images = {}
var renderer_commands = []
// Convert high-level draw commands to low-level renderer commands
function translate_draw_commands(commands) {
if (!graphics) return
var renderer_commands = []
updateCameraMatrix(camera,500,500)
renderer_commands.length = 0
commands.forEach(function(cmd) {
if (cmd.material && cmd.material.color) {
@@ -120,7 +141,7 @@ function translate_draw_commands(commands) {
switch(cmd.cmd) {
case "draw_rect":
cmd.rect = worldToScreenRect(cmd.rect, camera,500, 500)
cmd.rect = worldToScreenRect(cmd.rect, camera)
// Handle rectangles with optional rounding and thickness
if (cmd.opt && cmd.opt.radius && cmd.opt.radius > 0) {
// Rounded rectangle
@@ -165,7 +186,7 @@ function translate_draw_commands(commands) {
case "draw_circle":
case "draw_ellipse":
cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500)
cmd.pos = worldToScreenPoint(cmd.pos, camera)
// Rasterize ellipse to points or rects
var radii = cmd.radii || [cmd.radius, cmd.radius]
var raster_result = rasterize.ellipse(cmd.pos, radii, cmd.opt || {})
@@ -187,12 +208,12 @@ function translate_draw_commands(commands) {
case "draw_line":
renderer_commands.push({
op: "line",
data: {points: cmd.points.map(p => worldToScreenPoint(p, camera, 500, 500))}
data: {points: cmd.points.map(p => worldToScreenPoint(p, camera))}
})
break
case "draw_point":
cmd.pos = worldToScreenPoint(cmd.pos, camera, 500, 500)
cmd.pos = worldToScreenPoint(cmd.pos, camera)
renderer_commands.push({
op: "point",
data: {points: [cmd.pos]}
@@ -201,18 +222,19 @@ function translate_draw_commands(commands) {
case "draw_image":
var img = graphics.texture(cmd.image)
if (!img.gpu) break
var gpu = img.gpu
if (!gpu) break
cmd.rect.width ??= img.width
cmd.rect.height ??= img.height
cmd.rect = worldToScreenRect(cmd.rect, camera, 500, 500)
cmd.rect = worldToScreenRect(cmd.rect, camera)
renderer_commands.push({
op: "texture",
data: {
texture_id: img.gpu.id,
texture_id: gpu.id,
dst: cmd.rect,
src: {x:0,y:0,width:img.width,height:img.height},
src: img.rect
}
})
break
@@ -230,66 +252,162 @@ function translate_draw_commands(commands) {
}
})
break
case "tilemap":
var texid
tilemap.for(cmd.tilemap, (tile,{x,y}) => {
if (!texid) texid = graphics.texture(tile)
return graphics.texture(tile)
})
var geom = geometry.tilemap_to_data(cmd.tilemap)
if (texid.gpu)
geom.texture_id = texid.gpu.id
renderer_commands.push({
op: "geometry_raw",
data: geom
})
break
}
})
return renderer_commands
}
function loop(time)
{
send(video, {kind:'input', op:'get'}, e => {
for (var event of e) {
if (event.type === 'quit')
$_.stop()
}
})
var parseq = use('parseq', $_.delay)
send(gameactor, {kind:'update', dt:1/60}, e => {
send(gameactor, {kind:'draw'}, draw_commands => {
var batch_commands = []
batch_commands.push({
op: "set",
prop: "drawColor",
value: [0.1,0.1,0.15,1]
})
// Clear the screen
batch_commands.push({
op: "clear"
})
if (draw_commands && draw_commands.length > 0) {
var renderer_commands = translate_draw_commands(draw_commands)
batch_commands = batch_commands.concat(renderer_commands)
}
batch_commands.push({
op: "present"
})
send(video, {
kind: "renderer",
op: "batch",
data: batch_commands
}, _ => {
})
// Wrap `send(actor,msg,cb)` into a parseq “requestor”
// • on success: cb(data) → value=data, reason=undefined
// • on failure: cb(undefined,err)
function rpc_req(actor, msg) {
return (cb, _) => {
send(actor, msg, data => {
if (data.error)
cb(undefined, data)
else
cb(data)
})
})
$_.delay(loop, 1/30)
}
}
var game_rec = parseq.sequence([
rpc_req(gameactor, {kind:'update', dt:1/60}),
rpc_req(gameactor, {kind:'draw'})
])
var pending_draw = null
var pending_next = null
var last_time = time.number()
var frames = []
var frame_avg = 0
var input = use('input')
var input_state = {
poll: 1/60
}
// 1) input runs completely independently
function poll_input() {
send(video, {kind:'input', op:'get'}, evs => {
for (var ev of evs) {
if (ev.type === 'quit')
$_.stop()
if (ev.type.includes('mouse')) {
if (ev.pos)
ev.pos = screenToWorldPoint(ev.pos, camera, 500,500)
if (ev.d_pos)
ev.d_pos.y *= -1
}
if (ev.type.includes('key')) {
if (ev.key)
ev.key = input.keyname(ev.key)
}
}
send(gameactor, evs)
})
$_.delay(poll_input, input_state.poll)
}
// 2) helper to build & send a batch, then call done()
function create_batch(draw_cmds, done) {
const batch = [
{op:'set', prop:'drawColor', value:[0.1,0.1,0.15,1]},
{op:'clear'}
]
if (draw_cmds && draw_cmds.length)
batch.push(...translate_draw_commands(draw_cmds))
batch.push(
{op:'set', prop:'drawColor', value:[1,1,1,1]},
{op:'debugText', data:{pos:{x:10,y:10}, text:`Fps: ${(1/frame_avg).toFixed(2)}`}},
{op:'present'}
)
send(video, {kind:'renderer', op:'batch', data:batch}, () => {
const now = time.number()
const dt = now - last_time
last_time = now
frames.push(dt)
if (frames.length > 60) frames.shift()
let sum = 0
for (let f of frames) sum += f
frame_avg = sum / frames.length
done(dt)
})
}
// 3) kick off the very first update→draw
function start_pipeline() {
poll_input()
send(gameactor, {kind:'update', dt:1/60}, () => {
send(gameactor, {kind:'draw'}, cmds => {
pending_draw = cmds
render_step()
})
})
}
function render_step() {
// a) fire off the next update→draw immediately
const dt = time.number() - last_time
send(gameactor, {kind:'update', dt:1/60}, () =>
send(gameactor, {kind:'draw'}, cmds => pending_next = cmds)
)
// c) render the current frame
create_batch(pending_draw, ttr => { // time to render
// only swap in when there's a new set of commands
if (pending_next) {
pending_draw = pending_next
pending_next = null
}
// d) schedule the next render step
const render_dur = time.number() - last_time
const wait = Math.max(0, 1/60 - ttr)
$_.delay(render_step, 0)
})
}
$_.receiver(e => {
if (e.type === 'quit')
$_.stop()
if (e.type.includes('mouse')) {
if (e.pos)
e.pos = screenToWorldPoint(e.pos, camera, 500, 500)
if (e.d_pos)
e.d_pos.y *= -1
switch(e.op) {
case 'resolution':
log.console(json.encode(e))
send(video, {
kind:'renderer',
op:'set',
prop:'logicalPresentation',
value: {...e}
})
break
}
})

View File

@@ -217,223 +217,251 @@ function handle_window(msg) {
}
}
// Renderer operation functions
var renderfuncs = {
destroy: function(msg) {
ren = undefined
return {success: true};
},
clear: function(msg) {
ren.clear();
return {success: true};
},
present: function(msg) {
ren.present();
return {success: true};
},
flush: function(msg) {
ren.flush();
return {success: true};
},
get: function(msg) {
var prop = msg.data ? msg.data.property : null;
if (!prop) return {error: "Missing property name"};
// Handle special getters that might return objects
if (prop === 'drawColor') {
var color = ren[prop];
if (color && typeof color === 'object') {
// Convert color object to array format [r,g,b,a]
return {data: [color.r || 0, color.g || 0, color.b || 0, color.a || 255]};
}
}
return {data: ren[prop]};
},
set: function(msg) {
var prop = msg.prop
var value = msg.value
if (!prop) return {error: "Missing property name"};
if (!value) return {error: "No value to set"}
// Validate property is settable
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
if (readonly.indexOf(prop) !== -1) {
return {error: "Property '" + prop + "' is read-only"};
}
// Special handling for render target
if (prop === 'target' && value !== null && value !== undefined) {
var tex = resources.texture[value];
if (!tex) return {error: "Invalid texture id"};
value = tex;
}
ren[prop] = value;
return {success: true};
},
line: function(msg) {
if (!msg.data || !msg.data.points) return {error: "Missing points array"};
ren.line(msg.data.points);
return {success: true};
},
point: function(msg) {
if (!msg.data || !msg.data.points) return {error: "Missing points"};
ren.point(msg.data.points);
return {success: true};
},
rect: function(msg) {
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.rect(msg.data.rect);
return {success: true};
},
fillRect: function(msg) {
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.fillRect(msg.data.rect);
return {success: true};
},
rects: function(msg) {
if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
ren.rects(msg.data.rects);
return {success: true};
},
lineTo: function(msg) {
if (!msg.data || !msg.data.a || !msg.data.b) return {error: "Missing points a and b"};
ren.lineTo(msg.data.a, msg.data.b);
return {success: true};
},
texture: function(msg) {
if (!msg.data) return {error: "Missing texture data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.texture(
resources.texture[tex_id],
msg.data.src,
msg.data.dst,
msg.data.angle || 0,
msg.data.anchor || {x:0.5, y:0.5}
);
return {success: true};
},
copyTexture: function(msg) {
if (!msg.data) return {error: "Missing texture data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
var tex = resources.texture[tex_id];
// Use the texture method with normalized coordinates
ren.texture(
tex,
msg.data.src || {x:0, y:0, width:tex.width, height:tex.height},
msg.data.dest || {x:0, y:0, width:tex.width, height:tex.height},
0, // No rotation
{x:0, y:0} // Top-left anchor
);
return {success: true};
},
sprite: function(msg) {
if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
ren.sprite(msg.data.sprite);
return {success: true};
},
geometry: function(msg) {
if (!msg.data) return {error: "Missing geometry data"};
var tex_id = msg.data.texture_id;
var tex = tex_id ? resources.texture[tex_id] : null;
ren.geometry(tex, msg.data.geometry);
return {success: true};
},
geometry_raw: function geometry_raw(msg) {
var geom = msg.data
ren.geometry_raw(resources.texture[geom.texture_id], geom.xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices);
},
debugText: function(msg) {
if (!msg.data || !msg.data.text) return {error: "Missing text"};
ren.debugText([msg.data.pos.x, msg.data.pos.y], msg.data.text);
return {success: true};
},
clipEnabled: function(msg) {
return {data: ren.clipEnabled()};
},
texture9Grid: function(msg) {
if (!msg.data) return {error: "Missing data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.texture9Grid(
resources.texture[tex_id],
msg.data.src,
msg.data.leftWidth,
msg.data.rightWidth,
msg.data.topHeight,
msg.data.bottomHeight,
msg.data.scale,
msg.data.dst
);
return {success: true};
},
textureTiled: function(msg) {
if (!msg.data) return {error: "Missing data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.textureTiled(
resources.texture[tex_id],
msg.data.src,
msg.data.scale || 1.0,
msg.data.dst
);
return {success: true};
},
readPixels: function(msg) {
var surf = ren.readPixels(msg.data ? msg.data.rect : null);
if (!surf) return {error: "Failed to read pixels"};
var surf_id = allocate_id();
resources.surface[surf_id] = surf;
return {id: surf_id};
},
loadTexture: function(msg) {
if (!msg.data) throw new Error("Missing data")
var tex;
// Direct surface data
var surf = new surface(msg.data)
if (!surf)
throw new Error("Must provide surface_id or surface data")
tex = ren.load_texture(surf);
if (!tex) throw new Error("Failed to load texture")
var tex_id = allocate_id();
resources.texture[tex_id] = tex;
return {
id: tex_id,
};
},
coordsFromWindow: function(msg) {
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsFromWindow(msg.data.pos)};
},
coordsToWindow: function(msg) {
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsToWindow(msg.data.pos)};
},
batch: function(msg) {
if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
for (var i = 0; i < msg.data.length; i++)
handle_renderer(msg.data[i]);
return {success:true};
}
};
// Renderer operations
function handle_renderer(msg) {
if (!ren) return{reason:'no renderer!'}
switch (msg.op) {
case 'destroy':
ren = undefined
return {success: true};
case 'clear':
ren.clear();
return {success: true};
case 'present':
ren.present();
return {success: true};
case 'flush':
ren.flush();
return {success: true};
case 'get':
var prop = msg.data ? msg.data.property : null;
if (!prop) return {error: "Missing property name"};
// Handle special getters that might return objects
if (prop === 'drawColor') {
var color = ren[prop];
if (color && typeof color === 'object') {
// Convert color object to array format [r,g,b,a]
return {data: [color.r || 0, color.g || 0, color.b || 0, color.a || 255]};
}
}
return {data: ren[prop]};
case 'set':
var prop = msg.prop
var value = msg.value
if (!prop) return {error: "Missing property name"};
if (!value) return {error: "No value to set"}
// Validate property is settable
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
if (readonly.indexOf(prop) !== -1) {
return {error: "Property '" + prop + "' is read-only"};
}
// Special handling for render target
if (prop === 'target' && value !== null && value !== undefined) {
var tex = resources.texture[value];
if (!tex) return {error: "Invalid texture id"};
value = tex;
}
ren[prop] = value;
return {success: true};
case 'line':
if (!msg.data || !msg.data.points) return {error: "Missing points array"};
ren.line(msg.data.points);
return {success: true};
case 'point':
if (!msg.data || !msg.data.points) return {error: "Missing points"};
ren.point(msg.data.points);
return {success: true};
case 'rect':
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.rect(msg.data.rect);
return {success: true};
case 'fillRect':
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.fillRect(msg.data.rect);
return {success: true};
case 'rects':
if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
ren.rects(msg.data.rects);
return {success: true};
case 'lineTo':
if (!msg.data || !msg.data.a || !msg.data.b) return {error: "Missing points a and b"};
ren.lineTo(msg.data.a, msg.data.b);
return {success: true};
case 'texture':
if (!msg.data) return {error: "Missing texture data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.texture(
resources.texture[tex_id],
msg.data.src,
msg.data.dst,
msg.data.angle || 0,
msg.data.anchor || {x:0.5, y:0.5}
);
return {success: true};
case 'copyTexture':
if (!msg.data) return {error: "Missing texture data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
var tex = resources.texture[tex_id];
// Use the texture method with normalized coordinates
ren.texture(
tex,
msg.data.src || {x:0, y:0, width:tex.width, height:tex.height},
msg.data.dest || {x:0, y:0, width:tex.width, height:tex.height},
0, // No rotation
{x:0, y:0} // Top-left anchor
);
return {success: true};
case 'sprite':
if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
ren.sprite(msg.data.sprite);
return {success: true};
case 'geometry':
if (!msg.data) return {error: "Missing geometry data"};
var tex_id = msg.data.texture_id;
var tex = tex_id ? resources.texture[tex_id] : null;
ren.geometry(tex, msg.data.geometry);
return {success: true};
case 'debugText':
if (!msg.data || !msg.data.text) return {error: "Missing text"};
ren.debugText([msg.data.pos.x, msg.data.pos.y], msg.data.text);
return {success: true};
case 'clipEnabled':
return {data: ren.clipEnabled()};
case 'texture9Grid':
if (!msg.data) return {error: "Missing data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.texture9Grid(
resources.texture[tex_id],
msg.data.src,
msg.data.leftWidth,
msg.data.rightWidth,
msg.data.topHeight,
msg.data.bottomHeight,
msg.data.scale,
msg.data.dst
);
return {success: true};
case 'textureTiled':
if (!msg.data) return {error: "Missing data"};
var tex_id = msg.data.texture_id;
if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
ren.textureTiled(
resources.texture[tex_id],
msg.data.src,
msg.data.scale || 1.0,
msg.data.dst
);
return {success: true};
case 'readPixels':
var surf = ren.readPixels(msg.data ? msg.data.rect : null);
if (!surf) return {error: "Failed to read pixels"};
var surf_id = allocate_id();
resources.surface[surf_id] = surf;
return {id: surf_id};
case 'loadTexture':
if (!msg.data) throw new Error("Missing data")
var tex;
// Direct surface data
var surf = new surface(msg.data)
if (!surf)
throw new Error("Must provide surface_id or surface data")
tex = ren.load_texture(surf);
if (!tex) throw new Error("Failed to load texture")
var tex_id = allocate_id();
resources.texture[tex_id] = tex;
return {
id: tex_id,
};
case 'flush':
ren.flush();
return {success: true};
case 'coordsFromWindow':
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsFromWindow(msg.data.pos)};
case 'coordsToWindow':
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsToWindow(msg.data.pos)};
case 'batch':
if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
var results = [];
for (var i = 0; i < msg.data.length; i++) {
var result = handle_renderer(msg.data[i]);
results.push(result);
}
return {success:true};
default:
return {error: "Unknown renderer operation: " + msg.op};
var func = renderfuncs[msg.op];
if (func) {
return func(msg);
} else {
return {error: "Unknown renderer operation: " + msg.op};
}
}

78
prosperon/tilemap.cm Normal file
View File

@@ -0,0 +1,78 @@
// tilemap
function tilemap()
{
this.tiles = [];
this.offset_x = 0;
this.offset_y = 0;
this.size_x = 32;
this.size_y = 32;
return this;
}
tilemap.for = function (map, fn) {
for (var x = 0; x < map.tiles.length; x++) {
if (!map.tiles[x]) continue;
for (var y = 0; y < map.tiles[x].length; y++) {
if (map.tiles[x][y] !== undefined) {
var result = fn(map.tiles[x][y], {
x: x + map.offset_x,
y: y + map.offset_y
});
if (result !== undefined) {
map.tiles[x][y] = result;
}
}
}
}
}
tilemap.prototype =
{
at(pos) {
var x = pos.x - this.offset_x;
var y = pos.y - this.offset_y;
if (!this.tiles[x]) return undefined;
return this.tiles[x][y];
},
set(pos, image) {
// Shift arrays if negative indices
if (pos.x < this.offset_x) {
var shift = this.offset_x - pos.x;
var new_tiles = [];
for (var i = 0; i < shift; i++) new_tiles[i] = [];
this.tiles = new_tiles.concat(this.tiles);
this.offset_x = pos.x;
}
if (pos.y < this.offset_y) {
var shift = this.offset_y - pos.y;
for (var i = 0; i < this.tiles.length; i++) {
if (!this.tiles[i]) this.tiles[i] = [];
var new_col = [];
for (var j = 0; j < shift; j++) new_col[j] = undefined;
this.tiles[i] = new_col.concat(this.tiles[i]);
}
this.offset_y = pos.y;
}
var x = pos.x - this.offset_x;
var y = pos.y - this.offset_y;
// Ensure array exists up to x
while (this.tiles.length <= x) this.tiles.push([]);
// Set the value
this.tiles[x][y] = image;
},
draw() {
return {
cmd:'tilemap',
tilemap:this
}
},
}
return tilemap

View File

@@ -43,7 +43,7 @@ var console_mod = cell.hidden.console
var logs = {}
logs.console = function(msg)
{
var caller = caller_data(4)
var caller = caller_data(2)
console_mod.print(console_rec(caller.line, caller.file, msg))
}
@@ -82,6 +82,10 @@ var use_dyn = hidden.use_dyn
var enet = hidden.enet
var nota = hidden.nota
// Wota decode timing tracking
var wota_decode_times = []
var last_wota_flush = 0
// Strip hidden from cell so nothing else can access it
delete cell.hidden
@@ -831,4 +835,6 @@ $_.clock(_ => {
throw new Error('Program must not return anything');
})
log.console(`startup took ${time.number()-st_now}`)
})()

View File

@@ -41,6 +41,8 @@
#ifdef TRACY_ENABLE
#include <tracy/TracyC.h>
#endif
#if defined(__APPLE__)
#include <malloc/malloc.h>
#define MALLOC_OVERHEAD 0
@@ -51,10 +53,12 @@
#define _GNU_SOURCE
#include <malloc.h>
#define MALLOC_OVERHEAD 8
#else
#define MALLOC_OVERHEAD 0
#endif
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
int tracy_profiling_enabled = 0;
@@ -184,6 +188,7 @@ void actor_free(cell_rt *actor)
SDL_UnlockMutex(actor->msg_mutex);
SDL_DestroyMutex(actor->msg_mutex);
mi_heap_destroy(actor->heap);
free(actor);
SDL_LockMutex(actors_mutex);
@@ -197,10 +202,7 @@ void js_dofree(JSRuntime *rt, void *opaque, void *ptr)
js_free_rt(rt, ptr);
}
SDL_TLSID prosperon_id;
#ifdef TRACY_ENABLE
static size_t js_tracy_malloc_usable_size(const void *ptr)
static size_t js_mi_malloc_usable_size(const void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
@@ -215,56 +217,86 @@ static size_t js_tracy_malloc_usable_size(const void *ptr)
#endif
}
static void *js_tracy_malloc(JSMallocState *s, size_t size)
{
void *js_mi_malloc(JSMallocState *s, size_t sz) {
void *ptr;
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit)) return NULL;
ptr = malloc(size);
assert(sz != 0);
if (unlikely(s->malloc_size + sz > s->malloc_limit)) return NULL;
cell_rt *actor = (cell_rt*)s->opaque;
ptr = mi_heap_malloc(actor->heap, sz);
if (!ptr) return NULL;
s->malloc_count++;
s->malloc_size += js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
s->malloc_size += js_mi_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
TracyCAllocN(ptr, js_mi_malloc_usable_size(ptr) + MALLOC_OVERHEAD, actor->name);
#endif
return ptr;
}
static void js_tracy_free(JSMallocState *s, void *ptr)
{
if (!ptr) return;
void js_mi_free(JSMallocState *s, void *p) {
if (!p) return;
s->malloc_count--;
s->malloc_size -= js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
TracyCFreeN(ptr, "quickjs");
free(ptr);
s->malloc_size -= js_mi_malloc_usable_size(p) + MALLOC_OVERHEAD;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled) {
cell_rt *actor = s->opaque;
TracyCFreeN(p, actor->name);
}
#endif
mi_free(p);
}
static void *js_tracy_realloc(JSMallocState *s, void *ptr, size_t size)
{
void *js_mi_realloc(JSMallocState *s, void *p, size_t sz) {
size_t old_size;
if (!ptr) return size ? js_tracy_malloc(s, size) : NULL;
old_size = js_tracy_malloc_usable_size(ptr);
if (!size) {
cell_rt *actor = (cell_rt*)s->opaque;
if (!p) return sz ? js_mi_malloc(s, sz) : NULL;
old_size = js_mi_malloc_usable_size(p);
if (!sz) {
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
TracyCFreeN(ptr, "quickjs");
free(ptr);
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
TracyCFreeN(p, actor->name);
#endif
mi_free(p);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit) return NULL;
TracyCFreeN(ptr, "quickjs");
ptr = realloc(ptr, size);
if (!ptr) return NULL;
s->malloc_size += js_tracy_malloc_usable_size(ptr) - old_size;
TracyCAllocN(ptr, js_tracy_malloc_usable_size(ptr) + MALLOC_OVERHEAD, "quickjs");
return ptr;
if (s->malloc_size + sz - old_size > s->malloc_limit) return NULL;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
TracyCFreeN(p, actor->name);
#endif
p = mi_heap_realloc(actor->heap, p, sz);
if (!p) return NULL;
s->malloc_size += js_mi_malloc_usable_size(p) - old_size;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
TracyCAllocN(p, js_mi_malloc_usable_size(p) + MALLOC_OVERHEAD, actor->name);
#endif
return p;
}
static const JSMallocFunctions tracy_malloc_funcs = {
js_tracy_malloc,
js_tracy_free,
js_tracy_realloc,
js_tracy_malloc_usable_size
static const JSMallocFunctions mimalloc_funcs = {
js_mi_malloc,
js_mi_free,
js_mi_realloc,
js_mi_malloc_usable_size
};
#endif
static void free_zip(void)
{
@@ -364,6 +396,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
cell_rt *create_actor(void *wota)
{
cell_rt *actor = calloc(sizeof(*actor), 1);
actor->heap = mi_heap_new();
actor->init_wota = wota;
actor->idx_buffer = JS_UNDEFINED;
actor->message_handle = JS_UNDEFINED;
@@ -515,8 +548,11 @@ void actor_turn(cell_rt *actor)
SDL_LockMutex(actor->mutex);
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
int entered = 0;
if (tracy_profiling_enabled && TracyCIsConnected) {
TracyCFiberEnter(actor->name);
entered = 1;
}
#endif
actor->state = ACTOR_RUNNING;
@@ -560,7 +596,7 @@ void actor_turn(cell_rt *actor)
actor->state = ACTOR_IDLE;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
if (tracy_profiling_enabled && entered)
TracyCFiberLeave(actor->name);
#endif
@@ -698,14 +734,7 @@ void script_startup(cell_rt *prt)
{
JSRuntime *rt;
#ifdef TRACY_ENABLE
if (tracy_profiling_enabled)
rt = JS_NewRuntime2(&tracy_malloc_funcs, NULL);
else
rt = JS_NewRuntime();
#else
rt = JS_NewRuntime();
#endif
rt = JS_NewRuntime2(&mimalloc_funcs, prt);
JSContext *js = JS_NewContextRaw(rt);
JS_SetInterruptHandler(rt, actor_interrupt_cb, prt);

View File

@@ -7,6 +7,8 @@
#include "qjs_blob.h"
#include "blob.h"
#include <mimalloc.h>
/* Letter type for unified message queue */
typedef enum {
LETTER_BLOB, /* Blob message */
@@ -47,6 +49,7 @@ typedef struct {
typedef struct cell_rt {
JSContext *context;
mi_heap_t *heap;
JSValue idx_buffer;
JSValue on_exception;
JSValue message_handle;

633
source/cutils.c Normal file
View File

@@ -0,0 +1,633 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "cutils.h"
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
/* strcat and truncate. */
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
int has_suffix(const char *str, const char *suffix)
{
size_t len = strlen(str);
size_t slen = strlen(suffix);
return (len >= slen && !memcmp(str + len - slen, suffix, slen));
}
/* Dynamic buffer package */
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
{
memset(s, 0, sizeof(*s));
if (!realloc_func)
realloc_func = dbuf_default_realloc;
s->opaque = opaque;
s->realloc_func = realloc_func;
}
void dbuf_init(DynBuf *s)
{
dbuf_init2(s, NULL, NULL);
}
/* return < 0 if error */
int dbuf_realloc(DynBuf *s, size_t new_size)
{
size_t size;
uint8_t *new_buf;
if (new_size > s->allocated_size) {
if (s->error)
return -1;
size = s->allocated_size * 3 / 2;
if (size > new_size)
new_size = size;
new_buf = s->realloc_func(s->opaque, s->buf, new_size);
if (!new_buf) {
s->error = TRUE;
return -1;
}
s->buf = new_buf;
s->allocated_size = new_size;
}
return 0;
}
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
{
size_t end;
end = offset + len;
if (dbuf_realloc(s, end))
return -1;
memcpy(s->buf + offset, data, len);
if (end > s->size)
s->size = end;
return 0;
}
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy_no_ub(s->buf + s->size, data, len);
s->size += len;
return 0;
}
int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
{
if (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len))
return -1;
}
memcpy(s->buf + s->size, s->buf + offset, len);
s->size += len;
return 0;
}
int dbuf_putc(DynBuf *s, uint8_t c)
{
return dbuf_put(s, &c, 1);
}
int dbuf_putstr(DynBuf *s, const char *str)
{
return dbuf_put(s, (const uint8_t *)str, strlen(str));
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...)
{
va_list ap;
char buf[128];
int len;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len < 0)
return -1;
if (len < sizeof(buf)) {
/* fast case */
return dbuf_put(s, (uint8_t *)buf, len);
} else {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
va_start(ap, fmt);
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
fmt, ap);
va_end(ap);
s->size += len;
}
return 0;
}
void dbuf_free(DynBuf *s)
{
/* we test s->buf as a fail safe to avoid crashing if dbuf_free()
is called twice */
if (s->buf) {
s->realloc_func(s->opaque, s->buf, 0);
}
memset(s, 0, sizeof(*s));
}
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
are output. */
int unicode_to_utf8(uint8_t *buf, unsigned int c)
{
uint8_t *q = buf;
if (c < 0x80) {
*q++ = c;
} else {
if (c < 0x800) {
*q++ = (c >> 6) | 0xc0;
} else {
if (c < 0x10000) {
*q++ = (c >> 12) | 0xe0;
} else {
if (c < 0x00200000) {
*q++ = (c >> 18) | 0xf0;
} else {
if (c < 0x04000000) {
*q++ = (c >> 24) | 0xf8;
} else if (c < 0x80000000) {
*q++ = (c >> 30) | 0xfc;
*q++ = ((c >> 24) & 0x3f) | 0x80;
} else {
return 0;
}
*q++ = ((c >> 18) & 0x3f) | 0x80;
}
*q++ = ((c >> 12) & 0x3f) | 0x80;
}
*q++ = ((c >> 6) & 0x3f) | 0x80;
}
*q++ = (c & 0x3f) | 0x80;
}
return q - buf;
}
static const unsigned int utf8_min_code[5] = {
0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
};
static const unsigned char utf8_first_code_mask[5] = {
0x1f, 0xf, 0x7, 0x3, 0x1,
};
/* return -1 if error. *pp is not updated in this case. max_len must
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
{
int l, c, b, i;
c = *p++;
if (c < 0x80) {
*pp = p;
return c;
}
switch(c) {
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
case 0xc4: case 0xc5: case 0xc6: case 0xc7:
case 0xc8: case 0xc9: case 0xca: case 0xcb:
case 0xcc: case 0xcd: case 0xce: case 0xcf:
case 0xd0: case 0xd1: case 0xd2: case 0xd3:
case 0xd4: case 0xd5: case 0xd6: case 0xd7:
case 0xd8: case 0xd9: case 0xda: case 0xdb:
case 0xdc: case 0xdd: case 0xde: case 0xdf:
l = 1;
break;
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
case 0xec: case 0xed: case 0xee: case 0xef:
l = 2;
break;
case 0xf0: case 0xf1: case 0xf2: case 0xf3:
case 0xf4: case 0xf5: case 0xf6: case 0xf7:
l = 3;
break;
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
l = 4;
break;
case 0xfc: case 0xfd:
l = 5;
break;
default:
return -1;
}
/* check that we have enough characters */
if (l > (max_len - 1))
return -1;
c &= utf8_first_code_mask[l - 1];
for(i = 0; i < l; i++) {
b = *p++;
if (b < 0x80 || b >= 0xc0)
return -1;
c = (c << 6) | (b & 0x3f);
}
if (c < utf8_min_code[l - 1])
return -1;
*pp = p;
return c;
}
#if 0
#if defined(EMSCRIPTEN) || defined(__ANDROID__)
static void *rqsort_arg;
static int (*rqsort_cmp)(const void *, const void *, void *);
static int rqsort_cmp2(const void *p1, const void *p2)
{
return rqsort_cmp(p1, p2, rqsort_arg);
}
/* not reentrant, but not needed with emscripten */
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg)
{
rqsort_arg = arg;
rqsort_cmp = cmp;
qsort(base, nmemb, size, rqsort_cmp2);
}
#endif
#else
typedef void (*exchange_f)(void *a, void *b, size_t size);
typedef int (*cmp_f)(const void *, const void *, void *opaque);
static void exchange_bytes(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
while (size-- != 0) {
uint8_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_byte(void *a, void *b, size_t size) {
uint8_t *ap = (uint8_t *)a;
uint8_t *bp = (uint8_t *)b;
uint8_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int16s(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
for (size /= sizeof(uint16_t); size-- != 0;) {
uint16_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int16(void *a, void *b, size_t size) {
uint16_t *ap = (uint16_t *)a;
uint16_t *bp = (uint16_t *)b;
uint16_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int32s(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
for (size /= sizeof(uint32_t); size-- != 0;) {
uint32_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int32(void *a, void *b, size_t size) {
uint32_t *ap = (uint32_t *)a;
uint32_t *bp = (uint32_t *)b;
uint32_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int64s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t); size-- != 0;) {
uint64_t t = *ap;
*ap++ = *bp;
*bp++ = t;
}
}
static void exchange_one_int64(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = *ap;
*ap = *bp;
*bp = t;
}
static void exchange_int128s(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
}
static void exchange_one_int128(void *a, void *b, size_t size) {
uint64_t *ap = (uint64_t *)a;
uint64_t *bp = (uint64_t *)b;
uint64_t t = ap[0];
uint64_t u = ap[1];
ap[0] = bp[0];
ap[1] = bp[1];
bp[0] = t;
bp[1] = u;
}
static inline exchange_f exchange_func(const void *base, size_t size) {
switch (((uintptr_t)base | (uintptr_t)size) & 15) {
case 0:
if (size == sizeof(uint64_t) * 2)
return exchange_one_int128;
else
return exchange_int128s;
case 8:
if (size == sizeof(uint64_t))
return exchange_one_int64;
else
return exchange_int64s;
case 4:
case 12:
if (size == sizeof(uint32_t))
return exchange_one_int32;
else
return exchange_int32s;
case 2:
case 6:
case 10:
case 14:
if (size == sizeof(uint16_t))
return exchange_one_int16;
else
return exchange_int16s;
default:
if (size == 1)
return exchange_one_byte;
else
return exchange_bytes;
}
}
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
uint8_t *basep = (uint8_t *)base;
size_t i, n, c, r;
exchange_f swap = exchange_func(base, size);
if (nmemb > 1) {
i = (nmemb / 2) * size;
n = nmemb * size;
while (i > 0) {
i -= size;
for (r = i; (c = r * 2 + size) < n; r = c) {
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
for (i = n - size; i > 0; i -= size) {
swap(basep, basep + i, size);
for (r = 0; (c = r * 2 + size) < i; r = c) {
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
c += size;
if (cmp(basep + r, basep + c, opaque) > 0)
break;
swap(basep + r, basep + c, size);
}
}
}
}
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
{
return cmp(a, b, opaque) < 0 ?
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
}
/* pointer based version with local stack and insertion sort threshhold */
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
{
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
size_t m4, i, lt, gt, span, span2;
int c, depth;
exchange_f swap = exchange_func(base, size);
exchange_f swap_block = exchange_func(base, size | 128);
if (nmemb < 2 || size <= 0)
return;
sp->base = (uint8_t *)base;
sp->count = nmemb;
sp->depth = 0;
sp++;
while (sp > stack) {
sp--;
ptr = sp->base;
nmemb = sp->count;
depth = sp->depth;
while (nmemb > 6) {
if (++depth > 50) {
/* depth check to ensure worst case logarithmic time */
heapsortx(ptr, nmemb, size, cmp, opaque);
nmemb = 0;
break;
}
/* select median of 3 from 1/4, 1/2, 3/4 positions */
/* should use median of 5 or 9? */
m4 = (nmemb >> 2) * size;
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
swap(ptr, m, size); /* move the pivot to the start or the array */
i = lt = 1;
pi = plt = ptr + size;
gt = nmemb;
pj = pgt = top = ptr + nmemb * size;
for (;;) {
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
if (c == 0) {
swap(plt, pi, size);
lt++;
plt += size;
}
i++;
pi += size;
}
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
if (c == 0) {
gt--;
pgt -= size;
swap(pgt, pj, size);
}
}
if (pi >= pj)
break;
swap(pi, pj, size);
i++;
pi += size;
}
/* array has 4 parts:
* from 0 to lt excluded: elements identical to pivot
* from lt to pi excluded: elements smaller than pivot
* from pi to gt excluded: elements greater than pivot
* from gt to n excluded: elements identical to pivot
*/
/* move elements identical to pivot in the middle of the array: */
/* swap values in ranges [0..lt[ and [i-lt..i[
swapping the smallest span between lt and i-lt is sufficient
*/
span = plt - ptr;
span2 = pi - plt;
lt = i - lt;
if (span > span2)
span = span2;
swap_block(ptr, pi - span, span);
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[
swapping the smallest span between top-gt and gt-i is sufficient
*/
span = top - pgt;
span2 = pgt - pi;
pgt = top - span2;
gt = nmemb - (gt - i);
if (span > span2)
span = span2;
swap_block(pi, top - span, span);
/* now array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* stack the larger segment and keep processing the smaller one
to minimize stack use for pathological distributions */
if (lt > nmemb - gt) {
sp->base = ptr;
sp->count = lt;
sp->depth = depth;
sp++;
ptr = pgt;
nmemb -= gt;
} else {
sp->base = pgt;
sp->count = nmemb - gt;
sp->depth = depth;
sp++;
nmemb = lt;
}
}
/* Use insertion sort for small fragments */
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
swap(pj, pj - size, size);
}
}
}
#endif

423
source/cutils.h Normal file
View File

@@ -0,0 +1,423 @@
/*
* C utilities
*
* Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef container_of
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define minimum_length(n) static n
#else
#define minimum_length(n) n
#endif
typedef int BOOL;
#ifndef FALSE
enum {
FALSE = 0,
TRUE = 1,
};
#endif
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
if (n)
memcpy(dest, src, n);
}
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
static inline uint32_t max_uint32(uint32_t a, uint32_t b)
{
if (a > b)
return a;
else
return b;
}
static inline uint32_t min_uint32(uint32_t a, uint32_t b)
{
if (a < b)
return a;
else
return b;
}
static inline int64_t max_int64(int64_t a, int64_t b)
{
if (a > b)
return a;
else
return b;
}
static inline int64_t min_int64(int64_t a, int64_t b)
{
if (a < b)
return a;
else
return b;
}
/* WARNING: undefined if a = 0 */
static inline int clz32(unsigned int a)
{
return __builtin_clz(a);
}
/* WARNING: undefined if a = 0 */
static inline int clz64(uint64_t a)
{
return __builtin_clzll(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz32(unsigned int a)
{
return __builtin_ctz(a);
}
/* WARNING: undefined if a = 0 */
static inline int ctz64(uint64_t a)
{
return __builtin_ctzll(a);
}
struct __attribute__((packed)) packed_u64 {
uint64_t v;
};
struct __attribute__((packed)) packed_u32 {
uint32_t v;
};
struct __attribute__((packed)) packed_u16 {
uint16_t v;
};
static inline uint64_t get_u64(const uint8_t *tab)
{
return ((const struct packed_u64 *)tab)->v;
}
static inline int64_t get_i64(const uint8_t *tab)
{
return (int64_t)((const struct packed_u64 *)tab)->v;
}
static inline void put_u64(uint8_t *tab, uint64_t val)
{
((struct packed_u64 *)tab)->v = val;
}
static inline uint32_t get_u32(const uint8_t *tab)
{
return ((const struct packed_u32 *)tab)->v;
}
static inline int32_t get_i32(const uint8_t *tab)
{
return (int32_t)((const struct packed_u32 *)tab)->v;
}
static inline void put_u32(uint8_t *tab, uint32_t val)
{
((struct packed_u32 *)tab)->v = val;
}
static inline uint32_t get_u16(const uint8_t *tab)
{
return ((const struct packed_u16 *)tab)->v;
}
static inline int32_t get_i16(const uint8_t *tab)
{
return (int16_t)((const struct packed_u16 *)tab)->v;
}
static inline void put_u16(uint8_t *tab, uint16_t val)
{
((struct packed_u16 *)tab)->v = val;
}
static inline uint32_t get_u8(const uint8_t *tab)
{
return *tab;
}
static inline int32_t get_i8(const uint8_t *tab)
{
return (int8_t)*tab;
}
static inline void put_u8(uint8_t *tab, uint8_t val)
{
*tab = val;
}
#ifndef bswap16
static inline uint16_t bswap16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
#endif
#ifndef bswap32
static inline uint32_t bswap32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
#endif
#ifndef bswap64
static inline uint64_t bswap64(uint64_t v)
{
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
}
#endif
/* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
typedef struct DynBuf {
uint8_t *buf;
size_t size;
size_t allocated_size;
BOOL error; /* true if a memory allocation error occurred */
DynBufReallocFunc *realloc_func;
void *opaque; /* for realloc_func */
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size);
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str);
static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
{
return dbuf_put(s, (uint8_t *)&val, 2);
}
static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
{
return dbuf_put(s, (uint8_t *)&val, 4);
}
static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
static inline void dbuf_set_error(DynBuf *s)
{
s->error = TRUE;
}
#define UTF8_CHAR_LEN_MAX 6
int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline BOOL is_surrogate(uint32_t c)
{
return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF
}
static inline BOOL is_hi_surrogate(uint32_t c)
{
return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF
}
static inline BOOL is_lo_surrogate(uint32_t c)
{
return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF
}
static inline uint32_t get_hi_surrogate(uint32_t c)
{
return (c >> 10) - (0x10000 >> 10) + 0xD800;
}
static inline uint32_t get_lo_surrogate(uint32_t c)
{
return (c & 0x3FF) | 0xDC00;
}
static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo)
{
return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00);
}
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *),
void *arg);
static inline uint64_t float64_as_uint64(double d)
{
union {
double d;
uint64_t u64;
} u;
u.d = d;
return u.u64;
}
static inline double uint64_as_float64(uint64_t u64)
{
union {
double d;
uint64_t u64;
} u;
u.u64 = u64;
return u.d;
}
static inline double fromfp16(uint16_t v)
{
double d;
uint32_t v1;
v1 = v & 0x7fff;
if (unlikely(v1 >= 0x7c00))
v1 += 0x1f8000; /* NaN or infinity */
d = uint64_as_float64(((uint64_t)(v >> 15) << 63) | ((uint64_t)v1 << (52 - 10)));
return d * 0x1p1008;
}
static inline uint16_t tofp16(double d)
{
uint64_t a, addend;
uint32_t v, sgn;
int shift;
a = float64_as_uint64(d);
sgn = a >> 63;
a = a & 0x7fffffffffffffff;
if (unlikely(a > 0x7ff0000000000000)) {
/* nan */
v = 0x7c01;
} else if (a < 0x3f10000000000000) { /* 0x1p-14 */
/* subnormal f16 number or zero */
if (a <= 0x3e60000000000000) { /* 0x1p-25 */
v = 0x0000; /* zero */
} else {
shift = 1051 - (a >> 52);
a = ((uint64_t)1 << 52) | (a & (((uint64_t)1 << 52) - 1));
addend = ((a >> shift) & 1) + (((uint64_t)1 << (shift - 1)) - 1);
v = (a + addend) >> shift;
}
} else {
/* normal number or infinity */
a -= 0x3f00000000000000; /* adjust the exponent */
/* round */
addend = ((a >> (52 - 10)) & 1) + (((uint64_t)1 << (52 - 11)) - 1);
v = (a + addend) >> (52 - 10);
/* overflow ? */
if (unlikely(v > 0x7c00))
v = 0x7c00;
}
return v | (sgn << 15);
}
static inline int isfp16nan(uint16_t v)
{
return (v & 0x7FFF) > 0x7C00;
}
static inline int isfp16zero(uint16_t v)
{
return (v & 0x7FFF) == 0;
}
#endif /* CUTILS_H */

1620
source/dtoa.c Normal file

File diff suppressed because it is too large Load Diff

83
source/dtoa.h Normal file
View File

@@ -0,0 +1,83 @@
/*
* Tiny float64 printing and parsing library
*
* Copyright (c) 2024 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//#define JS_DTOA_DUMP_STATS
/* maximum number of digits for fixed and frac formats */
#define JS_DTOA_MAX_DIGITS 101
/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */
/* use as many digits as necessary */
#define JS_DTOA_FORMAT_FREE (0 << 0)
/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */
#define JS_DTOA_FORMAT_FIXED (1 << 0)
/* force fractional format: [-]dd.dd with n_digits fractional digits.
0 <= n_digits <= JS_DTOA_MAX_DIGITS */
#define JS_DTOA_FORMAT_FRAC (2 << 0)
#define JS_DTOA_FORMAT_MASK (3 << 0)
/* select exponential notation either in fixed or free format */
#define JS_DTOA_EXP_AUTO (0 << 2)
#define JS_DTOA_EXP_ENABLED (1 << 2)
#define JS_DTOA_EXP_DISABLED (2 << 2)
#define JS_DTOA_EXP_MASK (3 << 2)
#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */
/* only accepts integers (no dot, no exponent) */
#define JS_ATOD_INT_ONLY (1 << 0)
/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1)
/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2)
/* accept _ between digits as a digit separator */
#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3)
typedef struct {
uint64_t mem[37];
} JSDTOATempMem;
typedef struct {
uint64_t mem[27];
} JSATODTempMem;
/* return a maximum bound of the string length */
int js_dtoa_max_len(double d, int radix, int n_digits, int flags);
/* return the string length */
int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,
JSDTOATempMem *tmp_mem);
double js_atod(const char *str, const char **pnext, int radix, int flags,
JSATODTempMem *tmp_mem);
#ifdef JS_DTOA_DUMP_STATS
void js_dtoa_dump_stats(void);
#endif
/* additional exported functions */
size_t u32toa(char *buf, uint32_t n);
size_t i32toa(char *buf, int32_t n);
size_t u64toa(char *buf, uint64_t n);
size_t i64toa(char *buf, int64_t n);
size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix);
size_t i64toa_radix(char *buf, int64_t n, unsigned int radix);

View File

@@ -1532,6 +1532,7 @@ JS_SetPrototype(js, js_##NAME, PARENT); \
JSValue js_layout_use(JSContext *js);
JSValue js_miniz_use(JSContext *js);
JSValue js_num_use(JSContext *js);
JSValue js_graphics_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
@@ -1578,7 +1579,9 @@ void ffi_load(JSContext *js)
JS_FreeValue(js, js_blob_use(js)); // juice blob
m_seedRand(&rt->mrand, time(NULL));
uint64_t rr;
randombytes(&rr,4);
m_seedRand(&rt->mrand, rr);
// cell modules
arrput(rt->module_registry, MISTLINE(time));
@@ -1594,6 +1597,7 @@ void ffi_load(JSContext *js)
arrput(rt->module_registry, MISTLINE(http));
arrput(rt->module_registry, MISTLINE(crypto));
arrput(rt->module_registry, MISTLINE(miniz));
arrput(rt->module_registry, MISTLINE(num));
arrput(rt->module_registry, MISTLINE(kim));
arrput(rt->module_registry, MISTLINE(utf8));
arrput(rt->module_registry, MISTLINE(fit));

67
source/libregexp-opcode.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char_i, 3)
DEF(char32, 5)
DEF(char32_i, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_start_m, 1)
DEF(line_end, 1)
DEF(line_end_m, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(word_boundary_i, 1)
DEF(not_word_boundary, 1)
DEF(not_word_boundary_i, 1)
DEF(back_reference, 2)
DEF(back_reference_i, 2) /* must come after */
DEF(backward_back_reference, 2) /* must come after */
DEF(backward_back_reference_i, 2) /* must come after */
DEF(range, 3) /* variable length */
DEF(range_i, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(range32_i, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

3259
source/libregexp.c Normal file

File diff suppressed because it is too large Load Diff

61
source/libregexp.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include <stdint.h>
#define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UNICODE (1 << 4)
#define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
#define LRE_FLAG_UNICODE_SETS (1 << 8)
#define LRE_RET_MEMORY_ERROR (-1)
#define LRE_RET_TIMEOUT (-2)
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
const char *lre_get_groupnames(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
/* must be provided by the user, return non zero if overflow */
int lre_check_stack_overflow(void *opaque, size_t alloca_size);
/* must be provided by the user, return non zero if time out */
int lre_check_timeout(void *opaque);
void *lre_realloc(void *opaque, void *ptr, size_t size);
#endif /* LIBREGEXP_H */

5149
source/libunicode-table.h Normal file

File diff suppressed because it is too large Load Diff

2123
source/libunicode.c Normal file

File diff suppressed because it is too large Load Diff

186
source/libunicode.h Normal file
View File

@@ -0,0 +1,186 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <stdint.h>
/* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
CR_OP_SUB,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_op1(CharRange *cr, const uint32_t *b_pt, int b_len, int op);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_op1(cr, b_pt, 2, CR_OP_UNION);
}
int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr, const char *script_name, int is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
typedef void UnicodeSequencePropCB(void *opaque, const uint32_t *buf, int len);
int unicode_sequence_prop(const char *prop_name, UnicodeSequencePropCB *cb, void *opaque,
CharRange *cr);
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, int is_unicode);
/* Code point type categories */
enum {
UNICODE_C_SPACE = (1 << 0),
UNICODE_C_DIGIT = (1 << 1),
UNICODE_C_UPPER = (1 << 2),
UNICODE_C_LOWER = (1 << 3),
UNICODE_C_UNDER = (1 << 4),
UNICODE_C_DOLLAR = (1 << 5),
UNICODE_C_XDIGIT = (1 << 6),
};
extern uint8_t const lre_ctype_bits[256];
/* zero or non-zero return value */
int lre_is_cased(uint32_t c);
int lre_is_case_ignorable(uint32_t c);
int lre_is_id_start(uint32_t c);
int lre_is_id_continue(uint32_t c);
static inline int lre_is_space_byte(uint8_t c) {
return lre_ctype_bits[c] & UNICODE_C_SPACE;
}
static inline int lre_is_id_start_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
}
static inline int lre_is_id_continue_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
UNICODE_C_DIGIT);
}
int lre_is_space_non_ascii(uint32_t c);
static inline int lre_is_space(uint32_t c) {
if (c < 256)
return lre_is_space_byte(c);
else
return lre_is_space_non_ascii(c);
}
static inline int lre_js_is_ident_first(uint32_t c) {
if (c < 128) {
return lre_is_id_start_byte(c);
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
static inline int lre_js_is_ident_next(uint32_t c) {
if (c < 128) {
return lre_is_id_continue_byte(c);
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
if (c >= 0x200C && c <= 0x200D)
return TRUE;
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
#endif /* LIBUNICODE_H */

99
source/list.h Normal file
View File

@@ -0,0 +1,99 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) container_of(el, type, member)
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

View File

@@ -546,22 +546,3 @@ JSValue js_enet_use(JSContext *ctx)
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
return export_obj;
}
static int js_enet_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExport(ctx, m, "default", js_enet_use(ctx));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_enet
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_enet_init);
if (!m) return NULL;
JS_AddModuleExport(ctx, m, "default");
return m;
}

View File

@@ -243,7 +243,6 @@ static inline void tile_region(text_vert **verts, rect src_uv, rect dst, float t
}
}
JSC_CCALL(gpu_slice9,
JSValue jstex = argv[0];
rect dst = js2rect(js, argv[1]);
@@ -808,6 +807,153 @@ JSC_CCALL(geometry_rect_transform,
return rect2js(js, newrect);
)
JSC_CCALL(geometry_tilemap_to_data,
JSValue tilemap_obj = argv[0];
// Get tilemap properties
double offset_x, offset_y, size_x, size_y;
JS_GETPROP(js, offset_x, tilemap_obj, offset_x, number)
JS_GETPROP(js, offset_y, tilemap_obj, offset_y, number)
JS_GETPROP(js, size_x, tilemap_obj, size_x, number)
JS_GETPROP(js, size_y, tilemap_obj, size_y, number)
JSValue tiles_array = JS_GetPropertyStr(js, tilemap_obj, "tiles");
if (!JS_IsArray(js, tiles_array)) {
JS_FreeValue(js, tiles_array);
return JS_ThrowTypeError(js, "tilemap.tiles must be an array");
}
// Count tiles
int tile_count = 0;
int tiles_len = JS_ArrayLength(js, tiles_array);
for (int x = 0; x < tiles_len; x++) {
JSValue col = JS_GetPropertyUint32(js, tiles_array, x);
if (JS_IsArray(js, col)) {
int col_len = JS_ArrayLength(js, col);
for (int y = 0; y < col_len; y++) {
JSValue tile = JS_GetPropertyUint32(js, col, y);
if (!JS_IsUndefined(tile) && !JS_IsNull(tile)) {
tile_count++;
}
JS_FreeValue(js, tile);
}
}
JS_FreeValue(js, col);
}
if (tile_count == 0) {
JS_FreeValue(js, tiles_array);
return JS_NewObject(js);
}
// Allocate buffers - 4 vertices per tile
int vertex_count = tile_count * 4;
int index_count = tile_count * 6;
float *xy_data = malloc(vertex_count * 2 * sizeof(float));
float *uv_data = malloc(vertex_count * 2 * sizeof(float));
SDL_FColor *color_data = malloc(vertex_count * sizeof(SDL_FColor));
uint16_t *index_data = malloc(index_count * sizeof(uint16_t));
// Generate vertices
int vertex_idx = 0;
int index_idx = 0;
for (int x = 0; x < tiles_len; x++) {
JSValue col = JS_GetPropertyUint32(js, tiles_array, x);
if (JS_IsArray(js, col)) {
int col_len = JS_ArrayLength(js, col);
for (int y = 0; y < col_len; y++) {
JSValue tile = JS_GetPropertyUint32(js, col, y);
if (!JS_IsUndefined(tile) && !JS_IsNull(tile)) {
// Calculate world position
float world_x = (x + offset_x) * size_x;
float world_y = (y + offset_y) * size_y;
// Set vertex positions (4 corners of the tile)
int base = vertex_idx * 2;
xy_data[base + 0] = world_x;
xy_data[base + 1] = world_y;
xy_data[base + 2] = world_x + size_x;
xy_data[base + 3] = world_y;
xy_data[base + 4] = world_x;
xy_data[base + 5] = world_y + size_y;
xy_data[base + 6] = world_x + size_x;
xy_data[base + 7] = world_y + size_y;
// Set UVs (normalized 0-1 for now)
uv_data[base + 0] = 0.0f;
uv_data[base + 1] = 0.0f;
uv_data[base + 2] = 1.0f;
uv_data[base + 3] = 0.0f;
uv_data[base + 4] = 0.0f;
uv_data[base + 5] = 1.0f;
uv_data[base + 6] = 1.0f;
uv_data[base + 7] = 1.0f;
// Set colors (check if tile has color property)
SDL_FColor default_color = {1.0f, 1.0f, 1.0f, 1.0f};
if (JS_IsObject(tile)) {
JSValue color_val = JS_GetPropertyStr(js, tile, "color");
if (!JS_IsUndefined(color_val)) {
HMM_Vec4 color = js2color(js, color_val);
default_color.r = color.r;
default_color.g = color.g;
default_color.b = color.b;
default_color.a = color.a;
JS_FreeValue(js, color_val);
}
}
for (int i = 0; i < 4; i++) {
color_data[vertex_idx + i] = default_color;
}
// Set indices (two triangles per tile)
uint16_t base_idx = vertex_idx;
index_data[index_idx++] = base_idx + 0;
index_data[index_idx++] = base_idx + 1;
index_data[index_idx++] = base_idx + 2;
index_data[index_idx++] = base_idx + 1;
index_data[index_idx++] = base_idx + 3;
index_data[index_idx++] = base_idx + 2;
vertex_idx += 4;
}
JS_FreeValue(js, tile);
}
}
JS_FreeValue(js, col);
}
JS_FreeValue(js, tiles_array);
// Create result object with blob data
ret = JS_NewObject(js);
// Create blobs for each data type
JSValue xy_blob = js_new_blob_stoned_copy(js, xy_data, vertex_count * 2 * sizeof(float));
JSValue uv_blob = js_new_blob_stoned_copy(js, uv_data, vertex_count * 2 * sizeof(float));
JSValue color_blob = js_new_blob_stoned_copy(js, color_data, vertex_count * sizeof(SDL_FColor));
JSValue index_blob = js_new_blob_stoned_copy(js, index_data, index_count * sizeof(uint16_t));
JS_SetPropertyStr(js, ret, "xy", xy_blob);
JS_SetPropertyStr(js, ret, "xy_stride", JS_NewInt32(js, 2 * sizeof(float)));
JS_SetPropertyStr(js, ret, "uv", uv_blob);
JS_SetPropertyStr(js, ret, "uv_stride", JS_NewInt32(js, 2 * sizeof(float)));
JS_SetPropertyStr(js, ret, "color", color_blob);
JS_SetPropertyStr(js, ret, "color_stride", JS_NewInt32(js, sizeof(SDL_FColor)));
JS_SetPropertyStr(js, ret, "indices", index_blob);
JS_SetPropertyStr(js, ret, "num_vertices", JS_NewInt32(js, vertex_count));
JS_SetPropertyStr(js, ret, "num_indices", JS_NewInt32(js, index_count));
JS_SetPropertyStr(js, ret, "size_indices", JS_NewInt32(js, 2)); // using uint16_t
free(xy_data);
free(uv_data);
free(color_data);
free(index_data);
)
static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_intersection, 2),
MIST_FUNC_DEF(geometry, rect_intersects, 2),
@@ -819,6 +965,7 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
MIST_FUNC_DEF(geometry, rect_pos, 1),
MIST_FUNC_DEF(geometry, rect_move, 2),
MIST_FUNC_DEF(geometry, rect_transform, 2),
MIST_FUNC_DEF(geometry, tilemap_to_data, 1),
MIST_FUNC_DEF(gpu, tile, 4),
MIST_FUNC_DEF(gpu, slice9, 3),
MIST_FUNC_DEF(gpu, make_sprite_mesh, 2),

View File

@@ -882,23 +882,3 @@ JSValue js_imgui_use(JSContext *js)
return imgui;
}
}
static int js_init_imgui(JSContext *js, JSModuleDef *m) {
JS_SetModuleExportList(js, m, js_imgui_funcs, sizeof(js_imgui_funcs)/sizeof(JSCFunctionListEntry));
JS_SetModuleExport(js, m, "default", js_imgui_use(js));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_imgui
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *name) {
JSModuleDef *m;
m = JS_NewCModule(js, name, js_init_imgui);
if (!m) return NULL;
JS_AddModuleExport(js, m, "default");
return m;
}

View File

@@ -30,12 +30,12 @@ static JSClassDef js_writer_class = {
static mz_zip_archive *js2reader(JSContext *js, JSValue v)
{
return JS_GetOpaque2(js, v, js_reader_class_id);
return JS_GetOpaque(v, js_reader_class_id);
}
static mz_zip_archive *js2writer(JSContext *js, JSValue v)
{
return JS_GetOpaque2(js, v, js_writer_class_id);
return JS_GetOpaque(v, js_writer_class_id);
}
static JSValue js_miniz_read(JSContext *js, JSValue self, int argc, JSValue *argv)
@@ -397,21 +397,3 @@ JSValue js_miniz_use(JSContext *js)
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
return export;
}
static int js_miniz_init(JSContext *ctx, JSModuleDef *m) {
JS_SetModuleExport(ctx, m, "default",js_miniz_use(ctx));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_miniz
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_miniz_init);
if (!m) return NULL;
JS_AddModuleExport(ctx, m, "default");
return m;
}

View File

@@ -349,26 +349,8 @@ static const JSCFunctionListEntry js_nota_funcs[] = {
JS_CFUNC_DEF("decode", 1, js_nota_decode),
};
static int js_nota_init(JSContext *ctx, JSModuleDef *m) {
JS_SetModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
return 0;
}
JSValue js_nota_use(JSContext *js) {
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
return export;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_nota
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_nota_init);
if (!m) return NULL;
JS_AddModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
return m;
}

699
source/qjs_num.c Normal file
View File

@@ -0,0 +1,699 @@
#include "quickjs.h"
#include <string.h>
#include <math.h>
#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#include <lapacke.h>
#endif
static JSClassID js_matrix_class_id;
static JSClassID js_array_class_id;
typedef struct {
double *data;
int rows;
int cols;
} matrix_t;
typedef struct {
double *data;
int size;
} array_t;
static void js_matrix_finalizer(JSRuntime *rt, JSValue val) {
matrix_t *mat = JS_GetOpaque(val, js_matrix_class_id);
if (mat) {
js_free_rt(rt, mat->data);
js_free_rt(rt, mat);
}
}
static void js_array_finalizer(JSRuntime *rt, JSValue val) {
array_t *arr = JS_GetOpaque(val, js_array_class_id);
if (arr) {
js_free_rt(rt, arr->data);
js_free_rt(rt, arr);
}
}
static JSClassDef js_matrix_class = {
"Matrix",
.finalizer = js_matrix_finalizer,
};
static JSClassDef js_array_class = {
"Array",
.finalizer = js_array_finalizer,
};
static matrix_t *js2matrix(JSContext *ctx, JSValue v) {
return JS_GetOpaque(v, js_matrix_class_id);
}
static array_t *js2array(JSContext *ctx, JSValue v) {
return JS_GetOpaque(v, js_array_class_id);
}
static JSValue js_matrix_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
if (!JS_IsArray(ctx, argv[0]))
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
int32_t rows;
JS_ToInt32(ctx, &rows, length_val);
JS_FreeValue(ctx, length_val);
if (rows == 0)
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 rows");
// Get first row to determine columns
JSValue first_row = JS_GetPropertyUint32(ctx, argv[0], 0);
if (!JS_IsArray(ctx, first_row)) {
JS_FreeValue(ctx, first_row);
return JS_ThrowTypeError(ctx, "Matrix rows must be arrays");
}
JSValue col_length_val = JS_GetPropertyStr(ctx, first_row, "length");
int32_t cols;
JS_ToInt32(ctx, &cols, col_length_val);
JS_FreeValue(ctx, col_length_val);
JS_FreeValue(ctx, first_row);
if (cols == 0)
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 columns");
matrix_t *mat = js_malloc(ctx, sizeof(matrix_t));
if (!mat)
return JS_EXCEPTION;
mat->rows = rows;
mat->cols = cols;
mat->data = js_malloc(ctx, sizeof(double) * rows * cols);
if (!mat->data) {
js_free(ctx, mat);
return JS_EXCEPTION;
}
// Fill matrix data
for (int i = 0; i < rows; i++) {
JSValue row = JS_GetPropertyUint32(ctx, argv[0], i);
if (!JS_IsArray(ctx, row)) {
js_free(ctx, mat->data);
js_free(ctx, mat);
JS_FreeValue(ctx, row);
return JS_ThrowTypeError(ctx, "All matrix rows must be arrays");
}
JSValue row_length_val = JS_GetPropertyStr(ctx, row, "length");
int32_t row_cols;
JS_ToInt32(ctx, &row_cols, row_length_val);
JS_FreeValue(ctx, row_length_val);
if (row_cols != cols) {
js_free(ctx, mat->data);
js_free(ctx, mat);
JS_FreeValue(ctx, row);
return JS_ThrowTypeError(ctx, "All matrix rows must have the same length");
}
for (int j = 0; j < cols; j++) {
JSValue elem = JS_GetPropertyUint32(ctx, row, j);
double val;
if (JS_ToFloat64(ctx, &val, elem)) {
js_free(ctx, mat->data);
js_free(ctx, mat);
JS_FreeValue(ctx, elem);
JS_FreeValue(ctx, row);
return JS_EXCEPTION;
}
mat->data[i * cols + j] = val;
JS_FreeValue(ctx, elem);
}
JS_FreeValue(ctx, row);
}
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
JS_SetOpaque(obj, mat);
return obj;
}
static JSValue js_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
if (!JS_IsArray(ctx, argv[0]))
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
int32_t size;
JS_ToInt32(ctx, &size, length_val);
JS_FreeValue(ctx, length_val);
if (size == 0)
return JS_ThrowRangeError(ctx, "Array cannot have 0 elements");
array_t *arr = js_malloc(ctx, sizeof(array_t));
if (!arr)
return JS_EXCEPTION;
arr->size = size;
arr->data = js_malloc(ctx, sizeof(double) * size);
if (!arr->data) {
js_free(ctx, arr);
return JS_EXCEPTION;
}
// Fill array data
for (int i = 0; i < size; i++) {
JSValue elem = JS_GetPropertyUint32(ctx, argv[0], i);
double val;
if (JS_ToFloat64(ctx, &val, elem)) {
js_free(ctx, arr->data);
js_free(ctx, arr);
JS_FreeValue(ctx, elem);
return JS_EXCEPTION;
}
arr->data[i] = val;
JS_FreeValue(ctx, elem);
}
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
JS_SetOpaque(obj, arr);
return obj;
}
static JSValue js_matrix_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
matrix_t *mat = js2matrix(ctx, this_val);
if (!mat)
return JS_EXCEPTION;
if (mat->rows != mat->cols)
return JS_ThrowTypeError(ctx, "Matrix must be square for inversion");
int n = mat->rows;
// Create a copy of the matrix
matrix_t *result = js_malloc(ctx, sizeof(matrix_t));
if (!result)
return JS_EXCEPTION;
result->rows = n;
result->cols = n;
result->data = js_malloc(ctx, sizeof(double) * n * n);
if (!result->data) {
js_free(ctx, result);
return JS_EXCEPTION;
}
memcpy(result->data, mat->data, sizeof(double) * n * n);
// Allocate pivot array
int *ipiv = js_malloc(ctx, sizeof(int) * n);
if (!ipiv) {
js_free(ctx, result->data);
js_free(ctx, result);
return JS_EXCEPTION;
}
// LU decomposition
#ifdef __APPLE__
// For macOS with ILP64, use 64-bit integers
__LAPACK_int nn = n;
__LAPACK_int info = 0;
__LAPACK_int *ipiv_lapack = js_malloc(ctx, sizeof(__LAPACK_int) * n);
if (!ipiv_lapack) {
js_free(ctx, ipiv);
js_free(ctx, result->data);
js_free(ctx, result);
return JS_EXCEPTION;
}
// LAPACK uses column-major, but we'll transpose the result
__LAPACK_int lda = n;
dgetrf_(&nn, &nn, result->data, &lda, ipiv_lapack, &info);
if (info != 0) {
js_free(ctx, ipiv_lapack);
js_free(ctx, ipiv);
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
}
// Compute inverse
__LAPACK_int lwork = n * n;
double *work = js_malloc(ctx, sizeof(double) * lwork);
if (!work) {
js_free(ctx, ipiv_lapack);
js_free(ctx, ipiv);
js_free(ctx, result->data);
js_free(ctx, result);
return JS_EXCEPTION;
}
dgetri_(&nn, result->data, &lda, ipiv_lapack, work, &lwork, &info);
js_free(ctx, work);
js_free(ctx, ipiv_lapack);
#else
int info = LAPACKE_dgetrf(LAPACK_ROW_MAJOR, n, n, result->data, n, ipiv);
if (info != 0) {
js_free(ctx, ipiv);
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
}
// Compute inverse
info = LAPACKE_dgetri(LAPACK_ROW_MAJOR, n, result->data, n, ipiv);
#endif
js_free(ctx, ipiv);
if (info != 0) {
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowInternalError(ctx, "Matrix inversion failed");
}
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
JS_SetOpaque(obj, result);
return obj;
}
static JSValue js_matrix_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "multiply requires an argument");
matrix_t *A = js2matrix(ctx, this_val);
if (!A)
return JS_EXCEPTION;
// Check if multiplying by another matrix
matrix_t *B = js2matrix(ctx, argv[0]);
if (B) {
if (A->cols != B->rows)
return JS_ThrowTypeError(ctx, "Matrix dimensions incompatible for multiplication");
matrix_t *C = js_malloc(ctx, sizeof(matrix_t));
if (!C)
return JS_EXCEPTION;
C->rows = A->rows;
C->cols = B->cols;
C->data = js_malloc(ctx, sizeof(double) * C->rows * C->cols);
if (!C->data) {
js_free(ctx, C);
return JS_EXCEPTION;
}
// C = A * B
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
A->rows, B->cols, A->cols,
1.0, A->data, A->cols,
B->data, B->cols,
0.0, C->data, C->cols);
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
JS_SetOpaque(obj, C);
return obj;
}
// Check if multiplying by an array (matrix-vector multiplication)
array_t *x = js2array(ctx, argv[0]);
if (x) {
if (A->cols != x->size)
return JS_ThrowTypeError(ctx, "Matrix columns must match array size");
array_t *y = js_malloc(ctx, sizeof(array_t));
if (!y)
return JS_EXCEPTION;
y->size = A->rows;
y->data = js_malloc(ctx, sizeof(double) * y->size);
if (!y->data) {
js_free(ctx, y);
return JS_EXCEPTION;
}
// y = A * x
cblas_dgemv(CblasRowMajor, CblasNoTrans,
A->rows, A->cols,
1.0, A->data, A->cols,
x->data, 1,
0.0, y->data, 1);
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
JS_SetOpaque(obj, y);
return obj;
}
return JS_ThrowTypeError(ctx, "multiply requires a Matrix or Array argument");
}
static JSValue js_matrix_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
matrix_t *mat = js2matrix(ctx, this_val);
if (!mat)
return JS_EXCEPTION;
JSValue arr = JS_NewArray(ctx);
if (JS_IsException(arr))
return arr;
for (int i = 0; i < mat->rows; i++) {
JSValue row = JS_NewArray(ctx);
if (JS_IsException(row)) {
JS_FreeValue(ctx, arr);
return row;
}
for (int j = 0; j < mat->cols; j++) {
JS_SetPropertyUint32(ctx, row, j, JS_NewFloat64(ctx, mat->data[i * mat->cols + j]));
}
JS_SetPropertyUint32(ctx, arr, i, row);
}
return arr;
}
static JSValue js_array_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
JSValue jsarr = JS_NewArray(ctx);
for (int i = 0; i < arr->size; i++) {
JS_SetPropertyUint32(ctx, jsarr, i, JS_NewFloat64(ctx, arr->data[i]));
}
return jsarr;
}
static JSValue js_array_dot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "dot requires an argument");
array_t *a = js2array(ctx, this_val);
if (!a)
return JS_EXCEPTION;
array_t *b = js2array(ctx, argv[0]);
if (!b)
return JS_ThrowTypeError(ctx, "dot requires an Array argument");
if (a->size != b->size)
return JS_ThrowTypeError(ctx, "Arrays must have the same size for dot product");
double result = cblas_ddot(a->size, a->data, 1, b->data, 1);
return JS_NewFloat64(ctx, result);
}
static JSValue js_array_norm(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
double norm = cblas_dnrm2(arr->size, arr->data, 1);
return JS_NewFloat64(ctx, norm);
}
static JSValue js_array_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "multiply requires an argument");
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
// Create result array
array_t *result = js_malloc(ctx, sizeof(array_t));
if (!result)
return JS_EXCEPTION;
result->size = arr->size;
result->data = js_malloc(ctx, sizeof(double) * result->size);
if (!result->data) {
js_free(ctx, result);
return JS_EXCEPTION;
}
// Copy original data
memcpy(result->data, arr->data, sizeof(double) * arr->size);
// Check if multiplying by scalar
if (JS_IsNumber(argv[0])) {
double scalar;
if (JS_ToFloat64(ctx, &scalar, argv[0]))
return JS_EXCEPTION;
// Multiply each element by scalar
if (result->size <= 4)
for (int i = 0; i < result->size; i++)
result->data[i] *= scalar;
else
cblas_dscal(result->size, scalar, result->data, 1);
} else {
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowTypeError(ctx, "multiply requires a number argument");
}
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
JS_SetOpaque(obj, result);
return obj;
}
static JSValue js_array_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "add requires an argument");
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
// Create result array
array_t *result = js_malloc(ctx, sizeof(array_t));
if (!result)
return JS_EXCEPTION;
result->size = arr->size;
result->data = js_malloc(ctx, sizeof(double) * result->size);
if (!result->data) {
js_free(ctx, result);
return JS_EXCEPTION;
}
// Copy original data
memcpy(result->data, arr->data, sizeof(double) * arr->size);
// Check if adding scalar
if (JS_IsNumber(argv[0])) {
double scalar;
if (JS_ToFloat64(ctx, &scalar, argv[0]))
return JS_EXCEPTION;
// Add scalar to each element
for (int i = 0; i < result->size; i++)
result->data[i] += scalar;
} else {
// Check if adding another array
array_t *other = js2array(ctx, argv[0]);
if (other) {
if (arr->size != other->size) {
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
}
if (arr->size <= 4) {
for (int i = 0; i < arr->size; i++)
result->data[i] += other->data[i];
} else
cblas_daxpy(result->size, 1.0, other->data, 1, result->data, 1);
} else {
js_free(ctx, result->data);
js_free(ctx, result);
return JS_ThrowTypeError(ctx, "add requires a number or Array argument");
}
}
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
JS_SetOpaque(obj, result);
return obj;
}
static JSValue js_array_slice(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
array_t *arr = js2array(js, self);
if (!arr)
return JS_EXCEPTION;
int32_t start = 0;
int32_t end = arr->size;
// Parse start argument
if (argc >= 1 && !JS_IsUndefined(argv[0])) {
if (JS_ToInt32(js, &start, argv[0]))
return JS_EXCEPTION;
// Handle negative indices
if (start < 0) {
start = arr->size + start;
if (start < 0) start = 0;
} else if (start > arr->size) {
start = arr->size;
}
}
// Parse end argument
if (argc >= 2 && !JS_IsUndefined(argv[1])) {
if (JS_ToInt32(js, &end, argv[1]))
return JS_EXCEPTION;
// Handle negative indices
if (end < 0) {
end = arr->size + end;
if (end < 0) end = 0;
} else if (end > arr->size) {
end = arr->size;
}
}
// Ensure start <= end
if (start > end) {
end = start; // Return empty array
}
// Create JavaScript array
JSValue jsarr = JS_NewArray(js);
if (JS_IsException(jsarr))
return jsarr;
// Fill with sliced values
for (int32_t i = start; i < end; i++) {
JS_SetPropertyUint32(js, jsarr, i - start, JS_NewFloat64(js, arr->data[i]));
}
return jsarr;
}
static JSValue js_array_multiplyf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "multiplyf requires an argument");
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
// Check if multiplying by scalar
if (JS_IsNumber(argv[0])) {
double scalar;
if (JS_ToFloat64(ctx, &scalar, argv[0]))
return JS_EXCEPTION;
// Multiply each element by scalar in-place
if (arr->size <= 4) {
for (int i = 0; i < arr->size; i++)
arr->data[i] *= scalar;
} else {
cblas_dscal(arr->size, scalar, arr->data, 1);
}
return JS_DupValue(ctx, this_val);
} else {
return JS_ThrowTypeError(ctx, "multiplyf requires a number argument");
}
}
static JSValue js_array_addf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1)
return JS_ThrowTypeError(ctx, "addf requires an argument");
array_t *arr = js2array(ctx, this_val);
if (!arr)
return JS_EXCEPTION;
// Check if adding scalar
if (JS_IsNumber(argv[0])) {
double scalar;
if (JS_ToFloat64(ctx, &scalar, argv[0]))
return JS_EXCEPTION;
// Add scalar to each element in-place
for (int i = 0; i < arr->size; i++)
arr->data[i] += scalar;
} else {
// Check if adding another array
array_t *other = js2array(ctx, argv[0]);
if (other) {
if (arr->size != other->size) {
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
}
// Add arrays element-wise in-place
if (arr->size <= 4) {
for (int i = 0; i < arr->size; i++)
arr->data[i] += other->data[i];
} else {
cblas_daxpy(arr->size, 1.0, other->data, 1, arr->data, 1);
}
} else {
return JS_ThrowTypeError(ctx, "addf requires a number or Array argument");
}
}
return JS_DupValue(ctx, this_val);
}
static const JSCFunctionListEntry js_matrix_proto_funcs[] = {
JS_CFUNC_DEF("inverse", 0, js_matrix_inverse),
JS_CFUNC_DEF("multiply", 1, js_matrix_multiply),
JS_CFUNC_DEF("toArray", 0, js_matrix_to_array),
};
static const JSCFunctionListEntry js_array_proto_funcs[] = {
JS_CFUNC_DEF("dot", 1, js_array_dot),
JS_CFUNC_DEF("norm", 0, js_array_norm),
JS_CFUNC_DEF("multiply", 1, js_array_multiply),
JS_CFUNC_DEF("add", 1, js_array_add),
JS_CFUNC_DEF("multiplyf", 1, js_array_multiplyf),
JS_CFUNC_DEF("addf", 1, js_array_addf),
JS_CFUNC_DEF("toArray", 0, js_array_to_array),
JS_CFUNC_DEF("toJSON", 0, js_array_to_array), /* ← for JSON.stringify */
JS_CFUNC_DEF("slice", 2, js_array_slice),
};
JSValue js_num_use(JSContext *ctx) {
// Register Matrix class
JS_NewClassID(&js_matrix_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_matrix_class_id, &js_matrix_class);
JSValue matrix_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, matrix_proto, js_matrix_proto_funcs,
sizeof(js_matrix_proto_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(ctx, js_matrix_class_id, matrix_proto);
// Create Matrix constructor
JSValue matrix_ctor = JS_NewCFunction2(ctx, js_matrix_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
JS_SetConstructor(ctx, matrix_ctor, matrix_proto);
// Register Array class
JS_NewClassID(&js_array_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_array_class_id, &js_array_class);
JSValue array_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, array_proto, js_array_proto_funcs,
sizeof(js_array_proto_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(ctx, js_array_class_id, array_proto);
// Create Array constructor
JSValue array_ctor = JS_NewCFunction2(ctx, js_array_constructor, "Array", 1, JS_CFUNC_constructor, 0);
JS_SetConstructor(ctx, array_ctor, array_proto);
JSValue export = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, export, "Matrix", matrix_ctor);
JS_SetPropertyStr(ctx, export, "Array", array_ctor);
return export;
}

View File

@@ -300,24 +300,3 @@ JSValue js_qr_use(JSContext *js) {
JS_SetPropertyFunctionList(js, exports, js_qr_funcs, sizeof(js_qr_funcs) / sizeof(JSCFunctionListEntry));
return exports;
}
// Module init
static int js_qr_init(JSContext *js, JSModuleDef *m) {
JS_SetModuleExport(js, m, "default", js_qr_use(js));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_qr
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *module_name) {
JSModuleDef *m = JS_NewCModule(js, module_name, js_qr_init);
if (!m)
return NULL;
JS_AddModuleExport(js, m, "default");
return m;
}

View File

@@ -888,6 +888,39 @@ JSC_CCALL(renderer_geometry,
JS_FreeValue(js,indices);
)
JSC_CCALL(renderer_geometry_raw,
SDL_Renderer *r = js2SDL_Renderer(js,self);
// argv[0] is texture
SDL_Texture *tex = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0]))
tex = js2SDL_Texture(js,argv[0]);
// Get blob data
size_t xy_size, color_size, uv_size, indices_size;
void *xy_data = js_get_blob_data(js, &xy_size, argv[1]);
int xy_stride = js2number(js, argv[2]);
void *color_data = js_get_blob_data(js, &color_size, argv[3]);
int color_stride = js2number(js, argv[4]);
void *uv_data = js_get_blob_data(js, &uv_size, argv[5]);
int uv_stride = js2number(js, argv[6]);
int num_vertices = js2number(js, argv[7]);
void *indices_data = js_get_blob_data(js, &indices_size, argv[8]);
int num_indices = js2number(js, argv[9]);
int size_indices = js2number(js, argv[10]);
if (!xy_data || !color_data || !uv_data)
return JS_ThrowTypeError(js, "Invalid geometry data");
if (!SDL_RenderGeometryRaw(r, tex,
(const float *)xy_data, xy_stride,
(const SDL_FColor *)color_data, color_stride,
(const float *)uv_data, uv_stride,
num_vertices,
indices_data, num_indices, size_indices))
return JS_ThrowReferenceError(js, "Error rendering geometry: %s", SDL_GetError());
)
JSC_CCALL(renderer_geometry2,
SDL_Renderer *r = js2SDL_Renderer(js, self);
@@ -1378,6 +1411,7 @@ static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = {
MIST_FUNC_DEF(renderer, texture, 5),
MIST_FUNC_DEF(renderer, rects, 1),
MIST_FUNC_DEF(renderer, geometry, 2),
MIST_FUNC_DEF(renderer, geometry_raw, 11),
MIST_FUNC_DEF(renderer, geometry2, 2),
MIST_FUNC_DEF(renderer, sprite, 1),
MIST_FUNC_DEF(renderer, load_texture, 1),

View File

@@ -217,22 +217,3 @@ JSValue js_soloud_use(JSContext *js)
JS_SetPropertyFunctionList(js, export, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
return export;
}
static int js_soloud_init(JSContext *js, JSModuleDef *m) {
js_soloud_use(js);
JS_SetModuleExportList(js, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_soloud
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_soloud_init);
if (!m) return NULL;
JS_AddModuleExportList(ctx, m, js_soloud_funcs, sizeof(js_soloud_funcs)/sizeof(JSCFunctionListEntry));
return m;
}

View File

@@ -381,26 +381,6 @@ static const JSCFunctionListEntry js_wota_funcs[] = {
JS_CFUNC_DEF("decode", 2, js_wota_decode),
};
static int js_wota_init(JSContext *ctx, JSModuleDef *m)
{
JS_SetModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_wota
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_wota_init);
if (!m) return NULL;
JS_AddModuleExportList(ctx, m, js_wota_funcs, sizeof(js_wota_funcs)/sizeof(js_wota_funcs[0]));
return m;
}
JSValue js_wota_use(JSContext *ctx)
{
JSValue exports = JS_NewObject(ctx);

270
source/quickjs-atom.h Normal file
View File

@@ -0,0 +1,270 @@
/*
* QuickJS atom definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DEF
/* Note: first atoms are considered as keywords in the parser */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
DEF(await, "await")
/* empty string */
DEF(empty_string, "")
/* identifiers */
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(columnNumber, "columnNumber")
DEF(message, "message")
DEF(cause, "cause")
DEF(errors, "errors")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")
DEF(toLocaleString, "toLocaleString")
DEF(valueOf, "valueOf")
DEF(eval, "eval")
DEF(prototype, "prototype")
DEF(constructor, "constructor")
DEF(configurable, "configurable")
DEF(writable, "writable")
DEF(enumerable, "enumerable")
DEF(value, "value")
DEF(get, "get")
DEF(set, "set")
DEF(of, "of")
DEF(__proto__, "__proto__")
DEF(undefined, "undefined")
DEF(number, "number")
DEF(boolean, "boolean")
DEF(string, "string")
DEF(object, "object")
DEF(symbol, "symbol")
DEF(integer, "integer")
DEF(unknown, "unknown")
DEF(arguments, "arguments")
DEF(callee, "callee")
DEF(caller, "caller")
DEF(_eval_, "<eval>")
DEF(_ret_, "<ret>")
DEF(_var_, "<var>")
DEF(_arg_var_, "<arg_var>")
DEF(_with_, "<with>")
DEF(lastIndex, "lastIndex")
DEF(target, "target")
DEF(index, "index")
DEF(input, "input")
DEF(defineProperties, "defineProperties")
DEF(apply, "apply")
DEF(join, "join")
DEF(concat, "concat")
DEF(split, "split")
DEF(construct, "construct")
DEF(getPrototypeOf, "getPrototypeOf")
DEF(setPrototypeOf, "setPrototypeOf")
DEF(isExtensible, "isExtensible")
DEF(preventExtensions, "preventExtensions")
DEF(has, "has")
DEF(deleteProperty, "deleteProperty")
DEF(defineProperty, "defineProperty")
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
DEF(ownKeys, "ownKeys")
DEF(add, "add")
DEF(done, "done")
DEF(next, "next")
DEF(values, "values")
DEF(source, "source")
DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
DEF(computed_field, "<computed_field>")
DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
DEF(class_fields_init, "<class_fields_init>")
DEF(brand, "<brand>")
DEF(hash_constructor, "#constructor")
DEF(as, "as")
DEF(from, "from")
DEF(meta, "meta")
DEF(_default_, "*default*")
DEF(_star_, "*")
DEF(Module, "Module")
DEF(then, "then")
DEF(resolve, "resolve")
DEF(reject, "reject")
DEF(promise, "promise")
DEF(proxy, "proxy")
DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
DEF(indices, "indices")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
DEF(bigint, "bigint")
DEF(minus_zero, "-0")
DEF(Infinity, "Infinity")
DEF(minus_Infinity, "-Infinity")
DEF(NaN, "NaN")
DEF(hasIndices, "hasIndices")
DEF(ignoreCase, "ignoreCase")
DEF(multiline, "multiline")
DEF(dotAll, "dotAll")
DEF(sticky, "sticky")
DEF(unicodeSets, "unicodeSets")
/* the following 3 atoms are only used with CONFIG_ATOMICS */
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
/* */
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
DEF(Array, "Array")
DEF(Error, "Error")
DEF(Number, "Number")
DEF(String, "String")
DEF(Boolean, "Boolean")
DEF(Symbol, "Symbol")
DEF(Arguments, "Arguments")
DEF(Math, "Math")
DEF(JSON, "JSON")
DEF(Date, "Date")
DEF(Function, "Function")
DEF(GeneratorFunction, "GeneratorFunction")
DEF(ForInIterator, "ForInIterator")
DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
DEF(Float16Array, "Float16Array")
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
DEF(BigInt, "BigInt")
DEF(WeakRef, "WeakRef")
DEF(FinalizationRegistry, "FinalizationRegistry")
DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator")
DEF(String_Iterator, "String Iterator")
DEF(RegExp_String_Iterator, "RegExp String Iterator")
DEF(Generator, "Generator")
DEF(Proxy, "Proxy")
DEF(Promise, "Promise")
DEF(PromiseResolveFunction, "PromiseResolveFunction")
DEF(PromiseRejectFunction, "PromiseRejectFunction")
DEF(AsyncFunction, "AsyncFunction")
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
DEF(AsyncFunctionReject, "AsyncFunctionReject")
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
DEF(AsyncGenerator, "AsyncGenerator")
DEF(EvalError, "EvalError")
DEF(RangeError, "RangeError")
DEF(ReferenceError, "ReferenceError")
DEF(SyntaxError, "SyntaxError")
DEF(TypeError, "TypeError")
DEF(URIError, "URIError")
DEF(InternalError, "InternalError")
/* private symbols */
DEF(Private_brand, "<brand>")
/* symbols */
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
DEF(Symbol_iterator, "Symbol.iterator")
DEF(Symbol_match, "Symbol.match")
DEF(Symbol_matchAll, "Symbol.matchAll")
DEF(Symbol_replace, "Symbol.replace")
DEF(Symbol_search, "Symbol.search")
DEF(Symbol_split, "Symbol.split")
DEF(Symbol_toStringTag, "Symbol.toStringTag")
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#endif /* DEF */

4344
source/quickjs-libc.c Normal file

File diff suppressed because it is too large Load Diff

66
source/quickjs-libc.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* QuickJS C library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QUICKJS_LIBC_H
#define QUICKJS_LIBC_H
#include <stdio.h>
#include <stdlib.h>
#include "quickjs.h"
#ifdef __cplusplus
extern "C" {
#endif
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx);
JSValue js_std_await(JSContext *ctx, JSValue obj);
void js_std_init_handlers(JSRuntime *rt);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
JS_BOOL use_realpath, JS_BOOL is_main);
int js_module_test_json(JSContext *ctx, JSValueConst attributes);
int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes);
JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque,
JSValueConst attributes);
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
void js_std_eval_binary_json_module(JSContext *ctx,
const uint8_t *buf, size_t buf_len,
const char *module_name);
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* QUICKJS_LIBC_H */

370
source/quickjs-opcode.h Normal file
View File

@@ -0,0 +1,370 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( private_symbol, 5, 0, 1, atom)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none)
DEF( init_ctor, 1, 0, 1, none)
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF( return_async, 1, 1, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_error, 6, 0, 0, atom_u8)
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 2, 1, none) /* dynamic module import */
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( get_array_el3, 1, 2, 3, none) /* obj prop -> obj prop1 value */
DEF( put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF( define_field, 5, 2, 1, atom)
DEF( set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF( set_proto, 1, 2, 1, none)
DEF(set_home_object, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
DEF( to_propkey, 1, 1, 1, none)
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
DEF( for_in_start, 1, 1, 1, none)
DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */
DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */
DEF( iterator_close, 1, 3, 0, none)
DEF( iterator_next, 1, 4, 4, none)
DEF( iterator_call, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
DEF( yield, 1, 1, 2, none)
DEF( yield_star, 1, 1, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
DEF( private_in, 1, 2, 1, none)
DEF(push_bigint_i32, 5, 0, 1, i32)
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
/* temporary opcodes: never emitted in the final bytecode */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
/* the following opcodes must be in the same order as the 'with_x' and
get_var_undef, get_var and put_var opcodes */
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
DEF( get_length, 1, 1, 1, none)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
DEF(typeof_is_undefined, 1, 1, 1, none)
DEF( typeof_is_function, 1, 1, 1, none)
#endif
#undef DEF
#undef def
#endif /* DEF */

55604
source/quickjs.c Normal file

File diff suppressed because it is too large Load Diff

1201
source/quickjs.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +0,0 @@
[wrap-git]
url = https://gitea.pockle.world/john/dull.git
revision = head
depth = 1
[provide]
quickjs = quickjs_dep

View File

@@ -0,0 +1,23 @@
// Test property access for num.Array
var num = use('num');
// Create an array
var arr = new num.Array([10, 20, 30, 40, 50]);
log.console("Testing property access:");
log.console("arr[0] =", arr[0]);
log.console("arr[1] =", arr[1]);
log.console("arr[2] =", arr[2]);
log.console("arr[4] =", arr[4]);
arr[0] = 15
log.console(arr[0])
log.console("arr[10] =", arr[10]); // Should be undefined
log.console("arr.length =", arr.length);
log.console(arr instanceof num.Array)
log.console(json.encode(arr))
$_.stop();

32
tests/num_setter_test.ce Normal file
View File

@@ -0,0 +1,32 @@
// Test setter functionality for num.Array
var num = use('num');
// Create an array
var arr = new num.Array([1, 2, 3, 4, 5]);
log.console("Original values:");
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
// Test setting values
arr[0] = 100;
arr[1] = 200;
arr[2] = 300.5;
log.console("After setting:");
log.console("arr[0] =", arr[0], "arr[1] =", arr[1], "arr[2] =", arr[2]);
// Test setting with different types
arr[3] = "123.7"; // Should convert string to number
arr[4] = true; // Should convert boolean to number
log.console("After type conversion:");
log.console("arr[3] =", arr[3], "(from string)");
log.console("arr[4] =", arr[4], "(from boolean)");
// Test bounds checking - this should fail silently
arr[10] = 999;
log.console("arr[10] after out-of-bounds set =", arr[10]);
log.console("Final array:", arr.toArray());
$_.stop();

54
tests/num_test.ce Normal file
View File

@@ -0,0 +1,54 @@
// Test the num module
var num = use('num');
// Test matrix creation and operations
var A = new num.Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 10]
]);
log.console("Matrix A:");
log.console(A.toArray());
// Test matrix inversion
var A_inv = A.inverse();
log.console("\nMatrix A inverse:");
log.console(A_inv.toArray());
// Verify A * A_inv = I (approximately)
var I = A.multiply(A_inv);
log.console("\nA * A_inv (should be identity):");
log.console(I.toArray());
// Test array creation
var v = new num.Array([1, 2, 3]);
log.console("\nVector v:");
log.console(v.toArray());
// Test matrix-vector multiplication
var result = A.multiply(v);
log.console("\nA * v:");
log.console(result.toArray());
// Test dot product
var u = new num.Array([4, 5, 6]);
var dot_product = v.dot(u);
log.console("\nv · u =", dot_product);
// Test norm
var v_norm = v.norm();
log.console("||v|| =", v_norm);
// Test matrix-matrix multiplication
var B = new num.Matrix([
[1, 0, 0],
[0, 2, 0],
[0, 0, 3]
]);
var C = A.multiply(B);
log.console("\nA * B:");
log.console(C.toArray());
$_.stop()