40 Commits
qjs ... js-go

Author SHA1 Message Date
John Alanbrook
e63115e152 initial attempt 2025-06-18 18:06:27 -05:00
John Alanbrook
3d5f345236 all compilation is strict 2025-06-18 16:37:55 -05:00
John Alanbrook
4689ea1167 remove module eval branches 2025-06-18 14:10:51 -05:00
John Alanbrook
15d85096a2 remove global symbols 2025-06-18 14:03:12 -05:00
John Alanbrook
f2c2ecf692 remove all private stuff 2025-06-18 13:59:47 -05:00
John Alanbrook
458215f838 remove arguments object 2025-06-18 13:24:22 -05:00
John Alanbrook
7f002e306d remove reflect 2025-06-18 08:46:42 -05:00
John Alanbrook
91a3fef065 finish removing big int stuff; fixes memory leak 2025-06-17 21:36:06 -05:00
John Alanbrook
843b4bd8a8 remove usage of map 2025-06-17 21:16:35 -05:00
John Alanbrook
41fdf49df5 remove bigint 2025-06-17 15:02:40 -05:00
John Alanbrook
c9adbed3ff remove undefined; only use null now 2025-06-17 14:32:27 -05:00
John Alanbrook
3459d85a82 binary logic only works on ints; returns null otherwise 2025-06-17 12:54:20 -05:00
John Alanbrook
9ecdaae7a7 simpler addition rule 2025-06-16 20:40:41 -05:00
John Alanbrook
43b55b29f3 comparing anything but two numbers or two strings is an error 2025-06-16 18:45:57 -05:00
John Alanbrook
a88cee7fae Remove weak-everything. remove throwing on type differences in equality; remove using 'def' as some variable names 2025-06-16 17:02:24 -05:00
John Alanbrook
8b3f5476a9 remove module checks 2025-06-16 15:56:04 -05:00
John Alanbrook
233f59e04a clean up unused functions 2025-06-16 15:49:54 -05:00
John Alanbrook
7b16259c00 Merge branch 'js-rm-eq' into js-rm-modules 2025-06-16 14:20:23 -05:00
John Alanbrook
43baa23dfe Merge remote-tracking branch 'origin/js-rm-htmldda' into js-rm-modules 2025-06-16 14:18:40 -05:00
John Alanbrook
a9ebea9f26 Merge remote-tracking branch 'origin/js-def' into js-rm-modules 2025-06-16 14:16:01 -05:00
John Alanbrook
19b729afbf Merge remote-tracking branch 'origin/js-rm-with' into js-rm-modules 2025-06-16 14:09:29 -05:00
John Alanbrook
47729c225f Merge branch 'js-rm-ta' into js-rm-modules 2025-06-16 14:04:06 -05:00
John Alanbrook
ea6cf5db49 Merge remote-tracking branch 'origin/js-rm-class' into js-rm-modules 2025-06-16 14:01:12 -05:00
John Alanbrook
794baf8598 remove dynamic equality 2025-06-16 13:48:09 -05:00
John Alanbrook
a551368681 def now works as const 2025-06-16 08:07:45 -05:00
John Alanbrook
c20ca8c937 initial attempt 2025-06-15 19:48:48 -05:00
John Alanbrook
0217cf0da6 remove with 2025-06-15 19:40:58 -05:00
John Alanbrook
6bd7251933 initial rm 2025-06-15 10:30:58 -05:00
John Alanbrook
6dc8d97001 remove htmldda 2025-06-15 09:47:09 -05:00
John Alanbrook
9c0565d34f montecarlo faster; rm qjs-layout 2025-06-15 05:18:22 -05:00
John Alanbrook
0702e3495d finish remove? 2025-06-15 05:15:51 -05:00
John Alanbrook
108c39d22d initial attempt 2025-06-14 20:40:28 -05:00
John Alanbrook
946ebe5cd7 remove date intrinsic 2025-06-14 18:04:27 -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
150 changed files with 62284 additions and 1937 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 = 1000000;
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

@@ -42,34 +42,34 @@ for (let i = 0; i < 100; i++) {
// Calculate statistics
function getStats(arr) {
const avg = arr.reduce((a, b) => a + b) / arr.length;
const min = Math.min(...arr);
const max = Math.max(...arr);
def avg = arr.reduce((a, b) => a + b) / arr.length;
def min = Math.min(...arr);
def max = Math.max(...arr);
return { avg, min, max };
}
// Pretty print results
log.console("\n=== Performance Test Results (100 iterations) ===");
log.console("\n== Performance Test Results (100 iterations) ==");
log.console("\nJSON Decoding (ms):");
const jsonDecStats = getStats(jsonDecodeTimes);
def jsonDecStats = getStats(jsonDecodeTimes);
log.console(`Average: ${jsonDecStats.avg.toFixed(2)} ms`);
log.console(`Min: ${jsonDecStats.min.toFixed(2)} ms`);
log.console(`Max: ${jsonDecStats.max.toFixed(2)} ms`);
log.console("\nJSON Encoding (ms):");
const jsonEncStats = getStats(jsonEncodeTimes);
def jsonEncStats = getStats(jsonEncodeTimes);
log.console(`Average: ${jsonEncStats.avg.toFixed(2)} ms`);
log.console(`Min: ${jsonEncStats.min.toFixed(2)} ms`);
log.console(`Max: ${jsonEncStats.max.toFixed(2)} ms`);
log.console("\nNOTA Encoding (ms):");
const notaEncStats = getStats(notaEncodeTimes);
def notaEncStats = getStats(notaEncodeTimes);
log.console(`Average: ${notaEncStats.avg.toFixed(2)} ms`);
log.console(`Min: ${notaEncStats.min.toFixed(2)} ms`);
log.console(`Max: ${notaEncStats.max.toFixed(2)} ms`);
log.console("\nNOTA Decoding (ms):");
const notaDecStats = getStats(notaDecodeTimes);
def notaDecStats = getStats(notaDecodeTimes);
log.console(`Average: ${notaDecStats.avg.toFixed(2)} ms`);
log.console(`Min: ${notaDecStats.min.toFixed(2)} ms`);
log.console(`Max: ${notaDecStats.max.toFixed(2)} ms`);

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

@@ -36,7 +36,7 @@ function roundTripWota(value) {
// iterations: how many times to loop
//
// You can tweak these as you like for heavier or lighter tests.
const benchmarks = [
def benchmarks = [
{
name: "Small Integers",
data: [0, 42, -1, 2023],
@@ -76,7 +76,7 @@ const benchmarks = [
// Print a header
log.console("Wota Encode/Decode Benchmark");
log.console("============================\n");
log.console("===================\n");
// We'll run each benchmark scenario in turn.
for (let bench of benchmarks) {

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 = [
def 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) {
@@ -53,29 +62,29 @@ const libraries = [
// Each scenario has { name, data, iterations }
////////////////////////////////////////////////////////////////////////////////
const benchmarks = [
def 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)
//
////////////////////////////////////////////////////////////////////////////////
@@ -130,7 +139,7 @@ function runBenchmarkForLibrary(lib, bench) {
let e = lib.encode(bench.data[j]);
// store only in the very first iteration, so we can decode them later
// but do not store them every iteration or we blow up memory.
if (i === 0) {
if (i == 0) {
encodedList.push(e);
totalSize += lib.getSize(e);
}
@@ -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

@@ -18,7 +18,7 @@ var state = {
// Helper to calculate progress percentage
function get_progress() {
if (state.total_bytes === 0) {
if (state.total_bytes == 0) {
return 0;
}
return Math.round((state.downloaded_bytes / state.total_bytes) * 100);
@@ -150,7 +150,7 @@ function read_next_chunk() {
try {
var chunk = http.fetch_read_chunk(state.connection);
if (chunk === null) {
if (chunk == null) {
// Download complete
finish_download();
return;

View File

@@ -18,7 +18,7 @@ $_.portal(e => {
send(waiting_client, e.actor)
send(e, waiting_client.actor)
waiting_client = undefined
waiting_client = null
return
}

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,32 +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()
message('⚙ System qjs-layout not found, building subproject...')
deps += dependency('qjs-layout', static:true)
else
deps += qjs_layout_dep
endif
miniz_dep = dependency('miniz', static: true, required: false)
if not miniz_dep.found()
message('⚙ System miniz not found, building subproject...')
@@ -186,14 +169,6 @@ else
deps += miniz_dep
endif
libuv_dep = dependency('libuv', static: true, required: false)
if not libuv_dep.found()
message('⚙ System libuv not found, building subproject...')
deps += dependency('libuv', static:true, fallback: ['libuv', 'libuv_dep'])
else
deps += libuv_dep
endif
# Try to find system-installed physfs first
physfs_dep = dependency('physfs', static: true, required: false)
if not physfs_dep.found()
@@ -204,6 +179,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 +201,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 +271,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 +279,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

@@ -2,7 +2,7 @@ var graphics = use('graphics')
var color = use('color')
var sprite = {
image: undefined,
image: null,
set color(x) { this._sprite.color = x; },
get color() { return this._sprite.color; },
anim_speed: 1,
@@ -12,7 +12,7 @@ var sprite = {
return;
}
if (typeof str === 'string') {
if (typeof str == 'string') {
if (!this.animset[str]) {
fn?.();
return;
@@ -26,8 +26,8 @@ var sprite = {
this.del_anim?.();
this.del_anim = () => {
this.del_anim = undefined;
advance = undefined;
this.del_anim = null;
advance = null;
stop?.();
};
@@ -38,10 +38,10 @@ var sprite = {
var done = false;
if (reverse) {
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
if (f === playing.frames.length - 1) done = true;
if (f == playing.frames.length - 1) done = true;
} else {
f = (f + 1) % playing.frames.length;
if (f === 0) done = true;
if (f == 0) done = true;
}
this.image = playing.frames[f];
@@ -102,11 +102,11 @@ var sprite = {
},
garbage: function() {
this.del_anim?.();
this.anim = undefined;
this.anim = null;
tree.delete(this._sprite)
this.transform.parent = undefined
this.transform.parent = null
for (var t of this.transform.children())
t.parent = undefined
t.parent = null
delete this.transform.sprite
delete this._sprite
// log.console("CLEARED SPRITE")
@@ -216,7 +216,7 @@ sprite.to_queue = function(ysort = false)
};
var culled = sprite.tree.query(camrect)
if (culled.length == 0) return [];
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, undefined, 1);
var cmd = graphics.make_sprite_queue(culled, prosperon.camera, null, 1);
return cmd;
}

View File

@@ -11,18 +11,18 @@ var input = use('input')
var lay_ctx = layout.make_context();
var clay_base = {
font: undefined,
background_image: undefined,
font: null,
background_image: null,
slice: 0,
font: 'smalle.16',
font_size: undefined,
font_size: null,
color: [1,1,1,1],
spacing:0,
padding:0,
margin:0,
offset:[0,0],
size:undefined,
background_color: undefined
size:null,
background_color: null
};
var root_item;
@@ -125,8 +125,8 @@ function add_item(config)
// Adjust for child_gap
if (root_config._childIndex > 0) {
var parentContain = root_config.contain || 0;
var isVStack = (parentContain & layout.contain.column) !== 0;
var isHStack = (parentContain & layout.contain.row) !== 0;
var isVStack = (parentContain & layout.contain.column) != 0;
var isHStack = (parentContain & layout.contain.row) != 0;
if (isVStack) {
margin.t += childGap;
@@ -162,7 +162,7 @@ function add_item(config)
function rectify_configs(config_array)
{
if (config_array.length === 0)
if (config_array.length == 0)
config_array = [{}];
for (var i = config_array.length-1; i > 0; i--)
@@ -216,8 +216,8 @@ clay.button = function button(str, action, config = {})
config.action = action;
}
var hovered = undefined;
clay.newframe = function() { hovered = undefined; }
var hovered = null;
clay.newframe = function() { hovered = null; }
// mousepos given in hud coordinates
clay.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos()))

View File

@@ -1,6 +1,6 @@
function tohex(n) {
var s = Math.floor(n).toString(16);
if (s.length === 1) s = "0" + s;
if (s.length == 1) s = "0" + s;
return s.toUpperCase();
};
@@ -98,14 +98,14 @@ Color.normalize = function (c) {
};
for (var p of Object.keys(c)) {
if (typeof c[p] !== "object") continue;
if (typeof c[p] != "object") continue;
if (!Array.isArray(c[p])) {
Color.normalize(c[p]);
continue;
}
// Add alpha channel if not present
if (c[p].length === 3) {
if (c[p].length == 3) {
c[p][3] = 1;
}

View File

@@ -107,9 +107,9 @@ input.mouse.normal.doc = "Set the mouse to show again after hiding.";
input.keyboard = {};
input.keyboard.down = function (code) {
if (typeof code === "number") return downkeys[code];
if (typeof code === "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()];
return undefined;
if (typeof code == "number") return downkeys[code];
if (typeof code == "string") return downkeys[code.toUpperCase().charCodeAt()] || downkeys[code.toLowerCase().charCodeAt()];
return null;
};
input.print_pawn_kbm = function (pawn) {
@@ -157,7 +157,7 @@ input.print_md_kbm = function print_md_kbm(pawn) {
};
input.has_bind = function (pawn, bind) {
return typeof pawn.inputs?.[bind] === "function";
return typeof pawn.inputs?.[bind] == "function";
};
input.action = {
@@ -176,17 +176,17 @@ input.tabcomplete = function tabcomplete(val, list) {
if (!val) return val;
list = filter(x => x.startsWith(val))
if (list.length === 1) {
if (list.length == 1) {
return list[0];
}
var ret = undefined;
var ret = null;
var i = val.length;
while (!ret && list.length !== 0) {
while (!ret && list.length != 0) {
var char = list[0][i];
if (
!list.every(function (x) {
return x[i] === char;
return x[i] == char;
})
)
ret = list[0].slice(0, i);
@@ -213,7 +213,7 @@ var Player = {
mouse_input(type, ...args) {
for (var pawn of [...this.pawns].reverse()) {
if (typeof pawn.inputs?.mouse?.[type] === "function") {
if (typeof pawn.inputs?.mouse?.[type] == "function") {
pawn.inputs.mouse[type].call(pawn, ...args);
pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) return;
@@ -223,7 +223,7 @@ var Player = {
char_input(c) {
for (var pawn of [...this.pawns].reverse()) {
if (typeof pawn.inputs?.char === "function") {
if (typeof pawn.inputs?.char == "function") {
pawn.inputs.char.call(pawn, c);
pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) return;
@@ -270,16 +270,16 @@ var Player = {
fn = inputs[cmd].released;
break;
case "down":
if (typeof inputs[cmd].down === "function") fn = inputs[cmd].down;
if (typeof inputs[cmd].down == "function") fn = inputs[cmd].down;
else if (inputs[cmd].down) fn = inputs[cmd];
}
var consumed = false;
if (typeof fn === "function") {
if (typeof fn == "function") {
fn.call(pawn, ...args);
consumed = true;
}
if (state === "released") inputs.release_post?.call(pawn);
if (state == "released") inputs.release_post?.call(pawn);
if (inputs.block) return;
if (consumed) return;
}

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,16 +75,9 @@ 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
draw.ellipse = function(pos, radii, defl, material) {
var opt = defl ? {...ellipse_def, ...defl} : ellipse_def
if (opt.thickness <= 0) opt.thickness = Math.max(radii[0], radii[1])
add_command("draw_ellipse", {
@@ -135,9 +88,9 @@ draw.ellipse = function(pos, radii, def, material) {
})
}
draw.line = function(points, def, material)
draw.line = function(points, defl, material)
{
var opt = def ? {...line_def, ...def} : line_def
var opt = defl ? {...line_def, ...defl} : line_def
add_command("draw_line", {
points: points,
@@ -146,24 +99,24 @@ draw.line = function(points, def, material)
})
}
draw.cross = function render_cross(pos, size, def, material) {
draw.cross = function render_cross(pos, size, defl, material) {
var a = [pos.add([0, size]), pos.add([0, -size])]
var b = [pos.add([size, 0]), pos.add([-size, 0])]
draw.line(a, def, material)
draw.line(b, def, material)
draw.line(a, defl, material)
draw.line(b, defl, material)
}
draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, def, material) {
draw.arrow = function render_arrow(start, end, wingspan = 4, wingangle = 10, defl, material) {
var dir = math.norm(end.sub(start))
var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end]
var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end]
draw.line([start, end], def, material)
draw.line(wing1, def, material)
draw.line(wing2, def, material)
draw.line([start, end], defl, material)
draw.line(wing1, defl, material)
draw.line(wing2, defl, material)
}
draw.rectangle = function render_rectangle(rect, def, material) {
var opt = def ? {...rect_def, ...def} : rect_def
draw.rectangle = function render_rectangle(rect, defl, material) {
var opt = defl ? {...rect_def, ...defl} : rect_def
add_command("draw_rect", {
rect: rect,
@@ -184,27 +137,25 @@ 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,
})
}
draw.circle = function render_circle(pos, radius, def, material) {
draw.ellipse(pos, [radius,radius], def, material)
draw.circle = function render_circle(pos, radius, defl, material) {
draw.ellipse(pos, [radius,radius], defl, material)
}
draw.text = function text(text, pos, font = 'fonts/c64.ttf', size = 8, color = color.white, wrap = 0) {

View File

@@ -111,7 +111,7 @@ ex.draw = function()
if (!diff) throw new Error("emitter does not have a proper diffuse texture")
var mesh = graphics.make_sprite_mesh(this.particles)
if (mesh.num_indices === 0) return
if (mesh.num_indices == 0) return
render.queue({
type:'geometry',
mesh,

View File

@@ -66,5 +66,5 @@ this.hud = function() {
draw.images(bunnyTex, bunnies)
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + bunnies.length
draw.text(msg, {x:0, y:0, width:config.width, height:40}, undefined, 0, color.white, 0)
draw.text(msg, {x:0, y:0, width:config.width, height:40}, null, 0, color.white, 0)
}

View File

@@ -41,7 +41,7 @@ function updateTitle() {
break;
case 'connected':
if (myColor) {
title += (mover.turn === myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
title += (mover.turn == myColor ? "Your turn (" + myColor + ")" : "Opponent's turn (" + mover.turn + ")");
} else {
title += mover.turn + " turn";
}
@@ -64,10 +64,10 @@ var opponentHoldingPiece = false;
var opponentSelectPos = null;
function handleMouseButtonDown(e) {
if (e.which !== 0) return;
if (e.which != 0) return;
// Don't allow piece selection unless we have an opponent
if (gameState !== 'connected' || !opponent) return;
if (gameState != 'connected' || !opponent) return;
var mx = e.mouse.x;
var my = e.mouse.y;
@@ -76,7 +76,7 @@ function handleMouseButtonDown(e) {
if (!grid.inBounds(c)) return;
var cell = grid.at(c);
if (cell.length && cell[0].colour === mover.turn) {
if (cell.length && cell[0].colour == mover.turn) {
selectPos = c;
holdingPiece = true;
// Send pickup notification to opponent
@@ -92,10 +92,10 @@ function handleMouseButtonDown(e) {
}
function handleMouseButtonUp(e) {
if (e.which !== 0 || !holdingPiece || !selectPos) return;
if (e.which != 0 || !holdingPiece || !selectPos) return;
// Don't allow moves unless we have an opponent and it's our turn
if (gameState !== 'connected' || !opponent || !isMyTurn) {
if (gameState != 'connected' || !opponent || !isMyTurn) {
holdingPiece = false;
return;
}
@@ -147,7 +147,7 @@ function handleMouseMotion(e) {
hoverPos = c;
// Send mouse position to opponent in real-time
if (opponent && gameState === 'connected') {
if (opponent && gameState == 'connected') {
send(opponent, {
type: 'mouse_move',
pos: c,
@@ -159,11 +159,11 @@ function handleMouseMotion(e) {
function handleKeyDown(e) {
// S key - start server
if (e.scancode === 22 && gameState === 'waiting') { // S key
if (e.scancode == 22 && gameState == 'waiting') { // S key
startServer();
}
// J key - join server
else if (e.scancode === 13 && gameState === 'waiting') { // J key
else if (e.scancode == 13 && gameState == 'waiting') { // J key
joinServer();
}
}
@@ -181,8 +181,8 @@ var opponentMouseColor = [1.0, 0.0, 0.0, 1.0]; // Red for opponent mouse
function drawBoard() {
for (var y = 0; y < 8; ++y)
for (var x = 0; x < 8; ++x) {
var isMyHover = hoverPos && hoverPos[0] === x && hoverPos[1] === y;
var isOpponentHover = opponentMousePos && opponentMousePos[0] === x && opponentMousePos[1] === y;
var isMyHover = hoverPos && hoverPos[0] == x && hoverPos[1] == y;
var isOpponentHover = opponentMousePos && opponentMousePos[0] == x && opponentMousePos[1] == y;
var isValidMove = selectPos && holdingPiece && isValidMoveForTurn(selectPos, [x, y]);
var color = ((x+y)&1) ? dark : light;
@@ -211,7 +211,7 @@ function isValidMoveForTurn(from, to) {
// Check if the destination has a piece of the same color
var destCell = grid.at(to);
if (destCell.length && destCell[0].colour === piece.colour) {
if (destCell.length && destCell[0].colour == piece.colour) {
return false;
}
@@ -225,15 +225,15 @@ function drawPieces() {
// Skip drawing the piece being held (by me or opponent)
if (holdingPiece && selectPos &&
piece.coord[0] === selectPos[0] &&
piece.coord[1] === selectPos[1]) {
piece.coord[0] == selectPos[0] &&
piece.coord[1] == selectPos[1]) {
return;
}
// Skip drawing the piece being held by opponent
if (opponentHoldingPiece && opponentSelectPos &&
piece.coord[0] === opponentSelectPos[0] &&
piece.coord[1] === opponentSelectPos[1]) {
piece.coord[0] == opponentSelectPos[0] &&
piece.coord[1] == opponentSelectPos[1]) {
return;
}
@@ -329,10 +329,10 @@ $_.receiver(e => {
send(e, update(e.dt))
else if (e.kind == 'draw')
send(e, draw())
else if (e.type === 'game_start' || e.type === 'move' || e.type === 'greet')
else if (e.type == 'game_start' || e.type == 'move' || e.type == 'greet')
log.console("Receiver got message:", e.type, e);
if (e.type === 'greet') {
if (e.type == 'greet') {
log.console("Server received greet from client");
// Store the client's actor object for ongoing communication
opponent = e.client_actor;
@@ -348,13 +348,13 @@ $_.receiver(e => {
});
log.console("game_start message sent to client");
}
else if (e.type === 'game_start') {
else if (e.type == 'game_start') {
log.console("Game starting, I am:", e.your_color);
myColor = e.your_color;
isMyTurn = (myColor === 'white');
isMyTurn = (myColor == 'white');
gameState = 'connected';
updateTitle();
} else if (e.type === 'move') {
} else if (e.type == 'move') {
log.console("Received move from opponent:", e.from, "to", e.to);
// Apply opponent's move
var fromCell = grid.at(e.from);
@@ -370,26 +370,26 @@ $_.receiver(e => {
} else {
log.console("No piece found at from position");
}
} else if (e.type === 'mouse_move') {
} else if (e.type == 'mouse_move') {
// Update opponent's mouse position
opponentMousePos = e.pos;
opponentHoldingPiece = e.holding;
opponentSelectPos = e.selectPos;
} else if (e.type === 'piece_pickup') {
} else if (e.type == 'piece_pickup') {
// Opponent picked up a piece
opponentSelectPos = e.pos;
opponentHoldingPiece = true;
} else if (e.type === 'piece_drop') {
} else if (e.type == 'piece_drop') {
// Opponent dropped their piece
opponentHoldingPiece = false;
opponentSelectPos = null;
} else if (e.type === 'mouse_button_down') {
} else if (e.type == 'mouse_button_down') {
handleMouseButtonDown(e)
} else if (e.type === 'mouse_button_up') {
} else if (e.type == 'mouse_button_up') {
handleMouseButtonUp(e)
} else if (e.type === 'mouse_motion') {
} else if (e.type == 'mouse_motion') {
handleMouseMotion(e)
} else if (e.type === 'key_down') {
} else if (e.type == 'key_down') {
handleKeyDown(e)
}
})

View File

@@ -1,57 +1,69 @@
var CELLS = Symbol()
var key = function key(x,y) { return `${x},${y}` }
function grid(w, h)
{
this[CELLS] = new Map()
function grid(w, h) {
this.width = w;
this.height = h;
// create a height×width array of empty lists
this.cells = new Array(h);
for (let y = 0; y < h; y++) {
this.cells[y] = new Array(w);
for (let x = 0; x < w; x++) {
this.cells[y][x] = []; // each cell holds its own list
}
}
}
grid.prototype = {
cell(x,y) {
var k = key(x,y)
if (!this[CELLS].has(k)) this[CELLS].set(k,[])
return this[CELLS].get(k)
// return the array at (x,y)
cell(x, y) {
return this.cells[y][x];
},
// alias for cell
at(pos) {
return this.cell(pos.x, pos.y);
},
// add an entity into a cell
add(entity, pos) {
this.cell(pos.x, pos.y).push(entity);
entity.coord = pos.slice();
},
// remove an entity from a cell
remove(entity, pos) {
var c = this.cell(pos.x, pos.y);
c.splice(c.indexOf(entity), 1);
},
at(pos) {
return this.cell(pos.x, pos.y);
const c = this.cell(pos.x, pos.y);
const i = c.indexOf(entity);
if (i !== -1) c.splice(i, 1);
},
// bounds check
inBounds(pos) {
return pos.x >= 0 && pos.x < this.width && pos.y >= 0 && pos.y < this.height;
return (
pos.x >= 0 && pos.x < this.width &&
pos.y >= 0 && pos.y < this.height
);
},
// call fn(entity, coord) for every entity in every cell
each(fn) {
for (var [k, list] of this[CELLS])
for (var p of list) fn(p, p.coord);
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const list = this.cells[y][x];
for (let entity of list) {
fn(entity, entity.coord);
}
}
}
},
// printable representation
toString() {
var out = `grid [${this.width}x${this.height}]
`
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var cell = this.at([x,y]);
out += cell.length
let out = `grid [${this.width}×${this.height}]\n`;
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
out += this.cells[y][x].length;
}
if (y !== this.height - 1) out += "\n"
if (y !== this.height - 1) out += "\n";
}
return out
},
}
return grid
return out;
}
};

View File

@@ -5,17 +5,17 @@ var MovementSystem = function(grid, rules) {
}
MovementSystem.prototype.tryMove = function (piece, to) {
if (piece.colour !== this.turn) return false;
if (piece.colour != this.turn) return false;
// normalise to into our hybrid coord
var dest = [to.x !== undefined ? to.x : to[0],
to.y !== undefined ? to.y : to[1]];
var dest = [to.x ?? t[0],
to.y ?? to[1]];
if (!this.grid.inBounds(dest)) return false;
if (!this.rules.canMove(piece, piece.coord, dest, this.grid)) return false;
var victims = this.grid.at(dest);
if (victims.length && victims[0].colour === piece.colour) return false;
if (victims.length && victims[0].colour == piece.colour) return false;
if (victims.length) victims[0].captured = true;
this.grid.remove(piece, piece.coord);
@@ -25,7 +25,7 @@ MovementSystem.prototype.tryMove = function (piece, to) {
piece.coord.x = dest.x;
piece.coord.y = dest.y;
this.turn = (this.turn === 'white') ? 'black' : 'white';
this.turn = (this.turn == 'white') ? 'black' : 'white';
return true;
};

View File

@@ -1,32 +1,32 @@
/* helper robust coord access */
function cx(c) { return (c.x !== undefined) ? c.x : c[0]; }
function cy(c) { return (c.y !== undefined) ? c.y : c[1]; }
function cx(c) { return c.x ?? c[0] }
function cy(c) { return c.y ?? c[1] }
/* simple move-shape checks */
var deltas = {
pawn: function (pc, dx, dy, grid, to) {
var dir = (pc.colour === 'white') ? -1 : 1;
var base = (pc.colour === 'white') ? 6 : 1;
var one = (dy === dir && dx === 0 && grid.at(to).length === 0);
var two = (dy === 2 * dir && dx === 0 && cy(pc.coord) === base &&
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length === 0 &&
grid.at(to).length === 0);
var cap = (dy === dir && Math.abs(dx) === 1 && grid.at(to).length);
var dir = (pc.colour == 'white') ? -1 : 1;
var base = (pc.colour == 'white') ? 6 : 1;
var one = (dy == dir && dx == 0 && grid.at(to).length == 0);
var two = (dy == 2 * dir && dx == 0 && cy(pc.coord) == base &&
grid.at({ x: cx(pc.coord), y: cy(pc.coord)+dir }).length == 0 &&
grid.at(to).length == 0);
var cap = (dy == dir && Math.abs(dx) == 1 && grid.at(to).length);
return one || two || cap;
},
rook : function (pc, dx, dy) { return (dx === 0 || dy === 0); },
bishop: function (pc, dx, dy) { return Math.abs(dx) === Math.abs(dy); },
queen : function (pc, dx, dy) { return (dx === 0 || dy === 0 || Math.abs(dx) === Math.abs(dy)); },
knight: function (pc, dx, dy) { return (Math.abs(dx) === 1 && Math.abs(dy) === 2) ||
(Math.abs(dx) === 2 && Math.abs(dy) === 1); },
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) === 1; }
rook : function (pc, dx, dy) { return (dx == 0 || dy == 0); },
bishop: function (pc, dx, dy) { return Math.abs(dx) == Math.abs(dy); },
queen : function (pc, dx, dy) { return (dx == 0 || dy == 0 || Math.abs(dx) == Math.abs(dy)); },
knight: function (pc, dx, dy) { return (Math.abs(dx) == 1 && Math.abs(dy) == 2) ||
(Math.abs(dx) == 2 && Math.abs(dy) == 1); },
king : function (pc, dx, dy) { return Math.max(Math.abs(dx), Math.abs(dy)) == 1; }
};
function clearLine(from, to, grid) {
var dx = Math.sign(cx(to) - cx(from));
var dy = Math.sign(cy(to) - cy(from));
var x = cx(from) + dx, y = cy(from) + dy;
while (x !== cx(to) || y !== cy(to)) {
while (x != cx(to) || y != cy(to)) {
if (grid.at({ x: x, y: y }).length) return false;
x += dx; y += dy;
}
@@ -38,7 +38,7 @@ function canMove(piece, from, to, grid) {
var dy = cy(to) - cy(from);
var f = deltas[piece.kind];
if (!f || !f(piece, dx, dy, grid, to)) return false;
if (piece.kind === 'knight') return true;
if (piece.kind == 'knight') return true;
return clearLine(from, to, grid);
}

View File

@@ -82,5 +82,5 @@ this.hud = function() {
// Simple score display
var msg = score1 + " " + score2
draw.text(msg, {x:0, y:10, width:config.width, height:40}, undefined, 0, color.white, 0)
draw.text(msg, {x:0, y:10, width:config.width, height:40}, null, 0, color.white, 0)
}

View File

@@ -36,7 +36,7 @@ function spawnApple() {
apple = {x:Math.floor(Math.random()*gridW), y:Math.floor(Math.random()*gridH)}
// Re-spawn if apple lands on snake
for (var i=0; i<snake.length; i++)
if (snake[i].x === apple.x && snake[i].y === apple.y) { spawnApple(); return }
if (snake[i].x == apple.x && snake[i].y == apple.y) { spawnApple(); return }
}
function wrap(pos) {
@@ -49,7 +49,7 @@ function wrap(pos) {
resetGame()
this.update = function(dt) {
if (gameState !== "playing") return
if (gameState != "playing") return
moveTimer += dt
if (moveTimer < moveInterval) return
moveTimer -= moveInterval
@@ -63,7 +63,7 @@ this.update = function(dt) {
// Check collision with body
for (var i=0; i<snake.length; i++) {
if (snake[i].x === head.x && snake[i].y === head.y) {
if (snake[i].x == head.x && snake[i].y == head.y) {
gameState = "gameover"
return
}
@@ -73,7 +73,7 @@ this.update = function(dt) {
snake.unshift(head)
// Eat apple?
if (head.x === apple.x && head.y === apple.y) spawnApple()
if (head.x == apple.x && head.y == apple.y) spawnApple()
else snake.pop()
}
@@ -90,9 +90,9 @@ this.hud = function() {
// Draw apple
draw.rectangle({x:apple.x*cellSize, y:apple.y*cellSize, width:cellSize, height:cellSize}, color.red)
if (gameState === "gameover") {
if (gameState == "gameover") {
var msg = "GAME OVER! Press SPACE to restart."
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, undefined, 0, color.white)
draw.text(msg, {x:0, y:config.height*0.5-10, width:config.width, height:20}, null, 0, color.white)
}
}
@@ -100,19 +100,19 @@ this.hud = function() {
// "Up" means y=1, so going physically up on screen
this.inputs = {
up: function() {
if (direction.y !== -1) nextDirection = {x:0,y:1}
if (direction.y != -1) nextDirection = {x:0,y:1}
},
down: function() {
if (direction.y !== 1) nextDirection = {x:0,y:-1}
if (direction.y != 1) nextDirection = {x:0,y:-1}
},
left: function() {
if (direction.x !== 1) nextDirection = {x:-1,y:0}
if (direction.x != 1) nextDirection = {x:-1,y:0}
},
right: function() {
if (direction.x !== -1) nextDirection = {x:1,y:0}
if (direction.x != -1) nextDirection = {x:1,y:0}
},
space: function() {
if (gameState==="gameover") resetGame()
if (gameState=="gameover") resetGame()
}
}

View File

@@ -128,7 +128,7 @@ function end_game(score) {
update_stat(STATS.TOTAL_SCORE, total_score, false);
// Check for achievements
if (games_played === 1) {
if (games_played == 1) {
unlock_achievement(ACHIEVEMENTS.FIRST_WIN);
}

View File

@@ -128,10 +128,10 @@ function clearLines() {
}
}
// Score
if (lines===1) score += 100
else if (lines===2) score += 300
else if (lines===3) score += 500
else if (lines===4) score += 800
if (lines==1) score += 100
else if (lines==2) score += 300
else if (lines==3) score += 500
else if (lines==4) score += 800
linesCleared += lines
level = Math.floor(linesCleared/10)
}
@@ -153,7 +153,7 @@ spawnPiece()
this.update = function(dt) {
if (gameOver) return
// ========== Horizontal Movement Gate ==========
// ======= Horizontal Movement Gate =======
var leftPressed = input.keyboard.down('a')
var rightPressed = input.keyboard.down('d')
var horizontalMove = 0
@@ -191,7 +191,7 @@ this.update = function(dt) {
hMoveTimer -= dt
prevLeft = leftPressed
prevRight = rightPressed
// ========== End Horizontal Movement Gate ==========
// ======= End Horizontal Movement Gate =======
// Rotate with W (once per press, no spinning)
if (input.keyboard.down('w')) {
@@ -249,7 +249,7 @@ this.hud = function() {
}
// Next piece window
draw.text("Next", {x:70, y:5, width:50, height:10}, undefined, 0, color.white)
draw.text("Next", {x:70, y:5, width:50, height:10}, null, 0, color.white)
if (nextPiece) {
for (var i=0; i<nextPiece.blocks.length; i++) {
var nx = nextPiece.blocks[i][0]
@@ -262,10 +262,10 @@ this.hud = function() {
// Score & Level
var info = "Score: " + score + "\nLines: " + linesCleared + "\nLevel: " + level
draw.text(info, {x:70, y:30, width:90, height:50}, undefined, 0, color.white)
draw.text(info, {x:70, y:30, width:90, height:50}, null, 0, color.white)
if (gameOver) {
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, undefined, 0, color.red)
draw.text("GAME OVER", {x:10, y:config.height*0.5-5, width:config.width-20, height:20}, null, 0, color.red)
}
}

View File

@@ -46,9 +46,9 @@ geometry.sphere.volume[cell.DOC] = `
`
geometry.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
if (typeof r === "number") r = [r, r]
if (typeof theta === "number") theta = [theta, theta]
if (typeof phi === "number") phi = [phi, phi]
if (typeof r == "number") r = [r, r]
if (typeof theta == "number") theta = [theta, theta]
if (typeof phi == "number") phi = [phi, phi]
var ra = Math.random_range(r[0], r[1])
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]))

View File

@@ -18,23 +18,22 @@ var CPU = Symbol()
var LASTUSE = Symbol()
var LOADING = Symbol()
var cache = new Map()
var cache = {}
// Image constructor function
graphics.Image = function(surfaceData) {
// Initialize private properties
this[CPU] = surfaceData || undefined;
this[GPU] = undefined;
this[CPU] = surfaceData || null;
this[GPU] = null;
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]
}
},
@@ -93,11 +89,11 @@ Object.defineProperties(graphics.Image.prototype, {
// Add methods to prototype
graphics.Image.prototype.unload_gpu = function() {
this[GPU] = undefined
this[GPU] = null
}
graphics.Image.prototype.unload_cpu = function() {
this[CPU] = undefined
this[CPU] = null
}
function calc_image_size(img) {
@@ -127,7 +123,7 @@ function make_handle(obj)
}
function wrapSurface(surf, maybeRect){
const h = make_handle(surf);
def h = make_handle(surf);
if(maybeRect) h.rect = maybeRect; /* honour frame sub-rect */
return h;
}
@@ -154,7 +150,7 @@ function decode_image(bytes, ext)
function create_image(path){
try{
const bytes = io.slurpbytes(path);
def bytes = io.slurpbytes(path);
let raw = decode_image(bytes, path.ext());
@@ -173,8 +169,8 @@ function create_image(path){
return makeAnim( wrapFrames(raw.frames), !!raw.loop );
/* ── Case D: ASE helpers returned { animName:{frames,loop}, … } ── */
const anims = {};
for(const [name, anim] of Object.entries(raw)){
def anims = {};
for(def [name, anim] of Object.entries(raw)){
if(anim && Array.isArray(anim.frames))
anims[name] = makeAnim( wrapFrames(anim.frames), !!anim.loop );
else if(anim && anim.surface) /* ase with flat surface */
@@ -223,7 +219,7 @@ graphics.is_image[cell.DOC] = `
graphics.texture_from_data = function(data)
{
if (!(data instanceof ArrayBuffer)) return undefined
if (!(data instanceof ArrayBuffer)) return null
var image = graphics.make_texture(data);
var img = make_handle(image)
@@ -240,7 +236,7 @@ graphics.from_surface = function(id, surf)
graphics.from = function(id, data)
{
if (typeof id !== 'string')
if (typeof id != 'string')
throw new Error('Expected a string ID')
if (data instanceof ArrayBuffer)
@@ -250,18 +246,18 @@ graphics.from = function(id, data)
graphics.texture = function texture(path) {
if (path instanceof graphics.Image) return path
if (typeof path !== 'string')
if (typeof path != 'string')
throw new Error('need a string for graphics.texture')
var id = path.split(':')[0]
if (cache.has(id)) return cache.get(id)
var id = path //.split(':')[0]
if (cache[id]) return cache[id]
var ipath = res.find_image(id)
if (!ipath)
throw new Error(`unknown image ${id}`)
var image = create_image(ipath)
cache.set(id, image)
cache[id] = image
return image
}
graphics.texture[cell.DOC] = `
@@ -369,8 +365,8 @@ Load a font from file if not cached, or retrieve from cache if already loaded.
`
graphics.queue_sprite_mesh = function(queue) {
var sprites = queue.filter(x => x.type === 'sprite')
if (sprites.length === 0) return []
var sprites = queue.filter(x => x.type == 'sprite')
if (sprites.length == 0) return []
var mesh = graphics.make_sprite_mesh(sprites)
for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh

View File

@@ -17,7 +17,7 @@ function imtoggle(name, obj, field) {
var changed = false;
var old = obj[field];
obj[field] = imgui.checkbox(name, obj[field]);
if (old !== obj[field]) return true;
if (old != obj[field]) return true;
return false;
};
@@ -32,7 +32,7 @@ if (render_menu) {
if (debug.console)
debug.console = imgui.window("console", _ => {
imgui.text(log.transcript);
replstr = imgui.textinput(undefined, replstr);
replstr = imgui.textinput(null, replstr);
imgui.button("submit", _ => {
eval(replstr);
replstr = "";
@@ -242,14 +242,14 @@ imgui.imagebutton[cell.DOC] = `Create an ImageButton widget which displays a tex
imgui.textinput[cell.DOC] = `Show a single-line text input widget.
:param label: The label text next to the input box.
:param text: The current text. If undefined, the box starts empty.
:param text: The current text. If null, the box starts empty.
:return: The updated text string after user editing.
`;
imgui.textbox[cell.DOC] = `Show a multi-line text input widget.
:param label: The label text next to the input box.
:param text: The current multi-line text. If undefined, starts empty.
:param text: The current multi-line text. If null, starts empty.
:return: The updated text after user editing.
`;

View File

@@ -3,13 +3,23 @@ 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
if (e.type != 'greet') return
video = e.actor
graphics = use('graphics', video)
send(video, {kind:"window", op:"makeRenderer"}, e => {
@@ -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
def sx = 1 / camera.size[0];
def sy = 1 / camera.size[1];
def ox = camera.pos[0] - camera.size[0] * camera.anchor[0];
def oy = camera.pos[1] - camera.size[1] * camera.anchor[1];
// NDC→pixels
def vx = camera.viewport.x * winW;
def vy = camera.viewport.y * winH;
def vw = camera.viewport.width * winW;
def 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
def x1 = camera.a * rect.x + camera.c;
def y1 = camera.e * rect.y + camera.f;
def x2 = camera.a * (rect.x + rect.width) + camera.c;
def 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
def x0 = x1 < x2 ? x1 : x2;
def 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,
@@ -92,7 +108,7 @@ var camera = {
ortho:true,
anchor:[0.5,0.5],//{x:0.5,y:0.5},
rotation:[0,0,0,1],
surface: undefined
surface: null
}
var util = use('util')
@@ -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,19 +141,19 @@ 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
var thickness = (cmd.opt.thickness === 0) ? 0 : (cmd.opt.thickness || 1)
var thickness = (cmd.opt.thickness == 0) ? 0 : (cmd.opt.thickness || 1)
var raster_result = rasterize.round_rect(cmd.rect, cmd.opt.radius, thickness)
if (raster_result.type === 'rect') {
if (raster_result.type == 'rect') {
renderer_commands.push({
op: "fillRect",
data: {rect: raster_result.data}
})
} else if (raster_result.type === 'rects') {
} else if (raster_result.type == 'rects') {
raster_result.data.forEach(function(rect) {
renderer_commands.push({
op: "fillRect",
@@ -144,12 +165,12 @@ function translate_draw_commands(commands) {
// Outlined rectangle
var raster_result = rasterize.outline_rect(cmd.rect, cmd.opt.thickness)
if (raster_result.type === 'rect') {
if (raster_result.type == 'rect') {
renderer_commands.push({
op: "fillRect",
data: {rect: raster_result.data}
})
} else if (raster_result.type === 'rects') {
} else if (raster_result.type == 'rects') {
renderer_commands.push({
op: "rects",
data: {rects: raster_result.data}
@@ -165,17 +186,17 @@ 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 || {})
if (raster_result.type === 'points') {
if (raster_result.type == 'points') {
renderer_commands.push({
op: "point",
data: {points: raster_result.data}
})
} else if (raster_result.type === 'rects') {
} else if (raster_result.type == 'rects') {
// Use 'rects' operation for multiple rectangles
renderer_commands.push({
op: "rects",
@@ -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,163 @@ 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) break
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)
// Wrap `send(actor,msg,cb)` into a parseq “requestor”
// • on success: cb(data) → value=data, reason=null
// • on failure: cb(null,err)
function rpc_req(actor, msg) {
return (cb, _) => {
send(actor, msg, data => {
if (data.error)
cb(null, data)
else
cb(data)
})
}
})
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
}, _ => {
})
})
})
$_.delay(loop, 1/30)
}
$_.receiver(e => {
if (e.type === 'quit')
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 (e.type.includes('mouse')) {
if (e.pos)
e.pos = screenToWorldPoint(e.pos, camera, 500, 500)
if (ev.type.includes('mouse')) {
if (ev.pos)
ev.pos = screenToWorldPoint(ev.pos, camera, 500,500)
if (e.d_pos)
e.d_pos.y *= -1
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) {
def 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}, () => {
def now = time.number()
def 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
def 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
def render_dur = time.number() - last_time
def wait = Math.max(0, 1/60 - ttr)
$_.delay(render_step, 0)
})
}
$_.receiver(e => {
switch(e.op) {
case 'resolution':
log.console(json.encode(e))
send(video, {
kind:'renderer',
op:'set',
prop:'logicalPresentation',
value: {...e}
})
break
}
})

View File

@@ -35,7 +35,7 @@ rasterize.ellipse = function ellipse(pos, radii, opt) {
ry_i = ry - thickness
var hole = (rx_i > 0 && ry_i > 0)
if (!hole && thickness === 1) {
if (!hole && thickness == 1) {
var points = []
var rx_sq = rx * rx, ry_sq = ry * ry
var two_rx_sq = rx_sq << 1, two_ry_sq = ry_sq << 1
@@ -82,9 +82,9 @@ rasterize.ellipse = function ellipse(pos, radii, opt) {
if (hole && Math.abs(dx) <= x_in) { run_start = null; continue }
if (!within_wedge(dx, dy, start, end, full_circle)) { run_start = null; continue }
if (run_start === null) run_start = cx + dx
if (run_start == null) run_start = cx + dx
var last = (dx === x_out)
var last = (dx == x_out)
var next_in_ring =
!last &&
!(hole && Math.abs(dx+1) <= x_in) &&

View File

@@ -2,12 +2,12 @@ var io = use('io')
Object.defineProperty(Function.prototype, "hashify", {
value: function () {
var hash = new Map()
var hash = {}
var fn = this
function hashified(...args) {
var key = args[0]
if (!hash.has(key)) hash.set(key, fn(...args))
return hash.get(key)
if (hash[key] == null) hash[key] = fn(...args)
return hash[key]
}
return hashified
},
@@ -41,12 +41,12 @@ function isRecognizedExtension(ext) {
}
function find_in_path(filename, exts = []) {
if (typeof filename !== 'string') return undefined
if (typeof filename != 'string') return null
if (filename.includes('.')) {
var candidate = filename // possibly need "/" ?
if (io.exists(candidate) && !io.is_directory(candidate)) return candidate
return undefined
return null
}
// Only check extensions if exts is provided and not empty
@@ -60,7 +60,7 @@ function find_in_path(filename, exts = []) {
var candidate = filename
if (io.exists(candidate) && !io.is_directory(candidate)) return candidate
}
return undefined
return null
}
// Return a canonical path (the real directory plus the path)

View File

@@ -16,8 +16,8 @@ function add_timer(obj, fn, seconds) {
var stop = function () {
if (!timer) return
timers.delete(stop)
timer.fn = undefined
timer = undefined
timer.fn = null
timer = null
}
function execute() {
@@ -45,7 +45,7 @@ globalThis.Register = {
var fns = []
n.register = function (fn, oname) {
if (typeof fn !== 'function') return
if (typeof fn != 'function') return
var dofn = function (...args) {
fn(...args)
@@ -59,7 +59,7 @@ globalThis.Register = {
while (left <= right) {
var mid = Math.floor((left + right) / 2)
if (fns[mid] === dofn.layer) {
if (fns[mid] == dofn.layer) {
left = mid
break
} else if (fns[mid].layer < dofn.layer) left = mid + 1
@@ -93,7 +93,7 @@ globalThis.Register = {
Register.pull_registers = function pull_registers(obj) {
var reggies = []
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
if (typeof obj[reg] == "function")
reggies.push(reg)
}
return reggies
@@ -115,7 +115,7 @@ Register.check_registers = function check_registers(obj) {
return
}
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
if (typeof obj[reg] == "function")
Register.register_obj(obj,reg)
}
}
@@ -203,9 +203,9 @@ actor.spawn = function spawn(script, config, actor_context) {
prog.prog_fn.call(underling, actor_dollar)
} catch(e) { throw e; }
if (underling[DEAD]) return undefined;
if (underling[DEAD]) return null;
if (typeof config === 'object') Object.assign(underling, config)
if (typeof config == 'object') Object.assign(underling, config)
if (!underling[REGGIES])
underling.__proto__[REGGIES] = Register.pull_registers(underling)
@@ -238,8 +238,8 @@ actor.kill = function kill() {
this[OVERLING][UNDERLINGS].delete(this)
delete this[UNDERLINGS]
if (typeof this.garbage === "function") this.garbage()
if (typeof this.then === "function") this.then()
if (typeof this.garbage == "function") this.garbage()
if (typeof this.then == "function") this.then()
act.tag_clear_guid(this)
}
@@ -264,7 +264,7 @@ function eachobj(obj, fn) {
var val = fn(obj)
if (val) return val
for (var o in obj.objects) {
if (obj.objects[o] === obj) log.error(`Object ${obj.toString()} is referenced by itself.`)
if (obj.objects[o] == obj) log.error(`Object ${obj.toString()} is referenced by itself.`)
val = eachobj(obj.objects[o], fn)
if (val) return val
}
@@ -276,7 +276,7 @@ ex.all_objects = function (fn, startobj = world) {
ex.all_objects[cell.DOC] = `
:param fn: A callback function that receives each object. If it returns a truthy value, iteration stops and that value is returned.
:param startobj: The root object at which iteration begins, default is the global "world".
:return: The first truthy value returned by fn, or undefined if none.
:return: The first truthy value returned by fn, or null if none.
Iterate over each object (and its sub-objects) in the hierarchy, calling fn for each one.
`
@@ -379,10 +379,10 @@ var script_fn = function script_fn(path, args) {
// Create a context object with args
var context = Object.create(parsed.module_ret)
context.__args__ = args || []
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = undefined; var arg = this.__args__; ${parsed.module}})`
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = null; var arg = this.__args__; ${parsed.module}})`
var module_fn = js.eval(file, mod_script)
parsed.module_ret = module_fn.call(context)
if (parsed.module_ret === undefined || parsed.module_ret === null)
if (parsed.module_ret == null || parsed.module_ret == null)
throw new Error(`Module ${module_name} must return a value`)
parsed.module_fn = module_fn
}

View File

@@ -138,14 +138,14 @@ function get_pipeline_ubo_slot(pipeline, name) {
if (ubo.name.endsWith(name))
return i;
}
return undefined;
return null;
}
get_pipeline_ubo_slot[cell.DOC] = `Return the index of a uniform buffer block within the pipeline's vertex reflection data by name suffix.
:param pipeline: The pipeline whose vertex reflection is inspected.
:param name: A string suffix to match against the uniform buffer block name.
:return: The integer index of the matching UBO, or undefined if not found.
:return: The integer index of the matching UBO, or null if not found.
`
function transpose4x4(val) {
@@ -181,7 +181,7 @@ function ubo_obj_to_array(pipeline, name, obj) {
var val = obj[mem.name];
if (!val) throw new Error (`Could not find ${mem.name} on supplied object`);
if (mem.name === 'model')
if (mem.name == 'model')
val = transpose4x4(val.array());
for (var i = 0; i < val.length; i++)
@@ -226,9 +226,9 @@ var shader_times = {};
function make_pipeline(pipeline) {
if (pipeline.hasOwnProperty("gpu")) return; // this pipeline has already been made
if (typeof pipeline.vertex === 'string')
if (typeof pipeline.vertex == 'string')
pipeline.vertex = make_shader(pipeline.vertex);
if (typeof pipeline.fragment === 'string')
if (typeof pipeline.fragment == 'string')
pipeline.fragment = make_shader(pipeline.fragment)
// 1) Reflection data for vertex shader
@@ -309,7 +309,7 @@ function make_shader(sh_file) {
num_textures: 0,
num_storage_buffers: refl.separate_storage_buffers ? refl.separate_storage_buffers.length : 0,
num_uniform_buffers: refl.ubos ? refl.ubos.length : 0,
entrypoint: shader_type === "msl" ? "main0" : "main"
entrypoint: shader_type == "msl" ? "main0" : "main"
}
shader.gpu = context.make_shader(shader)
@@ -349,7 +349,7 @@ var std_sampler = {
function upload_model(model) {
var bufs = [];
for (var i in model) {
if (typeof model[i] !== 'object') continue;
if (typeof model[i] != 'object') continue;
bufs.push(model[i]);
}
context.upload(this, bufs);
@@ -405,7 +405,7 @@ bind_mat[cell.DOC] = `Bind the material images and samplers needed by the pipeli
`
function group_sprites_by_texture(sprites, mesh) {
if (sprites.length === 0) return;
if (sprites.length == 0) return;
for (var i = 0; i < sprites.length; i++) {
sprites[i].mesh = mesh;
sprites[i].first_index = i*6;
@@ -418,7 +418,7 @@ function group_sprites_by_texture(sprites, mesh) {
var group = {image:sprites[0].image, first_index:0};
var count = 1;
for (var i = 1; i < sprites.length; i++) {
if (sprites[i].image === group.image) {
if (sprites[i].image == group.image) {
count++;
continue;
}
@@ -495,7 +495,7 @@ function render_camera(cmds, camera) {
buffers = buffers.concat(graphics.queue_sprite_mesh(hud_queue));
for (var q of hud_queue)
if (q.type === 'geometry') buffers = buffers.concat([q.mesh.pos, q.mesh.color, q.mesh.uv, q.mesh.indices]);
if (q.type == 'geometry') buffers = buffers.concat([q.mesh.pos, q.mesh.color, q.mesh.uv, q.mesh.indices]);
full_upload(buffers)
@@ -505,11 +505,11 @@ function render_camera(cmds, camera) {
bind_pipeline(pass,pipeline);
var camslot = get_pipeline_ubo_slot(pipeline, 'TransformBuffer');
if (typeof camslot !== 'undefined')
if (camslot != null)
cmds.camera(camera, camslot);
modelslot = get_pipeline_ubo_slot(pipeline, "model");
if (typeof modelslot !== 'undefined') {
if (modelslot != null) {
var ubo = ubo_obj_to_array(pipeline, 'model', sprite_model_ubo);
cmds.push_vertex_uniform_data(modelslot, ubo);
}
@@ -537,7 +537,7 @@ function render_camera(cmds, camera) {
cmds.push_debug_group("hud")
var camslot = get_pipeline_ubo_slot(pipeline, 'TransformBuffer');
if (typeof camslot !== 'undefined')
if (camslot != null)
cmds.hud(camera.size, camslot);
for (var group of hud_queue) {
@@ -651,7 +651,7 @@ var stencil_invert = {
};
function mask(image, pos, scale, rotation = 0, ref = 1) {
if (typeof image === 'string')
if (typeof image == 'string')
image = graphics.texture(image);
var tex = image.texture;
@@ -661,7 +661,7 @@ function mask(image, pos, scale, rotation = 0, ref = 1) {
var pipe = stencil_writer(ref);
render.use_shader('sprite.cg', pipe);
var t = new transform;
t.trs(pos, undefined, scale);
t.trs(pos, null, scale);
set_model(t);
render.use_mat({
diffuse:image.texture,

View File

@@ -17,8 +17,8 @@ var default_window = {
height: 480,
// Position - can be numbers or "centered"
x: undefined, // SDL_WINDOWPOS_UNDEFINED by default
y: undefined, // SDL_WINDOWPOS_UNDEFINED by default
x: null, // SDL_WINDOWPOS_null by default
y: null, // SDL_WINDOWPOS_null by default
// Window behavior flags
resizable: true,
@@ -52,7 +52,7 @@ var default_window = {
metal: false, // Force Metal context (macOS)
// Advanced properties
parent: undefined, // Parent window for tooltips/popups/modal
parent: null, // Parent window for tooltips/popups/modal
modal: false, // Modal to parent window (requires parent)
externalGraphicsContext: false, // Use external graphics context
@@ -129,7 +129,7 @@ function handle_window(msg) {
switch (msg.op) {
case 'destroy':
win.destroy();
win = undefined
win = null
return {success: true};
case 'show':
@@ -145,7 +145,7 @@ function handle_window(msg) {
if (!prop) return {error: "Missing property name"};
// Handle special cases
if (prop === 'surface') {
if (prop == 'surface') {
var surf = win.surface;
if (!surf) return {data: null};
var surf_id = allocate_id();
@@ -157,12 +157,12 @@ function handle_window(msg) {
case 'set':
var prop = msg.data ? msg.data.property : null;
var value = msg.data ? msg.data.value : undefined;
var value = msg.data ? msg.data.value : null;
if (!prop) return {error: "Missing property name"};
// Validate property is settable
var readonly = ['id', 'pixelDensity', 'displayScale', 'sizeInPixels', 'flags', 'surface'];
if (readonly.indexOf(prop) !== -1) {
if (readonly.indexOf(prop) != -1) {
return {error: "Property '" + prop + "' is read-only"};
}
@@ -217,43 +217,45 @@ function handle_window(msg) {
}
}
// Renderer operations
function handle_renderer(msg) {
if (!ren) return{reason:'no renderer!'}
switch (msg.op) {
case 'destroy':
ren = undefined
// Renderer operation functions
var renderfuncs = {
destroy: function(msg) {
ren = null
return {success: true};
},
case 'clear':
clear: function(msg) {
ren.clear();
return {success: true};
},
case 'present':
present: function(msg) {
ren.present();
return {success: true};
},
case 'flush':
flush: function(msg) {
ren.flush();
return {success: true};
},
case 'get':
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') {
if (prop == 'drawColor') {
var color = ren[prop];
if (color && typeof color === 'object') {
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':
set: function(msg) {
var prop = msg.prop
var value = msg.value
if (!prop) return {error: "Missing property name"};
@@ -262,12 +264,12 @@ function handle_renderer(msg) {
// Validate property is settable
var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
if (readonly.indexOf(prop) !== -1) {
if (readonly.indexOf(prop) != -1) {
return {error: "Property '" + prop + "' is read-only"};
}
// Special handling for render target
if (prop === 'target' && value !== null && value !== undefined) {
if (prop == 'target' && value != null && value != null) {
var tex = resources.texture[value];
if (!tex) return {error: "Invalid texture id"};
value = tex;
@@ -275,38 +277,45 @@ function handle_renderer(msg) {
ren[prop] = value;
return {success: true};
},
case 'line':
line: function(msg) {
if (!msg.data || !msg.data.points) return {error: "Missing points array"};
ren.line(msg.data.points);
return {success: true};
},
case 'point':
point: function(msg) {
if (!msg.data || !msg.data.points) return {error: "Missing points"};
ren.point(msg.data.points);
return {success: true};
},
case 'rect':
rect: function(msg) {
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.rect(msg.data.rect);
return {success: true};
},
case 'fillRect':
fillRect: function(msg) {
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
ren.fillRect(msg.data.rect);
return {success: true};
},
case 'rects':
rects: function(msg) {
if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
ren.rects(msg.data.rects);
return {success: true};
},
case 'lineTo':
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};
},
case 'texture':
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"};
@@ -318,8 +327,9 @@ function handle_renderer(msg) {
msg.data.anchor || {x:0.5, y:0.5}
);
return {success: true};
},
case 'copyTexture':
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"};
@@ -334,28 +344,38 @@ function handle_renderer(msg) {
{x:0, y:0} // Top-left anchor
);
return {success: true};
},
case 'sprite':
sprite: function(msg) {
if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
ren.sprite(msg.data.sprite);
return {success: true};
},
case 'geometry':
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};
},
case 'debugText':
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};
},
case 'clipEnabled':
clipEnabled: function(msg) {
return {data: ren.clipEnabled()};
},
case 'texture9Grid':
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"};
@@ -370,8 +390,9 @@ function handle_renderer(msg) {
msg.data.dst
);
return {success: true};
},
case 'textureTiled':
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"};
@@ -382,15 +403,17 @@ function handle_renderer(msg) {
msg.data.dst
);
return {success: true};
},
case 'readPixels':
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};
},
case 'loadTexture':
loadTexture: function(msg) {
if (!msg.data) throw new Error("Missing data")
var tex;
@@ -408,31 +431,36 @@ function handle_renderer(msg) {
return {
id: tex_id,
};
},
case 'flush':
ren.flush();
return {success: true};
case 'coordsFromWindow':
coordsFromWindow: function(msg) {
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsFromWindow(msg.data.pos)};
},
case 'coordsToWindow':
coordsToWindow: function(msg) {
if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
return {data: ren.coordsToWindow(msg.data.pos)};
},
case 'batch':
batch: function(msg) {
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);
}
for (var i = 0; i < msg.data.length; i++)
handle_renderer(msg.data[i]);
return {success:true};
}
};
default:
// Renderer operations
function handle_renderer(msg) {
if (!ren) return{reason:'no renderer!'}
var func = renderfuncs[msg.op];
if (func) {
return func(msg);
} else {
return {error: "Unknown renderer operation: " + msg.op};
}
}
@@ -440,7 +468,7 @@ function handle_renderer(msg) {
// Texture operations
function handle_texture(msg) {
// Special case: create needs a renderer
if (msg.op === 'create') {
if (msg.op == 'create') {
if (!msg.data) return {error: "Missing texture data"};
var ren_id = msg.data.renderer_id;
if (!ren_id || !resources.renderer[ren_id]) return {error: "Invalid renderer id"};
@@ -494,12 +522,12 @@ function handle_texture(msg) {
case 'set':
var prop = msg.data ? msg.data.property : null;
var value = msg.data ? msg.data.value : undefined;
var value = msg.data ? msg.data.value : null;
if (!prop) return {error: "Missing property name"};
// Validate property is settable
var readonly = ['size', 'width', 'height'];
if (readonly.indexOf(prop) !== -1) {
if (readonly.indexOf(prop) != -1) {
return {error: "Property '" + prop + "' is read-only"};
}
@@ -588,12 +616,12 @@ function handle_mouse(msg) {
switch (msg.op) {
case 'show':
if (msg.data === undefined) return {error: "Missing show parameter"};
if (msg.data == null) return {error: "Missing show parameter"};
mouse.show(msg.data);
return {success: true};
case 'capture':
if (msg.data === undefined) return {error: "Missing capture parameter"};
if (msg.data == null) return {error: "Missing capture parameter"};
mouse.capture(msg.data);
return {success: true};
@@ -627,7 +655,7 @@ function handle_mouse(msg) {
if (!cursor) return {data: null};
// Find or create cursor ID
for (var id in resources.cursor) {
if (resources.cursor[id] === cursor) {
if (resources.cursor[id] == cursor) {
return {data: id};
}
}
@@ -641,7 +669,7 @@ function handle_mouse(msg) {
if (!cursor) return {data: null};
// Find or create cursor ID
for (var id in resources.cursor) {
if (resources.cursor[id] === cursor) {
if (resources.cursor[id] == cursor) {
return {data: id};
}
}
@@ -651,7 +679,7 @@ function handle_mouse(msg) {
return {data: cursor_id};
case 'create_system_cursor':
if (msg.data === undefined) return {error: "Missing cursor type"};
if (msg.data == null) return {error: "Missing cursor type"};
var cursor = mouse.create_system_cursor(msg.data);
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
@@ -662,7 +690,7 @@ function handle_mouse(msg) {
if (!window) return {data: null};
// Find window ID
for (var id in resources.window) {
if (resources.window[id] === window) {
if (resources.window[id] == window) {
return {data: id};
}
}
@@ -689,7 +717,7 @@ function handle_keyboard(msg) {
if (!window) return {data: null};
// Find window ID
for (var id in resources.window) {
if (resources.window[id] === window) {
if (resources.window[id] == window) {
return {data: id};
}
}

View File

@@ -34,7 +34,7 @@ audio.cry = function cry(file) {
if (!voice) return;
return function() {
voice.stop();
voice = undefined;
voice = null;
}
};
audio.cry[doc.sym] =

View File

@@ -1,6 +1,6 @@
/* anim.js drop this at top of your script or in a module */
var Anim = (() => {
const DEFAULT_MIN = 1 / 60; /* 16 ms one frame */
def DEFAULT_MIN = 1 / 60; /* 16 ms one frame */
function play(source, loop=true){
return {
@@ -13,9 +13,9 @@ var Anim = (() => {
function update(a, dt){
a.timer += dt;
const frames = a.src.frames;
def frames = a.src.frames;
while(true){
const time = Math.max(frames[a.idx].time || 0, Anim.minDelay);
def time = Math.max(frames[a.idx].time || 0, Anim.minDelay);
if(a.timer < time) break; /* still on current frame */
a.timer -= time;
@@ -29,7 +29,7 @@ var Anim = (() => {
}
function current(a){ return a.src.frames[a.idx].image; }
function updateAll(arr, dt){ for(const a of arr) update(a, dt); }
function updateAll(arr, dt){ for(def a of arr) update(a, dt); }
function draw(a, pos, opt, pipe){
draw2d.image(current(a), pos, 0, [0,0], [0,0], opt, pipe);
}
@@ -53,17 +53,17 @@ var camera = {
fov:50,
near_z: 0,
far_z: 1000,
surface: undefined,
surface: null,
viewport: {x:0,y:0,width:1,height:1},
ortho:true,
anchor:[0,0],
}
/* ── load animations ───────────────────── */
const crab = gfx.texture('tests/crab'); // gif → Animation
const warrior = gfx.texture('tests/warrior'); // ase → {Original:Animation}
def crab = gfx.texture('tests/crab'); // gif → Animation
def warrior = gfx.texture('tests/warrior'); // ase → {Original:Animation}
const anims = [
def anims = [
Anim.play(crab), // crab.frames
Anim.play(warrior.Run) // warrior.Original.frames
];
@@ -76,8 +76,8 @@ Anim.minDelay = 1 / 100; // 10 ms, feel free to tune later
let last = os.now();
function loop(){
const now = os.now();
const dt = now - last; // real frame time
def now = os.now();
def dt = now - last; // real frame time
last = now;
Anim.updateAll(anims, dt);

View File

@@ -18,7 +18,7 @@ var camera = {
fov:50,
near_z: 0,
far_z: 1000,
surface: undefined,
surface: null,
viewport: {x:0,y:0,width:1,height:1},
ortho:true,
anchor:[0,0],

View File

@@ -19,7 +19,7 @@ var camera = {
fov:50,
near_z: 0,
far_z: 1000,
surface: undefined,
surface: null,
viewport: {x:0,y:0,width:1,height:1},
ortho:true,
anchor:[0.5,0.5],
@@ -31,7 +31,7 @@ var hudcam = {
fov:50,
near_z: 0,
far_z: 1000,
surface: undefined,
surface: null,
viewport: {x:0,y:0,width:1,height:1},
ortho:true,
anchor:[0,0],

View File

@@ -4,7 +4,7 @@ var json = use('json');
// Get list of cameras
var cameras = camera.list();
if (cameras.length === 0) {
if (cameras.length == 0) {
log.console("No cameras found!");
$_. stop();
}

View File

@@ -5,7 +5,7 @@ var json = use('json');
// Get first camera
var cameras = camera.list();
if (cameras.length === 0) {
if (cameras.length == 0) {
log.console("No cameras found!");
$_.stop();
}
@@ -30,10 +30,10 @@ log.console(" Colorspace:", format.colorspace);
// Handle camera approval
var approved = false;
$_.receiver(e => {
if (e.type === 'camera_device_approved') {
if (e.type == 'camera_device_approved') {
log.console("\nCamera approved!");
approved = true;
} else if (e.type === 'camera_device_denied') {
} else if (e.type == 'camera_device_denied') {
log.error("Camera access denied!");
$_.stop();
}
@@ -63,7 +63,7 @@ function capture_test() {
log.console("\nTesting colorspace conversions:");
// Convert to sRGB if not already
if (format.colorspace !== "srgb") {
if (format.colorspace != "srgb") {
try {
var srgb_surf = surf.convert(surf.format, "srgb");
log.console(" Converted to sRGB colorspace");
@@ -89,7 +89,7 @@ function capture_test() {
}
// If YUV format, try BT.709 (HD video standard)
if (surf.format.indexOf("yuv") !== -1 || surf.format.indexOf("yuy") !== -1) {
if (surf.format.indexOf("yuv") != -1 || surf.format.indexOf("yuy") != -1) {
try {
var hd_surf = surf.convert(surf.format, "bt709_limited");
log.console(" Converted to BT.709 limited (HD video standard)");

View File

@@ -41,7 +41,7 @@ if (cameras.length > 0) {
// Try to find a 640x480 format
var preferred_format = null;
for (var i = 0; i < formats.length; i++) {
if (formats[i].width === 640 && formats[i].height === 480) {
if (formats[i].width == 640 && formats[i].height == 480) {
preferred_format = formats[i];
break;
}

View File

@@ -20,10 +20,10 @@ var webcam_texture = null;
// Handle camera events
$_.receiver(e => {
if (e.type === 'camera_device_approved' && e.which === cam_id) {
if (e.type == 'camera_device_approved' && e.which == cam_id) {
log.console("Camera approved!");
cam_approved = true;
} else if (e.type === 'camera_device_denied' && e.which === cam_id) {
} else if (e.type == 'camera_device_denied' && e.which == cam_id) {
log.error("Camera access denied!");
$_.stop();
}
@@ -67,7 +67,7 @@ send(video_actor, {
// List available cameras
var cameras = camera.list();
if (cameras.length === 0) {
if (cameras.length == 0) {
log.error("No cameras found!");
log.console(json.encode(cameras))
$_.stop();
@@ -89,10 +89,10 @@ send(video_actor, {
// Look for a 640x480 format with preferred colorspace
var preferred_format = null;
for (var i = 0; i < formats.length; i++) {
if (formats[i].width === 640 && formats[i].height === 480) {
if (formats[i].width == 640 && formats[i].height == 480) {
preferred_format = formats[i];
// Prefer JPEG or sRGB colorspace if available
if (formats[i].colorspace === "jpeg" || formats[i].colorspace === "srgb") {
if (formats[i].colorspace == "jpeg" || formats[i].colorspace == "srgb") {
break;
}
}

View File

@@ -49,7 +49,7 @@ send(ioguy, {
})
$_.receiver(e => {
if (e.type === 'quit')
if (e.type == 'quit')
os.exit()
else
log.console(json.encode(e))

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] != null) {
var result = fn(map.tiles[x][y], {
x: x + map.offset_x,
y: y + map.offset_y
});
if (result != null) {
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 null;
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] = null;
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,15 +43,15 @@ Ease.quint = make_easing_fns(5)
Ease.expo = {
in(t) {
return t === 0 ? 0 : Math.pow(2, 10 * t - 10)
return t == 0 ? 0 : Math.pow(2, 10 * t - 10)
},
out(t) {
return t === 1 ? 1 : 1 - Math.pow(2, -10 * t)
return t == 1 ? 1 : 1 - Math.pow(2, -10 * t)
},
inout(t) {
return t === 0
return t == 0
? 0
: t === 1
: t == 1
? 1
: t < 0.5
? Math.pow(2, 20 * t - 10) / 2
@@ -93,26 +93,26 @@ Ease.sine = {
Ease.elastic = {
in(t) {
return t === 0
return t == 0
? 0
: t === 1
: t == 1
? 1
: -Math.pow(2, 10 * t - 10) *
Math.sin((t * 10 - 10.75) * this.c4)
},
out(t) {
return t === 0
return t == 0
? 0
: t === 1
: t == 1
? 1
: Math.pow(2, -10 * t) *
Math.sin((t * 10 - 0.75) * this.c4) +
1
},
inout(t) {
t === 0
t == 0
? 0
: t === 1
: t == 1
? 1
: t < 0.5
? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2
@@ -128,10 +128,10 @@ var tween = function (from, to, time, fn, cb) {
function cleanup() {
stop()
fn = undefined
stop = undefined
cb = undefined
update = undefined
fn = null
stop = null
cb = null
update = null
}
var update = function tween_update(dt) {
@@ -159,8 +159,8 @@ var Tween = {
var defn = Object.create(this.default)
Object.assign(defn, options)
if (defn.loop === "circle") tvals.push(tvals[0])
else if (defn.loop === "yoyo") {
if (defn.loop == "circle") tvals.push(tvals[0])
else if (defn.loop == "yoyo") {
for (var i = tvals.length - 2; i >= 0; i--) tvals.push(tvals[i])
}
@@ -170,15 +170,15 @@ var Tween = {
defn.fn = function (dt) {
defn.accum += dt
if (defn.accum >= defn.time && defn.loop === "hold") {
if (typeof target === "string") obj[target] = tvals[tvals.length - 1]
if (defn.accum >= defn.time && defn.loop == "hold") {
if (typeof target == "string") obj[target] = tvals[tvals.length - 1]
else target(tvals[tvals.length - 1])
defn.pause()
defn.cb.call(obj)
return
}
defn.pct = (defn.accum % defn.time) / defn.time
if (defn.loop === "none" && defn.accum >= defn.time) defn.stop()
if (defn.loop == "none" && defn.accum >= defn.time) defn.stop()
var t = defn.whole ? defn.ease(defn.pct) : defn.pct
var nval = t / slicelen
@@ -186,7 +186,7 @@ var Tween = {
nval -= i
if (!defn.whole) nval = defn.ease(nval)
if (typeof target === "string") obj[target] = tvals[i].lerp(tvals[i + 1], nval)
if (typeof target == "string") obj[target] = tvals[i].lerp(tvals[i + 1], nval)
else target(tvals[i].lerp(tvals[i + 1], nval))
}
@@ -199,7 +199,7 @@ var Tween = {
}
defn.restart = function () {
defn.accum = 0
if (typeof target === "string") obj[target] = tvals[0]
if (typeof target == "string") obj[target] = tvals[0]
else target(tvals[0])
}
defn.stop = function () {

View File

@@ -1,30 +1,16 @@
Object.mixin = function (target, source) {
if (typeof source !== "object") return target;
if (typeof source != "object") return target;
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
return target;
};
Object.mixin[cell.DOC] = `Copy all property descriptors from 'source' into 'target'.
:param target: The object that will receive properties.
:param source: The object whose properties are to be copied.
:return: The updated 'target' object.
`;
Object.defineProperty(Object.prototype, "mixin", {
value: function mixin(obj) {
if (typeof obj === "string") obj = use(obj);
if (typeof obj == "string") obj = use(obj);
if (obj) Object.mixin(this, obj);
},
});
(Object.prototype.mixin)[cell.DOC] = `Mix properties from 'obj' into the current object. If 'obj' is a string,
it first calls 'use(obj)' to retrieve the object.
:param obj: The object (or string reference to an object) to mix in.
:return: None
`;
/* STRING DEFS */
Object.defineProperty(String.prototype, "rm", {
value: function (index, endidx = index + 1) {
@@ -32,28 +18,14 @@ Object.defineProperty(String.prototype, "rm", {
},
});
(String.prototype.rm)[cell.DOC] = `Remove characters from this string between 'index' (inclusive)
and 'endidx' (exclusive). If 'endidx' is omitted, it defaults to 'index + 1'.
:param index: The starting index to remove.
:param endidx: The ending index (exclusive).
:return: A new string with the specified characters removed.
`;
Object.defineProperty(String.prototype, "tolast", {
value: function (val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return this.slice();
if (idx == -1) return this.slice();
return this.slice(0, idx);
},
});
(String.prototype.tolast)[cell.DOC] = `Return the substring of this string up to the last occurrence of 'val'.
If 'val' is not found, the entire string is returned.
:param val: The substring to locate from the end.
:return: A substring up to the last occurrence of 'val'.
`;
Object.defineProperty(String.prototype, "dir", {
value: function () {
@@ -62,11 +34,6 @@ Object.defineProperty(String.prototype, "dir", {
},
});
(String.prototype.dir)[cell.DOC] = `Return everything before the last slash ('/') in the string.
If no slash is found, return an empty string.
:return: The directory portion of the path.
`;
Object.defineProperty(String.prototype, "next", {
value: function (char, from) {
@@ -75,7 +42,7 @@ Object.defineProperty(String.prototype, "next", {
else if (!from) from = 0;
var find = this.slice(from).search(char[0]);
if (find === -1) return -1;
if (find == -1) return -1;
else return from + find;
var i = 0;
@@ -89,14 +56,6 @@ Object.defineProperty(String.prototype, "next", {
},
});
(String.prototype.next)[cell.DOC] = `Search for the next occurrence of 'char' in this string, starting at 'from'.
If 'char' is an array, any of those characters qualifies. Return the matching index,
or -1 if none is found.
:param char: A character (or array of characters) to locate.
:param from: The index to start from.
:return: The index of the next occurrence, or -1 if not found.
`;
Object.defineProperty(String.prototype, "prev", {
value: function (char, from, count = 0) {
@@ -110,20 +69,11 @@ Object.defineProperty(String.prototype, "prev", {
count--;
}
if (find === -1) return 0;
if (find == -1) return 0;
else return find;
},
});
(String.prototype.prev)[cell.DOC] = `Search for the previous occurrence of 'char' before index 'from'.
If 'count' is greater than 1, skip multiple occurrences going backward.
Return the found index or 0 if not found.
:param char: The character to locate.
:param from: The index to start from (defaults to the end of the string).
:param count: How many occurrences to skip backward (default 0).
:return: The index of the previous occurrence, or 0 if not found.
`;
Object.defineProperty(String.prototype, "strip_ext", {
value: function () {
@@ -131,11 +81,6 @@ Object.defineProperty(String.prototype, "strip_ext", {
},
});
(String.prototype.strip_ext)[cell.DOC] = `Return the string up to (but not including) the last '.' character.
If '.' is not found, the entire string is returned.
:return: The string without its last extension.
`;
Object.defineProperty(String.prototype, "ext", {
value: function () {
@@ -143,11 +88,6 @@ Object.defineProperty(String.prototype, "ext", {
},
});
(String.prototype.ext)[cell.DOC] = `Return the substring after the last '.' in this string.
If '.' is not found, return an empty string.
:return: The file extension or an empty string.
`;
Object.defineProperty(String.prototype, "up_path", {
value: function () {
@@ -158,70 +98,42 @@ Object.defineProperty(String.prototype, "up_path", {
},
});
(String.prototype.up_path)[cell.DOC] = `Go up one directory level from the current path, preserving the file name at the end.
:return: A new path string one directory up, with the base filename preserved.
`;
Object.defineProperty(String.prototype, "fromlast", {
value: function (val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return "";
if (idx == -1) return "";
return this.slice(idx + 1);
},
});
(String.prototype.fromlast)[cell.DOC] = `Return the substring that appears after the last occurrence of 'val'.
If 'val' is not found, an empty string is returned.
:param val: The substring to locate.
:return: The substring after the last occurrence of 'val'.
`;
Object.defineProperty(String.prototype, "tofirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx === -1) return this.slice();
if (idx == -1) return this.slice();
return this.slice(0, idx);
},
});
(String.prototype.tofirst)[cell.DOC] = `Return the substring from the start of this string up to the first occurrence
of 'val' (excluded). If 'val' is not found, the entire string is returned.
:param val: The substring to locate.
:return: A substring up to the first occurrence of 'val'.
`;
Object.defineProperty(String.prototype, "fromfirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx === -1) return this;
if (idx == -1) return this;
return this.slice(idx + val.length);
},
});
(String.prototype.fromfirst)[cell.DOC] = `Return the substring after the first occurrence of 'val'.
If 'val' is not found, the entire string is returned.
:param val: The substring to locate.
:return: The substring after 'val', or the entire string if 'val' not found.
`;
Object.defineProperty(String.prototype, "name", {
value: function () {
var idx = this.indexOf("/");
if (idx === -1) return this.tolast(".");
if (idx == -1) return this.tolast(".");
return this.fromlast("/").tolast(".");
},
});
(String.prototype.name)[cell.DOC] = `Return the "name" portion of the path without extension.
If no slash is found, it's up to the last '.' in the entire string.
If there is a slash, it's from the last slash up to (but not including) the last '.'.
:return: The name portion of the path without extension.
`;
Object.defineProperty(String.prototype, "set_name", {
value: function (name) {
@@ -230,12 +142,6 @@ Object.defineProperty(String.prototype, "set_name", {
},
});
(String.prototype.set_name)[cell.DOC] = `Set the base name (excluding extension) of the path to 'name', preserving
the original directory and extension.
:param name: The new name to use.
:return: A new path string with the updated name.
`;
Object.defineProperty(String.prototype, "base", {
value: function () {
@@ -243,25 +149,15 @@ Object.defineProperty(String.prototype, "base", {
},
});
(String.prototype.base)[cell.DOC] = `Return the portion of this string after the last '/' character.
If no '/' is present, the entire string is returned.
:return: The base name of the path.
`;
Object.defineProperty(String.prototype, "updir", {
value: function () {
if (this.lastIndexOf("/") === this.length - 1) return this.slice(0, this.length - 1);
if (this.lastIndexOf("/") == this.length - 1) return this.slice(0, this.length - 1);
var dir = (this + "/").dir();
return dir.dir();
},
});
(String.prototype.updir)[cell.DOC] = `Go up one directory from the current path, removing the last directory name.
If the path ends with a slash, remove it first. Then remove the final directory.
:return: A new string representing one directory level up.
`;
/* ARRAY DEFS */
Object.defineProperty(Array.prototype, "filter!", {
@@ -276,28 +172,15 @@ Object.defineProperty(Array.prototype, "filter!", {
},
});
(Array.prototype["filter!"])[cell.DOC] = `Perform an in-place filter of this array using the provided callback 'fn'.
Any element for which 'fn' returns a falsy value is removed. The array is modified
and then returned.
:param fn: A callback function(element, index, array) => boolean.
:return: The filtered (modified) array.
`;
Object.defineProperty(Array.prototype, "delete", {
value: function(item) {
var idx = this.indexOf(item);
if (idx > -1) this.splice(idx,1);
return undefined;
return null;
}
});
(Array.prototype.delete)[cell.DOC] = `Remove the first occurrence of 'item' from the array, if it exists.
Returns undefined.
:param item: The item to remove.
:return: undefined
`;
Object.defineProperty(Array.prototype, "copy", {
value: function () {
@@ -309,32 +192,21 @@ Object.defineProperty(Array.prototype, "copy", {
},
});
(Array.prototype.copy)[cell.DOC] = `Return a deep copy of this array by applying 'deep_copy' to each element.
The resulting array is entirely new.
:return: A new array that is a deep copy of the original.
`;
Object.defineProperty(Array.prototype, "equal", {
value: function equal(b) {
if (this.length !== b.length) return false;
if (this.length != b.length) return false;
if (b == null) return false;
if (this === b) return true;
return JSON.stringify(this) === JSON.stringify(b);
if (this == b) return true;
return JSON.stringify(this) == JSON.stringify(b);
for (var i = 0; i < this.length; i++) {
if (!this[i] === b[i]) return false;
if (!this[i] == b[i]) return false;
}
return true;
},
});
(Array.prototype.equal)[cell.DOC] = `Check if this array and array 'b' have the same elements in the same order.
If they are of different lengths, return false. Otherwise compare them via JSON.
:param b: Another array to compare against.
:return: True if they match, false otherwise.
`;
Object.defineProperty(Array.prototype, "last", {
value: function () {
@@ -342,10 +214,6 @@ Object.defineProperty(Array.prototype, "last", {
},
});
(Array.prototype.last)[cell.DOC] = `Return the last element of this array. If the array is empty, returns undefined.
:return: The last element of the array, or undefined if empty.
`;
Object.defineProperty(Array.prototype, "wrapped", {
value: function (x) {
@@ -355,12 +223,6 @@ Object.defineProperty(Array.prototype, "wrapped", {
},
});
(Array.prototype.wrapped)[cell.DOC] = `Return a copy of the array with the first 'x' elements appended to the end.
Does not modify the original array.
:param x: The number of leading elements to re-append.
:return: A new array with the leading elements wrapped to the end.
`;
Object.defineProperty(Array.prototype, "wrap_idx", {
value: function (x) {
@@ -371,12 +233,6 @@ Object.defineProperty(Array.prototype, "wrap_idx", {
},
});
(Array.prototype.wrap_idx)[cell.DOC] = `Wrap the integer 'x' around this array's length, ensuring the resulting index
lies within [0, this.length - 1].
:param x: The index to wrap.
:return: A wrapped index within this array's bounds.
`;
Object.defineProperty(Array.prototype, "mirrored", {
value: function (x) {
@@ -387,9 +243,3 @@ Object.defineProperty(Array.prototype, "mirrored", {
},
});
(Array.prototype.mirrored)[cell.DOC] = `Return a new array that appends a reversed copy (excluding the last element)
of itself to the end. For example, [1,2,3] -> [1,2,3,2,1]. If the array has length
<= 1, a copy of it is returned directly.
:return: A new "mirrored" array.
`;

View File

@@ -40,7 +40,7 @@ function parse_key(key) {
function get_nested(obj, path) {
var current = obj
for (var segment of path) {
if (!current || typeof current !== 'object') return undefined
if (!current || typeof current != 'object') return null
current = current[segment]
}
return current
@@ -51,7 +51,7 @@ function set_nested(obj, path, value) {
var current = obj
for (var i = 0; i < path.length - 1; i++) {
var segment = path[i]
if (!current[segment] || typeof current[segment] !== 'object') {
if (!current[segment] || typeof current[segment] != 'object') {
current[segment] = {}
}
current = current[segment]
@@ -62,8 +62,8 @@ function set_nested(obj, path, value) {
// Parse value string into appropriate type
function parse_value(str) {
// Boolean
if (str === 'true') return true
if (str === 'false') return false
if (str == 'true') return true
if (str == 'false') return false
// Number (including underscores)
var num_str = str.replace(/_/g, '')
@@ -76,8 +76,8 @@ function parse_value(str) {
// Format value for display
function format_value(val) {
if (typeof val === 'string') return '"' + val + '"'
if (typeof val === 'number' && val >= 1000) {
if (typeof val == 'string') return '"' + val + '"'
if (typeof val == 'number' && val >= 1000) {
// Add underscores to large numbers
return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '_')
}
@@ -90,7 +90,7 @@ function print_config(obj, prefix = '') {
var val = obj[key]
var full_key = prefix ? prefix + '.' + key : key
if (val && typeof val === 'object' && !Array.isArray(val)) {
if (val && typeof val == 'object' && !Array.isArray(val)) {
print_config(val, full_key)
} else {
log.console(full_key + ' = ' + format_value(val))
@@ -99,7 +99,7 @@ function print_config(obj, prefix = '') {
}
// Main command handling
if (args.length === 0) {
if (args.length == 0) {
print_help()
$_.stop()
return
@@ -137,9 +137,9 @@ switch (command) {
var path = parse_key(key)
var value = get_nested(config, path)
if (value === undefined) {
if (value == null) {
log.error("Key not found: " + key)
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
} else if (value && typeof value == 'object' && !Array.isArray(value)) {
// Print all nested values
print_config(value, key)
} else {
@@ -159,7 +159,7 @@ switch (command) {
var value = parse_value(value_str)
// Validate system keys
if (path[0] === 'system') {
if (path[0] == 'system') {
var valid_system_keys = [
'ar_timer', 'actor_memory', 'net_service',
'reply_timeout', 'actor_max', 'stack_max'
@@ -193,7 +193,7 @@ switch (command) {
switch (actor_cmd) {
case 'list':
if (Object.keys(config.actors[actor_name]).length === 0) {
if (Object.keys(config.actors[actor_name]).length == 0) {
log.console("No configuration for actor: " + actor_name)
} else {
log.console("# Configuration for actor: " + actor_name)
@@ -212,7 +212,7 @@ switch (command) {
var path = parse_key(key)
var value = get_nested(config.actors[actor_name], path)
if (value === undefined) {
if (value == null) {
log.error("Key not found for actor " + actor_name + ": " + key)
} else {
log.console('actors.' + actor_name + '.' + key + ' = ' + format_value(value))

View File

@@ -5,20 +5,20 @@ function docOf(obj, prop) {
// 1) If `block` is a string, that's the entire doc for `obj`.
// If a sub-property is requested, we have nowhere to look → return ''.
if (typeof block === 'string') {
if (typeof block == 'string') {
return prop ? '' : block;
}
// 2) Otherwise, if `block` is an object:
// (a) With no `prop`, return block.doc or block[cell.DOC].
// (b) If `prop` is given, look for doc specifically for that property (just one level).
if (typeof block === 'object') {
if (typeof block == 'object') {
// 2a) No property → top-level doc
if (!prop) {
if (typeof block.doc === 'string') {
if (typeof block.doc == 'string') {
return block.doc;
}
if (typeof block[cell.DOC] === 'string') {
if (typeof block[cell.DOC] == 'string') {
return block[cell.DOC];
}
return '';
@@ -27,14 +27,14 @@ function docOf(obj, prop) {
// 2b) If a prop is requested → see if there's a doc string or object for that property
var subBlock = block[prop];
if (!subBlock) return '';
if (typeof subBlock === 'string') {
if (typeof subBlock == 'string') {
return subBlock;
}
if (typeof subBlock === 'object') {
if (typeof subBlock.doc === 'string') {
if (typeof subBlock == 'object') {
if (typeof subBlock.doc == 'string') {
return subBlock.doc;
}
if (typeof subBlock[cell.DOC] === 'string') {
if (typeof subBlock[cell.DOC] == 'string') {
return subBlock[cell.DOC];
}
return '';
@@ -94,7 +94,7 @@ function parseDocStr(docStr) {
if (returnLines.length) {
// If there were param lines, ensure blank line before the returns
if (paramLines.length && returnLines[0] !== '') {
if (paramLines.length && returnLines[0] != '') {
final.push('');
}
final.push.apply(final, returnLines);
@@ -131,14 +131,14 @@ function walkObject(obj, lines, level, name) {
var propNames = Object.getOwnPropertyNames(obj);
for (var i = 0; i < propNames.length; i++) {
var prop = propNames[i];
if (prop === 'constructor') continue;
if (prop == 'constructor') continue;
var desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc) continue;
// Check if accessor property (getter/setter)
var hasGetter = typeof desc.get === 'function';
var hasSetter = typeof desc.set === 'function';
var hasGetter = typeof desc.get == 'function';
var hasSetter = typeof desc.set == 'function';
if (hasGetter || hasSetter) {
writeProperty(lines, obj, prop, desc, level);
@@ -150,11 +150,11 @@ function walkObject(obj, lines, level, name) {
var val = desc.value;
// If it's a function, treat it like a method
if (typeof val === 'function') {
if (typeof val == 'function') {
writeMethod(lines, obj, prop, val, level);
}
// If it's an object, just print doc for that object (no deep recursion)
else if (val && typeof val === 'object') {
else if (val && typeof val == 'object') {
writeSubObject(lines, obj, prop, val, level);
}
// Otherwise, it's a primitive or something else
@@ -192,8 +192,8 @@ function writeProperty(lines, parentObj, prop, desc, level) {
var heading = '#'.repeat(level + 2) + ' ' + prop + ' <sub>accessor</sub>';
lines.push(heading + '\n');
var hasGetter = typeof desc.get === 'function';
var hasSetter = typeof desc.set === 'function';
var hasGetter = typeof desc.get == 'function';
var hasSetter = typeof desc.set == 'function';
if (hasGetter && !hasSetter) {
lines.push('(read only)\n');

View File

@@ -66,24 +66,24 @@ Angles in degrees or radians must first be converted prior to making a quaternio
`;
prosperon.c_types.transform[cell.DOC].parent = `Get or set the transform's parent. If set, this transform becomes a child of
the parent (re-parenting). Must be another transform object or undefined.
the parent (re-parenting). Must be another transform object or null.
:param value: (when setting) Another transform or undefined.
:param value: (when setting) Another transform or null.
:return: The current parent transform (when getting), or None (when setting).
`;
prosperon.c_types.transform[cell.DOC].change_hook = `A user-supplied function that's called whenever the transform's local matrix changes.
If undefined, no hook is called.
If null, no hook is called.
:param value: (when setting) A function.
:return: The current function or undefined.
:return: The current function or null.
`;
prosperon.c_types.transform[cell.DOC].trs = `Set the transform's position, rotation, and scale in one call.
:param pos: [x,y,z] for position, or undefined to keep existing.
:param quat: [qx,qy,qz,qw] for rotation, or undefined.
:param scale: [sx,sy,sz] for scale, or undefined.
:param pos: [x,y,z] for position, or null to keep existing.
:param quat: [qx,qy,qz,qw] for rotation, or null.
:param scale: [sx,sy,sz] for scale, or null.
:return: None
`;
@@ -231,7 +231,7 @@ prosperon.c_types.datastream[cell.DOC].framerate = `Return the framerate (FPS) o
prosperon.c_types.datastream[cell.DOC].callback = `A function to call whenever a new frame is decoded. If not set, no callback is invoked.
:param fn: (when setting) A function that receives (surface).
:return: The existing function or undefined if none.
:return: The existing function or null if none.
`;
@@ -325,10 +325,10 @@ prosperon.c_types.SDL_Camera[cell.DOC] = {}
prosperon.c_types.SDL_Camera[cell.DOC][cell.DOC] = `A handle to a physical camera device. Freed when references drop or camera is closed.
`;
prosperon.c_types.SDL_Camera[cell.DOC].frame = `Acquire the latest camera frame (as an SDL_Surface). Returns undefined if no
prosperon.c_types.SDL_Camera[cell.DOC].frame = `Acquire the latest camera frame (as an SDL_Surface). Returns null if no
new frame is available yet. Throws on error.
:return: SDL_Surface or undefined.
:return: SDL_Surface or null.
`;
prosperon.c_types.SDL_Camera[cell.DOC].release_frame = `Release a frame surface previously acquired via camera.frame(). Must be
@@ -376,9 +376,9 @@ prosperon.c_types.SDL_Window[cell.DOC].keyboard_shown = `Return whether the on-s
:return: True if shown, false otherwise.
`;
prosperon.c_types.SDL_Window[cell.DOC].theme = `Currently returns undefined. Placeholder for retrieving OS window theme info.
prosperon.c_types.SDL_Window[cell.DOC].theme = `Currently returns null. Placeholder for retrieving OS window theme info.
:return: undefined
:return: null
`;
prosperon.c_types.SDL_Window[cell.DOC].safe_area = `Return a rect describing any OS-specific "safe" region for UI, e.g. on iPhone with a notch.
@@ -502,7 +502,7 @@ prosperon.c_types.SDL_Renderer[cell.DOC].tile = `Tile a texture repeatedly withi
:return: None
`;
prosperon.c_types.SDL_Renderer[cell.DOC].get_image = `Read back the rendered pixels into a new SDL_Surface. If rect is undefined, capture entire output.
prosperon.c_types.SDL_Renderer[cell.DOC].get_image = `Read back the rendered pixels into a new SDL_Surface. If rect is null, capture entire output.
:param rect: Optional {x,y,w,h}.
:return: An SDL_Surface with the requested region's pixels.
@@ -518,7 +518,7 @@ prosperon.c_types.SDL_Renderer[cell.DOC].fasttext = `Draw debug text using an in
prosperon.c_types.SDL_Renderer[cell.DOC].geometry = `Render custom geometry from a mesh object {pos, uv, color, indices, count} with an optional texture.
:param texture: The SDL_Texture or undefined.
:param texture: The SDL_Texture or null.
:param meshObject: The geometry data with typed arrays.
:return: None
`;
@@ -536,15 +536,15 @@ For example, (320, 240) can auto-scale up to the window resolution.
:return: None
`;
prosperon.c_types.SDL_Renderer[cell.DOC].viewport = `Set the clipping viewport for rendering. Pass undefined to use the full render target.
prosperon.c_types.SDL_Renderer[cell.DOC].viewport = `Set the clipping viewport for rendering. Pass null to use the full render target.
:param rect: {x, y, w, h}, or undefined.
:param rect: {x, y, w, h}, or null.
:return: None
`;
prosperon.c_types.SDL_Renderer[cell.DOC].clip = `Set or clear the clipping rectangle for drawing. Pass undefined to clear.
prosperon.c_types.SDL_Renderer[cell.DOC].clip = `Set or clear the clipping rectangle for drawing. Pass null to clear.
:param rect: {x, y, w, h} or undefined.
:param rect: {x, y, w, h} or null.
:return: None
`;
@@ -579,9 +579,9 @@ prosperon.c_types.SDL_Renderer[cell.DOC].screen2world = `Convert a screen coordi
:return: [wx, wy] in world space
`;
prosperon.c_types.SDL_Renderer[cell.DOC].target = `Set or clear the current render target texture. Pass undefined to reset to the default/window.
prosperon.c_types.SDL_Renderer[cell.DOC].target = `Set or clear the current render target texture. Pass null to reset to the default/window.
:param texture: An SDL_Texture or undefined
:param texture: An SDL_Texture or null
:return: None
`;

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
@@ -244,6 +248,7 @@ globalThis.use = function use(file, ...args) {
return ret
}
globalThis.json = use('json')
var time = use('time')
var st_now = time.number()
@@ -299,7 +304,7 @@ function deepFreeze(object) {
for (var name of propNames) {
var value = object[name];
if ((value && typeof value === "object") || typeof value === "function") {
if ((value && typeof value == "object") || typeof value == "function") {
deepFreeze(value);
}
}
@@ -370,10 +375,10 @@ $_.clock = function(fn) {
$_.clock[cell.DOC] = "takes a function input value that will eventually be called with the current time in number form."
var underlings = new Set() // this is more like "all actors that are notified when we die"
var overling = undefined
var root = undefined
var overling = null
var root = null
var receive_fn = undefined
var receive_fn = null
var greeters = {} // Router functions for when messages are received for a specific actor
globalThis.is_actor = function is_actor(actor) {
@@ -420,9 +425,9 @@ $_.connection[cell.DOC] = "The connection function takes a callback function, an
var peers = {}
var id_address = {}
var peer_queue = new WeakMap()
var portal = undefined
var portal_fn = undefined
var peer_queue = {}
var portal = null
var portal_fn = null
$_.portal = function(fn, port) {
if (portal) throw new Error(`Already started a portal listening on ${portal.port}`)
@@ -447,7 +452,7 @@ function handle_host(e) {
break
case "disconnect":
peer_queue.delete(e.peer)
for (var id in peers) if (peers[id] === e.peer) delete peers[id]
for (var id in peers) if (peers[id] == e.peer) delete peers[id]
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
break
case "receive":
@@ -457,7 +462,7 @@ function handle_host(e) {
data.replycc[ACTORDATA].port = e.peer.port
}
function populate_actor_addresses(obj) {
if (typeof obj !== 'object' || obj === null) return
if (typeof obj != 'object' || obj == null) return
if (obj[ACTORDATA] && !obj[ACTORDATA].address) {
obj[ACTORDATA].address = e.peer.address
obj[ACTORDATA].port = e.peer.port
@@ -490,7 +495,7 @@ $_.start = function start(cb, program, ...args) {
if (!program) return
var id = guid()
if (args.length === 1 && Array.isArray(args[0]))
if (args.length == 1 && Array.isArray(args[0]))
args = args[0]
var startup = {
@@ -539,7 +544,7 @@ $_.delay[cell.DOC] = "used to schedule the invocation of a function..."
var couplings = new Set()
$_.couple = function couple(actor) {
if (actor === $_) return // can't couple to self
if (actor == $_) return // can't couple to self
couplings.add(actor[ACTORDATA].id)
sys_msg(actor, {kind:'couple', from: $_})
log.system(`coupled to ${actor}`)
@@ -565,10 +570,10 @@ function actor_send(actor, message) {
if (!is_actor(actor) && !is_actor(actor.replycc)) throw new Error(`Must send to an actor object. Attempted send to ${json.encode(actor)}`)
if (typeof message !== 'object') throw new Error('Must send an object record.')
if (typeof message != 'object') throw new Error('Must send an object record.')
// message to self
if (actor[ACTORDATA].id === cell.id) {
if (actor[ACTORDATA].id == cell.id) {
if (receive_fn) receive_fn(message.data)
return
}
@@ -623,10 +628,10 @@ function send_messages() {
var replies = {}
globalThis.send = function send(actor, message, reply) {
if (typeof actor !== 'object')
if (typeof actor != 'object')
throw new Error('Must send to an actor object. Provided: ' + actor);
if (typeof message !== 'object')
if (typeof message != 'object')
throw new Error('Message must be an object')
var send = {type:"user", data: message}
@@ -644,7 +649,7 @@ globalThis.send = function send(actor, message, reply) {
replies[id] = reply
$_.delay(_ => {
if (replies[id]) {
replies[id](undefined, "timeout")
replies[id](null, "timeout")
delete replies[id]
}
}, REPLYTIMEOUT)
@@ -759,7 +764,7 @@ function handle_message(msg) {
Object.defineProperty(letter, HEADER, {
value: msg, enumerable: false
})
Object.defineProperty(letter, ACTORDATA, { // this is so is_actor === true
Object.defineProperty(letter, ACTORDATA, { // this is so is_actor == true
value: { reply: msg.reply }, enumerable: false
})
@@ -831,4 +836,6 @@ $_.clock(_ => {
throw new Error('Program must not return anything');
})
log.console(`startup took ${time.number()-st_now}`)
})()

View File

@@ -19,7 +19,7 @@ var parsed = shop.parse_locator(locator)
// If no version specified, append @head
if (!parsed) {
if (locator.indexOf('@') === -1) {
if (locator.indexOf('@') == -1) {
locator = locator + '@head'
parsed = shop.parse_locator(locator)
}

View File

@@ -8,7 +8,7 @@ This function enqueues an HTTP GET request for the specified URL. It supports bo
:param options: Either a callback function or an object with optional properties:
- 'callback': A function invoked upon request completion, receiving an object with 'data' (string or null) and 'error' (string or null) properties.
- 'on_data': A function invoked for each chunk of streaming data, receiving a string chunk as its argument. If supplied, 'callback.data' will be null.
:return: undefined
:return: null
:throws:
- An error if the URL is not a string or is invalid.
- An error if the options argument is neither a function nor an object.

View File

@@ -72,11 +72,11 @@ Throw on error.
:return: None
`
io.mount[cell.DOC] = `Mount a directory or archive at the specified mount point. An undefined mount
io.mount[cell.DOC] = `Mount a directory or archive at the specified mount point. An null mount
point mounts to '/'. Throw on error.
:param archiveOrDir: The directory or archive to mount.
:param mountPoint: The path at which to mount. If omitted or undefined, '/' is used.
:param mountPoint: The path at which to mount. If omitted or null, '/' is used.
:return: None
`
@@ -145,10 +145,10 @@ operations. Throw on error.
`
io.realdir[cell.DOC] = `Return the actual, real directory (on the host filesystem) that contains the given
file path. Return undefined if not found.
file path. Return null if not found.
:param path: The file path whose real directory is requested.
:return: A string with the real directory path, or undefined.
:return: A string with the real directory path, or null.
`
io.searchpath[cell.DOC] = `Return an array of all directories in the current paths.

View File

@@ -71,21 +71,21 @@ function encode_key(key)
function encode_val(b, val)
{
var type = typeof val
if (type === 'number') {
if (type == 'number') {
b.write_blob(FP_HEADER)
b.write_number(val)
} else if (type === 'string') {
} else if (type == 'string') {
b.write_fit(utf8.byte_length(val), 56)
b.write_blob(TEXT)
b.write_blob(utf8.encode(val))
} else if (type === 'boolean') {
} else if (type == 'boolean') {
if (val)
b.write_blob(TRUE_SYMBOL)
else
b.write_blob(FALSE_SYMBOL)
} else if (type === 'undefined') {
} else if (type == 'null') {
b.write_blob(NULL_SYMBOL)
} else if (type === 'object') {
} else if (type == 'object') {
if (Array.isArray(val)) {
b.write_fit(val.length, 56)
b.write_blob(ARRAY)
@@ -100,7 +100,7 @@ function encode_val(b, val)
b.write_fit(keys.length, 56)
b.write_blob(RECORD)
for (var key of keys) {
if (typeof val[key] === 'function') continue
if (typeof val[key] == 'function') continue
b.write_blob(encode_key(key))
encode_val(b, val[key])
}
@@ -118,7 +118,7 @@ function encode(val)
function decode(b)
{
return undefined
return null
}
return { INT, FP_HEADER, ARRAY, RECORD, BLOB, TEXT, NULL_SYMBOL, FALSE_SYMBOL, TRUE_SYMBOL, PRIVATE_SYMBOL, SYSTEM_SYMBOL, encode, decode }

View File

@@ -16,7 +16,7 @@ if (!config) {
}
// List dependencies
if (!config.dependencies || Object.keys(config.dependencies).length === 0) {
if (!config.dependencies || Object.keys(config.dependencies).length == 0) {
log.console("No modules installed")
return
}

View File

@@ -1,14 +1,14 @@
var miniz = this
miniz.read[cell.DOC] = `Create a zip reader from the given ArrayBuffer containing an entire ZIP archive.
Return undefined if the data is invalid.
Return null if the data is invalid.
:param data: An ArrayBuffer with the entire ZIP file.
:return: A 'zip reader' object with methods for reading from the archive (mod, exists, slurp).
`
miniz.write[cell.DOC] = `Create a zip writer that writes to the specified file path. Overwrites the file if
it already exists. Return undefined on error.
it already exists. Return null on error.
:param path: The file path where the ZIP archive will be written.
:return: A 'zip writer' object with methods for adding files to the archive (add_file).

View File

@@ -60,7 +60,7 @@ uses.download = function()
} else if (!io.exists(cache_path)) {
log.console(`${mod}: cache missing, will download`)
need_download = true
} else if (remote_commit && (!lock.modules[mod].commit || lock.modules[mod].commit !== remote_commit)) {
} else if (remote_commit && (!lock.modules[mod].commit || lock.modules[mod].commit != remote_commit)) {
log.console(`${mod}: remote has new commit`)
log.console(` local: ${lock.modules[mod].commit || 'unknown'}`)
log.console(` remote: ${remote_commit}`)
@@ -74,7 +74,7 @@ uses.download = function()
var hash = crypto.hash(zip)
var hash_b32 = text(hash, "t")
if (hash_b32 !== lock.modules[mod].hash) {
if (hash_b32 != lock.modules[mod].hash) {
log.console(`${mod}: hash mismatch, will redownload`)
log.console(` expected: ${lock.modules[mod].hash}`)
log.console(` actual: ${hash_b32}`)
@@ -136,7 +136,7 @@ uses.download = function()
// Strip the module name prefix if present
var prefix = mod + '/'
if (filename.indexOf(prefix) === 0)
if (filename.indexOf(prefix) == 0)
filename = filename.substring(prefix.length)
// Skip if filename is empty after stripping

View File

@@ -38,9 +38,9 @@ function resolve_relative(base, relative) {
for (var i = 0; i < rel_parts.length; i++) {
var part = rel_parts[i]
if (part === '.') {
if (part == '.') {
continue
} else if (part === '..') {
} else if (part == '..') {
parts.pop()
} else {
parts.push(part)
@@ -82,7 +82,7 @@ ModuleResolver.check_alias = function(request) {
}
// Check for single-alias fallback
if (config.dependencies && Object.keys(config.dependencies).length === 1) {
if (config.dependencies && Object.keys(config.dependencies).length == 1) {
// If only one dependency and no local file matches, route there
var only_dep = Object.keys(config.dependencies)[0]
return '/' + only_dep + '/' + request

View File

@@ -31,10 +31,10 @@ This function serializes JavaScript values (such as numbers, strings, booleans,
nota.decode[cell.DOC] = `Decode a NOTA-encoded ArrayBuffer into a JavaScript value.
This function deserializes a NOTA-formatted ArrayBuffer into its corresponding JavaScript representation, such as a number, string, boolean, array, object, or ArrayBuffer. If the input is invalid or empty, it returns undefined.
This function deserializes a NOTA-formatted ArrayBuffer into its corresponding JavaScript representation, such as a number, string, boolean, array, object, or ArrayBuffer. If the input is invalid or empty, it returns null.
:param buffer: An ArrayBuffer containing NOTA-encoded data to decode.
:return: The decoded JavaScript value (e.g., number, string, boolean, array, object, or ArrayBuffer), or undefined if no argument is provided.
:return: The decoded JavaScript value (e.g., number, string, boolean, array, object, or ArrayBuffer), or null if no argument is provided.
`
return nota

View File

@@ -14,18 +14,18 @@
 Each factory returns a **requestor** function as described by the spec.
*/
const delay = arg[0] // may be undefined
def delay = arg[0] // may be null
// ———————————————————————————————————————— helpers
function make_reason (factory, excuse, evidence) {
const reason = new Error(`parseq.${factory}${excuse ? ': ' + excuse : ''}`)
def reason = new Error(`parseq.${factory}${excuse ? ': ' + excuse : ''}`)
reason.evidence = evidence
return reason
}
function is_requestor (fn) {
return typeof fn === 'function' && (fn.length === 1 || fn.length === 2)
return typeof fn == 'function' && (fn.length == 1 || fn.length == 2)
}
function check_requestors (list, factory) {
@@ -34,13 +34,13 @@ function check_requestors (list, factory) {
}
function check_callback (cb, factory) {
if (typeof cb !== 'function' || cb.length !== 2)
if (typeof cb != 'function' || cb.length != 2)
throw make_reason(factory, 'Not a callback.', cb)
}
function schedule (fn, seconds) {
if (seconds === undefined || seconds <= 0) return fn()
if (typeof delay === 'function') return delay(fn, seconds)
if (seconds == null || seconds <= 0) return fn()
if (typeof delay == 'function') return delay(fn, seconds)
throw make_reason('schedule', '@.delay capability required for timeouts.')
}
@@ -52,41 +52,41 @@ function run (factory, requestors, initial, action, time_limit, throttle = 0) {
let timer_cancel
function cancel (reason = make_reason(factory, 'Cancel.')) {
if (timer_cancel) timer_cancel(), timer_cancel = undefined
if (timer_cancel) timer_cancel(), timer_cancel = null
if (!cancel_list) return
cancel_list.forEach(c => { try { if (typeof c === 'function') c(reason) } catch (_) {} })
cancel_list = undefined
cancel_list.forEach(c => { try { if (typeof c == 'function') c(reason) } catch (_) {} })
cancel_list = null
}
function start_requestor (value) {
if (!cancel_list || next >= requestors.length) return
let idx = next++
const req = requestors[idx]
def req = requestors[idx]
try {
cancel_list[idx] = req(function req_cb (val, reason) {
if (!cancel_list || idx === undefined) return
cancel_list[idx] = undefined
if (!cancel_list || idx == null) return
cancel_list[idx] = null
action(val, reason, idx)
idx = undefined
if (factory === 'sequence') start_requestor(val)
idx = null
if (factory == 'sequence') start_requestor(val)
else if (throttle) start_requestor(initial)
}, value)
} catch (ex) {
action(undefined, ex, idx)
idx = undefined
if (factory === 'sequence') start_requestor(value)
action(null, ex, idx)
idx = null
if (factory == 'sequence') start_requestor(value)
else if (throttle) start_requestor(initial)
}
}
if (time_limit !== undefined) {
if (typeof time_limit !== 'number' || time_limit < 0)
if (time_limit != null) {
if (typeof time_limit != 'number' || time_limit < 0)
throw make_reason(factory, 'Bad time limit.', time_limit)
if (time_limit > 0) timer_cancel = schedule(() => cancel(make_reason(factory, 'Timeout.', time_limit)), time_limit)
}
const concurrent = throttle ? Math.min(throttle, requestors.length) : requestors.length
def concurrent = throttle ? Math.min(throttle, requestors.length) : requestors.length
for (let i = 0; i < concurrent; i++) start_requestor(initial)
return cancel
@@ -96,9 +96,9 @@ function run (factory, requestors, initial, action, time_limit, throttle = 0) {
function _normalize (collection, factory) {
if (Array.isArray(collection)) return { names: null, list: collection }
if (collection && typeof collection === 'object') {
const names = Object.keys(collection)
const list = names.map(k => collection[k]).filter(is_requestor)
if (collection && typeof collection == 'object') {
def names = Object.keys(collection)
def list = names.map(k => collection[k]).filter(is_requestor)
return { names, list }
}
throw make_reason(factory, 'Expected array or record.', collection)
@@ -106,29 +106,29 @@ function _normalize (collection, factory) {
function _denormalize (names, list) {
if (!names) return list
const obj = Object.create(null)
def obj = Object.create(null)
names.forEach((k, i) => { obj[k] = list[i] })
return obj
}
function par_all (collection, time_limit, throttle) {
const factory = 'par_all'
const { names, list } = _normalize(collection, factory)
if (list.length === 0) return (cb, v) => cb(names ? {} : [])
def factory = 'par_all'
def { names, list } = _normalize(collection, factory)
if (list.length == 0) return (cb, v) => cb(names ? {} : [])
check_requestors(list, factory)
return function par_all_req (cb, initial) {
check_callback(cb, factory)
let pending = list.length
const results = new Array(list.length)
def results = new Array(list.length)
const cancel = run(factory, list, initial, (val, reason, idx) => {
if (val === undefined) {
def cancel = run(factory, list, initial, (val, reason, idx) => {
if (val == null) {
cancel(reason)
return cb(undefined, reason)
return cb(null, reason)
}
results[idx] = val
if (--pending === 0) cb(_denormalize(names, results))
if (--pending == 0) cb(_denormalize(names, results))
}, time_limit, throttle)
return cancel
@@ -136,24 +136,24 @@ function par_all (collection, time_limit, throttle) {
}
function par_any (collection, time_limit, throttle) {
const factory = 'par_any'
const { names, list } = _normalize(collection, factory)
if (list.length === 0) return (cb, v) => cb(names ? {} : [])
def factory = 'par_any'
def { names, list } = _normalize(collection, factory)
if (list.length == 0) return (cb, v) => cb(names ? {} : [])
check_requestors(list, factory)
return function par_any_req (cb, initial) {
check_callback(cb, factory)
let pending = list.length
const successes = new Array(list.length)
def successes = new Array(list.length)
const cancel = run(factory, list, initial, (val, reason, idx) => {
def cancel = run(factory, list, initial, (val, reason, idx) => {
pending--
if (val !== undefined) successes[idx] = val
if (successes.some(v => v !== undefined)) {
if (val != null) successes[idx] = val
if (successes.some(v => v != null)) {
if (!pending) cancel(make_reason(factory, 'Finished.'))
return cb(_denormalize(names, successes.filter(v => v !== undefined)))
return cb(_denormalize(names, successes.filter(v => v != null)))
}
if (!pending) cb(undefined, make_reason(factory, 'No successes.'))
if (!pending) cb(null, make_reason(factory, 'No successes.'))
}, time_limit, throttle)
return cancel
@@ -161,24 +161,24 @@ function par_any (collection, time_limit, throttle) {
}
function race (list, time_limit, throttle) {
const factory = throttle === 1 ? 'fallback' : 'race'
if (!Array.isArray(list) || list.length === 0)
def factory = throttle == 1 ? 'fallback' : 'race'
if (!Array.isArray(list) || list.length == 0)
throw make_reason(factory, 'No requestors.')
check_requestors(list, factory)
return function race_req (cb, initial) {
check_callback(cb, factory)
let done = false
const cancel = run(factory, list, initial, (val, reason, idx) => {
def cancel = run(factory, list, initial, (val, reason, idx) => {
if (done) return
if (val !== undefined) {
if (val != null) {
done = true
cancel(make_reason(factory, 'Loser.', idx))
cb(val)
} else if (--list.length === 0) {
} else if (--list.length == 0) {
done = true
cancel(reason)
cb(undefined, reason)
cb(null, reason)
}
}, time_limit, throttle)
return cancel
@@ -190,10 +190,10 @@ function fallback (list, time_limit) {
}
function sequence (list, time_limit) {
const factory = 'sequence'
def factory = 'sequence'
if (!Array.isArray(list)) throw make_reason(factory, 'Not an array.', list)
check_requestors(list, factory)
if (list.length === 0) return (cb, v) => cb(v)
if (list.length == 0) return (cb, v) => cb(v)
return function sequence_req (cb, initial) {
check_callback(cb, factory)
@@ -203,11 +203,11 @@ function sequence (list, time_limit) {
if (idx >= list.length) return cb(value)
try {
list[idx++](function seq_cb (val, reason) {
if (val === undefined) return cb(undefined, reason)
if (val == null) return cb(null, reason)
next(val)
}, value)
} catch (ex) {
cb(undefined, ex)
cb(null, ex)
}
}

View File

@@ -50,7 +50,7 @@ Shop.init = function() {
// Parse module locator (e.g., "git.world/jj/mod@v0.6.3")
Shop.parse_locator = function(locator) {
var parts = locator.split('@')
if (parts.length !== 2) {
if (parts.length != 2) {
return null
}

View File

@@ -2,7 +2,7 @@
var def = arg
if (arg.length === 0)
if (arg.length == 0)
arg = [
'send',
'stop',

View File

@@ -17,7 +17,7 @@ function to_radix(num, radix) {
var negative = n < 0;
n = Math.abs(n);
if (n === 0) return "0";
if (n == 0) return "0";
while (n > 0) {
result = digits[n % radix] + result;
@@ -29,9 +29,9 @@ function to_radix(num, radix) {
// Insert separator every n digits from right
function add_separator(str, sep, n) {
if (!n || n === 0) return str;
if (!n || n == 0) return str;
var negative = str[0] === '-';
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var parts = str.split('.');
@@ -41,7 +41,7 @@ function add_separator(str, sep, n) {
// Add separators to integer part
var result = "";
for (var i = integer.length - 1, count = 0; i >= 0; i--) {
if (count === n && i !== integer.length - 1) {
if (count == n && i != integer.length - 1) {
result = sep + result;
count = 0;
}
@@ -55,14 +55,14 @@ function add_separator(str, sep, n) {
// Format number with separator from left
function add_separator_left(str, sep, n) {
if (!n || n === 0) return str;
if (!n || n == 0) return str;
var negative = str[0] === '-';
var negative = str[0] == '-';
if (negative) str = str.substring(1);
var result = "";
for (var i = 0, count = 0; i < str.length; i++) {
if (count === n && i !== 0) {
if (count == n && i != 0) {
result += sep;
count = 0;
}
@@ -75,7 +75,7 @@ function add_separator_left(str, sep, n) {
/* -------- main text function --------------------------------------- */
function text() {
function text(...arguments) {
var arg = arguments[0];
// Handle blob conversion
@@ -87,7 +87,7 @@ function text() {
var bit_length = arg.length;
var result = "";
if (typeof format === 'string') {
if (typeof format == 'string') {
// Extract style from format
var style = '';
for (var i = 0; i < format.length; i++) {
@@ -120,7 +120,7 @@ function text() {
value = (value << 1) | (bit ? 1 : 0);
bits++;
if (bits === 3) {
if (bits == 3) {
result += value.toString();
bits = 0;
value = 0;
@@ -150,13 +150,13 @@ function text() {
var all_codepoints = true;
for (var i = 0; i < arg.length; i++) {
var item = arg[i];
if (!(typeof item === 'number' && item >= 0 && item <= 0x10FFFF && item === Math.floor(item))) {
if (!(typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item))) {
all_codepoints = false;
break;
}
}
if (all_codepoints && separator === "") {
if (all_codepoints && separator == "") {
// Use utf8 module to convert codepoints to string
return utf8.from_codepoints(arg);
} else {
@@ -166,7 +166,7 @@ function text() {
if (i > 0) result += separator;
var item = arg[i];
if (typeof item === 'number' && item >= 0 && item <= 0x10FFFF && item === Math.floor(item)) {
if (typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item)) {
// Single codepoint - use utf8 module
result += utf8.from_codepoints([item]);
} else {
@@ -178,16 +178,16 @@ function text() {
}
// Handle number conversion
if (typeof arg === 'number') {
if (typeof arg == 'number') {
var format = arguments[1];
// Simple radix conversion
if (typeof format === 'number') {
if (typeof format == 'number') {
return to_radix(arg, format);
}
// Format string conversion
if (typeof format === 'string') {
if (typeof format == 'string') {
return format_number(arg, format);
}
@@ -196,13 +196,13 @@ function text() {
}
// Handle text operations
if (typeof arg === 'string') {
if (arguments.length === 1) return arg;
if (typeof arg == 'string') {
if (arguments.length == 1) return arg;
var from = arguments[1];
var to = arguments[2];
if (typeof from !== 'number' || typeof to !== 'number') return arg;
if (typeof from != 'number' || typeof to != 'number') return arg;
var len = arg.length;
@@ -211,8 +211,8 @@ function text() {
if (to < 0) to += len;
// Default values
if (from === undefined) from = 0;
if (to === undefined) to = len;
if (from == null) from = 0;
if (to == null) to = len;
// Validate range
if (from < 0 || from > to || to > len) return null;
@@ -261,8 +261,8 @@ function format_number(num, format) {
if (i < format.length) return null;
// Real number styles
if (style === 'e' || style === 'n' || style === 's' ||
style === 'u' || style === 'd' || style === 'v' || style === 'l') {
if (style == 'e' || style == 'n' || style == 's' ||
style == 'u' || style == 'd' || style == 'v' || style == 'l') {
var decimal_point = '.';
var separator = '';
@@ -314,15 +314,15 @@ function format_number(num, format) {
break;
}
if (separation === 0) separation = default_separation;
if (places === 0 && style !== 'e' && style !== 'n') places = default_places;
if (separation == 0) separation = default_separation;
if (places == 0 && style != 'e' && style != 'n') places = default_places;
// Format the number
if (style === 'e') {
if (style == 'e') {
// Scientific notation
var str = places > 0 ? num.toExponential(places) : num.toExponential();
return str;
} else if (style === 'n' && (Math.abs(num) >= 1e21 || (Math.abs(num) < 1e-6 && num !== 0))) {
} else if (style == 'n' && (Math.abs(num) >= 1e21 || (Math.abs(num) < 1e-6 && num != 0))) {
// Use scientific notation for extreme values
return num.toExponential();
} else {
@@ -335,7 +335,7 @@ function format_number(num, format) {
}
// Replace decimal point if needed
if (decimal_point !== '.') {
if (decimal_point != '.') {
str = str.replace('.', decimal_point);
}
@@ -349,8 +349,8 @@ function format_number(num, format) {
}
// Integer styles
if (style === 'i' || style === 'b' || style === 'o' ||
style === 'h' || style === 't') {
if (style == 'i' || style == 'b' || style == 'o' ||
style == 'h' || style == 't') {
var radix = 10;
var default_separation = 0;
@@ -384,15 +384,15 @@ function format_number(num, format) {
break;
}
if (separation === 0) separation = default_separation;
if (places === 0) places = default_places;
if (separation == 0) separation = default_separation;
if (places == 0) places = default_places;
// Convert to integer
var n = Math.trunc(num);
var str = to_radix(n, radix).toUpperCase();
// Pad with zeros if needed
var negative = str[0] === '-';
var negative = str[0] == '-';
if (negative) str = str.substring(1);
while (str.length < places) {

View File

@@ -41,10 +41,10 @@ time.week2day = function() { return time.week / time.day; };
/* leap-year helpers */
time.yearsize = function yearsize(y)
{
if (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) return 366;
if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366;
return 365;
};
time.isleap = function(y) { return time.yearsize(y) === 366; };
time.isleap = function(y) { return time.yearsize(y) == 366; };
/* timecode utility */
time.timecode = function(t, fps = 24)
@@ -64,7 +64,7 @@ function record(num = now(),
dst = computer_dst())
{
/* caller passed an existing record → return it verbatim */
if (typeof num === "object") return num;
if (typeof num == "object") return num;
/* -------------------------------------------------------------------- */
/* convert seconds-since-epoch → broken-down record */
@@ -107,7 +107,7 @@ function record(num = now(),
rec.yday = day;
/* month & month-day */
if (time.yearsize(y) === 366) monthdays[1] = 29;
if (time.yearsize(y) == 366) monthdays[1] = 29;
var m = 0;
for (; day >= monthdays[m]; m++) day -= monthdays[m];
rec.month = m;
@@ -119,11 +119,15 @@ function record(num = now(),
function number(rec = now())
{
/* fall through for numeric input or implicit “now” */
if (typeof rec === "number") return rec;
if (typeof rec == "number") return rec;
/* -------------------------------------------------------------------- */
/* record → seconds-since-epoch */
log.console(typeof rec)
log.console(rec)
log.console(rec.minute)
var c = 0;
var year = rec.year || 0;
var hour = rec.hour || 0;
@@ -165,15 +169,15 @@ function text(num = now(),
zone = computer_zone(),
dst = computer_dst())
{
var rec = (typeof num === "number") ? record(num, zone, dst) : num;
var rec = (typeof num == "number") ? record(num, zone, dst) : num;
zone = rec.zone;
dst = rec.dst;
/* am/pm */
if (fmt.includes("a")) {
if (rec.hour >= 13) { rec.hour -= 12; fmt = fmt.replaceAll("a", "PM"); }
else if (rec.hour === 12) { fmt = fmt.replaceAll("a", "PM"); }
else if (rec.hour === 0) { rec.hour = 12; fmt = fmt.replaceAll("a", "AM"); }
else if (rec.hour == 12) { fmt = fmt.replaceAll("a", "PM"); }
else if (rec.hour == 0) { rec.hour = 12; fmt = fmt.replaceAll("a", "AM"); }
else fmt = fmt.replaceAll("a", "AM");
}

View File

@@ -42,9 +42,9 @@ function parse_toml(text) {
} else if (value.startsWith('[') && value.endsWith(']')) {
// Array
current_section[key] = parse_array(value)
} else if (value === 'true' || value === 'false') {
} else if (value == 'true' || value == 'false') {
// Boolean
current_section[key] = value === 'true'
current_section[key] = value == 'true'
} else if (!isNaN(Number(value))) {
// Number
current_section[key] = Number(value)
@@ -70,10 +70,10 @@ function parse_array(str) {
for (var i = 0; i < str.length; i++) {
var char = str[i]
if (char === '"' && (i === 0 || str[i-1] !== '\\')) {
if (char == '"' && (i == 0 || str[i-1] != '\\')) {
in_quotes = !in_quotes
current += char
} else if (char === ',' && !in_quotes) {
} else if (char == ',' && !in_quotes) {
items.push(parse_value(current.trim()))
current = ''
} else {
@@ -91,8 +91,8 @@ function parse_array(str) {
function parse_value(str) {
if (str.startsWith('"') && str.endsWith('"')) {
return str.slice(1, -1).replace(/\\"/g, '"')
} else if (str === 'true' || str === 'false') {
return str === 'true'
} else if (str == 'true' || str == 'false') {
return str == 'true'
} else if (!isNaN(Number(str))) {
return Number(str)
} else {
@@ -104,11 +104,11 @@ function encode_toml(obj) {
var result = []
function encode_value(value) {
if (typeof value === 'string') {
if (typeof value == 'string') {
return '"' + value.replace(/"/g, '\\"') + '"'
} else if (typeof value === 'boolean') {
} else if (typeof value == 'boolean') {
return value ? 'true' : 'false'
} else if (typeof value === 'number') {
} else if (typeof value == 'number') {
return String(value)
} else if (Array.isArray(value)) {
var items = []
@@ -125,7 +125,7 @@ function encode_toml(obj) {
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var value = obj[key]
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
if (value == null || typeof value != 'object' || Array.isArray(value)) {
result.push(key + ' = ' + encode_value(value))
}
}
@@ -138,7 +138,7 @@ function encode_toml(obj) {
var key = keys[i]
var value = obj[key]
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
if (value != null && typeof value == 'object' && !Array.isArray(value)) {
// Nested object - create section
var section_path = path ? path + '.' + key : key
result.push('[' + section_path + ']')
@@ -148,7 +148,7 @@ function encode_toml(obj) {
for (var j = 0; j < section_keys.length; j++) {
var sk = section_keys[j]
var sv = value[sk]
if (sv === null || typeof sv !== 'object' || Array.isArray(sv)) {
if (sv == null || typeof sv != 'object' || Array.isArray(sv)) {
result.push(sk + ' = ' + encode_value(sv))
}
}

View File

@@ -77,7 +77,7 @@ for (var alias in deps_to_check) {
local_commit: null,
remote_commit: remote_commit
})
} else if (local_commit !== remote_commit) {
} else if (local_commit != remote_commit) {
log.console(" Update available!")
log.console(" Local: " + local_commit.substring(0, 8))
log.console(" Remote: " + remote_commit.substring(0, 8))
@@ -95,7 +95,7 @@ for (var alias in deps_to_check) {
}
}
if (updates_available.length === 0) {
if (updates_available.length == 0) {
log.console("\nAll dependencies are up to date!")
$_.stop()
return

View File

@@ -4,7 +4,7 @@ return util
util.dainty_assign = function (target, source) {
Object.keys(source).forEach(function (k) {
if (typeof source[k] === "function") return
if (typeof source[k] == "function") return
if (!(k in target)) return
if (Array.isArray(source[k])) target[k] = deep_copy(source[k])
else if (Object.isObject(source[k])) Object.dainty_assign(target[k], source[k])
@@ -13,41 +13,41 @@ util.dainty_assign = function (target, source) {
}
util.get = function (obj, path, defValue) {
if (!path) return undefined
if (!path) return null
var pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g)
var result = pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj)
return result === undefined ? defValue : result
return result == null ? defValue : result
}
util.isEmpty = function(o) {
return Object.keys(o).length === 0
return Object.keys(o).length == 0
}
util.dig = function (obj, path, def = {}) {
util.dig = function (obj, path, deflt = {}) {
var pp = path.split(".")
for (var i = 0; i < pp.length - 1; i++) {
obj = obj[pp[i]] = obj[pp[i]] || {}
}
obj[pp[pp.length - 1]] = def
return def
obj[pp[pp.length - 1]] = deflt
return deflt
}
util.access = function (obj, name) {
var dig = name.split(".")
for (var i of dig) {
obj = obj[i]
if (!obj) return undefined
if (!obj) return null
}
return obj
}
util.mergekey = function (o1, o2, k) {
if (!o2) return
if (typeof o2[k] === "object") {
if (typeof o2[k] == "object") {
if (Array.isArray(o2[k])) o1[k] = deep_copy(o2[k])
else {
if (!o1[k]) o1[k] = {}
if (typeof o1[k] === "object") util.merge(o1[k], o2[k])
if (typeof o1[k] == "object") util.merge(o1[k], o2[k])
else o1[k] = o2[k]
}
} else o1[k] = o2[k]
@@ -74,15 +74,15 @@ util.obj_lerp = function(a,b,t) {
}
util.normalizeSpacing = function normalizeSpacing(spacing) {
if (typeof spacing === 'number') {
if (typeof spacing == 'number') {
return {l: spacing, r: spacing, t: spacing, b: spacing}
} else if (Array.isArray(spacing)) {
if (spacing.length === 2) {
if (spacing.length == 2) {
return {l: spacing[0], r: spacing[0], t: spacing[1], b: spacing[1]}
} else if (spacing.length === 4) {
} else if (spacing.length == 4) {
return {l: spacing[0], r: spacing[1], t: spacing[2], b: spacing[3]}
}
} else if (typeof spacing === 'object') {
} else if (typeof spacing == 'object') {
return {l: spacing.l || 0, r: spacing.r || 0, t: spacing.t || 0, b: spacing.b || 0}
} else {
return {l:0, r:0, t:0, b:0}

View File

@@ -33,11 +33,11 @@ This function serializes JavaScript values (such as numbers, strings, booleans,
wota.decode[cell.DOC] = `Decode a WOTA-encoded ArrayBuffer into a JavaScript value.
This function deserializes a WOTA-formatted ArrayBuffer into its corresponding JavaScript representation, such as a number, string, boolean, array, object, or ArrayBuffer. If the input is invalid or empty, it returns undefined.
This function deserializes a WOTA-formatted ArrayBuffer into its corresponding JavaScript representation, such as a number, string, boolean, array, object, or ArrayBuffer. If the input is invalid or empty, it returns null.
:param buffer: An ArrayBuffer containing WOTA-encoded data to decode.
:param reviver: An optional function that transforms the decoded values.
:return: The decoded JavaScript value (e.g., number, string, boolean, array, object, or ArrayBuffer), or undefined if no argument is provided.
:return: The decoded JavaScript value (e.g., number, string, boolean, array, object, or ArrayBuffer), or null if no argument is provided.
`
return wota

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)
{
@@ -346,7 +378,7 @@ void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
JS_FreeValue(actor->context, actor->unneeded);
if (!JS_IsFunction(actor->context, fn)) {
actor->unneeded = JS_UNDEFINED;
actor->unneeded = JS_NULL;
goto END;
}
@@ -364,11 +396,12 @@ 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;
actor->unneeded = JS_UNDEFINED;
actor->on_exception = JS_UNDEFINED;
actor->idx_buffer = JS_NULL;
actor->message_handle = JS_NULL;
actor->unneeded = JS_NULL;
actor->on_exception = JS_NULL;
actor->actor_sym = JS_ATOM_NULL;
arrsetcap(actor->letters, 5);
@@ -443,9 +476,9 @@ static Uint32 actor_remove_cb(cell_rt *actor, Uint32 id, Uint32 interval)
{
actor->disrupt = 1;
if (!JS_IsUndefined(actor->unneeded)) {
if (!JS_IsNull(actor->unneeded)) {
SDL_LockMutex(actor->mutex);
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_UNDEFINED, 0, NULL);
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_NULL, 0, NULL);
uncaught_exception(actor->context, ret);
SDL_UnlockMutex(actor->mutex);
}
@@ -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;
@@ -538,11 +574,11 @@ void actor_turn(cell_rt *actor)
size_t size = l.blob_data->length / 8; // Convert bits to bytes
JSValue arg = js_new_blob_stoned_copy(actor->context, l.blob_data->data, size);
blob_destroy(l.blob_data);
result = JS_Call(actor->context, actor->message_handle, JS_UNDEFINED, 1, &arg);
result = JS_Call(actor->context, actor->message_handle, JS_NULL, 1, &arg);
uncaught_exception(actor->context, result);
JS_FreeValue(actor->context, arg);
} else if (l.type == LETTER_CALLBACK) {
result = JS_Call(actor->context, l.callback, JS_UNDEFINED, 0, NULL);
result = JS_Call(actor->context, l.callback, JS_NULL, 0, NULL);
uncaught_exception(actor->context, result);
JS_FreeValue(actor->context, l.callback);
}
@@ -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
@@ -591,7 +627,7 @@ JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv)
JS_ToFloat64(js, &seconds, argv[1]);
if (seconds <= 0) {
actor_clock(actor, argv[0]);
return JS_UNDEFINED;
return JS_NULL;
}
SDL_LockMutex(actor->msg_mutex);
@@ -607,11 +643,11 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg
cell_rt *actor = JS_GetContextOpaque(js);
uint32_t timer_id;
JS_ToUint32(js, &timer_id, argv[0]);
if (timer_id == -1) return JS_UNDEFINED;
if (timer_id == -1) return JS_NULL;
SDL_RemoveTimer(timer_id);
JSValue cb = JS_UNDEFINED;
JSValue cb = JS_NULL;
SDL_LockMutex(actor->msg_mutex);
int id = hmgeti(actor->timers, timer_id);
@@ -623,7 +659,7 @@ JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *arg
JS_FreeValue(js,cb);
return JS_UNDEFINED;
return JS_NULL;
}
// Wrapper struct to keep the array pointer stable
@@ -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);
@@ -741,7 +770,7 @@ void script_startup(cell_rt *prt)
data[stat.filesize] = 0;
prt->state = ACTOR_RUNNING;
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, JS_EVAL_FLAG_STRICT);
JSValue v = JS_Eval(js, data, (size_t)stat.filesize, ENGINE, 0);
uncaught_exception(js, v);
prt->state = ACTOR_IDLE;
set_actor_state(prt);
@@ -759,7 +788,12 @@ int uncaught_exception(JSContext *js, JSValue v)
}
JSValue exp = JS_GetException(js);
JSValue ret = JS_Call(js, rt->on_exception, JS_UNDEFINED, 1, &exp);
JSValue stack = JS_GetPropertyStr(js,exp,"stack");
const char *stackstr = JS_ToCString(js, stack);
printf("ERROR: %s\n", stackstr);
JS_FreeCString(js,stackstr);
JS_FreeValue(js,stack);
JSValue ret = JS_Call(js, rt->on_exception, JS_NULL, 1, &exp);
JS_FreeValue(js,ret);
JS_FreeValue(js, exp);
JS_FreeValue(js,v);
@@ -841,7 +875,80 @@ int main(int argc, char **argv)
int profile_enabled = 0;
int script_start = 1;
/* Check for --profile flag first */
/* Check for --script flag - execute script directly without engine */
if (argc > 2 && strcmp(argv[1], "--script") == 0) {
/* Read and execute the script file */
FILE *f = fopen(argv[2], "rb");
if (!f) {
perror("fopen");
return 1;
}
fseek(f, 0, SEEK_END);
long size = ftell(f);
if (size < 0) {
perror("ftell");
fclose(f);
return 1;
}
char *script = malloc(size + 1);
if (!script) {
perror("malloc");
fclose(f);
return 1;
}
rewind(f);
if (fread(script, 1, size, f) != size) {
perror("fread");
free(script);
fclose(f);
return 1;
}
fclose(f);
script[size] = '\0';
/* Create minimal JS runtime */
JSRuntime *rt = JS_NewRuntime();
JSContext *js = JS_NewContextRaw(rt);
/* Add basic intrinsics */
JS_AddIntrinsicBaseObjects(js);
JS_AddIntrinsicEval(js);
JS_AddIntrinsicRegExp(js);
JS_AddIntrinsicJSON(js);
JS_AddIntrinsicMapSet(js);
JS_AddIntrinsicProxy(js);
cell_rt *prt = malloc(sizeof(*prt));
JS_SetContextOpaque(js, prt);
/* Load FFI functions */
ffi_load(js);
/* Execute the script */
JSValue result = JS_Eval(js, script, size, argv[2], 0);
free(script);
/* Check for exceptions */
if (JS_IsException(result)) {
JSValue exp = JS_GetException(js);
const char *str = JS_ToCString(js, exp);
if (str) {
fprintf(stderr, "Exception: %s\n", str);
JS_FreeCString(js, str);
}
JS_FreeValue(js, exp);
}
JS_FreeValue(js, result);
JS_FreeContext(js);
JS_FreeRuntime(rt);
return 0;
}
/* Check for --profile flag */
if (argc > 1 && strcmp(argv[1], "--profile") == 0) {
profile_enabled = 1; script_start = 2;
}

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

@@ -170,33 +170,6 @@ typedef struct texture_vertex {
uint8_t r, g, b,a;
} texture_vertex;
static inline size_t typed_array_bytes(JSTypedArrayEnum type) {
switch(type) {
case JS_TYPED_ARRAY_UINT8C:
case JS_TYPED_ARRAY_INT8:
case JS_TYPED_ARRAY_UINT8:
return 1;
case JS_TYPED_ARRAY_INT16:
case JS_TYPED_ARRAY_UINT16:
return 2;
case JS_TYPED_ARRAY_INT32:
case JS_TYPED_ARRAY_UINT32:
case JS_TYPED_ARRAY_FLOAT32:
return 4;
case JS_TYPED_ARRAY_BIG_INT64:
case JS_TYPED_ARRAY_BIG_UINT64:
case JS_TYPED_ARRAY_FLOAT64:
return 8;
default:
return 0; // Return 0 for unknown types
}
}
#define JS_GETNUM(JS,VAL,I,TO,TYPE) { \
JSValue val = JS_GetPropertyUint32(JS,VAL,I); \
TO = js2##TYPE(JS, val); \
@@ -270,34 +243,36 @@ void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr)
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index)
{
JSValue tstack[3];
tstack[1] = JS_UNDEFINED;
tstack[2] = JS_UNDEFINED;
tstack[1] = JS_NULL;
tstack[2] = JS_NULL;
// TODO: always copying; implement "takeover"
tstack[0] = js_new_blob_stoned_copy(js,data,size);//, make_gpu_buffer, NULL, 1);
JSValue ret = JS_NewTypedArray(js, 3, tstack, type);
JS_SetPropertyStr(js,ret,"stride", number2js(js,typed_array_bytes(type)*elements));
JS_SetPropertyStr(js,ret,"elen", number2js(js,typed_array_bytes(type)));
JS_SetPropertyStr(js,ret,"index", JS_NewBool(js,index));
JS_FreeValue(js,tstack[0]);
return ret;
// JSValue ret = JS_NewTypedArray(js, 3, tstack, type);
// JS_SetPropertyStr(js,ret,"stride", number2js(js,typed_array_bytes(type)*elements));
// JS_SetPropertyStr(js,ret,"elen", number2js(js,typed_array_bytes(type)));
// JS_SetPropertyStr(js,ret,"index", JS_NewBool(js,index));
// JS_FreeValue(js,tstack[0]);
// return ret;
return JS_NULL;
}
void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size)
{
size_t o, len, bytes, msize;
return NULL;
/* size_t o, len, bytes, msize;
JSValue buf = JS_GetTypedArrayBuffer(js, argv, &o, &len, &bytes);
void *data = js_get_blob_data(js, &msize, buf);
JS_FreeValue(js,buf);
if (stride) *stride = js_getnum_str(js, argv, "stride");
if (size) *size = msize;
return data;
return data;*/
}
JSValue make_quad_indices_buffer(JSContext *js, int quads)
{
cell_rt *rt = JS_GetContextOpaque(js);
int count = quads*6;
if (!JS_IsUndefined(rt->idx_buffer) && rt->idx_count >= count)
if (!JS_IsNull(rt->idx_buffer) && rt->idx_count >= count)
return JS_DupValue(js,rt->idx_buffer);
int verts = quads*4;
@@ -311,10 +286,10 @@ JSValue make_quad_indices_buffer(JSContext *js, int quads)
indices[i+5] = v+1;
}
if (!JS_IsUndefined(rt->idx_buffer))
if (!JS_IsNull(rt->idx_buffer))
JS_FreeValue(js,rt->idx_buffer);
rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1);
// rt->idx_buffer = make_gpu_buffer(js,indices, sizeof(*indices)*count, JS_TYPED_ARRAY_UINT16, 1,0,1);
rt->idx_count = count;
return JS_DupValue(js,rt->idx_buffer);
}
@@ -333,9 +308,9 @@ JSValue quads_to_mesh(JSContext *js, text_vert *buffer)
color[i] = buffer[i].color;
}
JSValue jspos = make_gpu_buffer(js, pos, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), JS_TYPED_ARRAY_FLOAT32, 4,0,0);
JSValue jspos = make_gpu_buffer(js, pos, sizeof(HMM_Vec2)*arrlen(buffer), 0, 2,0,0);
JSValue jsuv = make_gpu_buffer(js, uv, sizeof(HMM_Vec2)*arrlen(buffer), 0, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, color, sizeof(HMM_Vec4)*arrlen(buffer), 0, 4,0,0);
size_t quads = verts/4;
size_t count = verts/2*3;
@@ -405,10 +380,10 @@ double js2angle(JSContext *js,JSValue v) {
typedef HMM_Vec4 colorf;
colorf js2color(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return (colorf){1,1,1,1};
if (JS_IsNull(v)) return (colorf){1,1,1,1};
JSValue c[4];
for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyUint32(js,v,i);
float a = JS_IsUndefined(c[3]) ? 1.0 : js2number(js,c[3]);
float a = JS_IsNull(c[3]) ? 1.0 : js2number(js,c[3]);
colorf color = {
.r = js2number(js,c[0]),
.g = js2number(js,c[1]),
@@ -557,7 +532,7 @@ JSValue vecarr2js(JSContext *js,HMM_Vec2 *points, int n) {
}
rect js2rect(JSContext *js,JSValue v) {
if (JS_IsUndefined(v)) return (rect){0,0,1,1};
if (JS_IsNull(v)) return (rect){0,0,1,1};
rect rect;
JS_GETATOM(js,rect.x,v,x,number)
JS_GETATOM(js,rect.y,v,y,number)
@@ -575,7 +550,7 @@ rect js2rect(JSContext *js,JSValue v) {
irect js2irect(JSContext *js, JSValue v)
{
if (JS_IsUndefined(v)) return (irect){0,0,1,1};
if (JS_IsNull(v)) return (irect){0,0,1,1};
irect rect;
JS_GETATOM(js,rect.x,v,x,number)
JS_GETATOM(js,rect.y,v,y,number)
@@ -719,7 +694,7 @@ JSC_CCALL(os_make_text_buffer,
JSValue js_util_camera_globals(JSContext *js, JSValue self, int argc, JSValue *argv)
{
JSValue camera = argv[0];
if(JS_IsUndefined(camera)) return JS_UNDEFINED;
if(JS_IsNull(camera)) return JS_NULL;
HMM_Vec2 size; HMM_Vec3 pos; HMM_Quat rotation;
double fov = 0; int ortho; double near_z = 0; double far_z = 0;
@@ -840,10 +815,10 @@ JSC_CCALL(array_lerp,
)
JSValue js_array_get_x(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,0); }
JSValue js_array_set_x(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,0,val); return JS_UNDEFINED; }
JSValue js_array_set_x(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,0,val); return JS_NULL; }
JSValue js_array_get_y(JSContext *js, JSValue self) { return JS_GetPropertyUint32(js,self,1); }
JSValue js_array_set_y(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,1,val); return JS_UNDEFINED; }
JSValue js_array_set_y(JSContext *js, JSValue self, JSValue val) { JS_SetPropertyUint32(js,self,1,val); return JS_NULL; }
JSValue js_array_get_xy(JSContext *js, JSValue self)
{
@@ -857,7 +832,7 @@ JSValue js_array_set_xy(JSContext *js, JSValue self, JSValue v)
{
JS_SetPropertyUint32(js,self,0,JS_GetPropertyUint32(js,v,0));
JS_SetPropertyUint32(js,self,1,JS_GetPropertyUint32(js,v,1));
return JS_UNDEFINED;
return JS_NULL;
}
// Single-value accessors
@@ -866,25 +841,25 @@ JSValue js_array_get_r(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 0); }
JSValue js_array_set_r(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 0, val); return JS_UNDEFINED; }
{ JS_SetPropertyUint32(js, self, 0, val); return JS_NULL; }
JSValue js_array_get_g(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 1); }
JSValue js_array_set_g(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 1, val); return JS_UNDEFINED; }
{ JS_SetPropertyUint32(js, self, 1, val); return JS_NULL; }
JSValue js_array_get_b(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 2); }
JSValue js_array_set_b(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 2, val); return JS_UNDEFINED; }
{ JS_SetPropertyUint32(js, self, 2, val); return JS_NULL; }
JSValue js_array_get_a(JSContext *js, JSValue self)
{ return JS_GetPropertyUint32(js, self, 3); }
JSValue js_array_set_a(JSContext *js, JSValue self, JSValue val)
{ JS_SetPropertyUint32(js, self, 3, val); return JS_UNDEFINED; }
{ JS_SetPropertyUint32(js, self, 3, val); return JS_NULL; }
// Multi-value accessors
@@ -902,7 +877,7 @@ JSValue js_array_set_rgb(JSContext *js, JSValue self, JSValue val)
JS_SetPropertyUint32(js, self, 0, JS_GetPropertyUint32(js, val, 0));
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
return JS_UNDEFINED;
return JS_NULL;
}
JSValue js_array_get_rgba(JSContext *js, JSValue self)
@@ -921,7 +896,7 @@ JSValue js_array_set_rgba(JSContext *js, JSValue self, JSValue val)
JS_SetPropertyUint32(js, self, 1, JS_GetPropertyUint32(js, val, 1));
JS_SetPropertyUint32(js, self, 2, JS_GetPropertyUint32(js, val, 2));
JS_SetPropertyUint32(js, self, 3, JS_GetPropertyUint32(js, val, 3));
return JS_UNDEFINED;
return JS_NULL;
}
static const JSCFunctionListEntry js_array_funcs[] = {
@@ -1239,10 +1214,12 @@ JSValue make_color_buffer(JSContext *js, colorf c, int verts)
for (int i = 0; i < verts; i++)
colordata[i] = c;
return make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 4, 0, 0);
return make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, 0, 4, 0, 0);
}
JSC_CCALL(os_make_line_prim,
return JS_NULL;
/*
JSValue prim = JS_NewObject(js);
HMM_Vec2 *v = js2cpvec2arr(js,argv[0]);
@@ -1262,7 +1239,7 @@ JSC_CCALL(os_make_line_prim,
.closed = JS_ToBool(js,argv[3])
});
JS_SetPropertyStr(js, prim, "pos", make_gpu_buffer(js,m->positions,sizeof(*m->positions)*m->num_vertices, JS_TYPED_ARRAY_FLOAT32, 2,1,0));
JS_SetPropertyStr(js, prim, "pos", make_gpu_buffer(js,m->positions,sizeof(*m->positions)*m->num_vertices, 0, 2,1,0));
JS_SetPropertyStr(js, prim, "indices", make_gpu_buffer(js,m->triangle_indices,sizeof(*m->triangle_indices)*m->num_triangles*3, JS_TYPED_ARRAY_UINT32, 1,1,1));
@@ -1272,7 +1249,7 @@ JSC_CCALL(os_make_line_prim,
uv[i*2+1] = m->annotations[i].v_across_curve;
}
JS_SetPropertyStr(js, prim, "uv", make_gpu_buffer(js, uv, sizeof(uv), JS_TYPED_ARRAY_FLOAT32,2,1,0));
JS_SetPropertyStr(js, prim, "uv", make_gpu_buffer(js, uv, sizeof(uv), 0,2,1,0));
JS_SetPropertyStr(js,prim,"vertices", number2js(js,m->num_vertices));
JS_SetPropertyStr(js,prim,"color",make_color_buffer(js,js2color(js,argv[4]), m->num_vertices));
JS_SetPropertyStr(js,prim,"num_indices", number2js(js,m->num_triangles*3));
@@ -1281,10 +1258,11 @@ JSC_CCALL(os_make_line_prim,
parsl_destroy_context(par_ctx);
return prim;
*/
)
static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
if (JS_IsUndefined(ds->callback)) return;
if (JS_IsNull(ds->callback)) return;
uint8_t *rgb = malloc(frame->height*frame->width*4);
memset(rgb,255,frame->height*frame->width*4);
plm_frame_to_rgba(frame, rgb, frame->width*4);
@@ -1300,7 +1278,7 @@ static void render_frame(plm_t *mpeg, plm_frame_t *frame, datastream *ds) {
JSValue s[1];
s[0] = surfData;
JSValue cb = JS_DupValue(ds->js,ds->callback);
JSValue ret = JS_Call(ds->js, cb, JS_UNDEFINED, 1, s);
JSValue ret = JS_Call(ds->js, cb, JS_NULL, 1, s);
JS_FreeValue(ds->js,cb);
free(rgb);
uncaught_exception(ds->js,ret);
@@ -1312,7 +1290,7 @@ JSC_CCALL(os_make_video,
datastream *ds = ds_openvideo(data, len);
if (!ds) return JS_ThrowReferenceError(js, "Video file was not valid.");
ds->js = js;
ds->callback = JS_UNDEFINED;
ds->callback = JS_NULL;
plm_set_video_decode_callback(ds->plm, render_frame, ds);
return datastream2js(js,ds);
)
@@ -1336,7 +1314,7 @@ JSC_CCALL(os_rectpack,
int packed = stbrp_pack_rects(ctx, rects, num);
if (!packed) {
return JS_UNDEFINED;
return JS_NULL;
}
ret = JS_NewArray(js);
@@ -1361,7 +1339,7 @@ JSC_CCALL(os_insertion_sort,
while (j >= 0) {
JSValue arr_j = JS_GetPropertyUint32(js, arr, j);
JSValue ret = JS_Call(js, cmp, JS_UNDEFINED, 2, (JSValue[]){ arr_j, key });
JSValue ret = JS_Call(js, cmp, JS_NULL, 2, (JSValue[]){ arr_j, key });
if (JS_IsException(ret)) {
JS_FreeValue(js,arr_j);
JS_FreeValue(js,key);
@@ -1389,13 +1367,13 @@ JSC_CCALL(os_power_state,
SDL_PowerState state = SDL_GetPowerInfo(NULL, NULL);
switch(state) {
case SDL_POWERSTATE_ERROR: return JS_ThrowTypeError(js, "Error determining power status");
case SDL_POWERSTATE_UNKNOWN: return JS_UNDEFINED;
case SDL_POWERSTATE_UNKNOWN: return JS_NULL;
case SDL_POWERSTATE_ON_BATTERY: return JS_NewString(js, "on battery");
case SDL_POWERSTATE_NO_BATTERY: return JS_NewString(js, "no battery");
case SDL_POWERSTATE_CHARGING: return JS_NewString(js, "charging");
case SDL_POWERSTATE_CHARGED: return JS_NewString(js, "charged");
}
return JS_UNDEFINED;
return JS_NULL;
)
JSC_CCALL(os_cull_sprites,
@@ -1530,8 +1508,8 @@ js_##NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, js_##NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
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 +1556,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 +1574,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));
@@ -1617,7 +1598,6 @@ void ffi_load(JSContext *js)
arrput(rt->module_registry, MISTLINE(graphics));
arrput(rt->module_registry, MISTLINE(video));
arrput(rt->module_registry, MISTLINE(soloud));
arrput(rt->module_registry, MISTLINE(layout));
// arrput(rt->module_registry, MISTLINE(imgui));
arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use}));
@@ -1666,7 +1646,7 @@ void ffi_load(JSContext *js)
const char actorsym_script[] = "var sym = Symbol(`actordata`); sym;";
JSValue actorsym = JS_Eval(js, actorsym_script, sizeof(actorsym_script)-1, "internal", JS_EVAL_FLAG_STRICT);
JSValue actorsym = JS_Eval(js, actorsym_script, sizeof(actorsym_script)-1, "internal", 0);
JS_SetPropertyStr(js, hidden_fn, "actorsym", actorsym);

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

@@ -20,7 +20,7 @@ cell_rt *js2actor(JSContext *js, JSValue v)
cell_rt *crt = JS_GetContextOpaque(js);
JSValue actor_data = JS_GetProperty(js, v, crt->actor_sym);
if (JS_IsUndefined(actor_data)) {
if (JS_IsNull(actor_data)) {
JS_FreeValue(js, actor_data);
return NULL;
}
@@ -28,7 +28,7 @@ cell_rt *js2actor(JSContext *js, JSValue v)
JSValue id_val = JS_GetPropertyStr(js, actor_data, "id");
JS_FreeValue(js, actor_data);
if (JS_IsUndefined(id_val)) {
if (JS_IsNull(id_val)) {
JS_FreeValue(js, id_val);
return NULL;
}
@@ -78,7 +78,7 @@ JSC_CCALL(os_createactor,
if (rt->disrupt)
return JS_ThrowInternalError(js, "Can't start a new actor while disrupting.");
void *startup = value2wota(js, argv[0], JS_UNDEFINED, NULL);
void *startup = value2wota(js, argv[0], JS_NULL, NULL);
create_actor(startup);
)
@@ -98,7 +98,7 @@ JSC_CCALL(os_mailbox_push,
JS_FreeCString(js,id);
/* JSValue sys = JS_GetPropertyStr(js, argv[1], "__SYSTEM__");
if (!JS_IsUndefined(sys)) {
if (!JS_IsNull(sys)) {
const char *k = JS_ToCString(js,sys);
int stop = 0;
if (strcmp(k, "stop") == 0) {
@@ -108,7 +108,7 @@ JSC_CCALL(os_mailbox_push,
JS_FreeValue(js,sys);
JS_FreeCString(js,k);
if (stop) return JS_UNDEFINED;
if (stop) return JS_NULL;
}
*/
size_t size;

View File

@@ -60,7 +60,7 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
size_t bits_written = 0;
while (bits_written < length_bits) {
JSValue randval = JS_Call(ctx, argv[1], JS_UNDEFINED, 0, NULL);
JSValue randval = JS_Call(ctx, argv[1], JS_NULL, 0, NULL);
if (JS_IsException(randval)) {
blob_destroy(bd);
return JS_EXCEPTION;
@@ -151,7 +151,7 @@ static JSValue js_blob_write_bit(JSContext *ctx, JSValueConst this_val,
if (blob_write_bit(bd, bit_val) < 0) {
return JS_ThrowTypeError(ctx, "write_bit: cannot write (maybe stone or OOM)");
}
return JS_UNDEFINED;
return JS_NULL;
}
// blob.write_blob(second_blob)
@@ -173,7 +173,7 @@ static JSValue js_blob_write_blob(JSContext *ctx, JSValueConst this_val,
return JS_ThrowTypeError(ctx, "write_blob: cannot write to stone blob or OOM");
}
return JS_UNDEFINED;
return JS_NULL;
}
// blob.write_dec64(number)
@@ -194,7 +194,7 @@ static JSValue js_blob_write_number(JSContext *ctx, JSValueConst this_val,
if (blob_write_dec64(bd, d) < 0)
return JS_ThrowTypeError(ctx, "write_dec64: cannot write to stone blob or OOM");
return JS_UNDEFINED;
return JS_NULL;
}
// blob.write_fit(value, len)
@@ -217,7 +217,7 @@ static JSValue js_blob_write_fit(JSContext *ctx, JSValueConst this_val,
return JS_ThrowTypeError(ctx, "write_fit: value doesn't fit in specified bits, stone blob, or OOM");
}
return JS_UNDEFINED;
return JS_NULL;
}
// blob.write_kim(fit)
@@ -239,7 +239,7 @@ static JSValue js_blob_write_text(JSContext *ctx, JSValueConst this_val,
}
JS_FreeCString(ctx,str);
return JS_UNDEFINED;
return JS_NULL;
}
// blob.write_pad(block_size)
@@ -258,7 +258,7 @@ static JSValue js_blob_write_pad(JSContext *ctx, JSValueConst this_val,
if (blob_write_pad(bd, block_size) < 0)
return JS_ThrowTypeError(ctx, "write_pad: cannot write (stone blob, OOM, or invalid block size)");
return JS_UNDEFINED;
return JS_NULL;
}
// blob.read_logical(from)
@@ -439,7 +439,7 @@ static JSValue js_blob_stone(JSContext *ctx, JSValueConst this_val,
if (!bd->is_stone) {
blob_make_stone(bd);
}
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_blob_stonep(JSContext *ctx, JSValueConst this_val,

View File

@@ -73,7 +73,7 @@ JSValue js_dmon_watch(JSContext *js, JSValue self, int argc, JSValue *argv)
return JS_ThrowReferenceError(js, "Already watching a directory.");
watched = dmon_watch(".", watch_cb, DMON_WATCHFLAGS_RECURSIVE, NULL);
return JS_UNDEFINED;
return JS_NULL;
}
JSValue js_dmon_unwatch(JSContext *js, JSValue self, int argc, JSValue *argv)
@@ -83,7 +83,7 @@ JSValue js_dmon_unwatch(JSContext *js, JSValue self, int argc, JSValue *argv)
dmon_unwatch(watched);
watched.id = 0;
return JS_UNDEFINED;
return JS_NULL;
}
JSValue js_dmon_poll(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
@@ -110,9 +110,9 @@ JSValue js_dmon_poll(JSContext *js, JSValueConst this_val, int argc, JSValueCons
JS_SetPropertyStr(js, jsevent, "root", JS_NewString(js, event.rootdir));
JS_SetPropertyStr(js, jsevent, "file", JS_NewString(js, event.filepath));
JS_SetPropertyStr(js, jsevent, "old", JS_NewString(js, event.oldfilepath));
JS_Call(js, argv[0], JS_UNDEFINED, 1, &jsevent);
JS_Call(js, argv[0], JS_NULL, 1, &jsevent);
}
return JS_UNDEFINED;
return JS_NULL;
}
static const JSCFunctionListEntry js_dmon_funcs[] = {

View File

@@ -34,13 +34,13 @@ static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val)
static JSValue js_enet_initialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
if (enet_initialize() != 0) return JS_ThrowInternalError(ctx, "Error initializing ENet");
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
enet_deinitialize();
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -163,12 +163,12 @@ static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val, int a
break;
}
uncaught_exception(ctx, JS_Call(ctx, callback, JS_UNDEFINED, 1, &event_obj));
uncaught_exception(ctx, JS_Call(ctx, callback, JS_NULL, 1, &event_obj));
JS_FreeValue(ctx, event_obj);
}
JS_FreeValue(ctx, callback);
return JS_UNDEFINED;
return JS_NULL;
}
/* Host connect: client -> connect to server */
@@ -201,7 +201,7 @@ static JSValue js_enet_host_flush(JSContext *ctx, JSValueConst this_val, int arg
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
if (!host) return JS_EXCEPTION;
enet_host_flush(host);
return JS_UNDEFINED;
return JS_NULL;
}
/* Broadcast a string or ArrayBuffer */
@@ -231,7 +231,7 @@ static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int
if (!packet) return JS_ThrowInternalError(ctx, "Failed to create ENet packet");
enet_host_broadcast(host, 0, packet);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_host_get_port(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
@@ -264,7 +264,7 @@ static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val, in
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
enet_peer_disconnect(peer, 0);
return JS_UNDEFINED;
return JS_NULL;
}
/* Peer send must only accept string or ArrayBuffer */
@@ -294,7 +294,7 @@ static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc
if (!packet) return JS_ThrowInternalError(ctx, "Failed to create ENet packet");
if (enet_peer_send(peer, 0, packet) < 0) return JS_ThrowInternalError(ctx, "Unable to send packet");
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -302,7 +302,7 @@ static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
enet_peer_disconnect_now(peer, 0);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -310,7 +310,7 @@ static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_v
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
enet_peer_disconnect_later(peer, 0);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -318,7 +318,7 @@ static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int arg
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
enet_peer_reset(peer);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -326,7 +326,7 @@ static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
if (!peer) return JS_EXCEPTION;
enet_peer_ping(peer);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_throttle_configure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -339,7 +339,7 @@ static JSValue js_enet_peer_throttle_configure(JSContext *ctx, JSValueConst this
return JS_ThrowTypeError(ctx, "Expected 3 int arguments: interval, acceleration, deceleration");
enet_peer_throttle_configure(peer, interval, acceleration, deceleration);
return JS_UNDEFINED;
return JS_NULL;
}
static JSValue js_enet_peer_timeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
@@ -352,7 +352,7 @@ static JSValue js_enet_peer_timeout(JSContext *ctx, JSValueConst this_val, int a
return JS_ThrowTypeError(ctx, "Expected 3 integer arguments: timeout_limit, timeout_min, timeout_max");
enet_peer_timeout(peer, timeout_limit, timeout_min, timeout_max);
return JS_UNDEFINED;
return JS_NULL;
}
/* Class definitions */
@@ -372,7 +372,7 @@ JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue
// TODO: implement
const char *hostname = JS_ToCString(js, argv[0]);
JS_FreeCString(js, hostname);
return JS_UNDEFINED;
return JS_NULL;
}
/* Function lists */
@@ -525,9 +525,6 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
JS_CGETSET_DEF("address", js_enet_peer_get_address, NULL),
};
/* Module entry point */
static int js_enet_init(JSContext *ctx, JSModuleDef *m);
JSValue js_enet_use(JSContext *ctx)
{
JS_NewClassID(&enet_host_id);
@@ -546,22 +543,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

@@ -147,7 +147,7 @@ JSC_CCALL(fd_fsync,
if (fsync(fd) != 0)
return JS_ThrowReferenceError(js, "fsync failed: %s", strerror(errno));
return JS_UNDEFINED;
return JS_NULL;
)
JSC_CCALL(fd_close,
@@ -157,7 +157,7 @@ JSC_CCALL(fd_close,
if (close(fd) != 0)
return JS_ThrowReferenceError(js, "close failed: %s", strerror(errno));
return JS_UNDEFINED;
return JS_NULL;
)
JSC_CCALL(fd_fstat,

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]);
@@ -446,10 +445,10 @@ static BufferCheckResult get_or_extend_buffer(
int copy,
int index
) {
BufferCheckResult res = { JS_UNDEFINED, NULL, 0, 0 };
if (!JS_IsUndefined(old_mesh)) {
BufferCheckResult res = { JS_NULL, NULL, 0, 0 };
if (!JS_IsNull(old_mesh)) {
JSValue old_buf = JS_GetPropertyStr(js, old_mesh, prop);
if (!JS_IsUndefined(old_buf)) {
if (!JS_IsNull(old_buf)) {
size_t old_size;
void *data = get_gpu_buffer(js, old_buf, NULL, &old_size);
if (data && old_size >= needed_size) {
@@ -521,7 +520,7 @@ JSC_CCALL(gpu_make_sprite_mesh,
}
// Check old mesh
JSValue old_mesh = JS_UNDEFINED;
JSValue old_mesh = JS_NULL;
if (argc > 1)
old_mesh = argv[1];
@@ -530,9 +529,9 @@ JSC_CCALL(gpu_make_sprite_mesh,
size_t uv_size = sizeof(*uvdata)*verts;
size_t color_size = sizeof(*colordata)*verts;
BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1, 0);
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, JS_TYPED_ARRAY_FLOAT32, 4, 1, 0);
BufferCheckResult pos_chk = get_or_extend_buffer(js, old_mesh, "pos", pos_size, 0, 2, 1, 0);
BufferCheckResult uv_chk = get_or_extend_buffer(js, old_mesh, "uv", uv_size, 0, 2, 1, 0);
BufferCheckResult color_chk = get_or_extend_buffer(js, old_mesh, "color", color_size, 0, 4, 1, 0);
int need_new_all = pos_chk.need_new || uv_chk.need_new || color_chk.need_new;
@@ -540,9 +539,9 @@ JSC_CCALL(gpu_make_sprite_mesh,
if (need_new_all) {
// Create all new buffers
JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
JSValue new_color = make_gpu_buffer(js, colordata, color_size, JS_TYPED_ARRAY_FLOAT32, 0, 1,0);
JSValue new_pos = make_gpu_buffer(js, posdata, pos_size, 0, 2, 1,0);
JSValue new_uv = make_gpu_buffer(js, uvdata, uv_size, 0, 2, 1,0);
JSValue new_color = make_gpu_buffer(js, colordata, color_size, 0, 0, 1,0);
JS_SetPropertyStr(js, ret, "pos", new_pos);
JS_SetPropertyStr(js, ret, "uv", new_uv);
@@ -578,9 +577,9 @@ JSC_CCALL(gpu_make_sprite_mesh,
free(colordata);
// Free old buffer values if they were fetched
if (!JS_IsUndefined(pos_chk.val)) JS_FreeValue(js, pos_chk.val);
if (!JS_IsUndefined(uv_chk.val)) JS_FreeValue(js, uv_chk.val);
if (!JS_IsUndefined(color_chk.val)) JS_FreeValue(js, color_chk.val);
if (!JS_IsNull(pos_chk.val)) JS_FreeValue(js, pos_chk.val);
if (!JS_IsNull(uv_chk.val)) JS_FreeValue(js, uv_chk.val);
if (!JS_IsNull(color_chk.val)) JS_FreeValue(js, color_chk.val);
return ret;
)
@@ -604,9 +603,9 @@ struct quad_buffers quad_buffers_new(int verts)
JSValue quadbuffers_to_mesh(JSContext *js, struct quad_buffers buffers)
{
JSValue jspos = make_gpu_buffer(js, buffers.pos, sizeof(HMM_Vec2)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 2, 0, 0);
JSValue jsuv = make_gpu_buffer(js, buffers.uv, sizeof(HMM_Vec2)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, buffers.color, sizeof(HMM_Vec4)*buffers.verts, JS_TYPED_ARRAY_FLOAT32, 4,0,0);
JSValue jspos = make_gpu_buffer(js, buffers.pos, sizeof(HMM_Vec2)*buffers.verts, 0, 2, 0, 0);
JSValue jsuv = make_gpu_buffer(js, buffers.uv, sizeof(HMM_Vec2)*buffers.verts, 0, 2,0,0);
JSValue jscolor = make_gpu_buffer(js, buffers.color, sizeof(HMM_Vec4)*buffers.verts, 0, 4,0,0);
size_t quads = buffers.verts/4;
size_t count = buffers.verts/2*3;
@@ -686,11 +685,11 @@ JSC_CCALL(gpu_make_sprite_queue,
rect uv;
rect uv_px;
JSValue cur_img = JS_UNDEFINED;
JSValue cur_img = JS_NULL;
for (size_t i = 0; i < quads; i++) {
sprite *s = &sprites[i];
if (JS_IsUndefined(cur_img) || !JS_StrictEq(js, s->image, cur_img)) {
if (JS_IsNull(cur_img) || !JS_StrictEq(js, s->image, cur_img)) {
cur_img = s->image;
JS_GETATOM(js, uv, cur_img, rect, rect)
JS_GETATOM(js, uv_px, cur_img, rect_px, rect)
@@ -733,7 +732,7 @@ JSC_CCALL(gpu_make_sprite_queue,
int first_index = 0;
int count = 0;
int n = 0;
JSValue img = JS_UNDEFINED;
JSValue img = JS_NULL;
for (int i = 0; i < quads; i++) {
if (!JS_SameValue(js, sprites[i].image, img)) {
@@ -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_IsNull(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_IsNull(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_IsNull(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

@@ -106,38 +106,38 @@ JSC_CCALL(imgui_window,
const char *str = JS_ToCString(js,argv[0]);
bool active = true;
ImGui::Begin(str, &active);
JS_Call(js, argv[1], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[1], JS_NULL, 0, NULL);
ImGui::End();
JS_FreeCString(js,str);
return JS_NewBool(js,active);
)
JSC_SCALL(imgui_menu,
if (ImGui::BeginMenu(str)) {
JS_Call(js, argv[1], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[1], JS_NULL, 0, NULL);
ImGui::EndMenu();
}
)
JSC_CCALL(imgui_menubar,
if (ImGui::BeginMenuBar()) {
JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[0], JS_NULL, 0, NULL);
ImGui::EndMenuBar();
}
)
JSC_CCALL(imgui_mainmenubar,
if (ImGui::BeginMainMenuBar()) {
JS_Call(js, argv[0], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[0], JS_NULL, 0, NULL);
ImGui::EndMainMenuBar();
}
)
JSC_CCALL(imgui_menuitem,
const char *name = JS_ToCString(js,argv[0]);
const char *keyfn = JS_IsUndefined(argv[1]) ? NULL : JS_ToCString(js,argv[1]);
bool on = JS_IsUndefined(argv[3]) ? false : JS_ToBool(js,argv[3]);
const char *keyfn = JS_IsNull(argv[1]) ? NULL : JS_ToCString(js,argv[1]);
bool on = JS_IsNull(argv[3]) ? false : JS_ToBool(js,argv[3]);
if (ImGui::MenuItem(JS_ToBool(js,argv[0]) ? name : "##empty" ,keyfn, &on))
JS_Call(js, argv[2], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[2], JS_NULL, 0, NULL);
if (!JS_IsNull(argv[0])) JS_FreeCString(js,name);
if (keyfn) JS_FreeCString(js,keyfn);
@@ -147,7 +147,7 @@ JSC_CCALL(imgui_menuitem,
JSC_SCALL(imgui_plot,
if (ImPlot::BeginPlot(str)) {
JS_Call(js, argv[1], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[1], JS_NULL, 0, NULL);
ImPlot::EndPlot();
}
)
@@ -173,7 +173,7 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last)
else {
// Fill it with the x axis being the array index
for (int i = 0; i < JS_ArrayLength(js, v); i++) {
if (JS_IsUndefined(js_getpropidx(v,i))) continue;
if (JS_IsNull(js_getpropidx(v,i))) continue;
ImVec2 c;
c.x = i;
c.y = js2number(js, js_getpropidx(v,i));
@@ -181,7 +181,7 @@ void fill_plotdata(JSContext *js, JSValue v, JSValue last)
}
}
if (!JS_IsUndefined(last)) {
if (!JS_IsNull(last)) {
int frame = js2number(js, last);
ImVec2 c = js2vec2(js,last);
c.y = arrlast(plotdata).y;
@@ -195,7 +195,7 @@ PLOT_FN(stairplot, PlotStairs,,ImPlotStairsFlags_Shaded)
PLOT_FN(digitalplot, PlotDigital,,0)
JSC_SCALL(imgui_barplot,
fill_plotdata(js, argv[1], JS_UNDEFINED);
fill_plotdata(js, argv[1], JS_NULL);
ImPlot::PlotBars(str, &plotdata[0].x, &plotdata[0].y, JS_ArrayLength(js, argv[1]), js2number(js, argv[2]), 0, 0, sizeof(ImVec2));
)
@@ -243,7 +243,7 @@ JSC_CCALL(imgui_fitaxis,
JSC_SSCALL(imgui_textinput,
char buffer[512];
if (JS_IsUndefined(argv[1]))
if (JS_IsNull(argv[1]))
buffer[0] = 0;
else
strncpy(buffer, str2, sizeof(buffer)-1);
@@ -257,7 +257,7 @@ JSC_SSCALL(imgui_textinput,
JSC_SSCALL(imgui_textbox,
char buffer[512];
if (JS_IsUndefined(argv[1]))
if (JS_IsNull(argv[1]))
buffer[0] = 0;
else
strncpy(buffer, str2, sizeof(buffer)-1);
@@ -273,12 +273,12 @@ JSC_SCALL(imgui_text, ImGui::Text("%s", str) )
JSC_SCALL(imgui_button,
if (ImGui::Button(str))
JS_Call(js, argv[1], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[1], JS_NULL, 0, NULL);
)
JSC_SCALL(imgui_slider,
/* float low = JS_IsUndefined(argv[2]) ? 0.0 : js2number(js, argv[2]);
float high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
/* float low = JS_IsNull(argv[2]) ? 0.0 : js2number(js, argv[2]);
float high = JS_IsNull(argv[3]) ? 1.0 : js2number(js, argv[3]);
if (JS_IsArray(js, argv[1])) {
int n = JS_ArrayLength(js, argv[1]);
@@ -306,8 +306,8 @@ JSC_SCALL(imgui_slider,
)
JSC_SCALL(imgui_intslider,
int low = JS_IsUndefined(argv[2]) ? 0.0 : js2number(js, argv[2]);
int high = JS_IsUndefined(argv[3]) ? 1.0 : js2number(js, argv[3]);
int low = JS_IsNull(argv[2]) ? 0.0 : js2number(js, argv[2]);
int high = JS_IsNull(argv[3]) ? 1.0 : js2number(js, argv[3]);
JSValue arr = JS_NewTypedArray(js, 1, &argv[1], JS_TYPED_ARRAY_INT32);
size_t len;
@@ -353,7 +353,7 @@ JSC_CCALL(imgui_image,
JSC_SCALL(imgui_imagebutton,
SDL_GPUTexture *tex = js2SDL_GPUTexture(js,argv[1]);
if (ImGui::ImageButton(str, (ImTextureID)tex, ImVec2(100, 100)))
JS_Call(js, argv[2], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[2], JS_NULL, 0, NULL);
)
JSC_CCALL(imgui_sameline, ImGui::SameLine(js2number(js, argv[0])) )
@@ -364,21 +364,21 @@ JSC_SCALL(imgui_radio, ret = JS_NewBool(js,ImGui::RadioButton(str, JS_ToBool(js,
JSC_SCALL(imgui_tree,
if (ImGui::TreeNode(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::TreePop();
}
)
JSC_SCALL(imgui_tabbar,
if (ImGui::BeginTabBar(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::EndTabBar();
}
)
JSC_SCALL(imgui_tab,
if (ImGui::BeginTabItem(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::EndTabItem();
}
)
@@ -407,7 +407,7 @@ JSC_SCALL(imgui_open_popup,
JSC_SCALL(imgui_popup,
if (ImGui::BeginPopup(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::EndPopup();
}
)
@@ -418,14 +418,14 @@ JSC_CCALL(imgui_close_popup,
JSC_SCALL(imgui_modal,
if (ImGui::BeginPopupModal(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::EndPopup();
}
)
JSC_SCALL(imgui_context,
if (ImGui::BeginPopupContextItem(str)) {
JS_Call(js, argv[1],JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1],JS_NULL, 0,NULL);
ImGui::EndPopup();
}
)
@@ -433,11 +433,11 @@ JSC_SCALL(imgui_context,
JSC_SCALL(imgui_table,
int flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingStretchProp;
bool sort = false;
if (!JS_IsUndefined(argv[3])) sort = true;
if (!JS_IsNull(argv[3])) sort = true;
if (sort) flags |= ImGuiTableFlags_Sortable;
if (ImGui::BeginTable(str, js2number(js, argv[1]), flags)) {
JS_Call(js, argv[2], JS_UNDEFINED, 0, NULL);
JS_Call(js, argv[2], JS_NULL, 0, NULL);
if (sort) {
ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
@@ -448,7 +448,7 @@ JSC_SCALL(imgui_table,
JSValue send[2];
send[0] = JS_NewInt32(js,spec->ColumnIndex);
send[1] = JS_NewBool(js,spec->SortDirection == ImGuiSortDirection_Ascending);
JS_Call(js, argv[3], JS_UNDEFINED, 2, send);
JS_Call(js, argv[3], JS_NULL, 2, send);
JS_FreeValue(js, send[0]);
JS_FreeValue(js, send[1]);
}
@@ -470,7 +470,7 @@ JSC_SCALL(imgui_dnd,
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
double n = js2number(js, argv[1]);
ImGui::SetDragDropPayload(str, &n, sizeof(n));
// JS_Call(js, argv[2], JS_UNDEFINED, 2, send);
// JS_Call(js, argv[2], JS_NULL, 2, send);
ImGui::EndDragDropSource();
}
)
@@ -479,7 +479,7 @@ JSC_SCALL(imgui_dndtarget,
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload(str)) {
JSValue n = JS_NewFloat64(js,*(double*)payload->Data);
JS_Call(js, argv[1], JS_UNDEFINED, 1, &n);
JS_Call(js, argv[1], JS_NULL, 1, &n);
}
ImGui::EndDragDropTarget();
}
@@ -500,7 +500,7 @@ JSC_SCALL(imgui_color,
JSC_CCALL(imgui_startnode,
ImNodes::BeginNodeEditor();
JS_Call(js, argv[0], JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[0], JS_NULL, 0,NULL);
ImNodes::EndNodeEditor();
int start_attr;
int end_attr;
@@ -509,7 +509,7 @@ JSC_CCALL(imgui_startnode,
JSValue ab[2];
ab[0] = JS_NewInt32(js,start_attr);
ab[1] = JS_NewInt32(js,end_attr);
JS_Call(js,argv[1], JS_UNDEFINED, 2, ab);
JS_Call(js,argv[1], JS_NULL, 2, ab);
for (int i = 0; i < 2; i++) JS_FreeValue(js, ab[i]);
}
@@ -518,7 +518,7 @@ JSC_CCALL(imgui_startnode,
if (ImNodes::IsNodeHovered(&node_id))
{
JSValue a = JS_NewInt32(js,node_id);
JS_Call(js, argv[2], JS_UNDEFINED, 1,&a);
JS_Call(js, argv[2], JS_NULL, 1,&a);
JS_FreeValue(js,a);
}
@@ -526,26 +526,26 @@ JSC_CCALL(imgui_startnode,
if (ImNodes::IsLinkHovered(&link_id))
{
JSValue a = JS_NewInt32(js,link_id);
JS_Call(js, argv[3], JS_UNDEFINED, 1,&a);
JS_Call(js, argv[3], JS_NULL, 1,&a);
JS_FreeValue(js,a);
}
)
JSC_CCALL(imgui_node,
ImNodes::BeginNode(js2number(js, argv[0]));
JS_Call(js, argv[1], JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1], JS_NULL, 0,NULL);
ImNodes::EndNode();
)
JSC_CCALL(imgui_nodein,
ImNodes::BeginInputAttribute(js2number(js, argv[0]));
JS_Call(js, argv[1], JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1], JS_NULL, 0,NULL);
ImNodes::EndInputAttribute();
)
JSC_CCALL(imgui_nodeout,
ImNodes::BeginOutputAttribute(js2number(js, argv[0]));
JS_Call(js, argv[1], JS_UNDEFINED, 0,NULL);
JS_Call(js, argv[1], JS_NULL, 0,NULL);
ImNodes::EndOutputAttribute();
)
@@ -681,7 +681,7 @@ static JSValue axis_fmts[10];
void jsformatter(double value, char *buff, int size, JSValue *fmt)
{
/* JSValue v = JS_NewFloat64(js,value);
const char *str = JS_ToCString(js, JS_Call(js,*fmt, JS_UNDEFINED, 1, &v));
const char *str = JS_ToCString(js, JS_Call(js,*fmt, JS_NULL, 1, &v));
strncpy(buff,str, size);
JS_FreeCString(js, str);*/
}
@@ -689,9 +689,9 @@ void jsformatter(double value, char *buff, int size, JSValue *fmt)
JSC_CCALL(imgui_axisfmt,
int y = num_to_yaxis(js2number(js, argv[0]));
if (!JS_IsUndefined(axis_fmts[y])) {
if (!JS_IsNull(axis_fmts[y])) {
JS_FreeValue(js, axis_fmts[y]);
axis_fmts[y] = JS_UNDEFINED;
axis_fmts[y] = JS_NULL;
}
axis_fmts[y] = JS_DupValue(js,argv[1]);
@@ -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

@@ -159,14 +159,14 @@ JSC_SCALL(io_slurpwrite,
JSC_CCALL(io_mount,
const char *src = JS_ToCString(js, argv[0]);
const char *mountpoint;
if (JS_IsUndefined(argv[1]))
if (JS_IsNull(argv[1]))
mountpoint = NULL;
else
mountpoint = JS_ToCString(js, argv[1]);
int prepend = 0;
if (argc > 2 && !JS_IsUndefined(argv[2]))
if (argc > 2 && !JS_IsNull(argv[2]))
prepend = JS_ToBool(js, argv[2]);
if (!PHYSFS_mount(src, mountpoint, prepend))
@@ -274,7 +274,7 @@ JSC_CCALL(io_globfs,
data.globs = globs;
const char *path = NULL;
if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]);
if (!JS_IsNull(argv[1])) path = JS_ToCString(js,argv[1]);
PHYSFS_enumerate(path, globfs_cb, &data);
for (int i = 0; i < globs_len; i++)
@@ -297,7 +297,7 @@ JSC_SCALL(io_enumerate,
data.globs = NULL; /* not used here */
data.recurse = 0;
if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */
if (!JS_IsNull(argv[1])) /* parse second arg if provided */
data.recurse = JS_ToBool(js, argv[1]);
/* Enumerate the directory given by 'str'. */
@@ -322,7 +322,7 @@ JSC_SCALL(io_open,
JSC_SCALL(io_realdir,
const char *real = PHYSFS_getRealDir(str);
if (!real)
ret = JS_UNDEFINED;
ret = JS_NULL;
else
ret = JS_NewString(js,real);
)

View File

@@ -39,11 +39,11 @@ JSC_CCALL(os_calc_mem,
)
JSC_SSCALL(os_eval,
ret = JS_Eval(js,str2,strlen(str2),str, JS_EVAL_FLAG_STRICT);
ret = JS_Eval(js,str2,strlen(str2),str, 0);
)
JSC_SSCALL(js_compile,
ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_COMPILE_ONLY);
ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_COMPILE_ONLY);
)
JSC_CCALL(js_eval_compile,

View File

@@ -5,13 +5,13 @@
#define JS_SETSIG JSContext *js, JSValue self, JSValue val
#define JSC_CCALL(NAME, ...) static JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \
JSValue ret = JS_UNDEFINED; \
JSValue ret = JS_NULL; \
__VA_ARGS__ ;\
return ret; \
}
#define JSC_CCALL_EXTERN(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \
JSValue ret = JS_UNDEFINED; \
JSValue ret = JS_NULL; \
__VA_ARGS__ ;\
return ret; \
}
@@ -25,8 +25,8 @@
#define JSC_SSCALL(NAME, ...) JSC_CCALL(NAME, \
const char *str = NULL; \
const char *str2 = NULL; \
if (!JS_IsUndefined(argv[0])) str = JS_ToCString(js,argv[0]); \
if (!JS_IsUndefined(argv[1])) str2 = JS_ToCString(js,argv[1]); \
if (!JS_IsNull(argv[0])) str = JS_ToCString(js,argv[0]); \
if (!JS_IsNull(argv[1])) str2 = JS_ToCString(js,argv[1]); \
__VA_ARGS__ ; \
JS_FreeCString(js,str2); \
JS_FreeCString(js,str); \
@@ -41,21 +41,21 @@
#define CGETSET_ADD(ID, ENTRY) MIST_CGETSET_DEF(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY)
#define CGETSET_ADD_HID(ID, ENTRY) MIST_CGETSET_BASE(#ENTRY, js_##ID##_get_##ENTRY, js_##ID##_set_##ENTRY, JS_PROP_CONFIGURABLE)
#define JSC_DCALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(); return JS_UNDEFINED; }
#define JSC_DCALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(); return JS_NULL; }
#define JSC_1CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0])); return JS_UNDEFINED; }
#define JSC_2CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1])); return JS_UNDEFINED; }
#define JSC_3CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])); return JS_UNDEFINED; }
#define JSC_4CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])); return JS_UNDEFINED; }
#define JSC_5CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])); return JS_UNDEFINED; }
#define JSC_6CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]), js2number(js,argv[5])); return JS_UNDEFINED; }
#define JSC_7CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]), js2number(js,argv[5]), js2number(js,argv[6])); return JS_UNDEFINED; }
#define JSC_1CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0])); return JS_NULL; }
#define JSC_2CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1])); return JS_NULL; }
#define JSC_3CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])); return JS_NULL; }
#define JSC_4CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3])); return JS_NULL; }
#define JSC_5CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])); return JS_NULL; }
#define JSC_6CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]), js2number(js,argv[5])); return JS_NULL; }
#define JSC_7CALL(FN) JSValue js_##FN (JSContext *js, JSValue self, int argc, JSValue *argv) { FN(js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]), js2number(js,argv[5]), js2number(js,argv[6])); return JS_NULL; }
#define GETSETPAIR(ID, ENTRY, TYPE, FN) \
JSValue js_##ID##_set_##ENTRY (JS_SETSIG) { \
js2##ID (js, self)->ENTRY = js2##TYPE (js,val); \
{FN;} \
return JS_UNDEFINED; \
return JS_NULL; \
} \
\
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
@@ -67,9 +67,9 @@ JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
#define JSC_GETSET_CALLBACK(ID, ENTRY) \
JSValue js_##ID##_set_##ENTRY (JS_SETSIG) { \
JSValue fn = js2##ID (js, self)->ENTRY; \
if (!JS_IsUndefined(fn)) JS_FreeValue(js, fn); \
if (!JS_IsNull(fn)) JS_FreeValue(js, fn); \
js2##ID (js, self)->ENTRY = JS_DupValue(js, val); \
return JS_UNDEFINED; \
return JS_NULL; \
}\
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { return JS_DupValue(js, js2##ID (js, self)->ENTRY); } \

View File

@@ -68,7 +68,7 @@ static int gcd(int a, int b) {
JSC_CCALL(math_rotate,
HMM_Vec2 vec = js2vec2(js,argv[0]);
double angle = js2angle(js, argv[1]);
HMM_Vec2 pivot = JS_IsUndefined(argv[2]) ? v2zero : js2vec2(js,argv[2]);
HMM_Vec2 pivot = JS_IsNull(argv[2]) ? v2zero : js2vec2(js,argv[2]);
vec = HMM_SubV2(vec,pivot);
float r = HMM_LenV2(vec);
@@ -254,7 +254,7 @@ JSC_CCALL(math_project,
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
// We'll work up to the smaller length
@@ -262,7 +262,7 @@ JSC_CCALL(math_project,
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
// Compute dot products: a·b and b·b
@@ -299,14 +299,14 @@ JSC_CCALL(math_midpoint,
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
float *m = (float*)malloc(sizeof(float) * len);
@@ -334,14 +334,14 @@ JSC_CCALL(math_reflect,
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
// Reflect vector a across normal b
@@ -378,14 +378,14 @@ JSC_CCALL(math_direction,
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
// Direction vector from a to b (normalized)
@@ -421,7 +421,7 @@ JSC_CCALL(math_angle,
if (!v || len < 2) {
free(v);
return JS_UNDEFINED;
return JS_NULL;
}
// Return angle in radians for 2D vector
@@ -438,14 +438,14 @@ JSC_CCALL(math_distance,
if (!a || !b) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
size_t len = (alen < blen) ? alen : blen;
if (len == 0) {
free(a);
free(b);
return JS_UNDEFINED;
return JS_NULL;
}
float distSq = 0.0f;

Some files were not shown because too many files have changed in this diff Show More