Compare commits
748 Commits
working
...
serialize_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e407fe301 | ||
|
|
80d314c58f | ||
|
|
611fba2b6f | ||
|
|
f5fad52d47 | ||
|
|
2fc7d333ad | ||
|
|
19576533d9 | ||
|
|
c08249b6f1 | ||
|
|
dc348d023f | ||
|
|
fd5e4d155e | ||
|
|
e734353722 | ||
|
|
94f1645be1 | ||
|
|
41e3a6d91a | ||
|
|
04c569eab1 | ||
|
|
dc3c474b3a | ||
|
|
f1117bbd41 | ||
|
|
43faad95e0 | ||
|
|
acc9878b36 | ||
|
|
03c45ee8b0 | ||
|
|
a171a0d2af | ||
|
|
bb8d3930b3 | ||
|
|
522ae6128a | ||
|
|
16c26e4bf2 | ||
|
|
a9804785e0 | ||
|
|
11ae703693 | ||
|
|
f203278c3e | ||
|
|
a80557283a | ||
|
|
3e40885e07 | ||
|
|
e4b7de46f6 | ||
|
|
e680439a9b | ||
|
|
ae11504e00 | ||
|
|
893deaec23 | ||
|
|
69b032d3dc | ||
|
|
256a00c501 | ||
|
|
8e166b8f98 | ||
|
|
ddbdd00496 | ||
|
|
bdf0461e1f | ||
|
|
0b86af1d4c | ||
|
|
beac9608ea | ||
|
|
a04bebd0d7 | ||
|
|
ce74f726dd | ||
|
|
4d1ab60852 | ||
|
|
22ab6c8098 | ||
|
|
9a9775690f | ||
|
|
be71ae3bba | ||
|
|
f2a76cbb55 | ||
|
|
2d834c37b3 | ||
|
|
ba1b92aa78 | ||
|
|
c356fe462d | ||
|
|
b23b918f97 | ||
|
|
e720152bcd | ||
|
|
f093e6f5a3 | ||
|
|
4fc904de63 | ||
|
|
e53f55fb23 | ||
|
|
6150406905 | ||
|
|
a189440769 | ||
|
|
6c3c492446 | ||
|
|
bb83327a52 | ||
|
|
c74bee89a7 | ||
|
|
271a3d6724 | ||
|
|
c5ccc66e51 | ||
|
|
3a0ea31896 | ||
|
|
67e82fd12c | ||
|
|
3c59087c0c | ||
|
|
b79e07f57b | ||
|
|
fdcb374403 | ||
|
|
03feb370fd | ||
|
|
6712755940 | ||
|
|
b3f3bc8a5f | ||
|
|
a49b94e0a1 | ||
|
|
24ecff3f1c | ||
|
|
3ccaf68a5b | ||
|
|
64933260d4 | ||
|
|
561ab9d917 | ||
|
|
bcd6e641a5 | ||
|
|
2857581271 | ||
|
|
378ad6dc98 | ||
|
|
06f7791159 | ||
|
|
086508bacd | ||
|
|
8325253f1a | ||
|
|
802c94085b | ||
|
|
f9170b33e5 | ||
|
|
20f10ab887 | ||
|
|
d8b13548d2 | ||
|
|
0d93741c31 | ||
|
|
c6440ff98c | ||
|
|
a01b48dabc | ||
|
|
beea76949c | ||
|
|
36833db2c9 | ||
|
|
b7615bb801 | ||
|
|
e6838338fc | ||
|
|
4cf0ce00de | ||
|
|
0714017547 | ||
|
|
b60e79ccad | ||
|
|
420c2b859a | ||
|
|
6c1f53ec5f | ||
|
|
45d82438ca | ||
|
|
addb38da65 | ||
|
|
aa847ddf6e | ||
|
|
4da63db16e | ||
|
|
bfdd920178 | ||
|
|
26fce3a5a8 | ||
|
|
ef49606098 | ||
|
|
854d94e5c3 | ||
|
|
dc02d6899d | ||
|
|
b28ef39562 | ||
|
|
2841e91f40 | ||
|
|
8d601dfce3 | ||
|
|
2b60e3a242 | ||
|
|
823183c510 | ||
|
|
c051a99e75 | ||
|
|
b3c0837d49 | ||
|
|
ff18682485 | ||
|
|
9b3891c126 | ||
|
|
38a3697e28 | ||
|
|
cbf99295da | ||
|
|
5271688dd4 | ||
|
|
98cb2c3239 | ||
|
|
e695810e64 | ||
|
|
bbd2d298ba | ||
|
|
a7a323a74e | ||
|
|
97ece8e5cb | ||
|
|
45ee4a337c | ||
|
|
ce7d83ec91 | ||
|
|
b46406f755 | ||
|
|
ac91495679 | ||
|
|
5018901acb | ||
|
|
78a46b4a72 | ||
|
|
66a9ca27e2 | ||
|
|
ac4b47f075 | ||
|
|
6b9f75247e | ||
|
|
b039b0c4ba | ||
|
|
ffe7b61ae2 | ||
|
|
17b9aaaf51 | ||
|
|
5fd29366a6 | ||
|
|
f16586eaa2 | ||
|
|
86a70bce3a | ||
|
|
e04b15973a | ||
|
|
d044bde4f9 | ||
|
|
8403883b9d | ||
|
|
69245f82db | ||
|
|
8203f6d1c3 | ||
|
|
ef94b55058 | ||
|
|
3a3e77eccd | ||
|
|
438c90acb5 | ||
|
|
dd309b1a37 | ||
|
|
63cf76dcf9 | ||
|
|
df07069c38 | ||
|
|
eba0727247 | ||
|
|
b0f0a5f63f | ||
|
|
d12d77c22c | ||
|
|
d6468e7fd2 | ||
|
|
7ae5a0c06b | ||
|
|
0664c11af6 | ||
|
|
058ad89c96 | ||
|
|
a0038a7ab2 | ||
|
|
d0674e7921 | ||
|
|
b586df63ad | ||
|
|
05b57550da | ||
|
|
fa616ee444 | ||
|
|
0720368c48 | ||
|
|
4f3e2819fe | ||
|
|
3b53a9dcc3 | ||
|
|
6d7581eff8 | ||
|
|
dca1963b5d | ||
|
|
1833a3c74c | ||
|
|
4201be47ee | ||
|
|
c388fb311b | ||
|
|
c432bc211f | ||
|
|
5f471ee003 | ||
|
|
5d384e2706 | ||
|
|
a0daf98ca8 | ||
|
|
3b42426e6f | ||
|
|
a035e28100 | ||
|
|
4f076bc868 | ||
|
|
c8831c85d0 | ||
|
|
3cf7aa473e | ||
|
|
8b9e088385 | ||
|
|
d50f4119ee | ||
|
|
aa18a8c8d2 | ||
|
|
04a648a73e | ||
|
|
363ca1c3c1 | ||
|
|
3211b59408 | ||
|
|
169448f156 | ||
|
|
b98cef6e8d | ||
|
|
33dc6b053c | ||
|
|
6ff54d8347 | ||
|
|
bf421743a5 | ||
|
|
5a3e260821 | ||
|
|
d0f5dc6951 | ||
|
|
752479e250 | ||
|
|
37f2cff6ec | ||
|
|
657e342159 | ||
|
|
de4c9c724e | ||
|
|
5c5427fdd9 | ||
|
|
38d6b4d4e8 | ||
|
|
aae267a6e9 | ||
|
|
dbc1b2c31e | ||
|
|
32301cf61f | ||
|
|
270521a01e | ||
|
|
401f69b503 | ||
|
|
1bcdab64ff | ||
|
|
107c4a5dce | ||
|
|
16aba47782 | ||
|
|
14cf48931d | ||
|
|
c24a5079cb | ||
|
|
ce5949e0ee | ||
|
|
3f2b4177d6 | ||
|
|
687c6d264f | ||
|
|
abf1332bf5 | ||
|
|
7c0f4dcd5f | ||
|
|
33ebd8f45d | ||
|
|
5ac58dfbb0 | ||
|
|
825c6aa284 | ||
|
|
3bcc642158 | ||
|
|
ea510d609f | ||
|
|
744f64b83b | ||
|
|
7a6209df72 | ||
|
|
dde0efc8aa | ||
|
|
d6daa97ac7 | ||
|
|
230880a02f | ||
|
|
d229784d8d | ||
|
|
19e747c120 | ||
|
|
22d3adca93 | ||
|
|
20fa012b57 | ||
|
|
95ac4bd096 | ||
|
|
6b8464aca4 | ||
|
|
39ac340e01 | ||
|
|
fc87c05daf | ||
|
|
1886f10211 | ||
|
|
c8ce152ade | ||
|
|
1a9734f265 | ||
|
|
ac718fdb13 | ||
|
|
1769d7f456 | ||
|
|
85e0e3dab1 | ||
|
|
ddfa636ac0 | ||
|
|
ee3a890d4a | ||
|
|
76bdccc4ee | ||
|
|
814ee7c0db | ||
|
|
0c5609c4e9 | ||
|
|
20f5e4e81c | ||
|
|
139405a30b | ||
|
|
495f145524 | ||
|
|
6cfba975e5 | ||
|
|
fd4222f196 | ||
|
|
5971924785 | ||
|
|
8e4c60baf3 | ||
|
|
53085d7e3a | ||
|
|
c88d551cad | ||
|
|
4b5ceb4c02 | ||
|
|
2b90a160ba | ||
|
|
4df134b327 | ||
|
|
b44d79ccab | ||
|
|
b29cdc8b93 | ||
|
|
42bede58bc | ||
|
|
3800c23eae | ||
|
|
8fa80b4720 | ||
|
|
acf842e2a1 | ||
|
|
1d295d11e5 | ||
|
|
0ddbc3e953 | ||
|
|
9f696a0342 | ||
|
|
efe93b7206 | ||
|
|
a9cff079d9 | ||
|
|
d361cb0555 | ||
|
|
b577e889a1 | ||
|
|
e28e241485 | ||
|
|
2110688fa5 | ||
|
|
fd19ecb41e | ||
|
|
155d0bf4b5 | ||
|
|
311a57b1e2 | ||
|
|
2cf94f7c57 | ||
|
|
9b19d19698 | ||
|
|
67badc3e48 | ||
|
|
820413a72a | ||
|
|
8bc31e3ac6 | ||
|
|
b613c7b6fa | ||
|
|
8bdcaf7d9d | ||
|
|
2beb369af9 | ||
|
|
44c46f2bb4 | ||
|
|
20685d80f1 | ||
|
|
532cfd0ed0 | ||
|
|
34de9e6dc4 | ||
|
|
9881158e62 | ||
|
|
be416b0124 | ||
|
|
906b60276a | ||
|
|
d61f98f81d | ||
|
|
dbf9f8ef30 | ||
|
|
0082eab729 | ||
|
|
60d2d3b8d6 | ||
|
|
0dd4ad8046 | ||
|
|
c02b42f710 | ||
|
|
6961e19114 | ||
|
|
721ac3bb93 | ||
|
|
768ad399de | ||
|
|
a05dda4914 | ||
|
|
149ee23e45 | ||
|
|
c9b504ead4 | ||
|
|
a8eed4e0d8 | ||
|
|
708c01ee29 | ||
|
|
8a33be3550 | ||
|
|
175146ab37 | ||
|
|
ef7b492984 | ||
|
|
923b5bc3d6 | ||
|
|
5b2a88d520 | ||
|
|
b5496d93bc | ||
|
|
faf19db27e | ||
|
|
07595aad63 | ||
|
|
8033c161d0 | ||
|
|
625503e654 | ||
|
|
6a9a1a90dd | ||
|
|
ce5b4950d4 | ||
|
|
cba6f4fd59 | ||
|
|
e5c19e7e80 | ||
|
|
09c3d5cc4e | ||
|
|
5ae95aee01 | ||
|
|
d5789598a0 | ||
|
|
8ffb4ec73a | ||
|
|
fecc4b1285 | ||
|
|
f82d924577 | ||
|
|
f389609bd9 | ||
|
|
ff7b2f4db7 | ||
|
|
8fb50a129f | ||
|
|
b3c4b1fee9 | ||
|
|
bb3c2c34d0 | ||
|
|
7fb0d9e80a | ||
|
|
f49d4180ed | ||
|
|
7bfd244bf2 | ||
|
|
881407a64f | ||
|
|
e677832b12 | ||
|
|
13c1e7560a | ||
|
|
2e275adcd2 | ||
|
|
2607604a0b | ||
|
|
d1d9a296a8 | ||
|
|
7edbb85e4e | ||
|
|
874252db87 | ||
|
|
13dd685f65 | ||
|
|
4b817b8d1b | ||
|
|
09b78781e6 | ||
|
|
19ce1008b1 | ||
|
|
d1c7ff768d | ||
|
|
0d97b47728 | ||
|
|
c87f85cf6c | ||
|
|
f0afdfc7d9 | ||
|
|
ac9f40fd26 | ||
|
|
39152c1eb2 | ||
|
|
6ff50cb521 | ||
|
|
2b7b3985d5 | ||
|
|
a9b59750e3 | ||
|
|
525263a8a6 | ||
|
|
310a0db99e | ||
|
|
6e66bf59f6 | ||
|
|
71c0056df4 | ||
|
|
d4f0059419 | ||
|
|
d52d50fe61 | ||
|
|
1bc34bb99c | ||
|
|
8ac78e0be6 | ||
|
|
dca3ede464 | ||
|
|
7b622d9788 | ||
|
|
42087910ab | ||
|
|
c581935fd8 | ||
|
|
632b038561 | ||
|
|
6eb33b8e48 | ||
|
|
3d5f345236 | ||
|
|
4689ea1167 | ||
|
|
15d85096a2 | ||
|
|
f2c2ecf692 | ||
|
|
458215f838 | ||
|
|
7f002e306d | ||
|
|
91a3fef065 | ||
|
|
843b4bd8a8 | ||
|
|
41fdf49df5 | ||
|
|
c9adbed3ff | ||
|
|
3459d85a82 | ||
|
|
9ecdaae7a7 | ||
|
|
43b55b29f3 | ||
|
|
a88cee7fae | ||
|
|
8b3f5476a9 | ||
|
|
233f59e04a | ||
|
|
7b16259c00 | ||
|
|
43baa23dfe | ||
|
|
a9ebea9f26 | ||
|
|
19b729afbf | ||
|
|
47729c225f | ||
|
|
ea6cf5db49 | ||
|
|
794baf8598 | ||
|
|
a551368681 | ||
|
|
c20ca8c937 | ||
|
|
0217cf0da6 | ||
|
|
6bd7251933 | ||
|
|
6dc8d97001 | ||
|
|
9c0565d34f | ||
|
|
0702e3495d | ||
|
|
108c39d22d | ||
|
|
946ebe5cd7 | ||
|
|
fa12281ab9 | ||
|
|
38a52fcb73 | ||
|
|
95a95e55e3 | ||
|
|
fc978a5766 | ||
|
|
9082ee2c47 | ||
|
|
b8ad8431f4 | ||
|
|
1c2b8228fe | ||
|
|
a274fb174f | ||
|
|
3622a5ec58 | ||
|
|
8a5f8a4d74 | ||
|
|
c1d341eecd | ||
|
|
3176e6775d | ||
|
|
34dcd0a235 | ||
|
|
cbda7dfbc9 | ||
|
|
d039e2cfe6 | ||
|
|
c02bd06ec0 | ||
|
|
efa63771e6 | ||
|
|
9f6d27fb3c | ||
|
|
1a61ae6f77 | ||
|
|
83c816fd0e | ||
|
|
adbaa92dd5 | ||
|
|
580df9f233 | ||
|
|
d5d17560f9 | ||
|
|
cd05ab97b5 | ||
|
|
4eecbd692b | ||
|
|
72beed7177 | ||
|
|
e0595de71a | ||
|
|
6687008d1a | ||
|
|
5b9f1b8f51 | ||
|
|
c570de7f41 | ||
|
|
d0138a6c23 | ||
|
|
29aa25e866 | ||
|
|
ef28be93db | ||
|
|
0d7be6a94e | ||
|
|
4fe78c4a63 | ||
|
|
b52edb2746 | ||
|
|
79d5412fe6 | ||
|
|
fcec2cd1dc | ||
|
|
2038ce15a7 | ||
|
|
08557011cb | ||
|
|
3e87bfd6cc | ||
|
|
ef86dd3ecf | ||
|
|
c887bcf7b9 | ||
|
|
709f2459e4 | ||
|
|
cdf8686c64 | ||
|
|
2fdf74f6ee | ||
|
|
e689679aac | ||
|
|
f70f65d1c3 | ||
|
|
d9b316270d | ||
|
|
e2668b330e | ||
|
|
90b5d1430f | ||
|
|
7c47c43655 | ||
|
|
9e45219706 | ||
|
|
d098800c88 | ||
|
|
3a40076958 | ||
|
|
06108df3d4 | ||
|
|
a442cf5a4d | ||
|
|
6dee29d213 | ||
|
|
7711c644a0 | ||
|
|
aab0a56349 | ||
|
|
13245bbc98 | ||
|
|
c25166d35a | ||
|
|
fc09693c93 | ||
|
|
b71c72db8b | ||
|
|
66591e32b5 | ||
|
|
fba05fa0fb | ||
|
|
11357d4fb5 | ||
|
|
674eb237e0 | ||
|
|
939269b060 | ||
|
|
f54200a7dd | ||
|
|
9ae2357493 | ||
|
|
da525cd111 | ||
|
|
c3f07c0ef5 | ||
|
|
2e7643aa2a | ||
|
|
aca9baf585 | ||
|
|
b4371ba3e0 | ||
|
|
4e118dd8e9 | ||
|
|
9279e21b84 | ||
|
|
8d9bb4a2c9 | ||
|
|
1040c61863 | ||
|
|
e86bdf52fe | ||
|
|
53b3f0af9c | ||
|
|
09f48d08b9 | ||
|
|
4eb592b740 | ||
|
|
c603e8f006 | ||
|
|
f334a2ad56 | ||
|
|
a39f287a88 | ||
|
|
758b3e4704 | ||
|
|
aa70dcbdc2 | ||
|
|
3667d53eae | ||
|
|
01df337ccc | ||
|
|
ad182d68ec | ||
|
|
f7dcc8f57c | ||
|
|
f73f738459 | ||
|
|
bf74a3c7d4 | ||
|
|
e8fb50659d | ||
|
|
00df0899fa | ||
|
|
ae5ba67fc8 | ||
|
|
bc929988b2 | ||
|
|
2346040d46 | ||
|
|
2eb6b3e0b4 | ||
|
|
2edcd89780 | ||
|
|
a63e5c5b55 | ||
|
|
af21e10e97 | ||
|
|
1b97527120 | ||
|
|
8074e2a82e | ||
|
|
db1afb6477 | ||
|
|
45311408d6 | ||
|
|
1141fca63a | ||
|
|
7b70def11f | ||
|
|
aac0c3813b | ||
|
|
49786842f0 | ||
|
|
9f9dfe03a6 | ||
|
|
792da2ce4b | ||
|
|
0c9d78a3d3 | ||
|
|
e929f43a96 | ||
|
|
01eff40690 | ||
|
|
23813a4c31 | ||
|
|
1248b94244 | ||
|
|
f754d91e14 | ||
|
|
7246016b8b | ||
|
|
b42eec96f6 | ||
|
|
efd98460c5 | ||
|
|
698dbd81ae | ||
|
|
13c2a0ba0c | ||
|
|
d0fdb469dd | ||
|
|
32366483dc | ||
|
|
707b2845b1 | ||
|
|
693087afae | ||
|
|
51940080a8 | ||
|
|
a204fce4b5 | ||
|
|
7bab2f1b7a | ||
|
|
f5ee3aada6 | ||
|
|
449e25e0f3 | ||
|
|
3cbb95831c | ||
|
|
146baf1d23 | ||
|
|
e7cc716590 | ||
|
|
3aa2d549d1 | ||
|
|
901012064a | ||
|
|
bf2336a172 | ||
|
|
708a112449 | ||
|
|
85ee724754 | ||
|
|
ff2ee3d6db | ||
|
|
6bc04830d3 | ||
|
|
589bb365bd | ||
|
|
0b8a43eb91 | ||
|
|
bb3087dc37 | ||
|
|
92f56570d9 | ||
|
|
938da0d4dc | ||
|
|
3d94859151 | ||
|
|
a85b1873dd | ||
|
|
446ad080e1 | ||
|
|
ead61e648a | ||
|
|
600fbfd3b7 | ||
|
|
f3031d6cd0 | ||
|
|
7152ae093e | ||
|
|
2bd93ff9e0 | ||
|
|
f68e45f898 | ||
|
|
7eca07c7d1 | ||
|
|
ee4ec2fc39 | ||
|
|
b93a5a3ac0 | ||
|
|
de63e0e52d | ||
|
|
daef2fd2f2 | ||
|
|
6705ce8980 | ||
|
|
f443816355 | ||
|
|
c8c08d5fbe | ||
|
|
b8328657df | ||
|
|
8d235ddf12 | ||
|
|
05f284e3fa | ||
|
|
566baa250c | ||
|
|
19a8bd41a9 | ||
|
|
58cad839b6 | ||
|
|
34035ae6ac | ||
|
|
3a4547fb80 | ||
|
|
86b21bb6dd | ||
|
|
8cf114cbb4 | ||
|
|
f9100da8a2 | ||
|
|
f9c1a3e71a | ||
|
|
73594c8599 | ||
|
|
239f35389e | ||
|
|
95d3296dd9 | ||
|
|
c566f90d16 | ||
|
|
9410af3a69 | ||
|
|
8627fc52ef | ||
|
|
813cc8dbbc | ||
|
|
d90d81d7ff | ||
|
|
b1f62cc58c | ||
|
|
38da997069 | ||
|
|
88d5f6455b | ||
|
|
eb3a41be69 | ||
|
|
1332af93ab | ||
|
|
93adf50498 | ||
|
|
291fd9ead0 | ||
|
|
e86138ec00 | ||
|
|
c431f117e9 | ||
|
|
847a3ef314 | ||
|
|
bab09fed6d | ||
|
|
d56c983e01 | ||
|
|
69df7302d5 | ||
|
|
01f7e715a4 | ||
|
|
e71a823848 | ||
|
|
a8865594ca | ||
|
|
23d764c534 | ||
|
|
c7aee73dcb | ||
|
|
925d1fc437 | ||
|
|
a6dbedb3cd | ||
|
|
74a6b9bfe6 | ||
|
|
40126060fb | ||
|
|
6032c034bc | ||
|
|
6b4062eee6 | ||
|
|
0ea21e86eb | ||
|
|
30c5da879b | ||
|
|
1a76081fec | ||
|
|
045c4b49ef | ||
|
|
af0996f6ab | ||
|
|
6c390aeae3 | ||
|
|
e9519484cc | ||
|
|
b7da920f31 | ||
|
|
35647a5c5b | ||
|
|
8ea8f7fec7 | ||
|
|
5254b84704 | ||
|
|
f728a217c9 | ||
|
|
c27817b73a | ||
|
|
7ea79c8ced | ||
|
|
fb10c63882 | ||
|
|
96ef8ccba3 | ||
|
|
6148f18340 | ||
|
|
867a18e788 | ||
|
|
387c4364b5 | ||
|
|
60dce4a08f | ||
|
|
d2325c20bd | ||
|
|
29295607df | ||
|
|
dcd767e5f9 | ||
|
|
bff39b0e9f | ||
|
|
1ae73aed06 | ||
|
|
75c7e304de | ||
|
|
8169e1df34 | ||
|
|
f10b14afb3 | ||
|
|
7bafa906cc | ||
|
|
8ced43c389 | ||
|
|
479ba8f742 | ||
|
|
9badc3bb64 | ||
|
|
f689c551a5 | ||
|
|
678a66064b | ||
|
|
e6cfd88e58 | ||
|
|
93d5e19302 | ||
|
|
ae8fcb7157 | ||
|
|
c25c52faa0 | ||
|
|
375a6ad3a4 | ||
|
|
7283ced1ca | ||
|
|
7cffdab28a | ||
|
|
95c64f51de | ||
|
|
4361ad9daa | ||
|
|
c389e0744a | ||
|
|
81b42eec67 | ||
|
|
7e098d5869 | ||
|
|
499d2d6e63 | ||
|
|
cd6661d239 | ||
|
|
f472e0bd02 | ||
|
|
8bd7dd00c7 | ||
|
|
f53e46ee9e | ||
|
|
b41f00458b | ||
|
|
5e7c946d43 | ||
|
|
ede899e9a3 | ||
|
|
68ccd63ddc | ||
|
|
d4d3867b5f | ||
|
|
14feeb0c16 | ||
|
|
8a41899c5d | ||
|
|
b8857031f4 | ||
|
|
b561217073 | ||
|
|
9982dadd58 | ||
|
|
f5d6c071bf | ||
|
|
e6aac69358 | ||
|
|
de1c9a1485 | ||
|
|
56fc25d27d | ||
|
|
0a69bb96ba | ||
|
|
612111067b | ||
|
|
d174aa88d6 | ||
|
|
9e6f5e7eb1 | ||
|
|
3d7ea7d358 | ||
|
|
e628256f44 | ||
|
|
8a063850a5 | ||
|
|
8e95fd2355 | ||
|
|
b4d1277a9d | ||
|
|
a142b6d1f4 | ||
|
|
aa38fd2c19 | ||
|
|
406c7ea590 | ||
|
|
bae3a94fa1 | ||
|
|
545e7ccd6c | ||
|
|
96096adbc7 | ||
|
|
a24d4da3c2 | ||
|
|
aec3656b76 | ||
|
|
3f3d2e6b57 | ||
|
|
9ad22df21d | ||
|
|
28f0b5478b | ||
|
|
d8cad40c50 | ||
|
|
04273b3d43 | ||
|
|
9a2e897464 | ||
|
|
dab13a1f54 | ||
|
|
653748363f | ||
|
|
5feacca44c | ||
|
|
4524bd4f84 | ||
|
|
a7f7015212 | ||
|
|
58cf983e5e | ||
|
|
034ef8ac94 | ||
|
|
4753ef3a9b | ||
|
|
a76b8725cd | ||
|
|
4a7543fa68 | ||
|
|
b698b1862e | ||
|
|
0153cfbb68 | ||
|
|
293eb509da | ||
|
|
9d5243e9c3 | ||
|
|
996691d66f | ||
|
|
854e3b4c24 | ||
|
|
cd9bafd1c1 | ||
|
|
246ef7a566 | ||
|
|
d94f9eab59 | ||
|
|
4914af8a45 | ||
|
|
78fb926a5e | ||
|
|
a0b610d93f | ||
|
|
f3f0777de6 | ||
|
|
869a4fa999 | ||
|
|
27ce4cd9e2 | ||
|
|
ecbab2a2e4 | ||
|
|
42cfccfc33 | ||
|
|
a5b0088695 | ||
|
|
ac9fd39cf4 | ||
|
|
ae39e60095 | ||
|
|
92e07b3018 | ||
|
|
edc29cc28c | ||
|
|
5e1d37c2b8 | ||
|
|
166bc48c6b | ||
|
|
7748ce521d | ||
|
|
48904b10f0 | ||
|
|
dbb1bf0630 | ||
|
|
94d0a086c6 | ||
|
|
8313a2d33e | ||
|
|
cac380e673 | ||
|
|
222e9035c3 | ||
|
|
069c860ae1 | ||
|
|
0d12002677 | ||
|
|
ce8e553fec | ||
|
|
8f983c2b43 | ||
|
|
ba9b1c1c9e | ||
|
|
be453fdad1 | ||
|
|
d05948be47 | ||
|
|
140c61ab9b | ||
|
|
005730938c | ||
|
|
03b4f9b52e | ||
|
|
413812b249 | ||
|
|
7563cb5d38 | ||
|
|
6faa05fc62 | ||
|
|
b1fb260366 | ||
|
|
75c93b4cf9 | ||
|
|
a92f34db71 |
@@ -1,9 +1,20 @@
|
|||||||
BasedOnStyle: GNU
|
BasedOnStyle: GNU
|
||||||
|
Language: C
|
||||||
|
|
||||||
IndentWidth: 2
|
IndentWidth: 2
|
||||||
TabWidth: 2
|
TabWidth: 2
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
ContinuationIndentWidth: 2 # Indents continuation lines by 2 spaces
|
ContinuationIndentWidth: 2
|
||||||
|
|
||||||
AllowShortFunctionsOnASingleLine: true
|
AllowShortFunctionsOnASingleLine: true
|
||||||
AllowShortBlocksOnASingleLine: true
|
AllowShortBlocksOnASingleLine: true
|
||||||
AllowShortIfStatementsOnASingleLine: true
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
|
ColumnLimit: 0
|
||||||
|
BreakFunctionDefinitionParameters: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BinPackArguments: false
|
||||||
|
|
||||||
|
# --- Fix the "static T\nname(...)" style ---
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
BreakAfterReturnType: None
|
||||||
|
|||||||
23
.gitignore
vendored
23
.gitignore
vendored
@@ -1,30 +1,31 @@
|
|||||||
.git/
|
.git/
|
||||||
.obj/
|
.obj/
|
||||||
|
website/
|
||||||
bin/
|
bin/
|
||||||
build/
|
build/
|
||||||
*.zip
|
*.zip
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.d
|
*.d
|
||||||
tags
|
|
||||||
Jenkinsfile
|
|
||||||
*~
|
*~
|
||||||
*.log
|
*.log
|
||||||
*.gz
|
*.gz
|
||||||
*.tar
|
*.tar
|
||||||
.nova/
|
.nova/
|
||||||
packer*
|
|
||||||
primum
|
|
||||||
sokol-shdc*
|
|
||||||
source/shaders/*.h
|
source/shaders/*.h
|
||||||
core.cdb
|
|
||||||
primum.exe
|
|
||||||
core.cdb.h
|
|
||||||
jsc
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.html
|
*.html
|
||||||
.vscode
|
.vscode
|
||||||
*.icns
|
*.icns
|
||||||
game.zip
|
|
||||||
icon.ico
|
icon.ico
|
||||||
steam/
|
steam/
|
||||||
|
subprojects/*/
|
||||||
|
build_dbg/
|
||||||
|
modules/
|
||||||
|
sdk/
|
||||||
|
artifacts/
|
||||||
|
discord_social_sdk/
|
||||||
|
discord_partner_sdk/
|
||||||
|
steam_api64.dll
|
||||||
|
subprojects/.wraplock
|
||||||
|
.gemini
|
||||||
|
|||||||
25
CLAUDE.md
Normal file
25
CLAUDE.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Code style
|
||||||
|
All code is done with 2 spaces for indentation.
|
||||||
|
|
||||||
|
For cell script and its integration files, objects are preferred over classes, and preferrably limited use of prototypes, make objects sendable between actors (.ce files).
|
||||||
|
|
||||||
|
## cell script format
|
||||||
|
Cell script files end in .ce or .cm. Cell script is similar to Javascript but with some differences.
|
||||||
|
|
||||||
|
Variables are delcared with 'var'. Var behaves like let.
|
||||||
|
Constants are declared with 'def'.
|
||||||
|
!= and == are strict, there is no !== or ===.
|
||||||
|
There is no undefined, only null.
|
||||||
|
There are no classes, only objects and prototypes.
|
||||||
|
Prefer backticks for string interpolation. Otherwise, convering non strings with the text() function is required.
|
||||||
|
Everything should be lowercase.
|
||||||
|
|
||||||
|
There are no arraybuffers, only blobs, which work with bits. They must be stoned like stone(blob) before being read from.
|
||||||
|
|
||||||
|
## c format
|
||||||
|
For cell script integration files, everything should be declared static that can be. Most don't have headers at all. Files in a package are not shared between packages.
|
||||||
|
|
||||||
|
There is no undefined, so JS_IsNull and JS_NULL should be used only.
|
||||||
|
|
||||||
|
## how module loading is done in cell script
|
||||||
|
Within a package, a c file, if using the correct macros (CELL_USE_FUNCS etc), will be loaded as a module with its name; so png.c inside ac package is loaded as <package>/png, giving you access to its functions.
|
||||||
16
Info.plist
16
Info.plist
@@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>Prosperon</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>pockle.world.prosperon</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>Prosperon</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>0.5</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Copyright © 2024 Pockle World. All rights reserved.</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
26
LICENSE
26
LICENSE
@@ -1,26 +0,0 @@
|
|||||||
Prosperon Game Engine
|
|
||||||
|
|
||||||
Copyright (c) 2019-2024 John Alanbrook
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
(1) The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
(2) Any games or other derivative software must display the "Prosperon" logo
|
|
||||||
at near the beginning of the software's startup, before the chief purpose
|
|
||||||
of the software is underway.
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
103
Makefile
103
Makefile
@@ -1,25 +1,82 @@
|
|||||||
debug: FORCE
|
# Development build: creates libcell_runtime.dylib + thin main wrapper
|
||||||
|
# This is the default target for working on cell itself
|
||||||
|
#
|
||||||
|
# If cell doesn't exist yet, use 'make bootstrap' first (requires meson)
|
||||||
|
# or manually build with meson once.
|
||||||
|
#
|
||||||
|
# The cell shop is at ~/.cell and core scripts are installed to ~/.cell/core
|
||||||
|
|
||||||
|
CELL_SHOP = $(HOME)/.cell
|
||||||
|
CELL_CORE_PACKAGE = $(CELL_SHOP)/packages/core
|
||||||
|
|
||||||
|
maker: install
|
||||||
|
|
||||||
|
makecell:
|
||||||
|
cell pack core -o cell
|
||||||
|
cp cell /opt/homebrew/bin/
|
||||||
|
|
||||||
|
# Install core: symlink this directory to ~/.cell/core
|
||||||
|
install: bootstrap $(CELL_SHOP)
|
||||||
|
@echo "Linking cell core to $(CELL_CORE_PACKAGE)"
|
||||||
|
rm -rf $(CELL_CORE_PACKAGE)
|
||||||
|
ln -s $(PWD) $(CELL_CORE_PACKAGE)
|
||||||
|
cp cell /opt/homebrew/bin/
|
||||||
|
cp libcell_runtime.dylib /opt/homebrew/lib/
|
||||||
|
@echo "Core installed."
|
||||||
|
|
||||||
|
cell: libcell_runtime.dylib cell_main
|
||||||
|
cp cell_main cell
|
||||||
|
chmod +x cell
|
||||||
|
cp cell /opt/homebrew/bin/cell
|
||||||
|
cp libcell_runtime.dylib /opt/homebrew/lib/
|
||||||
|
|
||||||
|
# Build the shared runtime library (everything except main.c)
|
||||||
|
# Uses existing cell to run build -d
|
||||||
|
libcell_runtime.dylib: $(CELL_SHOP)/build/dynamic
|
||||||
|
cell build -d
|
||||||
|
cp $(CELL_SHOP)/build/dynamic/libcell_runtime.dylib .
|
||||||
|
|
||||||
|
# Build the thin main wrapper that links to libcell_runtime
|
||||||
|
cell_main: source/main.c libcell_runtime.dylib
|
||||||
|
cc -o cell_main source/main.c -L. -lcell_runtime -Wl,-rpath,@loader_path -Wl,-rpath,/opt/homebrew/lib
|
||||||
|
|
||||||
|
# Create the cell shop directories
|
||||||
|
$(CELL_SHOP):
|
||||||
|
mkdir -p $(CELL_SHOP)
|
||||||
|
mkdir -p $(CELL_SHOP)/packages
|
||||||
|
mkdir -p $(CELL_SHOP)/cache
|
||||||
|
mkdir -p $(CELL_SHOP)/build
|
||||||
|
|
||||||
|
$(CELL_CORE):
|
||||||
|
ln -s $(PWD) $(CELL_CORE)
|
||||||
|
|
||||||
|
# Static build: creates a fully static cell binary (for distribution)
|
||||||
|
static:
|
||||||
|
cell build
|
||||||
|
cp $(CELL_SHOP)/build/static/cell .
|
||||||
|
|
||||||
|
# Bootstrap: build cell from scratch using meson (only needed once)
|
||||||
|
# Also installs core scripts to ~/.cell/core
|
||||||
|
bootstrap:
|
||||||
|
meson setup build_bootstrap -Dbuildtype=debug -Db_sanitize=address
|
||||||
|
meson compile -C build_bootstrap
|
||||||
|
cp build_bootstrap/cell .
|
||||||
|
cp build_bootstrap/libcell_runtime.dylib .
|
||||||
|
@echo "Bootstrap complete. Cell shop initialized at $(CELL_SHOP)"
|
||||||
|
@echo "Now run 'make' to rebuild with cell itself."
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
clean:
|
||||||
|
rm -rf $(CELL_SHOP)/build build_bootstrap
|
||||||
|
rm -f cell cell_main libcell_runtime.dylib
|
||||||
|
|
||||||
|
# Ensure dynamic build directory exists
|
||||||
|
$(CELL_SHOP)/build/dynamic: $(CELL_SHOP)
|
||||||
|
mkdir -p $(CELL_SHOP)/build/dynamic
|
||||||
|
|
||||||
|
# Legacy meson target
|
||||||
|
meson:
|
||||||
meson setup build_dbg -Dbuildtype=debugoptimized
|
meson setup build_dbg -Dbuildtype=debugoptimized
|
||||||
meson compile -C build_dbg
|
meson install -C build_dbg
|
||||||
|
|
||||||
release: FORCE
|
.PHONY: cell static bootstrap clean meson install
|
||||||
meson setup -Dbuildtype=release -Db_lto=true -Db_ndebug=true -Db_pgo=use build_release
|
|
||||||
meson compile -C build_release
|
|
||||||
|
|
||||||
sanitize: FORCE
|
|
||||||
meson setup -Db_sanitize=address -Db_sanitize=memory -Db_sanitize=leak -Db_sanitize=undefined build_sani
|
|
||||||
meson compile -C build_sani
|
|
||||||
|
|
||||||
small: FORCE
|
|
||||||
meson setup -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true -Db_pgo=use build_small
|
|
||||||
meson compile -C build_small
|
|
||||||
|
|
||||||
web: FORCE
|
|
||||||
meson setup -Deditor=false -Dbuildtype=minsize -Db_lto=true -Db_ndebug=true --cross-file emscripten.cross build_web
|
|
||||||
meson compile -C build_web
|
|
||||||
|
|
||||||
crosswin: FORCE
|
|
||||||
meson setup -Dbuildtype=debugoptimized --cross-file mingw32.cross build_win
|
|
||||||
meson compile -C build_win
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -1,13 +1 @@
|
|||||||

|
Read the docs to get started.
|
||||||
|
|
||||||
The easily moddable, programming minded, 2D-first game engine. The aim is to make the fastest way to make games.
|
|
||||||
|
|
||||||
Using ...
|
|
||||||
* Sokol for rendering
|
|
||||||
* Chipmunk2D for physics
|
|
||||||
* imgui for easy editor UI
|
|
||||||
* Clay for game UI
|
|
||||||
|
|
||||||
Includes an implementation for Nota, and Kim.
|
|
||||||
|
|
||||||
*Prosperon is useful, but is a work in progress. Breaking changes are frequent.*
|
|
||||||
|
|||||||
103
add.ce
Normal file
103
add.ce
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// cell add <locator> [alias] - Add a dependency to the current package
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cell add <locator> Add a dependency using default alias
|
||||||
|
// cell add <locator> <alias> Add a dependency with custom alias
|
||||||
|
//
|
||||||
|
// This adds the dependency to cell.toml and installs it to the shop.
|
||||||
|
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var pkg = use('package')
|
||||||
|
var build = use('build')
|
||||||
|
var fd = use('fd')
|
||||||
|
|
||||||
|
var locator = null
|
||||||
|
var alias = null
|
||||||
|
|
||||||
|
array(args, function(arg) {
|
||||||
|
if (arg == '--help' || arg == '-h') {
|
||||||
|
log.console("Usage: cell add <locator> [alias]")
|
||||||
|
log.console("")
|
||||||
|
log.console("Add a dependency to the current package.")
|
||||||
|
log.console("")
|
||||||
|
log.console("Examples:")
|
||||||
|
log.console(" cell add gitea.pockle.world/john/prosperon")
|
||||||
|
log.console(" cell add gitea.pockle.world/john/cell-image image")
|
||||||
|
log.console(" cell add ../local-package")
|
||||||
|
$stop()
|
||||||
|
} else if (!starts_with(arg, '-')) {
|
||||||
|
if (!locator) {
|
||||||
|
locator = arg
|
||||||
|
} else if (!alias) {
|
||||||
|
alias = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!locator) {
|
||||||
|
log.console("Usage: cell add <locator> [alias]")
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve relative paths to absolute paths
|
||||||
|
if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
|
||||||
|
var resolved = fd.realpath(locator)
|
||||||
|
if (resolved) {
|
||||||
|
locator = resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate default alias from locator
|
||||||
|
if (!alias) {
|
||||||
|
// Use the last component of the locator as alias
|
||||||
|
var parts = array(locator, '/')
|
||||||
|
alias = parts[length(parts) - 1]
|
||||||
|
// Remove any version suffix
|
||||||
|
if (search(alias, '@') != null) {
|
||||||
|
alias = array(alias, '@')[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we're in a package directory
|
||||||
|
var cwd = fd.realpath('.')
|
||||||
|
if (!fd.is_file(cwd + '/cell.toml')) {
|
||||||
|
log.error("Not in a package directory (no cell.toml found)")
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("Adding " + locator + " as '" + alias + "'...")
|
||||||
|
|
||||||
|
// Add to local project's cell.toml
|
||||||
|
try {
|
||||||
|
pkg.add_dependency(null, locator, alias)
|
||||||
|
log.console(" Added to cell.toml")
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Failed to update cell.toml: " + e)
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install to shop
|
||||||
|
try {
|
||||||
|
shop.get(locator)
|
||||||
|
shop.extract(locator)
|
||||||
|
|
||||||
|
// Build scripts
|
||||||
|
shop.build_package_scripts(locator)
|
||||||
|
|
||||||
|
// Build C code if any
|
||||||
|
try {
|
||||||
|
var target = build.detect_host_target()
|
||||||
|
build.build_dynamic(locator, target, 'release')
|
||||||
|
} catch (e) {
|
||||||
|
// Not all packages have C code
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console(" Installed to shop")
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Failed to install: " + e)
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("Added " + alias + " (" + locator + ")")
|
||||||
|
|
||||||
|
$stop()
|
||||||
399
archive/miniz.c
Normal file
399
archive/miniz.c
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
#include "quickjs.h"
|
||||||
|
#include "miniz.h"
|
||||||
|
#include "cell.h"
|
||||||
|
|
||||||
|
static JSClassID js_reader_class_id;
|
||||||
|
static JSClassID js_writer_class_id;
|
||||||
|
|
||||||
|
static void js_reader_finalizer(JSRuntime *rt, JSValue val) {
|
||||||
|
mz_zip_archive *zip = JS_GetOpaque(val, js_reader_class_id);
|
||||||
|
mz_zip_reader_end(zip);
|
||||||
|
js_free_rt(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void js_writer_finalizer(JSRuntime *rt, JSValue val) {
|
||||||
|
mz_zip_archive *zip = JS_GetOpaque(val, js_writer_class_id);
|
||||||
|
mz_zip_writer_finalize_archive(zip);
|
||||||
|
mz_zip_writer_end(zip);
|
||||||
|
js_free_rt(zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSClassDef js_reader_class = {
|
||||||
|
"zip reader",
|
||||||
|
.finalizer = js_reader_finalizer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static JSClassDef js_writer_class = {
|
||||||
|
"zip writer",
|
||||||
|
.finalizer = js_writer_finalizer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static mz_zip_archive *js2reader(JSContext *js, JSValue v)
|
||||||
|
{
|
||||||
|
return JS_GetOpaque(v, js_reader_class_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mz_zip_archive *js2writer(JSContext *js, JSValue v)
|
||||||
|
{
|
||||||
|
return JS_GetOpaque(v, js_writer_class_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_miniz_read(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
void *data = js_get_blob_data(js, &len, argv[0]);
|
||||||
|
if (data == -1)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = calloc(sizeof(*zip), 1);
|
||||||
|
if (!zip)
|
||||||
|
return JS_ThrowOutOfMemory(js);
|
||||||
|
|
||||||
|
mz_bool success = mz_zip_reader_init_mem(zip, data, len, 0);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
int err = mz_zip_get_last_error(zip);
|
||||||
|
free(zip);
|
||||||
|
return JS_ThrowInternalError(js, "Failed to initialize zip reader: %s", mz_zip_get_error_string(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue jszip = JS_NewObjectClass(js, js_reader_class_id);
|
||||||
|
JS_SetOpaque(jszip, zip);
|
||||||
|
return jszip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_miniz_write(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
const char *file = JS_ToCString(js, argv[0]);
|
||||||
|
if (!file)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = calloc(sizeof(*zip), 1);
|
||||||
|
if (!zip) {
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowOutOfMemory(js);
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_bool success = mz_zip_writer_init_file(zip, file, 0);
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
int err = mz_zip_get_last_error(zip);
|
||||||
|
mz_zip_writer_end(zip);
|
||||||
|
free(zip);
|
||||||
|
return JS_ThrowInternalError(js, "Failed to initialize zip writer: %s", mz_zip_get_error_string(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue jszip = JS_NewObjectClass(js, js_writer_class_id);
|
||||||
|
JS_SetOpaque(jszip, zip);
|
||||||
|
return jszip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_miniz_compress(JSContext *js, JSValue this_val,
|
||||||
|
int argc, JSValueConst *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js,
|
||||||
|
"compress needs a string or ArrayBuffer");
|
||||||
|
|
||||||
|
/* ─── 1. Grab the input data ──────────────────────────────── */
|
||||||
|
const char *cstring = NULL;
|
||||||
|
size_t in_len = 0;
|
||||||
|
const void *in_ptr = NULL;
|
||||||
|
|
||||||
|
if (JS_IsText(argv[0])) {
|
||||||
|
/* String → UTF-8 bytes without the terminating NUL */
|
||||||
|
cstring = JS_ToCStringLen(js, &in_len, argv[0]);
|
||||||
|
if (!cstring)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
in_ptr = cstring;
|
||||||
|
} else {
|
||||||
|
in_ptr = js_get_blob_data(js, &in_len, argv[0]);
|
||||||
|
if (in_ptr == -1)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── 2. Allocate an output buffer big enough ────────────── */
|
||||||
|
mz_ulong out_len_est = mz_compressBound(in_len);
|
||||||
|
void *out_buf = js_malloc(js, out_len_est);
|
||||||
|
if (!out_buf) {
|
||||||
|
if (cstring) JS_FreeCString(js, cstring);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── 3. Do the compression (MZ_DEFAULT_COMPRESSION = level 6) */
|
||||||
|
mz_ulong out_len = out_len_est;
|
||||||
|
int st = mz_compress2(out_buf, &out_len,
|
||||||
|
in_ptr, in_len, MZ_DEFAULT_COMPRESSION);
|
||||||
|
|
||||||
|
/* clean-up for string input */
|
||||||
|
if (cstring) JS_FreeCString(js, cstring);
|
||||||
|
|
||||||
|
if (st != MZ_OK) {
|
||||||
|
js_free(js, out_buf);
|
||||||
|
return JS_ThrowInternalError(js,
|
||||||
|
"miniz: compression failed (%d)", st);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── 4. Hand JavaScript a copy of the compressed data ────── */
|
||||||
|
JSValue abuf = js_new_blob_stoned_copy(js, out_buf, out_len);
|
||||||
|
js_free(js, out_buf);
|
||||||
|
return abuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_miniz_decompress(JSContext *js,
|
||||||
|
JSValueConst this_val,
|
||||||
|
int argc,
|
||||||
|
JSValueConst *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js,
|
||||||
|
"decompress: need compressed ArrayBuffer");
|
||||||
|
|
||||||
|
/* grab compressed data */
|
||||||
|
size_t in_len;
|
||||||
|
void *in_ptr = js_get_blob_data(js, &in_len, argv[0]);
|
||||||
|
if (in_ptr == -1)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
/* zlib header present → tell tinfl to parse it */
|
||||||
|
size_t out_len = 0;
|
||||||
|
void *out_ptr = tinfl_decompress_mem_to_heap(
|
||||||
|
in_ptr, in_len, &out_len,
|
||||||
|
TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||||
|
|
||||||
|
if (!out_ptr)
|
||||||
|
return JS_ThrowInternalError(js,
|
||||||
|
"miniz: decompression failed");
|
||||||
|
|
||||||
|
JSValue ret;
|
||||||
|
ret = JS_NewStringLen(js, (const char *)out_ptr, out_len);
|
||||||
|
#ifdef MZ_FREE
|
||||||
|
MZ_FREE(out_ptr);
|
||||||
|
#else
|
||||||
|
free(out_ptr);
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_miniz_funcs[] = {
|
||||||
|
JS_CFUNC_DEF("read", 1, js_miniz_read),
|
||||||
|
JS_CFUNC_DEF("write", 1, js_miniz_write),
|
||||||
|
JS_CFUNC_DEF("compress", 1, js_miniz_compress),
|
||||||
|
JS_CFUNC_DEF("decompress", 1, js_miniz_decompress),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_writer_add_file(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 2)
|
||||||
|
return JS_ThrowTypeError(js, "add_file requires (path, arrayBuffer)");
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2writer(js, self);
|
||||||
|
const char *pathInZip = JS_ToCString(js, argv[0]);
|
||||||
|
if (!pathInZip)
|
||||||
|
return JS_ThrowTypeError(js, "Could not parse path argument");
|
||||||
|
|
||||||
|
size_t dataLen;
|
||||||
|
void *data = js_get_blob_data(js, &dataLen, argv[1]);
|
||||||
|
if (data == -1) {
|
||||||
|
JS_FreeCString(js, pathInZip);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
int success = mz_zip_writer_add_mem(zip, pathInZip, data, dataLen, MZ_DEFAULT_COMPRESSION);
|
||||||
|
JS_FreeCString(js, pathInZip);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
return JS_ThrowInternalError(js, "Failed to add memory to zip");
|
||||||
|
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_writer_funcs[] = {
|
||||||
|
JS_CFUNC_DEF("add_file", 1, js_writer_add_file),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
#ifndef MINIZ_NO_TIME
|
||||||
|
const char *file = JS_ToCString(js,argv[0]);
|
||||||
|
if (!file)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip) {
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_zip_archive_file_stat pstat;
|
||||||
|
mz_uint index = mz_zip_reader_locate_file(zip, file, NULL, 0);
|
||||||
|
|
||||||
|
if (index == (mz_uint)-1) {
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowReferenceError(js, "File '%s' not found in archive", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
|
||||||
|
if (!mz_zip_reader_file_stat(zip, index, &pstat)) {
|
||||||
|
int err = mz_zip_get_last_error(zip);
|
||||||
|
return JS_ThrowInternalError(js, "Failed to get file stats: %s", mz_zip_get_error_string(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
return JS_NewFloat64(js, pstat.m_time);
|
||||||
|
#else
|
||||||
|
return JS_ThrowInternalError(js, "MINIZ_NO_TIME is defined");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_exists(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
const char *file = JS_ToCString(js,argv[0]);
|
||||||
|
if (!file)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip) {
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
}
|
||||||
|
|
||||||
|
mz_uint index = mz_zip_reader_locate_file(zip, file, NULL, 0);
|
||||||
|
JS_FreeCString(js,file);
|
||||||
|
if (index == (mz_uint)-1) return JS_NewBool(js, 0);
|
||||||
|
return JS_NewBool(js, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_slurp(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
const char *file = JS_ToCString(js,argv[0]);
|
||||||
|
if (!file)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip) {
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len;
|
||||||
|
void *data = mz_zip_reader_extract_file_to_heap(zip, file, &len, 0);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
int err = mz_zip_get_last_error(zip);
|
||||||
|
const char *filename = file;
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
return JS_ThrowInternalError(js, "Failed to extract file '%s': %s", filename, mz_zip_get_error_string(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeCString(js, file);
|
||||||
|
|
||||||
|
JSValue ret = js_new_blob_stoned_copy(js, data, len);
|
||||||
|
free(data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_list(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip)
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
|
||||||
|
mz_uint num_files = mz_zip_reader_get_num_files(zip);
|
||||||
|
|
||||||
|
JSValue arr = JS_NewArray(js);
|
||||||
|
if (JS_IsException(arr))
|
||||||
|
return arr;
|
||||||
|
|
||||||
|
mz_uint arr_index = 0;
|
||||||
|
for (mz_uint i = 0; i < num_files; i++) {
|
||||||
|
mz_zip_archive_file_stat file_stat;
|
||||||
|
if (!mz_zip_reader_file_stat(zip, i, &file_stat))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
JSValue filename = JS_NewString(js, file_stat.m_filename);
|
||||||
|
if (JS_IsException(filename)) {
|
||||||
|
JS_FreeValue(js, arr);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
JS_SetPropertyUint32(js, arr, arr_index++, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_is_directory(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js, "is_directory requires a file index");
|
||||||
|
|
||||||
|
int32_t index;
|
||||||
|
if (JS_ToInt32(js, &index, argv[0]))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip)
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
|
||||||
|
return JS_NewBool(js, mz_zip_reader_is_file_a_directory(zip, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_get_filename(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js, "get_filename requires a file index");
|
||||||
|
|
||||||
|
int32_t index;
|
||||||
|
if (JS_ToInt32(js, &index, argv[0]))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip)
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
|
||||||
|
mz_zip_archive_file_stat file_stat;
|
||||||
|
if (!mz_zip_reader_file_stat(zip, index, &file_stat))
|
||||||
|
return JS_ThrowInternalError(js, "Failed to get file stats");
|
||||||
|
|
||||||
|
return JS_NewString(js, file_stat.m_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_reader_count(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
mz_zip_archive *zip = js2reader(js, self);
|
||||||
|
if (!zip)
|
||||||
|
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||||
|
return JS_NewUint32(js, mz_zip_reader_get_num_files(zip));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_reader_funcs[] = {
|
||||||
|
JS_CFUNC_DEF("mod", 1, js_reader_mod),
|
||||||
|
JS_CFUNC_DEF("exists", 1, js_reader_exists),
|
||||||
|
JS_CFUNC_DEF("slurp", 1, js_reader_slurp),
|
||||||
|
JS_CFUNC_DEF("list", 0, js_reader_list),
|
||||||
|
JS_CFUNC_DEF("is_directory", 1, js_reader_is_directory),
|
||||||
|
JS_CFUNC_DEF("get_filename", 1, js_reader_get_filename),
|
||||||
|
JS_CFUNC_DEF("count", 0, js_reader_count),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_miniz_use(JSContext *js)
|
||||||
|
{
|
||||||
|
JS_NewClassID(&js_reader_class_id);
|
||||||
|
JS_NewClass(JS_GetRuntime(js), js_reader_class_id, &js_reader_class);
|
||||||
|
JSValue reader_proto = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js, reader_proto, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
|
||||||
|
JS_SetClassProto(js, js_reader_class_id, reader_proto);
|
||||||
|
|
||||||
|
JS_NewClassID(&js_writer_class_id);
|
||||||
|
JS_NewClass(JS_GetRuntime(js), js_writer_class_id, &js_writer_class);
|
||||||
|
JSValue writer_proto = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js, writer_proto, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
|
||||||
|
JS_SetClassProto(js, js_writer_class_id, writer_proto);
|
||||||
|
|
||||||
|
JSValue export = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
||||||
|
return export;
|
||||||
|
}
|
||||||
574
bench.ce
Normal file
574
bench.ce
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
var shop = use('internal/shop')
|
||||||
|
var pkg = use('package')
|
||||||
|
var fd = use('fd')
|
||||||
|
var time = use('time')
|
||||||
|
var json = use('json')
|
||||||
|
var blob = use('blob')
|
||||||
|
var os = use('os')
|
||||||
|
var testlib = use('internal/testlib')
|
||||||
|
var math = use('math/radians')
|
||||||
|
|
||||||
|
if (!args) args = []
|
||||||
|
|
||||||
|
var target_pkg = null // null = current package
|
||||||
|
var target_bench = null // null = all benchmarks, otherwise specific bench file
|
||||||
|
var all_pkgs = false
|
||||||
|
|
||||||
|
// Benchmark configuration
|
||||||
|
def WARMUP_BATCHES = 3
|
||||||
|
def SAMPLES = 11 // Number of timing samples to collect
|
||||||
|
def TARGET_SAMPLE_NS = 20000000 // 20ms per sample (fast mode)
|
||||||
|
def MIN_SAMPLE_NS = 2000000 // 2ms minimum sample duration
|
||||||
|
def MIN_BATCH_SIZE = 1
|
||||||
|
def MAX_BATCH_SIZE = 100000000 // 100M iterations max per batch
|
||||||
|
|
||||||
|
// Statistical functions
|
||||||
|
function median(arr) {
|
||||||
|
if (length(arr) == 0) return 0
|
||||||
|
var sorted = sort(arr)
|
||||||
|
var mid = floor(length(arr) / 2)
|
||||||
|
if (length(arr) % 2 == 0) {
|
||||||
|
return (sorted[mid - 1] + sorted[mid]) / 2
|
||||||
|
}
|
||||||
|
return sorted[mid]
|
||||||
|
}
|
||||||
|
|
||||||
|
function mean(arr) {
|
||||||
|
if (length(arr) == 0) return 0
|
||||||
|
var sum = 0
|
||||||
|
arrfor(arr, function(val) {
|
||||||
|
sum += val
|
||||||
|
})
|
||||||
|
return sum / length(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stddev(arr, mean_val) {
|
||||||
|
if (length(arr) < 2) return 0
|
||||||
|
var sum_sq_diff = 0
|
||||||
|
arrfor(arr, function(val) {
|
||||||
|
var diff = val - mean_val
|
||||||
|
sum_sq_diff += diff * diff
|
||||||
|
})
|
||||||
|
return math.sqrt(sum_sq_diff / (length(arr) - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function percentile(arr, p) {
|
||||||
|
if (length(arr) == 0) return 0
|
||||||
|
var sorted = sort(arr)
|
||||||
|
var idx = floor(arr) * p / 100
|
||||||
|
if (idx >= length(arr)) idx = length(arr) - 1
|
||||||
|
return sorted[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse arguments similar to test.ce
|
||||||
|
function parse_args() {
|
||||||
|
if (length(args) == 0) {
|
||||||
|
if (!testlib.is_valid_package('.')) {
|
||||||
|
log.console('No cell.toml found in current directory')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
target_pkg = null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0] == 'all') {
|
||||||
|
if (!testlib.is_valid_package('.')) {
|
||||||
|
log.console('No cell.toml found in current directory')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
target_pkg = null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0] == 'package') {
|
||||||
|
if (length(args) < 2) {
|
||||||
|
log.console('Usage: cell bench package <name> [bench]')
|
||||||
|
log.console(' cell bench package all')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[1] == 'all') {
|
||||||
|
all_pkgs = true
|
||||||
|
log.console('Benchmarking all packages...')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = args[1]
|
||||||
|
var lock = shop.load_lock()
|
||||||
|
if (lock[name]) {
|
||||||
|
target_pkg = name
|
||||||
|
} else if (starts_with(name, '/') && testlib.is_valid_package(name)) {
|
||||||
|
target_pkg = name
|
||||||
|
} else {
|
||||||
|
if (testlib.is_valid_package('.')) {
|
||||||
|
var resolved = pkg.alias_to_package(null, name)
|
||||||
|
if (resolved) {
|
||||||
|
target_pkg = resolved
|
||||||
|
} else {
|
||||||
|
log.console(`Package not found: ${name}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.console(`Package not found: ${name}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(args) >= 3) {
|
||||||
|
target_bench = args[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console(`Benchmarking package: ${target_pkg}`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// cell bench benches/suite or cell bench <path>
|
||||||
|
var bench_path = args[0]
|
||||||
|
|
||||||
|
// Normalize path - add benches/ prefix if not present
|
||||||
|
if (!starts_with(bench_path, 'benches/') && !starts_with(bench_path, '/')) {
|
||||||
|
if (!fd.is_file(bench_path + '.cm') && !fd.is_file(bench_path)) {
|
||||||
|
if (fd.is_file('benches/' + bench_path + '.cm') || fd.is_file('benches/' + bench_path)) {
|
||||||
|
bench_path = 'benches/' + bench_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target_bench = bench_path
|
||||||
|
target_pkg = null
|
||||||
|
|
||||||
|
if (!testlib.is_valid_package('.')) {
|
||||||
|
log.console('No cell.toml found in current directory')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parse_args()) {
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect benchmark files from a package
|
||||||
|
function collect_benches(package_name, specific_bench) {
|
||||||
|
var prefix = testlib.get_pkg_dir(package_name)
|
||||||
|
var benches_dir = prefix + '/benches'
|
||||||
|
|
||||||
|
if (!fd.is_dir(benches_dir)) return []
|
||||||
|
|
||||||
|
var files = pkg.list_files(package_name)
|
||||||
|
var bench_files = []
|
||||||
|
arrfor(files, function(f) {
|
||||||
|
if (starts_with(f, "benches/") && ends_with(f, ".cm")) {
|
||||||
|
if (specific_bench) {
|
||||||
|
var bench_name = text(f, 0, -3)
|
||||||
|
var match_name = specific_bench
|
||||||
|
if (!starts_with(match_name, 'benches/')) match_name = 'benches/' + match_name
|
||||||
|
var match_base = ends_with(match_name, '.cm') ? text(match_name, 0, -3) : match_name
|
||||||
|
if (bench_name != match_base) return
|
||||||
|
}
|
||||||
|
push(bench_files, f)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return bench_files
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calibrate batch size for a benchmark
|
||||||
|
function calibrate_batch_size(bench_fn, is_batch) {
|
||||||
|
if (!is_batch) return 1
|
||||||
|
|
||||||
|
var n = MIN_BATCH_SIZE
|
||||||
|
var dt = 0
|
||||||
|
|
||||||
|
// Find a batch size that takes at least MIN_SAMPLE_NS
|
||||||
|
while (n < MAX_BATCH_SIZE) {
|
||||||
|
// Ensure n is a valid number before calling
|
||||||
|
if (!is_number(n) || n < 1) {
|
||||||
|
n = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var start = os.now()
|
||||||
|
bench_fn(n)
|
||||||
|
dt = os.now() - start
|
||||||
|
|
||||||
|
if (dt >= MIN_SAMPLE_NS) break
|
||||||
|
|
||||||
|
// Double the batch size
|
||||||
|
var new_n = n * 2
|
||||||
|
// Check if multiplication produced a valid number
|
||||||
|
if (!is_number(new_n) || new_n > MAX_BATCH_SIZE) {
|
||||||
|
n = MAX_BATCH_SIZE
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n = new_n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust to target sample duration
|
||||||
|
if (dt > 0 && dt < TARGET_SAMPLE_NS && is_number(n) && is_number(dt)) {
|
||||||
|
var calc = n * TARGET_SAMPLE_NS / dt
|
||||||
|
if (is_number(calc) && calc > 0) {
|
||||||
|
var target_n = floor(calc)
|
||||||
|
// Check if floor returned a valid number
|
||||||
|
if (is_number(target_n) && target_n > 0) {
|
||||||
|
if (target_n > MAX_BATCH_SIZE) target_n = MAX_BATCH_SIZE
|
||||||
|
if (target_n < MIN_BATCH_SIZE) target_n = MIN_BATCH_SIZE
|
||||||
|
n = target_n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety check - ensure we always return a valid batch size
|
||||||
|
if (!is_number(n) || n < 1) {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run a single benchmark function
|
||||||
|
function run_single_bench(bench_fn, bench_name) {
|
||||||
|
var timings_per_op = []
|
||||||
|
|
||||||
|
// Detect benchmark format:
|
||||||
|
// 1. Object with { setup, run, teardown } - structured format
|
||||||
|
// 2. Function that accepts (n) - batch format
|
||||||
|
// 3. Function that accepts () - legacy format
|
||||||
|
var is_structured = is_object(bench_fn) && bench_fn.run
|
||||||
|
var is_batch = false
|
||||||
|
var batch_size = 1
|
||||||
|
var setup_fn = null
|
||||||
|
var run_fn = null
|
||||||
|
var teardown_fn = null
|
||||||
|
|
||||||
|
if (is_structured) {
|
||||||
|
setup_fn = bench_fn.setup || function() { return null }
|
||||||
|
run_fn = bench_fn.run
|
||||||
|
teardown_fn = bench_fn.teardown || function(state) {}
|
||||||
|
|
||||||
|
// Check if run function accepts batch size
|
||||||
|
try {
|
||||||
|
var test_state = setup_fn()
|
||||||
|
run_fn(1, test_state)
|
||||||
|
is_batch = true
|
||||||
|
if (teardown_fn) teardown_fn(test_state)
|
||||||
|
} catch (e) {
|
||||||
|
is_batch = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create wrapper for calibration
|
||||||
|
var calibrate_fn = function(n) {
|
||||||
|
var state = setup_fn()
|
||||||
|
run_fn(n, state)
|
||||||
|
if (teardown_fn) teardown_fn(state)
|
||||||
|
}
|
||||||
|
batch_size = calibrate_batch_size(calibrate_fn, is_batch)
|
||||||
|
|
||||||
|
// Safety check for structured benchmarks
|
||||||
|
if (!is_number(batch_size) || batch_size < 1) {
|
||||||
|
batch_size = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Simple function format
|
||||||
|
try {
|
||||||
|
bench_fn(1)
|
||||||
|
is_batch = true
|
||||||
|
} catch (e) {
|
||||||
|
is_batch = false
|
||||||
|
}
|
||||||
|
batch_size = calibrate_batch_size(bench_fn, is_batch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety check - ensure batch_size is valid
|
||||||
|
if (!batch_size || batch_size < 1) {
|
||||||
|
batch_size = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup phase
|
||||||
|
for (var i = 0; i < WARMUP_BATCHES; i++) {
|
||||||
|
// Ensure batch_size is valid before warmup
|
||||||
|
if (!is_number(batch_size) || batch_size < 1) {
|
||||||
|
var type_str = is_null(batch_size) ? 'null' : is_number(batch_size) ? 'number' : is_text(batch_size) ? 'text' : is_object(batch_size) ? 'object' : is_array(batch_size) ? 'array' : is_function(batch_size) ? 'function' : is_logical(batch_size) ? 'logical' : 'unknown'
|
||||||
|
log.console(`WARNING: batch_size became ${type_str} = ${batch_size}, resetting to 1`)
|
||||||
|
batch_size = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_structured) {
|
||||||
|
var state = setup_fn()
|
||||||
|
if (is_batch) {
|
||||||
|
run_fn(batch_size, state)
|
||||||
|
} else {
|
||||||
|
run_fn(state)
|
||||||
|
}
|
||||||
|
if (teardown_fn) teardown_fn(state)
|
||||||
|
} else {
|
||||||
|
if (is_batch) {
|
||||||
|
bench_fn(batch_size)
|
||||||
|
} else {
|
||||||
|
bench_fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measurement phase - collect SAMPLES timing samples
|
||||||
|
for (var i = 0; i < SAMPLES; i++) {
|
||||||
|
// Double-check batch_size is valid (should never happen, but defensive)
|
||||||
|
if (!is_number(batch_size) || batch_size < 1) {
|
||||||
|
batch_size = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_structured) {
|
||||||
|
var state = setup_fn()
|
||||||
|
var start = os.now()
|
||||||
|
if (is_batch) {
|
||||||
|
run_fn(batch_size, state)
|
||||||
|
} else {
|
||||||
|
run_fn(state)
|
||||||
|
}
|
||||||
|
var duration = os.now() - start
|
||||||
|
if (teardown_fn) teardown_fn(state)
|
||||||
|
|
||||||
|
var ns_per_op = is_batch ? duration / batch_size : duration
|
||||||
|
push(timings_per_op, ns_per_op)
|
||||||
|
} else {
|
||||||
|
var start = os.now()
|
||||||
|
if (is_batch) {
|
||||||
|
bench_fn(batch_size)
|
||||||
|
} else {
|
||||||
|
bench_fn()
|
||||||
|
}
|
||||||
|
var duration = os.now() - start
|
||||||
|
|
||||||
|
var ns_per_op = is_batch ? duration / batch_size : duration
|
||||||
|
push(timings_per_op, ns_per_op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
var mean_ns = mean(timings_per_op)
|
||||||
|
var median_ns = median(timings_per_op)
|
||||||
|
var min_ns = reduce(timings_per_op, min)
|
||||||
|
var max_ns = reduce(timings_per_op, max)
|
||||||
|
var stddev_ns = stddev(timings_per_op, mean_ns)
|
||||||
|
var p95_ns = percentile(timings_per_op, 95)
|
||||||
|
var p99_ns = percentile(timings_per_op, 99)
|
||||||
|
|
||||||
|
// Calculate ops/s from median
|
||||||
|
var ops_per_sec = 0
|
||||||
|
if (median_ns > 0) {
|
||||||
|
ops_per_sec = floor(1000000000 / median_ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: bench_name,
|
||||||
|
batch_size: batch_size,
|
||||||
|
samples: SAMPLES,
|
||||||
|
mean_ns: round(mean_ns),
|
||||||
|
median_ns: round(median_ns),
|
||||||
|
min_ns: round(min_ns),
|
||||||
|
max_ns: round(max_ns),
|
||||||
|
stddev_ns: round(stddev_ns),
|
||||||
|
p95_ns: round(p95_ns),
|
||||||
|
p99_ns: round(p99_ns),
|
||||||
|
ops_per_sec: ops_per_sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format nanoseconds for display
|
||||||
|
function format_ns(ns) {
|
||||||
|
if (ns < 1000) return `${ns}ns`
|
||||||
|
if (ns < 1000000) return `${round(ns / 1000 * 100) / 100}µs`
|
||||||
|
if (ns < 1000000000) return `${round(ns / 1000000 * 100) / 100}ms`
|
||||||
|
return `${round(ns / 1000000000 * 100) / 100}s`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format ops/sec for display
|
||||||
|
function format_ops(ops) {
|
||||||
|
if (ops < 1000) return `${ops} ops/s`
|
||||||
|
if (ops < 1000000) return `${round(ops / 1000 * 100) / 100}K ops/s`
|
||||||
|
if (ops < 1000000000) return `${round(ops / 1000000 * 100) / 100}M ops/s`
|
||||||
|
return `${round(ops / 1000000000 * 100) / 100}G ops/s`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run benchmarks for a package
|
||||||
|
function run_benchmarks(package_name, specific_bench) {
|
||||||
|
var bench_files = collect_benches(package_name, specific_bench)
|
||||||
|
|
||||||
|
var pkg_result = {
|
||||||
|
package: package_name || "local",
|
||||||
|
files: [],
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(bench_files) == 0) return pkg_result
|
||||||
|
|
||||||
|
if (package_name) log.console(`Running benchmarks for ${package_name}`)
|
||||||
|
else log.console(`Running benchmarks for local package`)
|
||||||
|
|
||||||
|
arrfor(bench_files, function(f) {
|
||||||
|
var mod_path = text(f, 0, -3)
|
||||||
|
|
||||||
|
var file_result = {
|
||||||
|
name: f,
|
||||||
|
benchmarks: []
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var bench_mod
|
||||||
|
var use_pkg = package_name ? package_name : fd.realpath('.')
|
||||||
|
bench_mod = shop.use(mod_path, use_pkg)
|
||||||
|
|
||||||
|
var benches = []
|
||||||
|
if (is_function(bench_mod)) {
|
||||||
|
push(benches, {name: 'main', fn: bench_mod})
|
||||||
|
} else if (is_object(bench_mod)) {
|
||||||
|
arrfor(array(bench_mod), function(k) {
|
||||||
|
if (is_function(bench_mod[k]))
|
||||||
|
push(benches, {name: k, fn: bench_mod[k]})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(benches) > 0) {
|
||||||
|
log.console(` ${f}`)
|
||||||
|
arrfor(benches, function(b) {
|
||||||
|
try {
|
||||||
|
var result = run_single_bench(b.fn, b.name)
|
||||||
|
result.package = pkg_result.package
|
||||||
|
push(file_result.benchmarks, result)
|
||||||
|
pkg_result.total++
|
||||||
|
|
||||||
|
log.console(` ${result.name}`)
|
||||||
|
log.console(` ${format_ns(result.median_ns)}/op ${format_ops(result.ops_per_sec)}`)
|
||||||
|
log.console(` min: ${format_ns(result.min_ns)} max: ${format_ns(result.max_ns)} stddev: ${format_ns(result.stddev_ns)}`)
|
||||||
|
if (result.batch_size > 1) {
|
||||||
|
log.console(` batch: ${result.batch_size} samples: ${result.samples}`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.console(` ERROR ${b.name}: ${e}`)
|
||||||
|
log.error(e)
|
||||||
|
var error_result = {
|
||||||
|
package: pkg_result.package,
|
||||||
|
name: b.name,
|
||||||
|
error: e.toString()
|
||||||
|
}
|
||||||
|
push(file_result.benchmarks, error_result)
|
||||||
|
pkg_result.total++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.console(` Error loading ${f}: ${e}`)
|
||||||
|
var error_result = {
|
||||||
|
package: pkg_result.package,
|
||||||
|
name: "load_module",
|
||||||
|
error: `Error loading module: ${e}`
|
||||||
|
}
|
||||||
|
push(file_result.benchmarks, error_result)
|
||||||
|
pkg_result.total++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(file_result.benchmarks) > 0) {
|
||||||
|
push(pkg_result.files, file_result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return pkg_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all benchmarks
|
||||||
|
var all_results = []
|
||||||
|
|
||||||
|
if (all_pkgs) {
|
||||||
|
if (testlib.is_valid_package('.')) {
|
||||||
|
push(all_results, run_benchmarks(null, null))
|
||||||
|
}
|
||||||
|
|
||||||
|
var packages = shop.list_packages()
|
||||||
|
arrfor(packages, function(pkg) {
|
||||||
|
push(all_results, run_benchmarks(pkg, null))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
push(all_results, run_benchmarks(target_pkg, target_bench))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate totals
|
||||||
|
var total_benches = 0
|
||||||
|
arrfor(all_results, function(result) {
|
||||||
|
total_benches += result.total
|
||||||
|
})
|
||||||
|
|
||||||
|
log.console(`----------------------------------------`)
|
||||||
|
log.console(`Benchmarks: ${total_benches} total`)
|
||||||
|
|
||||||
|
// Generate reports
|
||||||
|
function generate_reports() {
|
||||||
|
var timestamp = text(floor(time.number()))
|
||||||
|
var report_dir = shop.get_reports_dir() + '/bench_' + timestamp
|
||||||
|
testlib.ensure_dir(report_dir)
|
||||||
|
|
||||||
|
var txt_report = `BENCHMARK REPORT
|
||||||
|
Date: ${time.text(time.number())}
|
||||||
|
Total benchmarks: ${total_benches}
|
||||||
|
|
||||||
|
=== SUMMARY ===
|
||||||
|
`
|
||||||
|
arrfor(all_results, function(pkg_res) {
|
||||||
|
if (pkg_res.total == 0) return
|
||||||
|
txt_report += `Package: ${pkg_res.package}\n`
|
||||||
|
arrfor(pkg_res.files, function(f) {
|
||||||
|
txt_report += ` ${f.name}\n`
|
||||||
|
arrfor(f.benchmarks, function(b) {
|
||||||
|
if (b.error) {
|
||||||
|
txt_report += ` ERROR ${b.name}: ${b.error}\n`
|
||||||
|
} else {
|
||||||
|
txt_report += ` ${b.name}: ${format_ns(b.median_ns)}/op (${format_ops(b.ops_per_sec)})\n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
txt_report += `\n=== DETAILED RESULTS ===\n`
|
||||||
|
arrfor(all_results, function(pkg_res) {
|
||||||
|
if (pkg_res.total == 0) return
|
||||||
|
|
||||||
|
arrfor(pkg_res.files, function(f) {
|
||||||
|
arrfor(f.benchmarks, function(b) {
|
||||||
|
if (b.error) return
|
||||||
|
|
||||||
|
txt_report += `\n${pkg_res.package}::${b.name}\n`
|
||||||
|
txt_report += ` batch_size: ${b.batch_size} samples: ${b.samples}\n`
|
||||||
|
txt_report += ` median: ${format_ns(b.median_ns)}/op\n`
|
||||||
|
txt_report += ` mean: ${format_ns(b.mean_ns)}/op\n`
|
||||||
|
txt_report += ` min: ${format_ns(b.min_ns)}\n`
|
||||||
|
txt_report += ` max: ${format_ns(b.max_ns)}\n`
|
||||||
|
txt_report += ` stddev: ${format_ns(b.stddev_ns)}\n`
|
||||||
|
txt_report += ` p95: ${format_ns(b.p95_ns)}\n`
|
||||||
|
txt_report += ` p99: ${format_ns(b.p99_ns)}\n`
|
||||||
|
txt_report += ` ops/s: ${format_ops(b.ops_per_sec)}\n`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
testlib.ensure_dir(report_dir)
|
||||||
|
fd.slurpwrite(`${report_dir}/bench.txt`, stone(blob(txt_report)))
|
||||||
|
log.console(`Report written to ${report_dir}/bench.txt`)
|
||||||
|
|
||||||
|
// Generate JSON per package
|
||||||
|
arrfor(all_results, function(pkg_res) {
|
||||||
|
if (pkg_res.total == 0) return
|
||||||
|
|
||||||
|
var pkg_benches = []
|
||||||
|
arrfor(pkg_res.files, function(f) {
|
||||||
|
arrfor(f.benchmarks, function(benchmark) {
|
||||||
|
push(pkg_benches, benchmark)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
var json_path = `${report_dir}/${replace(pkg_res.package, /\//, '_')}.json`
|
||||||
|
fd.slurpwrite(json_path, stone(blob(json.encode(pkg_benches))))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_reports()
|
||||||
|
$stop()
|
||||||
262
benches/micro_ops.cm
Normal file
262
benches/micro_ops.cm
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
// micro_ops.bench.ce (or .cm depending on your convention)
|
||||||
|
|
||||||
|
// Note: We use a function-local sink in each benchmark to avoid cross-contamination
|
||||||
|
function blackhole(sink, x) {
|
||||||
|
// Prevent dead-code elimination
|
||||||
|
return (sink + (x | 0)) | 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_obj_xy(x, y) {
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_obj_yx(x, y) {
|
||||||
|
// Different insertion order to force a different shape in many engines
|
||||||
|
return { y, x }
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_shapes(n) {
|
||||||
|
var out = []
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var o = { a: i }
|
||||||
|
o[`p${i}`] = i
|
||||||
|
push(out, o)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_packed_array(n) {
|
||||||
|
var a = []
|
||||||
|
for (var i = 0; i < n; i++) push(a, i)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_holey_array(n) {
|
||||||
|
var a = []
|
||||||
|
for (var i = 0; i < n; i += 2) a[i] = i
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 0) Baseline loop cost
|
||||||
|
loop_empty: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
for (var i = 0; i < n; i++) {}
|
||||||
|
return blackhole(sink, n)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 1) Numeric pipelines
|
||||||
|
i32_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1
|
||||||
|
for (var i = 0; i < n; i++) x = (x + 3) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
f64_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1.0
|
||||||
|
for (var i = 0; i < n; i++) x = x + 3.14159
|
||||||
|
return blackhole(sink, x | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
mixed_add: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 1
|
||||||
|
for (var i = 0; i < n; i++) x = x + 0.25
|
||||||
|
return blackhole(sink, x | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
bit_ops: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0x12345678
|
||||||
|
for (var i = 0; i < n; i++) x = ((x << 5) ^ (x >>> 3)) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
overflow_path: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0x70000000
|
||||||
|
for (var i = 0; i < n; i++) x = (x + 0x10000000) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2) Branching
|
||||||
|
branch_predictable: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
if ((i & 7) != 0) x++
|
||||||
|
else x += 2
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
branch_alternating: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
if ((i & 1) == 0) x++
|
||||||
|
else x += 2
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3) Calls
|
||||||
|
call_direct: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
function f(a) { return (a + 1) | 0 }
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) x = f(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
call_indirect: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
function f(a) { return (a + 1) | 0 }
|
||||||
|
var g = f
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) x = g(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
call_closure: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
function make_adder(k) {
|
||||||
|
return function(a) { return (a + k) | 0 }
|
||||||
|
}
|
||||||
|
var add3 = make_adder(3)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) x = add3(x)
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4) Object props (ICs / shapes)
|
||||||
|
prop_read_mono: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var o = make_obj_xy(1, 2)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) x = (x + o.x) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_read_poly_2: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_obj_xy(1, 2)
|
||||||
|
var b = make_obj_yx(1, 2)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var o = (i & 1) == 0 ? a : b
|
||||||
|
x = (x + o.x) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_read_mega: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var objs = make_shapes(32)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var o = objs[i & 31]
|
||||||
|
x = (x + o.a) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
prop_write_mono: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var o = make_obj_xy(1, 2)
|
||||||
|
for (var i = 0; i < n; i++) o.x = (o.x + 1) | 0
|
||||||
|
return blackhole(sink, o.x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5) Arrays
|
||||||
|
array_read_packed: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(1024)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) x = (x + a[i & 1023]) | 0
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_write_packed: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_packed_array(1024)
|
||||||
|
for (var i = 0; i < n; i++) a[i & 1023] = i
|
||||||
|
return blackhole(sink, a[17] | 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_read_holey: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var a = make_holey_array(2048)
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var v = a[(i & 2047)]
|
||||||
|
// If "missing" is a special value in your language, this stresses that path too
|
||||||
|
if (v) x = (x + v) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
array_push_steady: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
for (var j = 0; j < n; j++) {
|
||||||
|
var a = []
|
||||||
|
for (var i = 0; i < 256; i++) push(a, i)
|
||||||
|
x = (x + length(a)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 6) Strings
|
||||||
|
string_concat_small: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
for (var j = 0; j < n; j++) {
|
||||||
|
var s = ""
|
||||||
|
for (var i = 0; i < 16; i++) s = s + "x"
|
||||||
|
x = (x + length(s)) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 7) Allocation / GC pressure
|
||||||
|
alloc_tiny_objects: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var o = { a: i, b: i + 1, c: i + 2 }
|
||||||
|
x = (x + o.b) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
alloc_linked_list: function(n) {
|
||||||
|
var sink = 0
|
||||||
|
var head = null
|
||||||
|
for (var i = 0; i < n; i++) head = { v: i, next: head }
|
||||||
|
var x = 0
|
||||||
|
var p = head
|
||||||
|
while (p) {
|
||||||
|
x = (x + p.v) | 0
|
||||||
|
p = p.next
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 8) meme-specific (adapt these to your exact semantics)
|
||||||
|
meme_clone_read: function(n) {
|
||||||
|
// If meme(obj) clones like Object.create / prototypal clone, this hits it hard.
|
||||||
|
// Replace with your exact meme call form.
|
||||||
|
var sink = 0
|
||||||
|
var base = { x: 1, y: 2 }
|
||||||
|
var x = 0
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var o = meme(base)
|
||||||
|
x = (x + o.x) | 0
|
||||||
|
}
|
||||||
|
return blackhole(sink, x)
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
43
benchmarks/binarytree.ce
Normal file
43
benchmarks/binarytree.ce
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
function mainThread() {
|
||||||
|
var maxDepth = 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 (var 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) {
|
||||||
|
var check = 0;
|
||||||
|
for (var 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
|
||||||
|
? TreeNode(bottomUpTree(depth - 1), bottomUpTree(depth - 1))
|
||||||
|
: TreeNode(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainThread()
|
||||||
|
|
||||||
|
$stop()
|
||||||
25
benchmarks/eratosthenes.ce
Normal file
25
benchmarks/eratosthenes.ce
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
var blob = use('blob')
|
||||||
|
var math = use('math/radians')
|
||||||
|
|
||||||
|
function eratosthenes (n) {
|
||||||
|
var sieve = blob(n, true)
|
||||||
|
var sqrtN = whole(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 < length(sieve); i++)
|
||||||
|
if (sieve.read_logical(i)) c++
|
||||||
|
|
||||||
|
log.console(c)
|
||||||
|
|
||||||
|
$stop()
|
||||||
58
benchmarks/fannkuch.ce
Normal file
58
benchmarks/fannkuch.ce
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
function fannkuch(n) {
|
||||||
|
var perm1 = [n]
|
||||||
|
for (var 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) {
|
||||||
|
var 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
|
||||||
|
}
|
||||||
|
var p0 = perm1[0]
|
||||||
|
i = 0
|
||||||
|
while (i < r) {
|
||||||
|
var 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()
|
||||||
16
benchmarks/fib.ce
Normal file
16
benchmarks/fib.ce
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
var time = use('time')
|
||||||
|
|
||||||
|
function fib(n) {
|
||||||
|
if (n<2) return n
|
||||||
|
return fib(n-1) + fib(n-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = time.number()
|
||||||
|
var arr = [1,2,3,4,5]
|
||||||
|
arrfor(arr, function(i) {
|
||||||
|
log.console(fib(28))
|
||||||
|
})
|
||||||
|
|
||||||
|
log.console(`elapsed: ${time.number()-now}`)
|
||||||
|
|
||||||
|
$stop()
|
||||||
20
benchmarks/hyperfine_wota_nota_json.sh
Executable file
20
benchmarks/hyperfine_wota_nota_json.sh
Executable 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
395
benchmarks/js_perf.ce
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
var time = use('time')
|
||||||
|
var math = use('math/radians')
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// JavaScript Performance Benchmark Suite
|
||||||
|
// Tests core JS operations: property access, function calls, arithmetic, etc.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Test configurations
|
||||||
|
def 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++) {
|
||||||
|
push(arr, i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var arr = [];
|
||||||
|
for (var i = 0; i < 10000; i++) push(arr, 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 < length(arr); 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) {
|
||||||
|
return {x,y}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defructorTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.medium; i++) {
|
||||||
|
var p = 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 = meme(protoObj);
|
||||||
|
obj.x = i;
|
||||||
|
obj.y = i * 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
literalTime: literalTime,
|
||||||
|
defructorTime: defructorTime,
|
||||||
|
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++) {
|
||||||
|
push(strings, "string" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinTime = measureTime(function() {
|
||||||
|
for (var i = 0; i < iterations.complex; i++) {
|
||||||
|
var result = text(strings, ",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = array(str, ",");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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.sine(result) + math.cosine(i * 0.01);
|
||||||
|
result = math.sqrt(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++) {
|
||||||
|
push(funcs, makeAdder(i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var adders = [];
|
||||||
|
for (var i = 0; i < 1000; i++) {
|
||||||
|
push(adders, 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.defructorTime.toFixed(3) + "s => " +
|
||||||
|
(iterations.medium / objResults.defructorTime).toFixed(1) + " creates/sec [" +
|
||||||
|
(objResults.defructorTime / 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
40
benchmarks/mandelbrot.ce
Normal 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 (var y = 0; y < h; ++y) {
|
||||||
|
// Create a blob for the row - we need w bits
|
||||||
|
var row = blob(w);
|
||||||
|
|
||||||
|
for (var x = 0; x < w; ++x) {
|
||||||
|
zr = zi = tr = ti = 0;
|
||||||
|
cr = 2 * x / w - 1.5;
|
||||||
|
ci = 2 * y / h - 1;
|
||||||
|
for (var 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()
|
||||||
12
benchmarks/montecarlo.ce
Normal file
12
benchmarks/montecarlo.ce
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
var math = use('math/radians')
|
||||||
|
var N = 1000000;
|
||||||
|
var num = 0;
|
||||||
|
for (var i = 0; i < N; i ++) {
|
||||||
|
var x = 2 * $random();
|
||||||
|
var y = $random();
|
||||||
|
if (y < math.sine(x * x))
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
log.console(2 * num / N);
|
||||||
|
|
||||||
|
$stop()
|
||||||
155
benchmarks/nbody.ce
Normal file
155
benchmarks/nbody.ce
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
var math = use('math/radians')
|
||||||
|
var SOLAR_MASS = 4 * pi * pi;
|
||||||
|
var DAYS_PER_YEAR = 365.24;
|
||||||
|
|
||||||
|
function Body(x, y, z, vx, vy, vz, mass) {
|
||||||
|
return {x, y, z, vx, vy, vz, mass};
|
||||||
|
}
|
||||||
|
|
||||||
|
function Jupiter() {
|
||||||
|
return 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 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 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 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 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() {
|
||||||
|
var px = 0;
|
||||||
|
var py = 0;
|
||||||
|
var pz = 0;
|
||||||
|
var size = length(bodies);
|
||||||
|
for (var 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 = length(bodies);
|
||||||
|
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var bodyi = bodies[i];
|
||||||
|
var vxi = bodyi.vx;
|
||||||
|
var vyi = bodyi.vy;
|
||||||
|
var vzi = bodyi.vz;
|
||||||
|
for (var 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 (var 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() {
|
||||||
|
var e = 0;
|
||||||
|
var size = length(bodies);
|
||||||
|
|
||||||
|
for (var 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 (var 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] || 100000
|
||||||
|
|
||||||
|
offsetMomentum();
|
||||||
|
|
||||||
|
log.console(`n = ${n}`)
|
||||||
|
log.console(energy().toFixed(9))
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
advance(0.01);
|
||||||
|
log.console(energy().toFixed(9))
|
||||||
|
|
||||||
|
$stop()
|
||||||
78
benchmarks/nota.ce
Normal file
78
benchmarks/nota.ce
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
var nota = use('nota')
|
||||||
|
var os = use('os')
|
||||||
|
var io = use('fd')
|
||||||
|
var json = use('json')
|
||||||
|
|
||||||
|
var ll = io.slurp('benchmarks/nota.json')
|
||||||
|
|
||||||
|
var newarr = []
|
||||||
|
var accstr = ""
|
||||||
|
for (var i = 0; i < 10000; i++) {
|
||||||
|
accstr += i;
|
||||||
|
newarrpush(i.toString())
|
||||||
|
}
|
||||||
|
// Arrays to store timing results
|
||||||
|
var jsonDecodeTimes = [];
|
||||||
|
var jsonEncodeTimes = [];
|
||||||
|
var notaEncodeTimes = [];
|
||||||
|
var notaDecodeTimes = [];
|
||||||
|
var notaSizes = [];
|
||||||
|
|
||||||
|
// Run 100 tests
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
// JSON Decode test
|
||||||
|
var start = os.now();
|
||||||
|
var jll = json.decode(ll);
|
||||||
|
jsonDecodeTimespush((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// JSON Encode test
|
||||||
|
start = os.now();
|
||||||
|
var jsonStr = JSON.stringify(jll);
|
||||||
|
jsonEncodeTimespush((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// NOTA Encode test
|
||||||
|
start = os.now();
|
||||||
|
var nll = nota.encode(jll);
|
||||||
|
notaEncodeTimespush((os.now() - start) * 1000);
|
||||||
|
|
||||||
|
// NOTA Decode test
|
||||||
|
start = os.now();
|
||||||
|
var oll = nota.decode(nll);
|
||||||
|
notaDecodeTimespush((os.now() - start) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
function getStats(arr) {
|
||||||
|
return {
|
||||||
|
avg: reduce(arr, (a,b) => a+b, 0) / length(arr),
|
||||||
|
min: reduce(arr, min),
|
||||||
|
max: reduce(arr, max)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty print results
|
||||||
|
log.console("\n== Performance Test Results (100 iterations) ==");
|
||||||
|
log.console("\nJSON Decoding (ms):");
|
||||||
|
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):");
|
||||||
|
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):");
|
||||||
|
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):");
|
||||||
|
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`);
|
||||||
|
|
||||||
2132
benchmarks/nota.json
Normal file
2132
benchmarks/nota.json
Normal file
File diff suppressed because it is too large
Load Diff
52
benchmarks/spectral-norm.ce
Normal file
52
benchmarks/spectral-norm.ce
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
def math = use('math/radians');
|
||||||
|
|
||||||
|
function A(i,j) {
|
||||||
|
return 1/((i+j)*(i+j+1)/2+i+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Au(u,v) {
|
||||||
|
for (var i=0; i<length(u); ++i) {
|
||||||
|
var t = 0;
|
||||||
|
for (var j=0; j<length(u); ++j)
|
||||||
|
t += A(i,j) * u[j];
|
||||||
|
|
||||||
|
v[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Atu(u,v) {
|
||||||
|
for (var i=0; i<length(u); ++i) {
|
||||||
|
var t = 0;
|
||||||
|
for (var j=0; j<length(u); ++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()
|
||||||
95
benchmarks/wota.ce
Normal file
95
benchmarks/wota.ce
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// wota_benchmark.js
|
||||||
|
//
|
||||||
|
// Usage in QuickJS:
|
||||||
|
// qjs wota_benchmark.js
|
||||||
|
//
|
||||||
|
// Prerequisite:
|
||||||
|
var wota = use('wota');
|
||||||
|
var os = use('os');
|
||||||
|
// or otherwise ensure `wota` and `os` are available.
|
||||||
|
// Make sure wota_benchmark.js is loaded after wota.js or combined with it.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Helper to run a function repeatedly and measure total time in seconds.
|
||||||
|
// Returns elapsed time in seconds.
|
||||||
|
function measureTime(fn, iterations) {
|
||||||
|
var t1 = os.now();
|
||||||
|
for (var i = 0; i < iterations; i++) {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
var t2 = os.now();
|
||||||
|
return t2 - t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll define a function that does `encode -> decode` for a given value:
|
||||||
|
function roundTripWota(value) {
|
||||||
|
var encoded = wota.encode(value);
|
||||||
|
var decoded = wota.decode(encoded);
|
||||||
|
// Not doing a deep compare here, just measuring performance.
|
||||||
|
// (We trust the test suite to verify correctness.)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A small suite of data we want to benchmark. Each entry includes:
|
||||||
|
// name: label for printing
|
||||||
|
// data: the test value(s) to encode/decode
|
||||||
|
// iterations: how many times to loop
|
||||||
|
//
|
||||||
|
// You can tweak these as you like for heavier or lighter tests.
|
||||||
|
def benchmarks = [
|
||||||
|
{
|
||||||
|
name: "Small Integers",
|
||||||
|
data: [0, 42, -1, 2023],
|
||||||
|
iterations: 100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Strings (short, emoji)",
|
||||||
|
data: ["Hello, Wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||||
|
iterations: 100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Small Objects",
|
||||||
|
data: [
|
||||||
|
{ a:1, b:2.2, c:"3", d:false },
|
||||||
|
{ x:42, y:null, z:"test" }
|
||||||
|
],
|
||||||
|
iterations: 50000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nested Arrays",
|
||||||
|
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
||||||
|
iterations: 50000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Large Array (1k numbers)",
|
||||||
|
// A thousand random numbers
|
||||||
|
data: [ array(1000, i => i *0.5) ],
|
||||||
|
iterations: 1000
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Print a header
|
||||||
|
log.console("Wota Encode/Decode Benchmark");
|
||||||
|
log.console("===================\n");
|
||||||
|
|
||||||
|
// We'll run each benchmark scenario in turn.
|
||||||
|
arrfor(benchmarks, function(bench) {
|
||||||
|
var totalIterations = bench.iterations * length(bench.data);
|
||||||
|
|
||||||
|
// We'll define a function that does a roundTrip for *each* data item in bench.data
|
||||||
|
// to measure in one loop iteration. Then we multiply by bench.iterations.
|
||||||
|
function runAllData() {
|
||||||
|
arrfor(bench.data, roundTripWota)
|
||||||
|
}
|
||||||
|
|
||||||
|
var elapsedSec = measureTime(runAllData, bench.iterations);
|
||||||
|
var opsPerSec = (totalIterations / elapsedSec).toFixed(1);
|
||||||
|
|
||||||
|
log.console(`${bench.name}:`);
|
||||||
|
log.console(` Iterations: ${bench.iterations} × ${length(bench.data)} data items = ${totalIterations}`);
|
||||||
|
log.console(` Elapsed: ${elapsedSec.toFixed(3)} s`);
|
||||||
|
log.console(` Throughput: ${opsPerSec} encode+decode ops/sec\n`);
|
||||||
|
})
|
||||||
|
|
||||||
|
// All done
|
||||||
|
log.console("Benchmark completed.\n");
|
||||||
199
benchmarks/wota_nota_json.ce
Normal file
199
benchmarks/wota_nota_json.ce
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// benchmark_wota_nota_json.js
|
||||||
|
//
|
||||||
|
// Usage in QuickJS:
|
||||||
|
// qjs benchmark_wota_nota_json.js <LibraryName> <ScenarioName>
|
||||||
|
//
|
||||||
|
// Ensure wota, nota, json, and os are all available, e.g.:
|
||||||
|
var wota = use('wota');
|
||||||
|
var nota = use('nota');
|
||||||
|
var json = use('json');
|
||||||
|
var jswota = use('jswota')
|
||||||
|
var os = use('os');
|
||||||
|
//
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
if (length(arg) != 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
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
def libraries = [
|
||||||
|
{
|
||||||
|
name: "wota",
|
||||||
|
encode: wota.encode,
|
||||||
|
decode: wota.decode,
|
||||||
|
// wota produces an ArrayBuffer. We'll count `buffer.byteLength` as size.
|
||||||
|
getSize(encoded) {
|
||||||
|
return length(encoded);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nota",
|
||||||
|
encode: nota.encode,
|
||||||
|
decode: nota.decode,
|
||||||
|
// nota also produces an ArrayBuffer:
|
||||||
|
getSize(encoded) {
|
||||||
|
return length(encoded);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "json",
|
||||||
|
encode: json.encode,
|
||||||
|
decode: json.decode,
|
||||||
|
// 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
|
||||||
|
getSize(encodedStr) {
|
||||||
|
return length(encodedStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 2. Test data sets (similar to wota benchmarks).
|
||||||
|
// Each scenario has { name, data, iterations }
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
def benchmarks = [
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
data: [{}, {}, {}, {}],
|
||||||
|
iterations: 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "integers",
|
||||||
|
data: [0, 42, -1, 2023],
|
||||||
|
iterations: 100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "floats",
|
||||||
|
data: [0.1, 1e-50, 3.14159265359],
|
||||||
|
iterations: 100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "strings",
|
||||||
|
data: ["Hello, wota!", "short", "Emoji: \u{1f600}\u{1f64f}"],
|
||||||
|
iterations: 100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "objects",
|
||||||
|
data: [
|
||||||
|
{ a:1, b:2.2, c:"3", d:false },
|
||||||
|
{ x:42, y:null, z:"test" }
|
||||||
|
],
|
||||||
|
iterations: 50000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested",
|
||||||
|
data: [ [ [ [1,2], [3,4] ] ], [[[]]], [1, [2, [3, [4]]]] ],
|
||||||
|
iterations: 50000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "large_array",
|
||||||
|
data: [ array(1000, i => i) ],
|
||||||
|
iterations: 1000
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 3. Utility: measureTime(fn) => how long fn() takes in seconds.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function measureTime(fn) {
|
||||||
|
var start = os.now();
|
||||||
|
fn();
|
||||||
|
var end = os.now();
|
||||||
|
return (end - start); // in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function runBenchmarkForLibrary(lib, bench) {
|
||||||
|
// We'll encode and decode each item in `bench.data`.
|
||||||
|
// We do 'bench.iterations' times. Then sum up total time.
|
||||||
|
|
||||||
|
// Pre-store the encoded results for all items so we can measure decode time
|
||||||
|
// in a separate pass. Also measure total size once.
|
||||||
|
var encodedList = [];
|
||||||
|
var totalSize = 0;
|
||||||
|
|
||||||
|
// 1) Measure ENCODING
|
||||||
|
var encodeTime = measureTime(() => {
|
||||||
|
for (var i = 0; i < bench.iterations; i++) {
|
||||||
|
// For each data item, encode it
|
||||||
|
for (var j = 0; j < length(bench.data); j++) {
|
||||||
|
var 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) {
|
||||||
|
push(encodedList, e);
|
||||||
|
totalSize += lib.getSize(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) Measure DECODING
|
||||||
|
var decodeTime = measureTime(() => {
|
||||||
|
for (var i = 0; i < bench.iterations; i++) {
|
||||||
|
arrfor(encodedList, lib.decode)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { encodeTime, decodeTime, totalSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 5. Main driver: run only the specified library and scenario
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Find the requested library and scenario
|
||||||
|
var lib = libraries[find(libraries, l => l.name == lib_name)];
|
||||||
|
var bench = benchmarks[find(benchmarks, b => b.name == scenario_name)];
|
||||||
|
|
||||||
|
if (!lib) {
|
||||||
|
log.console('Unknown library:', lib_name);
|
||||||
|
log.console('Available libraries:', text(array(libraries, l => l.name), ', '));
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bench) {
|
||||||
|
log.console('Unknown scenario:', scenario_name);
|
||||||
|
log.console('Available scenarios:', text(array(benchmarks, b => b.name), ', '));
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 * length(bench.data);
|
||||||
|
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(result);
|
||||||
|
|
||||||
|
$stop()
|
||||||
124
build.ce
Normal file
124
build.ce
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// cell build [<locator>] - Build dynamic libraries locally for the current machine
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cell build Build dynamic libraries for all packages in shop
|
||||||
|
// cell build . Build dynamic library for current directory package
|
||||||
|
// cell build <locator> Build dynamic library for specific package
|
||||||
|
// cell build -t <target> Cross-compile dynamic libraries for target platform
|
||||||
|
// cell build -b <type> Build type: release (default), debug, or minsize
|
||||||
|
|
||||||
|
var build = use('build')
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var pkg_tools = use('package')
|
||||||
|
var fd = use('fd')
|
||||||
|
|
||||||
|
var target = null
|
||||||
|
var target_package = null
|
||||||
|
var buildtype = 'release'
|
||||||
|
var force_rebuild = false
|
||||||
|
var dry_run = false
|
||||||
|
|
||||||
|
for (var i = 0; i < length(args); i++) {
|
||||||
|
if (args[i] == '-t' || args[i] == '--target') {
|
||||||
|
if (i + 1 < length(args)) {
|
||||||
|
target = args[++i]
|
||||||
|
} else {
|
||||||
|
log.error('-t requires a target')
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
} else if (args[i] == '-p' || args[i] == '--package') {
|
||||||
|
// Legacy support for -p flag
|
||||||
|
if (i + 1 < length(args)) {
|
||||||
|
target_package = args[++i]
|
||||||
|
} else {
|
||||||
|
log.error('-p requires a package name')
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
} else if (args[i] == '-b' || args[i] == '--buildtype') {
|
||||||
|
if (i + 1 < length(args)) {
|
||||||
|
buildtype = args[++i]
|
||||||
|
if (buildtype != 'release' && buildtype != 'debug' && buildtype != 'minsize') {
|
||||||
|
log.error('Invalid buildtype: ' + buildtype + '. Must be release, debug, or minsize')
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error('-b requires a buildtype (release, debug, minsize)')
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
} else if (args[i] == '--force') {
|
||||||
|
force_rebuild = true
|
||||||
|
} else if (args[i] == '--dry-run') {
|
||||||
|
dry_run = true
|
||||||
|
} else if (args[i] == '--list-targets') {
|
||||||
|
log.console('Available targets:')
|
||||||
|
var targets = build.list_targets()
|
||||||
|
for (var t = 0; t < length(targets); t++) {
|
||||||
|
log.console(' ' + targets[t])
|
||||||
|
}
|
||||||
|
$stop()
|
||||||
|
} else if (!starts_with(args[i], '-') && !target_package) {
|
||||||
|
// Positional argument - treat as package locator
|
||||||
|
target_package = args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve local paths to absolute paths
|
||||||
|
if (target_package) {
|
||||||
|
if (target_package == '.' || starts_with(target_package, './') || starts_with(target_package, '../') || fd.is_dir(target_package)) {
|
||||||
|
var resolved = fd.realpath(target_package)
|
||||||
|
if (resolved) {
|
||||||
|
target_package = resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect target if not specified
|
||||||
|
if (!target) {
|
||||||
|
target = build.detect_host_target()
|
||||||
|
if (target) log.console('Target: ' + target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target && !build.has_target(target)) {
|
||||||
|
log.error('Invalid target: ' + target)
|
||||||
|
log.console('Available targets: ' + text(build.list_targets(), ', '))
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
var packages = shop.list_packages()
|
||||||
|
log.console('Preparing packages...')
|
||||||
|
arrfor(packages, function(package) {
|
||||||
|
if (package == 'core') return
|
||||||
|
shop.extract(package)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (target_package) {
|
||||||
|
// Build single package
|
||||||
|
log.console('Building ' + target_package + '...')
|
||||||
|
try {
|
||||||
|
var lib = build.build_dynamic(target_package, target, buildtype)
|
||||||
|
if (lib) {
|
||||||
|
log.console('Built: ' + lib)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.error('Build failed: ' + e)
|
||||||
|
$stop()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Build all packages
|
||||||
|
log.console('Building all packages...')
|
||||||
|
var results = build.build_all_dynamic(target, buildtype)
|
||||||
|
|
||||||
|
var success = 0
|
||||||
|
var failed = 0
|
||||||
|
for (var i = 0; i < length(results); i++) {
|
||||||
|
if (results[i].library) {
|
||||||
|
success++
|
||||||
|
} else if (results[i].error) {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console(`Build complete: ${success} libraries built${failed > 0 ? `, ${failed} failed` : ''}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
$stop()
|
||||||
455
build.cm
Normal file
455
build.cm
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
// build.cm - Simplified build utilities for Cell
|
||||||
|
//
|
||||||
|
// Key functions:
|
||||||
|
// Build.compile_file(pkg, file, target) - Compile a C file, returns object path
|
||||||
|
// Build.build_package(pkg, target) - Build all C files for a package
|
||||||
|
// Build.build_dynamic(pkg, target) - Build dynamic library for a package
|
||||||
|
// Build.build_static(packages, target, output) - Build static binary
|
||||||
|
|
||||||
|
var fd = use('fd')
|
||||||
|
var crypto = use('crypto')
|
||||||
|
var blob = use('blob')
|
||||||
|
var os = use('os')
|
||||||
|
var toolchains = use('toolchains')
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var pkg_tools = use('package')
|
||||||
|
|
||||||
|
var Build = {}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Sigil replacement
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Get the local directory for prebuilt libraries
|
||||||
|
function get_local_dir() {
|
||||||
|
return shop.get_local_dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace sigils in a string
|
||||||
|
// Currently supports: $LOCAL -> .cell/local full path
|
||||||
|
function replace_sigils(str) {
|
||||||
|
return replace(str, '$LOCAL', get_local_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace sigils in an array of flags
|
||||||
|
function replace_sigils_array(flags) {
|
||||||
|
var result = []
|
||||||
|
arrfor(flags, function(flag) {
|
||||||
|
push(result, replace_sigils(flag))
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Build.get_local_dir = get_local_dir
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Toolchain helpers
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
Build.list_targets = function() {
|
||||||
|
return array(toolchains)
|
||||||
|
}
|
||||||
|
|
||||||
|
Build.has_target = function(target) {
|
||||||
|
return toolchains[target] != null
|
||||||
|
}
|
||||||
|
|
||||||
|
Build.detect_host_target = function() {
|
||||||
|
var platform = os.platform()
|
||||||
|
var arch = os.arch ? os.arch() : 'arm64'
|
||||||
|
|
||||||
|
if (platform == 'macOS' || platform == 'darwin') {
|
||||||
|
return arch == 'x86_64' ? 'macos_x86_64' : 'macos_arm64'
|
||||||
|
} else if (platform == 'Linux' || platform == 'linux') {
|
||||||
|
return arch == 'x86_64' ? 'linux' : 'linux_arm64'
|
||||||
|
} else if (platform == 'Windows' || platform == 'windows') {
|
||||||
|
return 'windows'
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Content-addressed build cache
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
function content_hash(str) {
|
||||||
|
var bb = stone(blob(str))
|
||||||
|
return text(crypto.blake2(bb, 32), 'h')
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_build_dir() {
|
||||||
|
return shop.get_build_dir()
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensure_dir(path) {
|
||||||
|
if (fd.stat(path).isDirectory) return
|
||||||
|
var parts = array(path, '/')
|
||||||
|
var current = starts_with(path, '/') ? '/' : ''
|
||||||
|
for (var i = 0; i < length(parts); i++) {
|
||||||
|
if (parts[i] == '') continue
|
||||||
|
current += parts[i] + '/'
|
||||||
|
if (!fd.stat(current).isDirectory) fd.mkdir(current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Build.ensure_dir = ensure_dir
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Compilation
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Compile a single C file for a package
|
||||||
|
// Returns the object file path (content-addressed in .cell/build)
|
||||||
|
Build.compile_file = function(pkg, file, target, buildtype = 'release') {
|
||||||
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
|
var src_path = pkg_dir + '/' + file
|
||||||
|
|
||||||
|
if (!fd.is_file(src_path)) {
|
||||||
|
throw Error('Source file not found: ' + src_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get flags (with sigil replacement)
|
||||||
|
var cflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'CFLAGS', target))
|
||||||
|
var target_cflags = toolchains[target].c_args || []
|
||||||
|
var cc = toolchains[target].c
|
||||||
|
|
||||||
|
// Symbol name for this file
|
||||||
|
var sym_name = shop.c_symbol_for_file(pkg, file)
|
||||||
|
|
||||||
|
// Build command
|
||||||
|
var cmd_parts = [cc, '-c', '-fPIC']
|
||||||
|
|
||||||
|
// Add buildtype-specific flags
|
||||||
|
if (buildtype == 'release') {
|
||||||
|
cmd_parts = array(cmd_parts, ['-O3', '-DNDEBUG'])
|
||||||
|
} else if (buildtype == 'debug') {
|
||||||
|
cmd_parts = array(cmd_parts, ['-O2', '-g'])
|
||||||
|
} else if (buildtype == 'minsize') {
|
||||||
|
cmd_parts = array(cmd_parts, ['-Os', '-DNDEBUG'])
|
||||||
|
}
|
||||||
|
|
||||||
|
push(cmd_parts, '-DCELL_USE_NAME=' + sym_name)
|
||||||
|
push(cmd_parts, '-I"' + pkg_dir + '"')
|
||||||
|
|
||||||
|
// Add package CFLAGS (resolve relative -I paths)
|
||||||
|
arrfor(cflags, function(flag) {
|
||||||
|
if (starts_with(flag, '-I') && !starts_with(flag, '-I/')) {
|
||||||
|
flag = '-I"' + pkg_dir + '/' + text(flag, 2) + '"'
|
||||||
|
}
|
||||||
|
push(cmd_parts, flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add target CFLAGS
|
||||||
|
arrfor(target_cflags, function(flag) {
|
||||||
|
push(cmd_parts, flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
push(cmd_parts, '"' + src_path + '"')
|
||||||
|
|
||||||
|
var cmd_str = text(cmd_parts, ' ')
|
||||||
|
|
||||||
|
// Content hash: command + file content
|
||||||
|
var file_content = fd.slurp(src_path)
|
||||||
|
var hash_input = cmd_str + '\n' + text(file_content)
|
||||||
|
var hash = content_hash(hash_input)
|
||||||
|
|
||||||
|
var build_dir = get_build_dir()
|
||||||
|
ensure_dir(build_dir)
|
||||||
|
var obj_path = build_dir + '/' + hash
|
||||||
|
|
||||||
|
// Check if already compiled
|
||||||
|
if (fd.is_file(obj_path)) {
|
||||||
|
return obj_path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile
|
||||||
|
var full_cmd = cmd_str + ' -o "' + obj_path + '"'
|
||||||
|
log.console('Compiling ' + file)
|
||||||
|
var ret = os.system(full_cmd)
|
||||||
|
if (ret != 0) {
|
||||||
|
throw Error('Compilation failed: ' + file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj_path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build all C files for a package
|
||||||
|
// Returns array of object file paths
|
||||||
|
Build.build_package = function(pkg, target = Build.detect_host_target(), exclude_main, buildtype = 'release') {
|
||||||
|
var c_files = pkg_tools.get_c_files(pkg, target, exclude_main)
|
||||||
|
var objects = []
|
||||||
|
|
||||||
|
arrfor(c_files, function(file) {
|
||||||
|
var obj = Build.compile_file(pkg, file, target, buildtype)
|
||||||
|
push(objects, obj)
|
||||||
|
})
|
||||||
|
|
||||||
|
return objects
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Dynamic library building
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Compute link key from all inputs that affect the dylib output
|
||||||
|
function compute_link_key(objects, ldflags, target_ldflags, target, cc) {
|
||||||
|
// Sort objects for deterministic hash
|
||||||
|
var sorted_objects = sort(objects)
|
||||||
|
|
||||||
|
// Build a string representing all link inputs
|
||||||
|
var parts = []
|
||||||
|
push(parts, 'target:' + target)
|
||||||
|
push(parts, 'cc:' + cc)
|
||||||
|
arrfor(sorted_objects, function(obj) {
|
||||||
|
// Object paths are content-addressed, so the path itself is the hash
|
||||||
|
push(parts, 'obj:' + obj)
|
||||||
|
})
|
||||||
|
arrfor(ldflags, function(flag) {
|
||||||
|
push(parts, 'ldflag:' + flag)
|
||||||
|
})
|
||||||
|
arrfor(target_ldflags, function(flag) {
|
||||||
|
push(parts, 'target_ldflag:' + flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
return content_hash(text(parts, '\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a dynamic library for a package
|
||||||
|
// Output goes to .cell/lib/<package_name>.<ext>
|
||||||
|
// Dynamic libraries do NOT link against core; undefined symbols are resolved at dlopen time
|
||||||
|
// Uses content-addressed store + symlink for caching
|
||||||
|
Build.build_dynamic = function(pkg, target = Build.detect_host_target(), buildtype = 'release') {
|
||||||
|
var objects = Build.build_package(pkg, target, true, buildtype) // exclude main.c
|
||||||
|
|
||||||
|
if (length(objects) == 0) {
|
||||||
|
log.console('No C files in ' + pkg)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var lib_dir = shop.get_lib_dir()
|
||||||
|
var store_dir = lib_dir + '/store'
|
||||||
|
ensure_dir(lib_dir)
|
||||||
|
ensure_dir(store_dir)
|
||||||
|
|
||||||
|
var lib_name = shop.lib_name_for_package(pkg)
|
||||||
|
var dylib_ext = toolchains[target].system == 'windows' ? '.dll' : (toolchains[target].system == 'darwin' ? '.dylib' : '.so')
|
||||||
|
var stable_path = lib_dir + '/' + lib_name + dylib_ext
|
||||||
|
|
||||||
|
// Get link flags (with sigil replacement)
|
||||||
|
var ldflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'LDFLAGS', target))
|
||||||
|
var target_ldflags = toolchains[target].c_link_args || []
|
||||||
|
var cc = toolchains[target].cpp || toolchains[target].c
|
||||||
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
|
var local_dir = get_local_dir()
|
||||||
|
var tc = toolchains[target]
|
||||||
|
|
||||||
|
// Resolve relative -L paths in ldflags for hash computation
|
||||||
|
var resolved_ldflags = []
|
||||||
|
arrfor(ldflags, function(flag) {
|
||||||
|
if (starts_with(flag, '-L') && !starts_with(flag, '-L/')) {
|
||||||
|
flag = '-L"' + pkg_dir + '/' + text(flag, 2) + '"'
|
||||||
|
}
|
||||||
|
push(resolved_ldflags, flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Compute link key
|
||||||
|
var link_key = compute_link_key(objects, resolved_ldflags, target_ldflags, target, cc)
|
||||||
|
var store_path = store_dir + '/' + lib_name + '-' + link_key + dylib_ext
|
||||||
|
|
||||||
|
// Check if already linked in store
|
||||||
|
if (fd.is_file(store_path)) {
|
||||||
|
// Ensure symlink points to the store file
|
||||||
|
if (fd.is_link(stable_path)) {
|
||||||
|
var current_target = fd.readlink(stable_path)
|
||||||
|
if (current_target == store_path) {
|
||||||
|
// Already up to date
|
||||||
|
return stable_path
|
||||||
|
}
|
||||||
|
fd.unlink(stable_path)
|
||||||
|
} else if (fd.is_file(stable_path)) {
|
||||||
|
fd.unlink(stable_path)
|
||||||
|
}
|
||||||
|
fd.symlink(store_path, stable_path)
|
||||||
|
return stable_path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build link command
|
||||||
|
var cmd_parts = [cc, '-shared', '-fPIC']
|
||||||
|
|
||||||
|
// Platform-specific flags for undefined symbols (resolved at dlopen) and size optimization
|
||||||
|
if (tc.system == 'darwin') {
|
||||||
|
cmd_parts = array(cmd_parts, [
|
||||||
|
'-undefined', 'dynamic_lookup',
|
||||||
|
'-Wl,-dead_strip',
|
||||||
|
'-Wl,-install_name,' + stable_path,
|
||||||
|
'-Wl,-rpath,@loader_path/../local',
|
||||||
|
'-Wl,-rpath,' + local_dir
|
||||||
|
])
|
||||||
|
} else if (tc.system == 'linux') {
|
||||||
|
cmd_parts = array(cmd_parts, [
|
||||||
|
'-Wl,--allow-shlib-undefined',
|
||||||
|
'-Wl,--gc-sections',
|
||||||
|
'-Wl,-rpath,$ORIGIN/../local',
|
||||||
|
'-Wl,-rpath,' + local_dir
|
||||||
|
])
|
||||||
|
} else if (tc.system == 'windows') {
|
||||||
|
// Windows DLLs: use --allow-shlib-undefined for mingw
|
||||||
|
push(cmd_parts, '-Wl,--allow-shlib-undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add .cell/local to library search path
|
||||||
|
push(cmd_parts, '-L"' + local_dir + '"')
|
||||||
|
|
||||||
|
arrfor(objects, function(obj) {
|
||||||
|
push(cmd_parts, '"' + obj + '"')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do NOT link against core library - symbols resolved at dlopen time
|
||||||
|
cmd_parts = array(cmd_parts, resolved_ldflags)
|
||||||
|
cmd_parts = array(cmd_parts, target_ldflags)
|
||||||
|
|
||||||
|
push(cmd_parts, '-o')
|
||||||
|
push(cmd_parts, '"' + store_path + '"')
|
||||||
|
|
||||||
|
var cmd_str = text(cmd_parts, ' ')
|
||||||
|
|
||||||
|
log.console('Linking ' + lib_name + dylib_ext)
|
||||||
|
var ret = os.system(cmd_str)
|
||||||
|
if (ret != 0) {
|
||||||
|
throw Error('Linking failed: ' + pkg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update symlink to point to the new store file
|
||||||
|
if (fd.is_link(stable_path)) {
|
||||||
|
fd.unlink(stable_path)
|
||||||
|
} else if (fd.is_file(stable_path)) {
|
||||||
|
fd.unlink(stable_path)
|
||||||
|
}
|
||||||
|
fd.symlink(store_path, stable_path)
|
||||||
|
|
||||||
|
return stable_path
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Static binary building
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Build a static binary from multiple packages
|
||||||
|
// packages: array of package names
|
||||||
|
// output: output binary path
|
||||||
|
Build.build_static = function(packages, target = Build.detect_host_target(), output, buildtype = 'release') {
|
||||||
|
var all_objects = []
|
||||||
|
var all_ldflags = []
|
||||||
|
var seen_flags = {}
|
||||||
|
|
||||||
|
// Compile all packages
|
||||||
|
arrfor(packages, function(pkg) {
|
||||||
|
var is_core = (pkg == 'core')
|
||||||
|
|
||||||
|
// For core, include main.c; for others, exclude it
|
||||||
|
var objects = Build.build_package(pkg, target, !is_core, buildtype)
|
||||||
|
|
||||||
|
arrfor(objects, function(obj) {
|
||||||
|
push(all_objects, obj)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Collect LDFLAGS (with sigil replacement)
|
||||||
|
var ldflags = replace_sigils_array(pkg_tools.get_flags(pkg, 'LDFLAGS', target))
|
||||||
|
var pkg_dir = shop.get_package_dir(pkg)
|
||||||
|
|
||||||
|
// Deduplicate based on the entire LDFLAGS string for this package
|
||||||
|
var ldflags_key = pkg + ':' + text(ldflags, ' ')
|
||||||
|
if (!seen_flags[ldflags_key]) {
|
||||||
|
seen_flags[ldflags_key] = true
|
||||||
|
arrfor(ldflags, function(flag) {
|
||||||
|
// Resolve relative -L paths
|
||||||
|
if (starts_with(flag, '-L') && !starts_with(flag, '-L/')) {
|
||||||
|
flag = '-L"' + pkg_dir + '/' + text(flag, 2) + '"'
|
||||||
|
}
|
||||||
|
push(all_ldflags, flag)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (length(all_objects) == 0) {
|
||||||
|
throw Error('No object files to link')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
var cc = toolchains[target].c
|
||||||
|
var target_ldflags = toolchains[target].c_link_args || []
|
||||||
|
var exe_ext = toolchains[target].system == 'windows' ? '.exe' : ''
|
||||||
|
|
||||||
|
if (!ends_with(output, exe_ext) && exe_ext) {
|
||||||
|
output = output + exe_ext
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd_parts = [cc]
|
||||||
|
|
||||||
|
arrfor(all_objects, function(obj) {
|
||||||
|
push(cmd_parts, '"' + obj + '"')
|
||||||
|
})
|
||||||
|
|
||||||
|
arrfor(all_ldflags, function(flag) {
|
||||||
|
push(cmd_parts, flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
arrfor(target_ldflags, function(flag) {
|
||||||
|
push(cmd_parts, flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
push(cmd_parts, '-o', '"' + output + '"')
|
||||||
|
|
||||||
|
var cmd_str = text(cmd_parts, ' ')
|
||||||
|
|
||||||
|
log.console('Linking ' + output)
|
||||||
|
var ret = os.system(cmd_str)
|
||||||
|
if (ret != 0) {
|
||||||
|
throw Error('Linking failed with command: ' + cmd_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console('Built ' + output)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Convenience functions
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Build dynamic libraries for all installed packages
|
||||||
|
Build.build_all_dynamic = function(target, buildtype = 'release') {
|
||||||
|
target = target || Build.detect_host_target()
|
||||||
|
|
||||||
|
var packages = shop.list_packages()
|
||||||
|
var results = []
|
||||||
|
|
||||||
|
// Build core first
|
||||||
|
if (find(packages, 'core') != null) {
|
||||||
|
try {
|
||||||
|
var lib = Build.build_dynamic('core', target, buildtype)
|
||||||
|
push(results, { package: 'core', library: lib })
|
||||||
|
} catch (e) {
|
||||||
|
log.error('Failed to build core: ' + text(e))
|
||||||
|
push(results, { package: 'core', error: e })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build other packages
|
||||||
|
arrfor(packages, function(pkg) {
|
||||||
|
if (pkg == 'core') return
|
||||||
|
|
||||||
|
try {
|
||||||
|
var lib = Build.build_dynamic(pkg, target, buildtype)
|
||||||
|
push(results, { package: pkg, library: lib })
|
||||||
|
} catch (e) {
|
||||||
|
log.error('Failed to build ' + pkg + ': ')
|
||||||
|
log.console(e.message)
|
||||||
|
log.console(e.stack)
|
||||||
|
push(results, { package: pkg, error: e })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
return Build
|
||||||
1
cake/playdate.cm
Normal file
1
cake/playdate.cm
Normal file
@@ -0,0 +1 @@
|
|||||||
|
// cake file for making a playdate package
|
||||||
13
cell.toml
Normal file
13
cell.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[compilation]
|
||||||
|
CFLAGS = "-Isource -Wno-incompatible-pointer-types -Wno-missing-braces -Wno-strict-prototypes -Wno-unused-function -Wno-int-conversion"
|
||||||
|
LDFLAGS = "-lstdc++ -lm"
|
||||||
|
|
||||||
|
[compilation.macos_arm64]
|
||||||
|
CFLAGS = "-x objective-c"
|
||||||
|
LDFLAGS = "-framework CoreFoundation -framework CFNetwork"
|
||||||
|
|
||||||
|
[compilation.playdate]
|
||||||
|
CFLAGS = "-DMINIZ_NO_TIME -DTARGET_EXTENSION -DTARGET_PLAYDATE -I$LOCAL/PlaydateSDK/C_API"
|
||||||
|
|
||||||
|
[compilation.windows]
|
||||||
|
LDFLAGS = "-lws2_32 -lwinmm -liphlpapi -lbcrypt -lwinhttp -static-libgcc -static-libstdc++"
|
||||||
468
cellfs.cm
Normal file
468
cellfs.cm
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
var cellfs = {}
|
||||||
|
|
||||||
|
// CellFS: A filesystem implementation using miniz and raw OS filesystem
|
||||||
|
// Supports mounting multiple sources (fs, zip) and named mounts (@name)
|
||||||
|
|
||||||
|
var fd = use('fd')
|
||||||
|
var miniz = use('miniz')
|
||||||
|
var qop = use('qop')
|
||||||
|
var wildstar = use('wildstar')
|
||||||
|
|
||||||
|
// Internal state
|
||||||
|
var mounts = [] // Array of {source, type, handle, name}
|
||||||
|
|
||||||
|
var writepath = "."
|
||||||
|
|
||||||
|
// Helper to normalize paths
|
||||||
|
function normalize_path(path) {
|
||||||
|
if (!path) return ""
|
||||||
|
// Remove leading/trailing slashes and normalize
|
||||||
|
return replace(path, /^\/+|\/+$/, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a file exists in a specific mount
|
||||||
|
function mount_exists(mount, path) {
|
||||||
|
if (mount.type == 'zip') {
|
||||||
|
try {
|
||||||
|
mount.handle.mod(path)
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if (mount.type == 'qop') {
|
||||||
|
try {
|
||||||
|
return mount.handle.stat(path) != null
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else { // fs
|
||||||
|
var full_path = fd.join_paths(mount.source, path)
|
||||||
|
try {
|
||||||
|
var st = fd.stat(full_path)
|
||||||
|
return st.isFile || st.isDirectory
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a path refers to a directory in a specific mount
|
||||||
|
function is_directory(path) {
|
||||||
|
var res = resolve(path)
|
||||||
|
var mount = res.mount
|
||||||
|
if (mount.type == 'zip') {
|
||||||
|
try {
|
||||||
|
return mount.handle.is_directory(path);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (mount.type == 'qop') {
|
||||||
|
try {
|
||||||
|
return mount.handle.is_directory(path);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else { // fs
|
||||||
|
var full_path = fd.join_paths(mount.source, path)
|
||||||
|
try {
|
||||||
|
var st = fd.stat(full_path)
|
||||||
|
return st.isDirectory
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a path to a specific mount and relative path
|
||||||
|
// Returns { mount, path } or throws/returns null
|
||||||
|
function resolve(path, must_exist) {
|
||||||
|
path = normalize_path(path)
|
||||||
|
|
||||||
|
// Check for named mount
|
||||||
|
if (starts_with(path, "@")) {
|
||||||
|
var idx = search(path, "/")
|
||||||
|
var mount_name = ""
|
||||||
|
var rel_path = ""
|
||||||
|
|
||||||
|
if (idx == null) {
|
||||||
|
mount_name = text(path, 1)
|
||||||
|
rel_path = ""
|
||||||
|
} else {
|
||||||
|
mount_name = text(path, 1, idx)
|
||||||
|
rel_path = text(path, idx + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find named mount
|
||||||
|
var mount = null
|
||||||
|
arrfor(mounts, function(m) {
|
||||||
|
if (m.name == mount_name) {
|
||||||
|
mount = m
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}, false, true)
|
||||||
|
|
||||||
|
if (!mount) {
|
||||||
|
throw Error("Unknown mount point: @" + mount_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mount: mount, path: rel_path }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search path
|
||||||
|
var found_mount = null
|
||||||
|
arrfor(mounts, function(mount) {
|
||||||
|
if (mount_exists(mount, path)) {
|
||||||
|
found_mount = { mount: mount, path: path }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}, false, true)
|
||||||
|
|
||||||
|
if (found_mount) {
|
||||||
|
return found_mount
|
||||||
|
}
|
||||||
|
|
||||||
|
if (must_exist) {
|
||||||
|
throw Error("File not found in any mount: " + path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount a source
|
||||||
|
function mount(source, name) {
|
||||||
|
// Check if source exists
|
||||||
|
var st = fd.stat(source)
|
||||||
|
|
||||||
|
var mount_info = {
|
||||||
|
source: source,
|
||||||
|
name: name || null,
|
||||||
|
type: 'fs',
|
||||||
|
handle: null,
|
||||||
|
zip_blob: null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.isDirectory) {
|
||||||
|
mount_info.type = 'fs'
|
||||||
|
} else if (st.isFile) {
|
||||||
|
var blob = fd.slurp(source)
|
||||||
|
|
||||||
|
// Try QOP first (it's likely faster to fail?) or Zip?
|
||||||
|
// QOP open checks magic.
|
||||||
|
var qop_archive = null
|
||||||
|
try {
|
||||||
|
qop_archive = qop.open(blob)
|
||||||
|
} catch(e) {}
|
||||||
|
|
||||||
|
if (qop_archive) {
|
||||||
|
mount_info.type = 'qop'
|
||||||
|
mount_info.handle = qop_archive
|
||||||
|
mount_info.zip_blob = blob // keep blob alive
|
||||||
|
} else {
|
||||||
|
var zip = miniz.read(blob)
|
||||||
|
if (!is_object(zip) || !is_function(zip.count)) {
|
||||||
|
throw Error("Invalid archive file (not zip or qop): " + source)
|
||||||
|
}
|
||||||
|
|
||||||
|
mount_info.type = 'zip'
|
||||||
|
mount_info.handle = zip
|
||||||
|
mount_info.zip_blob = blob // keep blob alive
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Error("Unsupported mount source type: " + source)
|
||||||
|
}
|
||||||
|
|
||||||
|
push(mounts, mount_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount
|
||||||
|
function unmount(name_or_source) {
|
||||||
|
mounts = filter(mounts, function(mount) {
|
||||||
|
return mount.name != name_or_source && mount.source != name_or_source
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file
|
||||||
|
function slurp(path) {
|
||||||
|
var res = resolve(path, true)
|
||||||
|
if (!res) throw Error("File not found: " + path)
|
||||||
|
|
||||||
|
if (res.mount.type == 'zip') {
|
||||||
|
return res.mount.handle.slurp(res.path)
|
||||||
|
} else if (res.mount.type == 'qop') {
|
||||||
|
var data = res.mount.handle.read(res.path)
|
||||||
|
if (!data) throw Error("File not found in qop: " + path)
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
var full_path = fd.join_paths(res.mount.source, res.path)
|
||||||
|
return fd.slurp(full_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write file
|
||||||
|
function slurpwrite(path, data) {
|
||||||
|
var full_path = writepath + "/" + path
|
||||||
|
|
||||||
|
var f = fd.open(full_path, 'w')
|
||||||
|
fd.write(f, data)
|
||||||
|
fd.close(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check existence
|
||||||
|
function exists(path) {
|
||||||
|
var res = resolve(path, false)
|
||||||
|
if (starts_with(path, "@")) {
|
||||||
|
return mount_exists(res.mount, res.path)
|
||||||
|
}
|
||||||
|
return res != null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat
|
||||||
|
function stat(path) {
|
||||||
|
var res = resolve(path, true)
|
||||||
|
if (!res) throw Error("File not found: " + path)
|
||||||
|
|
||||||
|
if (res.mount.type == 'zip') {
|
||||||
|
var mod = res.mount.handle.mod(res.path)
|
||||||
|
return {
|
||||||
|
filesize: 0,
|
||||||
|
modtime: mod * 1000,
|
||||||
|
isDirectory: false
|
||||||
|
}
|
||||||
|
} else if (res.mount.type == 'qop') {
|
||||||
|
var s = res.mount.handle.stat(res.path)
|
||||||
|
if (!s) throw Error("File not found in qop: " + path)
|
||||||
|
return {
|
||||||
|
filesize: s.size,
|
||||||
|
modtime: s.modtime,
|
||||||
|
isDirectory: s.isDirectory
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var full_path = fd.join_paths(res.mount.source, res.path)
|
||||||
|
var s = fd.stat(full_path)
|
||||||
|
return {
|
||||||
|
filesize: s.size,
|
||||||
|
modtime: s.mtime,
|
||||||
|
isDirectory: s.isDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get search paths
|
||||||
|
function searchpath() {
|
||||||
|
return array(mounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount a package using the shop system
|
||||||
|
function mount_package(name) {
|
||||||
|
if (name == null) {
|
||||||
|
mount('.', null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var dir = shop.get_package_dir(name)
|
||||||
|
|
||||||
|
if (!dir) {
|
||||||
|
throw Error("Package not found: " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
mount(dir, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New functions for qjs_io compatibility
|
||||||
|
|
||||||
|
function match(str, pattern) {
|
||||||
|
return wildstar.match(pattern, str, wildstar.WM_PATHNAME | wildstar.WM_PERIOD | wildstar.WM_WILDSTAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rm(path) {
|
||||||
|
var res = resolve(path, true)
|
||||||
|
if (res.mount.type != 'fs') throw Error("Cannot delete from non-fs mount")
|
||||||
|
|
||||||
|
var full_path = fd.join_paths(res.mount.source, res.path)
|
||||||
|
var st = fd.stat(full_path)
|
||||||
|
if (st.isDirectory) fd.rmdir(full_path)
|
||||||
|
else fd.unlink(full_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkdir(path) {
|
||||||
|
var full = fd.join_paths(writepath, path)
|
||||||
|
fd.mkdir(full)
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_writepath(path) {
|
||||||
|
writepath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
function basedir() {
|
||||||
|
return fd.getcwd()
|
||||||
|
}
|
||||||
|
|
||||||
|
function prefdir(org, app) {
|
||||||
|
return "./"
|
||||||
|
}
|
||||||
|
|
||||||
|
function realdir(path) {
|
||||||
|
var res = resolve(path, false)
|
||||||
|
if (!res) return null
|
||||||
|
return fd.join_paths(res.mount.source, res.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function enumerate(path, recurse) {
|
||||||
|
if (path == null) path = ""
|
||||||
|
|
||||||
|
var res = resolve(path, true)
|
||||||
|
var results = []
|
||||||
|
|
||||||
|
function visit(curr_full, rel_prefix) {
|
||||||
|
var list = fd.readdir(curr_full)
|
||||||
|
if (!list) return
|
||||||
|
|
||||||
|
arrfor(list, function(item) {
|
||||||
|
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
|
||||||
|
push(results, item_rel)
|
||||||
|
|
||||||
|
if (recurse) {
|
||||||
|
var st = fd.stat(fd.join_paths(curr_full, item))
|
||||||
|
if (st.isDirectory) {
|
||||||
|
visit(fd.join_paths(curr_full, item), item_rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.mount.type == 'fs') {
|
||||||
|
var full = fd.join_paths(res.mount.source, res.path)
|
||||||
|
var st = fd.stat(full)
|
||||||
|
if (st && st.isDirectory) {
|
||||||
|
visit(full, "")
|
||||||
|
}
|
||||||
|
} else if (res.mount.type == 'qop') {
|
||||||
|
var all = res.mount.handle.list()
|
||||||
|
var prefix = res.path ? res.path + "/" : ""
|
||||||
|
var prefix_len = length(prefix)
|
||||||
|
|
||||||
|
// Use a set to avoid duplicates if we are simulating directories
|
||||||
|
var seen = {}
|
||||||
|
|
||||||
|
arrfor(all, function(p) {
|
||||||
|
if (starts_with(p, prefix)) {
|
||||||
|
var rel = text(p, prefix_len)
|
||||||
|
if (length(rel) == 0) return
|
||||||
|
|
||||||
|
if (!recurse) {
|
||||||
|
var slash = search(rel, '/')
|
||||||
|
if (slash != null) {
|
||||||
|
rel = text(rel, 0, slash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seen[rel]) {
|
||||||
|
seen[rel] = true
|
||||||
|
push(results, rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
function globfs(globs, dir) {
|
||||||
|
if (dir == null) dir = ""
|
||||||
|
var res = resolve(dir, true)
|
||||||
|
var results = []
|
||||||
|
|
||||||
|
function check_neg(path) {
|
||||||
|
var result = false
|
||||||
|
arrfor(globs, function(g) {
|
||||||
|
if (starts_with(g, "!") && wildstar.match(text(g, 1), path, wildstar.WM_WILDSTAR)) {
|
||||||
|
result = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}, false, true)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_pos(path) {
|
||||||
|
var result = false
|
||||||
|
arrfor(globs, function(g) {
|
||||||
|
if (!starts_with(g, "!") && wildstar.match(g, path, wildstar.WM_WILDSTAR)) {
|
||||||
|
result = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}, false, true)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function visit(curr_full, rel_prefix) {
|
||||||
|
if (rel_prefix && check_neg(rel_prefix)) return
|
||||||
|
|
||||||
|
var list = fd.readdir(curr_full)
|
||||||
|
if (!list) return
|
||||||
|
|
||||||
|
arrfor(list, function(item) {
|
||||||
|
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
|
||||||
|
|
||||||
|
var child_full = fd.join_paths(curr_full, item)
|
||||||
|
var st = fd.stat(child_full)
|
||||||
|
|
||||||
|
if (st.isDirectory) {
|
||||||
|
if (!check_neg(item_rel)) {
|
||||||
|
visit(child_full, item_rel)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!check_neg(item_rel) && check_pos(item_rel)) {
|
||||||
|
push(results, item_rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.mount.type == 'fs') {
|
||||||
|
var full = fd.join_paths(res.mount.source, res.path)
|
||||||
|
var st = fd.stat(full)
|
||||||
|
if (st && st.isDirectory) {
|
||||||
|
visit(full, "")
|
||||||
|
}
|
||||||
|
} else if (res.mount.type == 'qop') {
|
||||||
|
var all = res.mount.handle.list()
|
||||||
|
var prefix = res.path ? res.path + "/" : ""
|
||||||
|
var prefix_len = length(prefix)
|
||||||
|
|
||||||
|
arrfor(all, function(p) {
|
||||||
|
if (starts_with(p, prefix)) {
|
||||||
|
var rel = text(p, prefix_len)
|
||||||
|
if (length(rel) == 0) return
|
||||||
|
|
||||||
|
if (!check_neg(rel) && check_pos(rel)) {
|
||||||
|
push(results, rel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exports
|
||||||
|
cellfs.mount = mount
|
||||||
|
cellfs.mount_package = mount_package
|
||||||
|
cellfs.unmount = unmount
|
||||||
|
cellfs.slurp = slurp
|
||||||
|
cellfs.slurpwrite = slurpwrite
|
||||||
|
cellfs.exists = exists
|
||||||
|
cellfs.is_directory = is_directory
|
||||||
|
cellfs.stat = stat
|
||||||
|
cellfs.searchpath = searchpath
|
||||||
|
cellfs.match = match
|
||||||
|
cellfs.enumerate = enumerate
|
||||||
|
cellfs.globfs = globfs
|
||||||
|
cellfs.rm = rm
|
||||||
|
cellfs.mkdir = mkdir
|
||||||
|
cellfs.writepath = set_writepath
|
||||||
|
cellfs.basedir = basedir
|
||||||
|
cellfs.prefdir = prefdir
|
||||||
|
cellfs.realdir = realdir
|
||||||
|
|
||||||
|
cellfs.mount('.')
|
||||||
|
|
||||||
|
return cellfs
|
||||||
218
clean.ce
Normal file
218
clean.ce
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
// cell clean [<scope>] - Remove cached material to force refetch/rebuild
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cell clean Clean build outputs for current directory package
|
||||||
|
// cell clean . Clean build outputs for current directory package
|
||||||
|
// cell clean <locator> Clean build outputs for specific package
|
||||||
|
// cell clean shop Clean entire shop
|
||||||
|
// cell clean world Clean all world packages
|
||||||
|
//
|
||||||
|
// Options:
|
||||||
|
// --build Remove build outputs only (default)
|
||||||
|
// --fetch Remove fetched sources only
|
||||||
|
// --all Remove both build outputs and fetched sources
|
||||||
|
// --deep Apply to full dependency closure
|
||||||
|
// --dry-run Show what would be deleted
|
||||||
|
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var pkg = use('package')
|
||||||
|
var fd = use('fd')
|
||||||
|
|
||||||
|
var scope = null
|
||||||
|
var clean_build = false
|
||||||
|
var clean_fetch = false
|
||||||
|
var deep = false
|
||||||
|
var dry_run = false
|
||||||
|
|
||||||
|
for (var i = 0; i < length(args); i++) {
|
||||||
|
if (args[i] == '--build') {
|
||||||
|
clean_build = true
|
||||||
|
} else if (args[i] == '--fetch') {
|
||||||
|
clean_fetch = true
|
||||||
|
} else if (args[i] == '--all') {
|
||||||
|
clean_build = true
|
||||||
|
clean_fetch = true
|
||||||
|
} else if (args[i] == '--deep') {
|
||||||
|
deep = true
|
||||||
|
} else if (args[i] == '--dry-run') {
|
||||||
|
dry_run = true
|
||||||
|
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||||
|
log.console("Usage: cell clean [<scope>] [options]")
|
||||||
|
log.console("")
|
||||||
|
log.console("Remove cached material to force refetch/rebuild.")
|
||||||
|
log.console("")
|
||||||
|
log.console("Scopes:")
|
||||||
|
log.console(" <locator> Clean specific package")
|
||||||
|
log.console(" shop Clean entire shop")
|
||||||
|
log.console(" world Clean all world packages")
|
||||||
|
log.console("")
|
||||||
|
log.console("Options:")
|
||||||
|
log.console(" --build Remove build outputs only (default)")
|
||||||
|
log.console(" --fetch Remove fetched sources only")
|
||||||
|
log.console(" --all Remove both build outputs and fetched sources")
|
||||||
|
log.console(" --deep Apply to full dependency closure")
|
||||||
|
log.console(" --dry-run Show what would be deleted")
|
||||||
|
$stop()
|
||||||
|
} else if (!starts_with(args[i], '-')) {
|
||||||
|
scope = args[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to --build if nothing specified
|
||||||
|
if (!clean_build && !clean_fetch) {
|
||||||
|
clean_build = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default scope to current directory
|
||||||
|
if (!scope) {
|
||||||
|
scope = '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve local paths for single package scope
|
||||||
|
var is_shop_scope = (scope == 'shop')
|
||||||
|
var is_world_scope = (scope == 'world')
|
||||||
|
|
||||||
|
if (!is_shop_scope && !is_world_scope) {
|
||||||
|
if (scope == '.' || starts_with(scope, './') || starts_with(scope, '../') || fd.is_dir(scope)) {
|
||||||
|
var resolved = fd.realpath(scope)
|
||||||
|
if (resolved) {
|
||||||
|
scope = resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var files_to_delete = []
|
||||||
|
var dirs_to_delete = []
|
||||||
|
|
||||||
|
// Gather packages to clean
|
||||||
|
var packages_to_clean = []
|
||||||
|
|
||||||
|
if (is_shop_scope) {
|
||||||
|
packages_to_clean = shop.list_packages()
|
||||||
|
} else if (is_world_scope) {
|
||||||
|
// For now, world is the same as shop
|
||||||
|
packages_to_clean = shop.list_packages()
|
||||||
|
} else {
|
||||||
|
// Single package
|
||||||
|
push(packages_to_clean, scope)
|
||||||
|
|
||||||
|
if (deep) {
|
||||||
|
try {
|
||||||
|
var deps = pkg.gather_dependencies(scope)
|
||||||
|
arrfor(deps, function(dep) {
|
||||||
|
push(packages_to_clean, dep)
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
// Skip if can't read dependencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather files to clean
|
||||||
|
var lib_dir = shop.get_lib_dir()
|
||||||
|
var build_dir = shop.get_build_dir()
|
||||||
|
var packages_dir = replace(shop.get_package_dir(''), /\/$/, '') // Get base packages dir
|
||||||
|
|
||||||
|
if (clean_build) {
|
||||||
|
if (is_shop_scope) {
|
||||||
|
// Clean entire build and lib directories
|
||||||
|
if (fd.is_dir(build_dir)) {
|
||||||
|
push(dirs_to_delete, build_dir)
|
||||||
|
}
|
||||||
|
if (fd.is_dir(lib_dir)) {
|
||||||
|
push(dirs_to_delete, lib_dir)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clean specific package libraries
|
||||||
|
arrfor(packages_to_clean, function(p) {
|
||||||
|
if (p == 'core') return
|
||||||
|
|
||||||
|
var lib_name = shop.lib_name_for_package(p)
|
||||||
|
var dylib_ext = '.dylib'
|
||||||
|
var lib_path = lib_dir + '/' + lib_name + dylib_ext
|
||||||
|
|
||||||
|
if (fd.is_file(lib_path)) {
|
||||||
|
push(files_to_delete, lib_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check for .so and .dll
|
||||||
|
var so_path = lib_dir + '/' + lib_name + '.so'
|
||||||
|
var dll_path = lib_dir + '/' + lib_name + '.dll'
|
||||||
|
if (fd.is_file(so_path)) {
|
||||||
|
push(files_to_delete, so_path)
|
||||||
|
}
|
||||||
|
if (fd.is_file(dll_path)) {
|
||||||
|
push(files_to_delete, dll_path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clean_fetch) {
|
||||||
|
if (is_shop_scope) {
|
||||||
|
// Clean entire packages directory (dangerous!)
|
||||||
|
if (fd.is_dir(packages_dir)) {
|
||||||
|
push(dirs_to_delete, packages_dir)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clean specific package directories
|
||||||
|
arrfor(packages_to_clean, function(p) {
|
||||||
|
if (p == 'core') return
|
||||||
|
|
||||||
|
var pkg_dir = shop.get_package_dir(p)
|
||||||
|
if (fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)) {
|
||||||
|
push(dirs_to_delete, pkg_dir)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute or report
|
||||||
|
if (dry_run) {
|
||||||
|
log.console("Would delete:")
|
||||||
|
if (length(files_to_delete) == 0 && length(dirs_to_delete) == 0) {
|
||||||
|
log.console(" (nothing to clean)")
|
||||||
|
} else {
|
||||||
|
arrfor(files_to_delete, function(f) {
|
||||||
|
log.console(" [file] " + f)
|
||||||
|
})
|
||||||
|
arrfor(dirs_to_delete, function(d) {
|
||||||
|
log.console(" [dir] " + d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var deleted_count = 0
|
||||||
|
|
||||||
|
arrfor(files_to_delete, function(f) {
|
||||||
|
try {
|
||||||
|
fd.unlink(f)
|
||||||
|
log.console("Deleted: " + f)
|
||||||
|
deleted_count++
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Failed to delete " + f + ": " + e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
arrfor(dirs_to_delete, function(d) {
|
||||||
|
try {
|
||||||
|
if (fd.is_link(d)) {
|
||||||
|
fd.unlink(d)
|
||||||
|
} else {
|
||||||
|
fd.rmdir(d, 1) // recursive
|
||||||
|
}
|
||||||
|
log.console("Deleted: " + d)
|
||||||
|
deleted_count++
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Failed to delete " + d + ": " + e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (deleted_count == 0) {
|
||||||
|
log.console("Nothing to clean.")
|
||||||
|
} else {
|
||||||
|
log.console("")
|
||||||
|
log.console("Clean complete: " + text(deleted_count) + " item(s) deleted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stop()
|
||||||
121
clone.ce
Normal file
121
clone.ce
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// cell clone <origin> <path>
|
||||||
|
// Clones a cell package <origin> to the local <path>, and links it.
|
||||||
|
|
||||||
|
var shop = use('internal/shop')
|
||||||
|
var link = use('link')
|
||||||
|
var fd = use('fd')
|
||||||
|
var http = use('http')
|
||||||
|
var miniz = use('miniz')
|
||||||
|
|
||||||
|
if (length(args) < 2) {
|
||||||
|
log.console("Usage: cell clone <origin> <path>")
|
||||||
|
log.console("Clones a cell package to a local path and links it.")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var origin = args[0]
|
||||||
|
var target_path = args[1]
|
||||||
|
|
||||||
|
// Resolve target path to absolute
|
||||||
|
if (target_path == '.' || starts_with(target_path, './') || starts_with(target_path, '../')) {
|
||||||
|
var resolved = fd.realpath(target_path)
|
||||||
|
if (resolved) {
|
||||||
|
target_path = resolved
|
||||||
|
} else {
|
||||||
|
// Path doesn't exist yet, resolve relative to cwd
|
||||||
|
var cwd = fd.realpath('.')
|
||||||
|
if (target_path == '.') {
|
||||||
|
target_path = cwd
|
||||||
|
} else if (starts_with(target_path, './')) {
|
||||||
|
target_path = cwd + text(target_path, 1)
|
||||||
|
} else if (starts_with(target_path, '../')) {
|
||||||
|
// Go up one directory from cwd
|
||||||
|
var parent = fd.dirname(cwd)
|
||||||
|
target_path = parent + text(target_path, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if target already exists
|
||||||
|
if (fd.is_dir(target_path)) {
|
||||||
|
log.console("Error: " + target_path + " already exists")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("Cloning " + origin + " to " + target_path + "...")
|
||||||
|
|
||||||
|
// Get the latest commit
|
||||||
|
var info = shop.resolve_package_info(origin)
|
||||||
|
if (!info || info == 'local') {
|
||||||
|
log.console("Error: " + origin + " is not a remote package")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update to get the commit hash
|
||||||
|
var update_result = shop.update(origin)
|
||||||
|
if (!update_result) {
|
||||||
|
log.console("Error: Could not fetch " + origin)
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch and extract to the target path
|
||||||
|
var lock = shop.load_lock()
|
||||||
|
var entry = lock[origin]
|
||||||
|
if (!entry || !entry.commit) {
|
||||||
|
log.console("Error: No commit found for " + origin)
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var download_url = shop.get_download_url(origin, entry.commit)
|
||||||
|
log.console("Downloading from " + download_url)
|
||||||
|
|
||||||
|
try {
|
||||||
|
var zip_blob = http.fetch(download_url)
|
||||||
|
|
||||||
|
// Extract zip to target path
|
||||||
|
var zip = miniz.read(zip_blob)
|
||||||
|
if (!zip) {
|
||||||
|
log.console("Error: Failed to read zip archive")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create target directory
|
||||||
|
fd.mkdir(target_path)
|
||||||
|
|
||||||
|
var count = zip.count()
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
if (zip.is_directory(i)) continue
|
||||||
|
var filename = zip.get_filename(i)
|
||||||
|
var first_slash = search(filename, '/')
|
||||||
|
if (first_slash == null) continue
|
||||||
|
if (first_slash + 1 >= length(filename)) continue
|
||||||
|
|
||||||
|
var rel_path = text(filename, first_slash + 1)
|
||||||
|
var full_path = target_path + '/' + rel_path
|
||||||
|
var dir_path = fd.dirname(full_path)
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
if (!fd.is_dir(dir_path)) {
|
||||||
|
fd.mkdir(dir_path)
|
||||||
|
}
|
||||||
|
fd.slurpwrite(full_path, zip.slurp(filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.console("Extracted to " + target_path)
|
||||||
|
|
||||||
|
// Link the origin to the cloned path
|
||||||
|
link.add(origin, target_path, shop)
|
||||||
|
log.console("Linked " + origin + " -> " + target_path)
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
log.console("Error: " + e.message)
|
||||||
|
if (e.stack) log.console(e.stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
$stop()
|
||||||
249
config.ce
Normal file
249
config.ce
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
// cell config - Manage system and actor configurations
|
||||||
|
|
||||||
|
var toml = use('toml')
|
||||||
|
var pkg = use('package')
|
||||||
|
|
||||||
|
function print_help() {
|
||||||
|
log.console("Usage: cell config <command> [options]")
|
||||||
|
log.console("")
|
||||||
|
log.console("Commands:")
|
||||||
|
log.console(" get <key> Get a configuration value")
|
||||||
|
log.console(" set <key> <value> Set a configuration value")
|
||||||
|
log.console(" list List all configurations")
|
||||||
|
log.console(" actor <name> get <key> Get actor-specific config")
|
||||||
|
log.console(" actor <name> set <key> <val> Set actor-specific config")
|
||||||
|
log.console(" actor <name> list List actor configurations")
|
||||||
|
log.console("")
|
||||||
|
log.console("Examples:")
|
||||||
|
log.console(" cell config get system.ar_timer")
|
||||||
|
log.console(" cell config set system.net_service 0.2")
|
||||||
|
log.console(" cell config actor prosperon/_sdl_video set resolution 1920x1080")
|
||||||
|
log.console(" cell config actor extramath/spline set precision high")
|
||||||
|
log.console("")
|
||||||
|
log.console("System keys:")
|
||||||
|
log.console(" system.ar_timer - Seconds before idle actor reclamation")
|
||||||
|
log.console(" system.actor_memory - MB of memory an actor can use (0=unbounded)")
|
||||||
|
log.console(" system.net_service - Seconds per network service pull")
|
||||||
|
log.console(" system.reply_timeout - Seconds to hold callback for replies (0=unbounded)")
|
||||||
|
log.console(" system.actor_max - Max number of simultaneous actors")
|
||||||
|
log.console(" system.stack_max - MB of memory each actor's stack can grow to")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a dot-notation key into path segments
|
||||||
|
function parse_key(key) {
|
||||||
|
return array(key, '.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a value from nested object using path
|
||||||
|
function get_nested(obj, path) {
|
||||||
|
var current = obj
|
||||||
|
arrfor(path, function(segment) {
|
||||||
|
if (is_null(current) || !is_object(current)) return null
|
||||||
|
current = current[segment]
|
||||||
|
})
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a value in nested object using path
|
||||||
|
function set_nested(obj, path, value) {
|
||||||
|
var current = obj
|
||||||
|
for (var i = 0; i < length(path) - 1; i++) {
|
||||||
|
var segment = path[i]
|
||||||
|
if (is_null(current[segment]) || !is_object(current[segment])) {
|
||||||
|
current[segment] = {}
|
||||||
|
}
|
||||||
|
current = current[segment]
|
||||||
|
}
|
||||||
|
current[path[length(path) - 1]] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse value string into appropriate type
|
||||||
|
function parse_value(str) {
|
||||||
|
// Boolean
|
||||||
|
if (str == 'true') return true
|
||||||
|
if (str == 'false') return false
|
||||||
|
|
||||||
|
// Number (including underscores)
|
||||||
|
var num_str = replace(str, /_/g, '')
|
||||||
|
if (/^-?\d+$/.test(num_str)) return parseInt(num_str)
|
||||||
|
if (/^-?\d*\.\d+$/.test(num_str)) return parseFloat(num_str)
|
||||||
|
|
||||||
|
// String
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format value for display
|
||||||
|
function format_value(val) {
|
||||||
|
if (is_text(val)) return '"' + val + '"'
|
||||||
|
if (is_number(val) && val >= 1000) {
|
||||||
|
// Add underscores to large numbers
|
||||||
|
return replace(val.toString(), /\B(?=(\d{3})+(?!\d))/g, '_')
|
||||||
|
}
|
||||||
|
return text(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print configuration tree recursively
|
||||||
|
function print_config(obj, prefix = '') {
|
||||||
|
arrfor(array(obj), function(key) {
|
||||||
|
var val = obj[key]
|
||||||
|
var full_key = prefix ? prefix + '.' + key : key
|
||||||
|
|
||||||
|
if (is_object(val))
|
||||||
|
print_config(val, full_key)
|
||||||
|
else
|
||||||
|
log.console(full_key + ' = ' + format_value(val))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main command handling
|
||||||
|
if (length(args) == 0) {
|
||||||
|
print_help()
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = pkg.load_config()
|
||||||
|
if (!config) {
|
||||||
|
log.error("Failed to load cell.toml")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = args[0]
|
||||||
|
var key
|
||||||
|
var path
|
||||||
|
var value
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 'help':
|
||||||
|
case '-h':
|
||||||
|
case '--help':
|
||||||
|
print_help()
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'list':
|
||||||
|
log.console("# Cell Configuration")
|
||||||
|
log.console("")
|
||||||
|
print_config(config)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'get':
|
||||||
|
if (length(args) < 2) {
|
||||||
|
log.error("Usage: cell config get <key>")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key = args[1]
|
||||||
|
path = parse_key(key)
|
||||||
|
value = get_nested(config, path)
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
log.error("Key not found: " + key)
|
||||||
|
} else if (isa(value, object)) {
|
||||||
|
// Print all nested values
|
||||||
|
print_config(value, key)
|
||||||
|
} else {
|
||||||
|
log.console(key + ' = ' + format_value(value))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'set':
|
||||||
|
if (length(args) < 3) {
|
||||||
|
log.error("Usage: cell config set <key> <value>")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var key = args[1]
|
||||||
|
var value_str = args[2]
|
||||||
|
var path = parse_key(key)
|
||||||
|
var value = parse_value(value_str)
|
||||||
|
|
||||||
|
// Validate system keys
|
||||||
|
if (path[0] == 'system') {
|
||||||
|
var valid_system_keys = [
|
||||||
|
'ar_timer', 'actor_memory', 'net_service',
|
||||||
|
'reply_timeout', 'actor_max', 'stack_max'
|
||||||
|
]
|
||||||
|
if (find(valid_system_keys, path[1]) == null) {
|
||||||
|
log.error("Invalid system key. Valid keys: " + text(valid_system_keys, ', '))
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_nested(config, path, value)
|
||||||
|
pkg.save_config(config)
|
||||||
|
log.console("Set " + key + " = " + format_value(value))
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'actor':
|
||||||
|
// Handle actor-specific configuration
|
||||||
|
if (length(args) < 3) {
|
||||||
|
log.error("Usage: cell config actor <name> <command> [options]")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var actor_name = args[1]
|
||||||
|
var actor_cmd = args[2]
|
||||||
|
|
||||||
|
// Initialize actors section if needed
|
||||||
|
config.actors = config.actors || {}
|
||||||
|
config.actors[actor_name] = config.actors[actor_name] || {}
|
||||||
|
|
||||||
|
switch (actor_cmd) {
|
||||||
|
case 'list':
|
||||||
|
if (length(array(config.actors[actor_name])) == 0) {
|
||||||
|
log.console("No configuration for actor: " + actor_name)
|
||||||
|
} else {
|
||||||
|
log.console("# Configuration for actor: " + actor_name)
|
||||||
|
log.console("")
|
||||||
|
print_config(config.actors[actor_name], 'actors.' + actor_name)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'get':
|
||||||
|
if (length(args) < 4) {
|
||||||
|
log.error("Usage: cell config actor <name> get <key>")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key = args[3]
|
||||||
|
path = parse_key(key)
|
||||||
|
value = get_nested(config.actors[actor_name], path)
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
log.error("Key not found for actor " + actor_name + ": " + key)
|
||||||
|
} else {
|
||||||
|
log.console('actors.' + actor_name + '.' + key + ' = ' + format_value(value))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'set':
|
||||||
|
if (length(args) < 5) {
|
||||||
|
log.error("Usage: cell config actor <name> set <key> <value>")
|
||||||
|
$stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key = args[3]
|
||||||
|
var value_str = args[4]
|
||||||
|
path = parse_key(key)
|
||||||
|
value = parse_value(value_str)
|
||||||
|
|
||||||
|
set_nested(config.actors[actor_name], path, value)
|
||||||
|
pkg.save_config(config)
|
||||||
|
log.console("Set actors." + actor_name + "." + key + " = " + format_value(value))
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.error("Unknown actor command: " + actor_cmd)
|
||||||
|
log.console("Valid commands: list, get, set")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.error("Unknown command: " + command)
|
||||||
|
print_help()
|
||||||
|
}
|
||||||
|
|
||||||
|
$stop()
|
||||||
246
crypto.c
Normal file
246
crypto.c
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
#include "cell.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "monocypher.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Crypto Module Documentation
|
||||||
|
|
||||||
|
This module provides cryptographic functions using the Monocypher library.
|
||||||
|
All inputs and outputs are Blobs.
|
||||||
|
|
||||||
|
Functions:
|
||||||
|
|
||||||
|
- keypair() -> { public: Blob(256 bits), private: Blob(256 bits) }
|
||||||
|
Generates a new random X25519 keypair.
|
||||||
|
|
||||||
|
- shared(public_key, private_key) -> Blob(256 bits)
|
||||||
|
Computes a shared secret from your private key and another's public key (X25519).
|
||||||
|
Input keys must be 256 bits (32 bytes).
|
||||||
|
|
||||||
|
- blake2(data, [hash_size_bytes=32]) -> Blob
|
||||||
|
Computes the BLAKE2b hash of the data.
|
||||||
|
Default hash size is 32 bytes (256 bits). Supports 1-64 bytes.
|
||||||
|
|
||||||
|
- sign(secret_key, message) -> Blob(512 bits)
|
||||||
|
Signs a message using EdDSA.
|
||||||
|
secret_key must be 512 bits (64 bytes).
|
||||||
|
(Note: If you have a 32-byte seed, extend it first or use appropriate key generation).
|
||||||
|
Returns a 64-byte signature.
|
||||||
|
|
||||||
|
- verify(signature, public_key, message) -> bool
|
||||||
|
Verifies an EdDSA signature.
|
||||||
|
signature: 512 bits (64 bytes).
|
||||||
|
public_key: 256 bits (32 bytes).
|
||||||
|
Returns true if valid, false otherwise.
|
||||||
|
|
||||||
|
- lock(key, nonce, message, [ad]) -> Blob
|
||||||
|
Encrypts and authenticates a message using XChaCha20-Poly1305.
|
||||||
|
key: 256 bits (32 bytes).
|
||||||
|
nonce: 192 bits (24 bytes).
|
||||||
|
ad: Optional associated data (Blob).
|
||||||
|
Returns a blob containing the ciphertext followed by the 16-byte MAC.
|
||||||
|
|
||||||
|
- unlock(key, nonce, ciphertext_with_mac, [ad]) -> Blob or null
|
||||||
|
Decrypts and verifies a message.
|
||||||
|
key: 256 bits (32 bytes).
|
||||||
|
nonce: 192 bits (24 bytes).
|
||||||
|
ciphertext_with_mac: Must include the 16-byte MAC at the end.
|
||||||
|
ad: Optional associated data (Blob).
|
||||||
|
Returns the plaintext Blob if successful, or null if verification fails.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Helper to get blob data and check exact bit length
|
||||||
|
static void *get_blob_check_bits(JSContext *js, JSValue val, size_t expected_bits, const char *name) {
|
||||||
|
size_t bits;
|
||||||
|
void* result = js_get_blob_data_bits(js, &bits, val);
|
||||||
|
if (result == -1) {
|
||||||
|
return NULL; // Exception already thrown by js_get_blob_data_bits
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits != expected_bits) {
|
||||||
|
JS_ThrowTypeError(js, "%s: expected %zu bits, got %zu", name, expected_bits, bits);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to get any blob data (checking it is a stoned blob)
|
||||||
|
static void *get_blob_any(JSContext *js, JSValue val, size_t *out_bits, const char *name) {
|
||||||
|
void *result = js_get_blob_data_bits(js, out_bits, val);
|
||||||
|
if (result == -1)
|
||||||
|
return NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSValue js_crypto_shared(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
return JS_ThrowTypeError(js, "crypto.shared: expected public_key, private_key");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *pub = get_blob_check_bits(js, argv[0], 256, "crypto.shared public_key");
|
||||||
|
if (!pub) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t *priv = get_blob_check_bits(js, argv[1], 256, "crypto.shared private_key");
|
||||||
|
if (!priv) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t shared[32];
|
||||||
|
crypto_x25519(shared, priv, pub);
|
||||||
|
|
||||||
|
return js_new_blob_stoned_copy(js, shared, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_blake2(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return JS_ThrowTypeError(js, "crypto.blake2: expected data blob");
|
||||||
|
|
||||||
|
size_t data_bits;
|
||||||
|
uint8_t *data = get_blob_any(js, argv[0], &data_bits, "crypto.blake2 data");
|
||||||
|
if (!data) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
int32_t hash_len = 32;
|
||||||
|
if (argc > 1) {
|
||||||
|
if (JS_ToInt32(js, &hash_len, argv[1]))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
if (hash_len < 1 || hash_len > 64)
|
||||||
|
return JS_ThrowRangeError(js, "crypto.blake2: hash length must be between 1 and 64 bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t hash[64];
|
||||||
|
// Use (bits + 7) / 8 to get byte length covering all bits
|
||||||
|
crypto_blake2b(hash, hash_len, data, (data_bits + 7) / 8);
|
||||||
|
|
||||||
|
return js_new_blob_stoned_copy(js, hash, hash_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_sign(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||||
|
if (argc < 2) return JS_ThrowTypeError(js, "crypto.sign: expected secret_key, message");
|
||||||
|
|
||||||
|
uint8_t *sk = get_blob_check_bits(js, argv[0], 512, "crypto.sign secret_key");
|
||||||
|
if (!sk) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
size_t msg_bits;
|
||||||
|
uint8_t *msg = get_blob_any(js, argv[1], &msg_bits, "crypto.sign message");
|
||||||
|
if (!msg) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t sig[64];
|
||||||
|
crypto_eddsa_sign(sig, sk, msg, (msg_bits + 7) / 8);
|
||||||
|
|
||||||
|
return js_new_blob_stoned_copy(js, sig, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_verify(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||||
|
if (argc < 3) return JS_ThrowTypeError(js, "crypto.verify: expected signature, public_key, message");
|
||||||
|
|
||||||
|
uint8_t *sig = get_blob_check_bits(js, argv[0], 512, "crypto.verify signature");
|
||||||
|
if (!sig) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t *pk = get_blob_check_bits(js, argv[1], 256, "crypto.verify public_key");
|
||||||
|
if (!pk) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
size_t msg_bits;
|
||||||
|
uint8_t *msg = get_blob_any(js, argv[2], &msg_bits, "crypto.verify message");
|
||||||
|
if (!msg) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
int ret = crypto_eddsa_check(sig, pk, msg, (msg_bits + 7) / 8);
|
||||||
|
return JS_NewBool(js, ret == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_lock(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||||
|
if (argc < 3) return JS_ThrowTypeError(js, "crypto.lock: expected key, nonce, message, [ad]");
|
||||||
|
|
||||||
|
uint8_t *key = get_blob_check_bits(js, argv[0], 256, "crypto.lock key");
|
||||||
|
if (!key) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t *nonce = get_blob_check_bits(js, argv[1], 192, "crypto.lock nonce");
|
||||||
|
if (!nonce) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
size_t msg_bits;
|
||||||
|
uint8_t *msg = get_blob_any(js, argv[2], &msg_bits, "crypto.lock message");
|
||||||
|
if (!msg) return JS_EXCEPTION;
|
||||||
|
size_t msg_len = (msg_bits + 7) / 8;
|
||||||
|
|
||||||
|
size_t ad_len = 0;
|
||||||
|
uint8_t *ad = NULL;
|
||||||
|
if (argc > 3 && !JS_IsNull(argv[3])) {
|
||||||
|
size_t ad_bits;
|
||||||
|
ad = get_blob_any(js, argv[3], &ad_bits, "crypto.lock ad");
|
||||||
|
if (!ad) return JS_EXCEPTION;
|
||||||
|
ad_len = (ad_bits + 7) / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t out_len = msg_len + 16;
|
||||||
|
uint8_t *out = malloc(out_len);
|
||||||
|
if (!out) return JS_ThrowOutOfMemory(js);
|
||||||
|
|
||||||
|
// Output: [Ciphertext (msg_len)] [MAC (16)]
|
||||||
|
crypto_aead_lock(out, out + msg_len, key, nonce, ad, ad_len, msg, msg_len);
|
||||||
|
|
||||||
|
JSValue ret = js_new_blob_stoned_copy(js, out, out_len);
|
||||||
|
free(out);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue js_crypto_unlock(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||||
|
if (argc < 3) return JS_ThrowTypeError(js, "crypto.unlock: expected key, nonce, ciphertext, [ad]");
|
||||||
|
|
||||||
|
uint8_t *key = get_blob_check_bits(js, argv[0], 256, "crypto.unlock key");
|
||||||
|
if (!key) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
uint8_t *nonce = get_blob_check_bits(js, argv[1], 192, "crypto.unlock nonce");
|
||||||
|
if (!nonce) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
size_t cipher_bits;
|
||||||
|
uint8_t *cipher = get_blob_any(js, argv[2], &cipher_bits, "crypto.unlock ciphertext");
|
||||||
|
if (!cipher) return JS_EXCEPTION;
|
||||||
|
|
||||||
|
size_t cipher_len = (cipher_bits + 7) / 8;
|
||||||
|
if (cipher_len < 16) return JS_ThrowTypeError(js, "crypto.unlock: ciphertext too short (min 16 bytes)");
|
||||||
|
|
||||||
|
size_t msg_len = cipher_len - 16;
|
||||||
|
|
||||||
|
size_t ad_len = 0;
|
||||||
|
uint8_t *ad = NULL;
|
||||||
|
if (argc > 3 && !JS_IsNull(argv[3])) {
|
||||||
|
size_t ad_bits;
|
||||||
|
ad = get_blob_any(js, argv[3], &ad_bits, "crypto.unlock ad");
|
||||||
|
if (!ad) return JS_EXCEPTION;
|
||||||
|
ad_len = (ad_bits + 7) / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *out = malloc(msg_len > 0 ? msg_len : 1);
|
||||||
|
if (!out) return JS_ThrowOutOfMemory(js);
|
||||||
|
|
||||||
|
// MAC is at cipher + msg_len
|
||||||
|
const uint8_t *mac = cipher + msg_len;
|
||||||
|
|
||||||
|
if (crypto_aead_unlock(out, mac, key, nonce, ad, ad_len, cipher, msg_len) != 0) {
|
||||||
|
free(out);
|
||||||
|
return JS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue ret = js_new_blob_stoned_copy(js, out, msg_len);
|
||||||
|
free(out);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_crypto_funcs[] = {
|
||||||
|
JS_CFUNC_DEF("shared", 2, js_crypto_shared),
|
||||||
|
JS_CFUNC_DEF("blake2", 2, js_crypto_blake2),
|
||||||
|
JS_CFUNC_DEF("sign", 2, js_crypto_sign),
|
||||||
|
JS_CFUNC_DEF("verify", 3, js_crypto_verify),
|
||||||
|
JS_CFUNC_DEF("lock", 3, js_crypto_lock),
|
||||||
|
JS_CFUNC_DEF("unlock", 3, js_crypto_unlock),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_crypto_use(JSContext *js)
|
||||||
|
{
|
||||||
|
JSValue obj = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js, obj, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
40
debug/debug.c
Normal file
40
debug/debug.c
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "cell.h"
|
||||||
|
|
||||||
|
// Return the current stack depth.
|
||||||
|
JSC_CCALL(debug_stack_depth, return number2js(js,js_debugger_stack_depth(js)))
|
||||||
|
|
||||||
|
// Return a backtrace of the current call stack.
|
||||||
|
JSC_CCALL(debug_build_backtrace, return js_debugger_build_backtrace(js,NULL))
|
||||||
|
|
||||||
|
// Return the closure variables for a given function.
|
||||||
|
JSC_CCALL(debug_closure_vars, return js_debugger_closure_variables(js,argv[0]))
|
||||||
|
|
||||||
|
JSC_CCALL(debug_set_closure_var,
|
||||||
|
js_debugger_set_closure_variable(js,argv[0],argv[1],argv[2]);
|
||||||
|
return JS_NULL;
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the local variables for a specific stack frame.
|
||||||
|
JSC_CCALL(debug_local_vars, return js_debugger_local_variables(js, js2number(js,argv[0])))
|
||||||
|
|
||||||
|
// Return metadata about a given function.
|
||||||
|
JSC_CCALL(debug_fn_info, return js_debugger_fn_info(js, argv[0]))
|
||||||
|
|
||||||
|
// Return an array of functions in the current backtrace.
|
||||||
|
JSC_CCALL(debug_backtrace_fns, return js_debugger_backtrace_fns(js,NULL))
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_debug_funcs[] = {
|
||||||
|
MIST_FUNC_DEF(debug, stack_depth, 0),
|
||||||
|
MIST_FUNC_DEF(debug, build_backtrace, 0),
|
||||||
|
MIST_FUNC_DEF(debug, closure_vars, 1),
|
||||||
|
MIST_FUNC_DEF(debug, set_closure_var, 3),
|
||||||
|
MIST_FUNC_DEF(debug, local_vars, 1),
|
||||||
|
MIST_FUNC_DEF(debug, fn_info, 1),
|
||||||
|
MIST_FUNC_DEF(debug, backtrace_fns,0),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_debug_use(JSContext *js) {
|
||||||
|
JSValue mod = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js,mod,js_debug_funcs,countof(js_debug_funcs));
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
108
debug/js.c
Normal file
108
debug/js.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "cell.h"
|
||||||
|
|
||||||
|
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||||
|
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||||
|
|
||||||
|
// Compute the approximate size of a single JS value in memory.
|
||||||
|
JSC_CCALL(os_calc_mem,
|
||||||
|
JSMemoryUsage mu;
|
||||||
|
JS_ComputeMemoryUsage(JS_GetRuntime(js),&mu);
|
||||||
|
ret = JS_NewObject(js);
|
||||||
|
JS_SetPropertyStr(js,ret,"malloc_size",number2js(js,mu.malloc_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"malloc_limit",number2js(js,mu.malloc_limit));
|
||||||
|
JS_SetPropertyStr(js,ret,"memory_used_size",number2js(js,mu.memory_used_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"malloc_count",number2js(js,mu.malloc_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"memory_used_count",number2js(js,mu.memory_used_count));
|
||||||
|
/* atom_count and atom_size removed - atoms are now just strings */
|
||||||
|
JS_SetPropertyStr(js,ret,"str_count",number2js(js,mu.str_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"str_size",number2js(js,mu.str_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"obj_count",number2js(js,mu.obj_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"obj_size",number2js(js,mu.obj_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"prop_count",number2js(js,mu.prop_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"prop_size",number2js(js,mu.prop_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"shape_count",number2js(js,mu.shape_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"shape_size",number2js(js,mu.shape_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"js_func_count",number2js(js,mu.js_func_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"js_func_size",number2js(js,mu.js_func_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"js_func_code_size",number2js(js,mu.js_func_code_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"js_func_pc2line_count",number2js(js,mu.js_func_pc2line_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"js_func_pc2line_size",number2js(js,mu.js_func_pc2line_size));
|
||||||
|
JS_SetPropertyStr(js,ret,"c_func_count",number2js(js,mu.c_func_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"array_count",number2js(js,mu.array_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"fast_array_count",number2js(js,mu.fast_array_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"fast_array_elements",number2js(js,mu.fast_array_elements));
|
||||||
|
JS_SetPropertyStr(js,ret,"binary_object_count",number2js(js,mu.binary_object_count));
|
||||||
|
JS_SetPropertyStr(js,ret,"binary_object_size",number2js(js,mu.binary_object_size));
|
||||||
|
)
|
||||||
|
|
||||||
|
// Evaluate a string of JavaScript code in the current QuickJS context.
|
||||||
|
JSC_SSCALL(os_eval,
|
||||||
|
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
|
||||||
|
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
|
||||||
|
JSValue bytecode = JS_Compile(js, str2, strlen(str2), str);
|
||||||
|
if (JS_IsException(bytecode)) return bytecode;
|
||||||
|
ret = JS_Integrate(js, bytecode, JS_NULL);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compile a string of JavaScript code into a function object.
|
||||||
|
JSC_SSCALL(js_compile,
|
||||||
|
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
|
||||||
|
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
|
||||||
|
ret = JS_Compile(js, str2, strlen(str2), str);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Link compiled bytecode with environment and execute.
|
||||||
|
JSC_CCALL(js_integrate,
|
||||||
|
JSValue env = (argc > 1 && !JS_IsNull(argv[1])) ? argv[1] : JS_NULL;
|
||||||
|
ret = JS_Integrate(js, argv[0], env);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compile a function object into a bytecode blob.
|
||||||
|
JSC_CCALL(js_compile_blob,
|
||||||
|
size_t size;
|
||||||
|
uint8_t *data = JS_WriteObject(js, &size, argv[0], JS_WRITE_OBJ_BYTECODE);
|
||||||
|
if (!data) {
|
||||||
|
return JS_ThrowInternalError(js, "Failed to serialize bytecode");
|
||||||
|
}
|
||||||
|
ret = js_new_blob_stoned_copy(js, data, size);
|
||||||
|
js_free(js, data);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compile a bytecode blob into a function object.
|
||||||
|
JSC_CCALL(js_compile_unblob,
|
||||||
|
size_t size;
|
||||||
|
void *data = js_get_blob_data(js, &size, argv[0]);
|
||||||
|
if (data == -1) return JS_EXCEPTION;
|
||||||
|
if (!data) return JS_ThrowReferenceError(js, "No data present in blob.");
|
||||||
|
|
||||||
|
return JS_ReadObject(js, data, size, JS_READ_OBJ_BYTECODE);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Disassemble a function object into a string.
|
||||||
|
JSC_CCALL(js_disassemble,
|
||||||
|
return js_debugger_fn_bytecode(js, argv[0]);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return metadata about a given function.
|
||||||
|
JSC_CCALL(js_fn_info,
|
||||||
|
return js_debugger_fn_info(js, argv[0]);
|
||||||
|
)
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_js_funcs[] = {
|
||||||
|
MIST_FUNC_DEF(os, calc_mem, 0),
|
||||||
|
MIST_FUNC_DEF(os, mem_limit, 1),
|
||||||
|
MIST_FUNC_DEF(os, max_stacksize, 1),
|
||||||
|
MIST_FUNC_DEF(os, eval, 2),
|
||||||
|
MIST_FUNC_DEF(js, compile, 2),
|
||||||
|
MIST_FUNC_DEF(js, integrate, 2),
|
||||||
|
MIST_FUNC_DEF(js, compile_blob, 1),
|
||||||
|
MIST_FUNC_DEF(js, compile_unblob, 1),
|
||||||
|
MIST_FUNC_DEF(js, disassemble, 1),
|
||||||
|
MIST_FUNC_DEF(js, fn_info, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue js_js_use(JSContext *js) {
|
||||||
|
JSValue mod = JS_NewObject(js);
|
||||||
|
JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs));
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
9
docs/.pages
Normal file
9
docs/.pages
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
nav:
|
||||||
|
- index.md
|
||||||
|
- cellscript.md
|
||||||
|
- actors.md
|
||||||
|
- packages.md
|
||||||
|
- cli.md
|
||||||
|
- c-modules.md
|
||||||
|
- Standard Library: library
|
||||||
|
|
||||||
230
docs/actors.md
Normal file
230
docs/actors.md
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
# Actors and Modules
|
||||||
|
|
||||||
|
Cell organizes code into two types of scripts: **modules** (`.cm`) and **actors** (`.ce`).
|
||||||
|
|
||||||
|
## The Actor Model
|
||||||
|
|
||||||
|
Cell is built on the actor model of computation. Each actor:
|
||||||
|
|
||||||
|
- Has its own **isolated memory** — actors never share state
|
||||||
|
- Runs to completion each **turn** — no preemption
|
||||||
|
- Performs its own **garbage collection**
|
||||||
|
- Communicates only through **message passing**
|
||||||
|
|
||||||
|
This isolation makes concurrent programming safer and more predictable.
|
||||||
|
|
||||||
|
## Modules (.cm)
|
||||||
|
|
||||||
|
A module is a script that **returns a value**. The returned value is cached and frozen (made stone).
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// math_utils.cm
|
||||||
|
var math = use('math/radians')
|
||||||
|
|
||||||
|
function distance(x1, y1, x2, y2) {
|
||||||
|
var dx = x2 - x1
|
||||||
|
var dy = y2 - y1
|
||||||
|
return math.sqrt(dx * dx + dy * dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
function midpoint(x1, y1, x2, y2) {
|
||||||
|
return {
|
||||||
|
x: (x1 + x2) / 2,
|
||||||
|
y: (y1 + y2) / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
distance: distance,
|
||||||
|
midpoint: midpoint
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key properties:**
|
||||||
|
|
||||||
|
- **Must return a value** — it's an error not to
|
||||||
|
- **Executed once per actor** — subsequent `use()` calls return the cached value
|
||||||
|
- **Return value is stone** — immutable, safe to share
|
||||||
|
- Modules can import other modules with `use()`
|
||||||
|
|
||||||
|
### Using Modules
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var utils = use('math_utils')
|
||||||
|
var d = utils.distance(0, 0, 3, 4) // 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Actors (.ce)
|
||||||
|
|
||||||
|
An actor is a script that **does not return a value**. It runs as an independent unit of execution.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// worker.ce
|
||||||
|
log.console("Worker started")
|
||||||
|
|
||||||
|
$on_message = function(msg) {
|
||||||
|
log.console("Received:", msg)
|
||||||
|
// Process message...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key properties:**
|
||||||
|
|
||||||
|
- **Must not return a value** — it's an error to return
|
||||||
|
- Has access to **actor intrinsics** (functions starting with `$`)
|
||||||
|
- Runs until explicitly stopped or crashes
|
||||||
|
|
||||||
|
## Actor Intrinsics
|
||||||
|
|
||||||
|
Actors have access to special functions prefixed with `$`:
|
||||||
|
|
||||||
|
### $me
|
||||||
|
|
||||||
|
Reference to the current actor.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
log.console($me) // actor reference
|
||||||
|
```
|
||||||
|
|
||||||
|
### $stop()
|
||||||
|
|
||||||
|
Stop the current actor.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$stop()
|
||||||
|
```
|
||||||
|
|
||||||
|
### $send(actor, message, callback)
|
||||||
|
|
||||||
|
Send a message to another actor.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$send(other_actor, {type: "ping", data: 42}, function(reply) {
|
||||||
|
log.console("Got reply:", reply)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Messages are automatically **splatted** — flattened to plain data without prototypes.
|
||||||
|
|
||||||
|
### $start(callback, program)
|
||||||
|
|
||||||
|
Start a new actor from a script.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$start(function(new_actor) {
|
||||||
|
log.console("Started:", new_actor)
|
||||||
|
}, "worker")
|
||||||
|
```
|
||||||
|
|
||||||
|
### $delay(callback, seconds)
|
||||||
|
|
||||||
|
Schedule a callback after a delay.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$delay(function() {
|
||||||
|
log.console("5 seconds later")
|
||||||
|
}, 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
### $clock(callback)
|
||||||
|
|
||||||
|
Get called every frame/tick.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$clock(function(dt) {
|
||||||
|
// Called each tick with delta time
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### $receiver(callback)
|
||||||
|
|
||||||
|
Set up a message receiver.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$receiver(function(message, reply) {
|
||||||
|
// Handle incoming message
|
||||||
|
reply({status: "ok"})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### $portal(callback, port)
|
||||||
|
|
||||||
|
Open a network port.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$portal(function(connection) {
|
||||||
|
// Handle new connection
|
||||||
|
}, 8080)
|
||||||
|
```
|
||||||
|
|
||||||
|
### $contact(callback, record)
|
||||||
|
|
||||||
|
Connect to a remote address.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$contact(function(connection) {
|
||||||
|
// Connected
|
||||||
|
}, {host: "example.com", port: 80})
|
||||||
|
```
|
||||||
|
|
||||||
|
### $time_limit(requestor, seconds)
|
||||||
|
|
||||||
|
Wrap a requestor with a timeout.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$time_limit(my_requestor, 10) // 10 second timeout
|
||||||
|
```
|
||||||
|
|
||||||
|
## Module Resolution
|
||||||
|
|
||||||
|
When you call `use('name')`, Cell searches:
|
||||||
|
|
||||||
|
1. **Current package** — files relative to package root
|
||||||
|
2. **Dependencies** — packages declared in `cell.toml`
|
||||||
|
3. **Core** — built-in Cell modules
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// From within package 'myapp':
|
||||||
|
use('utils') // myapp/utils.cm
|
||||||
|
use('helper/math') // myapp/helper/math.cm
|
||||||
|
use('json') // core json module
|
||||||
|
use('otherlib/foo') // dependency 'otherlib', file foo.cm
|
||||||
|
```
|
||||||
|
|
||||||
|
Files starting with underscore (`_helper.cm`) are private to the package.
|
||||||
|
|
||||||
|
## Example: Simple Actor System
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// main.ce - Entry point
|
||||||
|
var config = use('config')
|
||||||
|
|
||||||
|
log.console("Starting application...")
|
||||||
|
|
||||||
|
$start(function(worker) {
|
||||||
|
$send(worker, {task: "process", data: [1, 2, 3]})
|
||||||
|
}, "worker")
|
||||||
|
|
||||||
|
$delay(function() {
|
||||||
|
log.console("Shutting down")
|
||||||
|
$stop()
|
||||||
|
}, 10)
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// worker.ce - Worker actor
|
||||||
|
$receiver(function(msg, reply) {
|
||||||
|
if (msg.task == "process") {
|
||||||
|
var result = array(msg.data, x => x * 2)
|
||||||
|
reply({result: result})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// config.cm - Shared configuration
|
||||||
|
return {
|
||||||
|
debug: true,
|
||||||
|
timeout: 30
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Cmdline
|
|
||||||
#### cmds
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"flag": "l",
|
|
||||||
"doc": "Set log level."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
#### orders
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### register_cmd(flag, fn, doc)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### register_order(order, fn, doc, usage = "")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### print_order(fn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
# Color
|
|
||||||
#### white
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### black
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### blue
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.32941176470588235,
|
|
||||||
0.43137254901960786,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### green
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.47058823529411764,
|
|
||||||
1,
|
|
||||||
0.0392156862745098,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### yellow
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.984313725490196,
|
|
||||||
1,
|
|
||||||
0.16862745098039217,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### red
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
0.1411764705882353,
|
|
||||||
0.0784313725490196,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### teal
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.3764705882352941,
|
|
||||||
0.9882352941176471,
|
|
||||||
0.9294117647058824,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### gray
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.7098039215686275,
|
|
||||||
0.7098039215686275,
|
|
||||||
0.7098039215686275,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### cyan
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### purple
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0.6352941176470588,
|
|
||||||
0.36470588235294116,
|
|
||||||
0.8901960784313725,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### editor
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tohtml(v)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Arkanoid
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Gameboy
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Apple
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Debug
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Editor
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### normalize(c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# ColorMap
|
|
||||||
#### makemap(map)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Jet
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### BlueRed
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### Inferno
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### Bathymetry
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### Viridis
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### sample(t, map = this)
|
|
||||||
|
|
||||||
Sample a given colormap at the given percentage (0 to 1).
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# Ease
|
|
||||||
#### linear(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### in(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### out(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### inout(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### quad
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### cubic
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### quart
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### quint
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### expo
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bounce
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### sine
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### elastic
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Event
|
|
||||||
#### events
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### observe(name, obj, fn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### unobserve(name, obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### rm_obj(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### notify(name, ...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Gizmos
|
|
||||||
#### pick_gameobject_points(worldpos, gameobject, points)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
173
docs/api/Mum.md
173
docs/api/Mum.md
@@ -1,173 +0,0 @@
|
|||||||
# Mum
|
|
||||||
#### padding
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
|
|
||||||
#### offset
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
|
|
||||||
#### font
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### selectable
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### selected
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### font_size
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### text_align
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### scale
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### angle
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### anchor
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### hovered
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### text_shadow
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### text_outline
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### color
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### margin
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
|
|
||||||
#### width
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### height
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### max_width
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### max_height
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### image_repeat
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### image_repeat_offset
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
|
|
||||||
#### debug
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### prestart()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### extend(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### text(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### button(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### window(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### image(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### column(def)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### debug_colors
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# Register
|
|
||||||
#### registries
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
"doc": "Called once per frame."
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{}
|
|
||||||
]
|
|
||||||
|
|
||||||
#### add_cb(name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### appupdate
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### update
|
|
||||||
**object**
|
|
||||||
|
|
||||||
Called once per frame.
|
|
||||||
|
|
||||||
#### physupdate
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gui
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### debug
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### screengui
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
# Resources
|
|
||||||
#### replpath(str, path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### replstrs(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### scripts
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
"jsoc",
|
|
||||||
"jsc",
|
|
||||||
"jso",
|
|
||||||
"js"
|
|
||||||
]
|
|
||||||
|
|
||||||
#### images
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
"png",
|
|
||||||
"gif",
|
|
||||||
"jpg",
|
|
||||||
"jpeg"
|
|
||||||
]
|
|
||||||
|
|
||||||
#### sounds
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
"wav",
|
|
||||||
"flac",
|
|
||||||
"mp3",
|
|
||||||
"qoa"
|
|
||||||
]
|
|
||||||
|
|
||||||
#### is_image(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### find_image(file)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### find_sound(file)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### find_script(file)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### is_sound(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### is_animation(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### is_path(str)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### texture
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gif
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
# Spline
|
|
||||||
#### sample_angle(type, points, angle)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_loop(cp)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_node_count(cp)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### is_bezier(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### is_catmull(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier2catmull(b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### catmull2bezier(c)
|
|
||||||
|
|
||||||
Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve.
|
|
||||||
|
|
||||||
#### catmull_loop(cp)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### catmull_caps(cp)
|
|
||||||
|
|
||||||
Given a set of control points cp, return the necessary caps added to the spline.
|
|
||||||
|
|
||||||
#### type
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_tan_partner(points, i)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_cp_mirror(points, i)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_point_handles(points, i)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_nodes(points)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_is_node(points, i)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### bezier_is_handle(points, i)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# SpriteAnim
|
|
||||||
Functions to create Primum animations from varying sources.
|
|
||||||
#### make(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gif(path)
|
|
||||||
|
|
||||||
Convert a gif.
|
|
||||||
|
|
||||||
#### strip(path, frames, time=0.05)
|
|
||||||
|
|
||||||
Given a path and number of frames, converts a horizontal strip animation, where each cell is the same width.
|
|
||||||
|
|
||||||
#### aseprite(path)
|
|
||||||
|
|
||||||
Given an aseprite json metadata, returns an object of animations defined in the aseprite file.
|
|
||||||
|
|
||||||
#### validate(anim)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### find(path)
|
|
||||||
|
|
||||||
Given a path, find the relevant animation for the file.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Tween
|
|
||||||
#### default
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### start(obj, target, tvals, options)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make(obj, target, tvals, options)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Vector
|
|
||||||
#### length(v)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### norm(v)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### project(a, b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### dot(a, b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### random()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### angle_between(a,b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### angle(v)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### rotate(v,angle)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### equal(v1, v2, tol)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### reflect(vec, plane)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### reflect_point(vec, point)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# actor
|
|
||||||
#### spawn(script, config, callback)
|
|
||||||
|
|
||||||
Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.
|
|
||||||
|
|
||||||
#### rm_pawn(pawn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### timers
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
#### kill()
|
|
||||||
|
|
||||||
Remove this actor and all its padawans from existence.
|
|
||||||
|
|
||||||
#### interval(fn, seconds)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### delay(fn, seconds)
|
|
||||||
|
|
||||||
Call 'fn' after 'seconds' with 'this' set to the actor.
|
|
||||||
|
|
||||||
#### padawans
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
{}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# ai
|
|
||||||
#### race(list)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### sequence(list)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### parallel(list)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### dofor(secs, fn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### wait(secs = 1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# allsprites
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# app
|
|
||||||
#### die()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# audio
|
|
||||||
#### channels
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### buffer_frames
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### samplerate
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# bbox
|
|
||||||
#### overlap(box1, box2)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### fromcwh(c, wh)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### frompoints(points)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### topoints(bb)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tocwh(bb)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### towh(bb)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pointin(bb, p)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### move(bb, pos)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### expand(oldbb, x)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### blwh(bl,wh)
|
|
||||||
|
|
||||||
Bounding box from (bottom left, width height)
|
|
||||||
|
|
||||||
#### fromobjs(objs)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# component
|
|
||||||
#### sprite(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### edge2d(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### circle2d(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### poly2d(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### seg2d(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
# console
|
|
||||||
#### print()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### rec()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### stdout_lvl
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### transcript
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### say(msg)
|
|
||||||
|
|
||||||
Write raw text to console, plus a newline.
|
|
||||||
|
|
||||||
#### log(msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pprint(msg, lvl = 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### spam(msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### debug(msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### info(msg)
|
|
||||||
|
|
||||||
Output info level message.
|
|
||||||
|
|
||||||
#### warn(msg)
|
|
||||||
|
|
||||||
Output warn level message.
|
|
||||||
|
|
||||||
#### error(msg)
|
|
||||||
|
|
||||||
Output error level message, and print stacktrace.
|
|
||||||
|
|
||||||
#### panic(msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### stackstr(skip = 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### stack(skip = 0)
|
|
||||||
|
|
||||||
Output a stacktrace to console.
|
|
||||||
|
|
||||||
#### trace(skip = 0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# convert
|
|
||||||
#### romanize(num)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### deromanize(str)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### buf2hex(buffer)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# debug
|
|
||||||
#### fn_break(fn,obj = globalThis)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw_phys
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw_bb
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw_gizmos
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw_names
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### inputs
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gif
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### api
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### log
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
# dspsound
|
|
||||||
#### noise()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pink()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### red()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pitchshift()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### noise_gate()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### limiter()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### compressor()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### crush()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### lpf()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### hpf()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### delay()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### fwd_delay()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### source()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mix()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### master()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### plugin_node()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### midi()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mod()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
script:0: [2024-07-03 12:05:54] [22;31merror[0m, script: ReferenceError :: 'editor' is not defined
|
|
||||||
at <eval> (<input>)
|
|
||||||
at <anonymous> (scripts/debug.js:193)
|
|
||||||
at <anonymous> (scripts/std.js:385)
|
|
||||||
at cmd_args (scripts/std.js:458)
|
|
||||||
at <eval> (C eval)
|
|
||||||
|
|
||||||
Initializing cpSpace - Chipmunk v7.0.3 (Debug Enabled)
|
|
||||||
Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks
|
|
||||||
USE REPORT
|
|
||||||
scripts/base.js 5.142 ms
|
|
||||||
scripts/render.js 1.197 ms
|
|
||||||
scripts/debug.js 521.4 us
|
|
||||||
scripts/input.js 1.055 ms
|
|
||||||
scripts/std.js 1.175 ms
|
|
||||||
scripts/diff.js 194.4 us
|
|
||||||
scripts/color.js 856.8 us
|
|
||||||
scripts/gui.js 597.1 us
|
|
||||||
scripts/tween.js 516.6 us
|
|
||||||
scripts/ai.js 128.0 us
|
|
||||||
scripts/physics.js 94.48 us
|
|
||||||
scripts/geometry.js 189.4 us
|
|
||||||
scripts/spline.js 253.1 us
|
|
||||||
scripts/components.js 1.761 ms
|
|
||||||
scripts/actor.js 185.2 us
|
|
||||||
scripts/entity.js 2.157 ms
|
|
||||||
scripts/widget.js 444.3 us
|
|
||||||
scripts/mum.js 76.63 us
|
|
||||||
scripts/editor.js 16.03 ms
|
|
||||||
|
|
||||||
ENTITY REPORT
|
|
||||||
|
|
||||||
scripts/engine.js:501: [2024-07-03 12:05:56] [22;36minfo[0m, script: QUITTING
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
script:0: [2024-07-03 12:13:12] [22;31merror[0m, script: TypeError :: cannot read property 'doc' of undefined
|
|
||||||
at <anonymous> (scripts/debug.js:179)
|
|
||||||
at <anonymous> (scripts/debug.js:223)
|
|
||||||
at <anonymous> (scripts/std.js:385)
|
|
||||||
at cmd_args (scripts/std.js:458)
|
|
||||||
at <eval> (C eval)
|
|
||||||
|
|
||||||
Initializing cpSpace - Chipmunk v7.0.3 (Debug Enabled)
|
|
||||||
Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks
|
|
||||||
USE REPORT
|
|
||||||
scripts/base.js 5.200 ms
|
|
||||||
scripts/render.js 1.284 ms
|
|
||||||
scripts/debug.js 516.9 us
|
|
||||||
scripts/input.js 970.4 us
|
|
||||||
scripts/std.js 1.103 ms
|
|
||||||
scripts/diff.js 230.1 us
|
|
||||||
scripts/color.js 1.148 ms
|
|
||||||
scripts/gui.js 665.1 us
|
|
||||||
scripts/tween.js 498.5 us
|
|
||||||
scripts/ai.js 125.5 us
|
|
||||||
scripts/physics.js 93.55 us
|
|
||||||
scripts/geometry.js 212.2 us
|
|
||||||
scripts/spline.js 252.9 us
|
|
||||||
scripts/components.js 1.780 ms
|
|
||||||
scripts/actor.js 188.5 us
|
|
||||||
scripts/entity.js 2.101 ms
|
|
||||||
scripts/widget.js 452.2 us
|
|
||||||
scripts/mum.js 70.77 us
|
|
||||||
scripts/editor.js 17.66 ms
|
|
||||||
|
|
||||||
ENTITY REPORT
|
|
||||||
|
|
||||||
scripts/engine.js:501: [2024-07-03 12:13:13] [22;36minfo[0m, script: QUITTING
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# entityreport
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# esc
|
|
||||||
Functions and constants for ANSI escape sequences.
|
|
||||||
#### reset
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### color(v)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# game
|
|
||||||
#### engine_start(s)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### startengine
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### timescale
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### all_objects(fn, startobj = world)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### find_object(fn, startobj = world)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tags
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tag_add(tag, obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tag_rm(tag, obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tag_clear_guid(guid)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### objects_with_tag(tag)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### texture(path, force = false)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### loadurs()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### ur
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:14] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# gui
|
|
||||||
#### scissor()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### text()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### font_set()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### scissor_win()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### input_lmouse_pressed()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### input_s_pressed()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### input_w_pressed()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### input_enter_pressed()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### controls
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
# input
|
|
||||||
#### show_keyboard()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keyboard_shown()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouse_mode()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouse_cursor()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### cursor_img()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keycodes
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### codekeys
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouse
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### keyboard
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### state2str(state)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### print_pawn_kbm(pawn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### procdown()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### print_md_kbm(pawn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### has_bind(pawn, bind)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### action
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### tabcomplete(val, list)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### do_uncontrol(pawn)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
# inputpanel
|
|
||||||
#### title
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### value
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### on
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pos
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
20,
|
|
||||||
460
|
|
||||||
]
|
|
||||||
|
|
||||||
#### wh
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
100,
|
|
||||||
100
|
|
||||||
]
|
|
||||||
|
|
||||||
#### anchor
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
|
|
||||||
#### padding
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
5,
|
|
||||||
-15
|
|
||||||
]
|
|
||||||
|
|
||||||
#### gui()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### guibody()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### open()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### close()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### action()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### closeonsubmit
|
|
||||||
**boolean**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### submit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### submit_check()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keycb()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### caret
|
|
||||||
**number**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### reset_value()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### input_backspace_pressrep()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### inputs
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
# io
|
|
||||||
Functions for filesystem input/output commands.
|
|
||||||
#### exists()
|
|
||||||
|
|
||||||
Returns true if a file exists.
|
|
||||||
|
|
||||||
#### ls()
|
|
||||||
|
|
||||||
List contents of the game directory.
|
|
||||||
|
|
||||||
#### cp(f1,f2)
|
|
||||||
|
|
||||||
Copy file f1 to f2.
|
|
||||||
|
|
||||||
#### mv()
|
|
||||||
|
|
||||||
Rename file f1 to f2.
|
|
||||||
|
|
||||||
#### rm(f)
|
|
||||||
|
|
||||||
Remove file f.
|
|
||||||
|
|
||||||
#### chdir()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mkdir()
|
|
||||||
|
|
||||||
Make dir.
|
|
||||||
|
|
||||||
#### chmod(file,mode)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### slurp(path)
|
|
||||||
|
|
||||||
Returns the contents of given file as a string.
|
|
||||||
|
|
||||||
#### slurpbytes(path)
|
|
||||||
|
|
||||||
Return the contents of a file as a byte array.
|
|
||||||
|
|
||||||
#### slurpwrite(path, c)
|
|
||||||
|
|
||||||
Write a given string to a given file.
|
|
||||||
|
|
||||||
#### save_qoa()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pack_start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pack_add()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pack_end()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mod()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### dumpfolder
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mkpath(dir)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### extensions(ext)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### glob(pat)
|
|
||||||
|
|
||||||
Glob files in game directory.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# joint
|
|
||||||
#### pin()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pivot()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gear()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### rotary()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### damped_rotary()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### damped_spring()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### groove()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### slide()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### ratchet()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### motor()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# json
|
|
||||||
json implementation.
|
|
||||||
#### encode(value, replacer, space = 1)
|
|
||||||
|
|
||||||
Encode a value to json.
|
|
||||||
|
|
||||||
#### decode(text, reviver)
|
|
||||||
|
|
||||||
Decode a json string to a value.
|
|
||||||
|
|
||||||
#### readout(obj)
|
|
||||||
|
|
||||||
Encode an object fully, including function definitions.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# listpanel
|
|
||||||
#### assets
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
#### allassets
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
#### mumlist
|
|
||||||
**object**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### submit_check()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keycb()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### guibody()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# nota
|
|
||||||
#### encode()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### decode()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# notifypanel
|
|
||||||
#### title
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### msg
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### action()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### guibody()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
149
docs/api/os.md
149
docs/api/os.md
@@ -1,149 +0,0 @@
|
|||||||
# os
|
|
||||||
#### cwd()
|
|
||||||
|
|
||||||
Get the absolute path of the current working directory.
|
|
||||||
|
|
||||||
#### env()
|
|
||||||
|
|
||||||
Return the value of the environment variable v.
|
|
||||||
|
|
||||||
#### sys()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### system()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### quit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### exit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### reindex_static()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gc()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### eval()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_body()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_circle2d()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_poly2d()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_seg2d()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_texture()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_tex_data()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_font()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_model()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_transform()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_emitter()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_buffer()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_line_prim()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_cylinder()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_cone()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_disk()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_torus()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_sphere()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_klein_bottle()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_trefoil_knot()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_hemisphere()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_plane()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### make_video()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### platform
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### user
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### home
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### prefpath()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### openurl(url)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# performance
|
|
||||||
#### barecall()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### unpack_num()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### unpack_array()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pack_num()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pack_string()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### unpack_string()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### unpack_32farr()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### call_fn_n()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
at <anonymous> (scripts/engine.js:198)
|
|
||||||
at <eval> (C eval)
|
|
||||||
at get iterations (native)
|
|
||||||
at <anonymous> (scripts/debug.js:185)
|
|
||||||
at <anonymous> (scripts/debug.js:223)
|
|
||||||
at <anonymous> (scripts/std.js:385)
|
|
||||||
at cmd_args (scripts/std.js:458)
|
|
||||||
at <eval> (C eval)
|
|
||||||
|
|
||||||
USE REPORT
|
|
||||||
scripts/base.js 5.646 ms
|
|
||||||
scripts/render.js 1.282 ms
|
|
||||||
scripts/debug.js 598.0 us
|
|
||||||
scripts/input.js 1.005 ms
|
|
||||||
scripts/std.js 1.606 ms
|
|
||||||
scripts/diff.js 214.2 us
|
|
||||||
scripts/color.js 989.1 us
|
|
||||||
scripts/gui.js 931.0 us
|
|
||||||
scripts/tween.js 631.2 us
|
|
||||||
scripts/ai.js 130.4 us
|
|
||||||
scripts/physics.js 100.2 us
|
|
||||||
scripts/geometry.js 186.1 us
|
|
||||||
scripts/spline.js 286.5 us
|
|
||||||
scripts/components.js 2.147 ms
|
|
||||||
scripts/actor.js 236.0 us
|
|
||||||
scripts/entity.js 2.627 ms
|
|
||||||
scripts/widget.js 496.0 us
|
|
||||||
scripts/mum.js 75.69 us
|
|
||||||
scripts/editor.js 18.19 ms
|
|
||||||
|
|
||||||
ENTITY REPORT
|
|
||||||
|
|
||||||
scripts/engine.js:501: [2024-07-03 12:13:09] [22;36minfo[0m, script: QUITTING
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:195: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of 'physlag', as it was not found.
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
# player
|
|
||||||
#### 0
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### 1
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### 2
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### 3
|
|
||||||
**object**
|
|
||||||
|
|
||||||
[object Object]
|
|
||||||
|
|
||||||
#### players
|
|
||||||
**array**
|
|
||||||
|
|
||||||
A list of current players.
|
|
||||||
|
|
||||||
#### input(fn, ...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouse_input(type, ...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### char_input(c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### raw_input(cmd, state, ...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### obj_controlled(obj)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### print_pawns()
|
|
||||||
|
|
||||||
Print out a list of the current pawn control stack.
|
|
||||||
|
|
||||||
#### create()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### pawns
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
#### control(pawn)
|
|
||||||
|
|
||||||
Control a provided object, if the object has an 'inputs' object.
|
|
||||||
|
|
||||||
#### uncontrol(pawn)
|
|
||||||
|
|
||||||
Uncontrol a previously controlled object.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
at <anonymous> (scripts/engine.js:198)
|
|
||||||
at <eval> (C eval)
|
|
||||||
at get radius (native)
|
|
||||||
at <anonymous> (scripts/debug.js:185)
|
|
||||||
at <anonymous> (scripts/debug.js:223)
|
|
||||||
at <anonymous> (scripts/std.js:385)
|
|
||||||
at cmd_args (scripts/std.js:458)
|
|
||||||
at <eval> (C eval)
|
|
||||||
|
|
||||||
USE REPORT
|
|
||||||
scripts/base.js 5.789 ms
|
|
||||||
scripts/render.js 2.126 ms
|
|
||||||
scripts/debug.js 709.5 us
|
|
||||||
scripts/input.js 1.222 ms
|
|
||||||
scripts/std.js 1.344 ms
|
|
||||||
scripts/diff.js 233.6 us
|
|
||||||
scripts/color.js 1.017 ms
|
|
||||||
scripts/gui.js 747.8 us
|
|
||||||
scripts/tween.js 625.2 us
|
|
||||||
scripts/ai.js 173.2 us
|
|
||||||
scripts/physics.js 124.2 us
|
|
||||||
scripts/geometry.js 221.7 us
|
|
||||||
scripts/spline.js 321.3 us
|
|
||||||
scripts/components.js 2.191 ms
|
|
||||||
scripts/actor.js 230.6 us
|
|
||||||
scripts/entity.js 4.996 ms
|
|
||||||
scripts/widget.js 607.5 us
|
|
||||||
scripts/mum.js 94.18 us
|
|
||||||
scripts/editor.js 22.40 ms
|
|
||||||
|
|
||||||
ENTITY REPORT
|
|
||||||
|
|
||||||
scripts/engine.js:501: [2024-07-03 12:13:09] [22;36minfo[0m, script: QUITTING
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:11] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
scripts/debug.js:205: [2024-07-03 12:13:10] [22;33mwarn[0m, script: Cannot print the API of something that isn't an object.
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
# profcache
|
|
||||||
#### scripts/base.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
5509269
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/render.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1371390
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/debug.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
590951
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/input.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1101579
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/std.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1516994
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/diff.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
242594
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/color.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1054188
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/gui.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
706472
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/tween.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
518110
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/ai.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
129610
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/physics.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
168777,
|
|
||||||
32773
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/geometry.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
216535
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/spline.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
284937
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/components.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
1904302
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/actor.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
196303
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/entity.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
2293362
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/widget.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
524872
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/mum.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
81892
|
|
||||||
]
|
|
||||||
|
|
||||||
#### scripts/editor.js
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[
|
|
||||||
17107128
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# profile
|
|
||||||
#### now()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### best_t(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### report(start, msg = "[undefined report]")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### addreport(cache, line, start)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### printreport(cache, name)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### cpu(fn, times = 1, q = "unnamed")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### ms(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### secs(t)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
# prosperon
|
|
||||||
#### phys2d_step()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### window_render()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### guid()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### version
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### revision
|
|
||||||
**string**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### semver
|
|
||||||
**object**
|
|
||||||
|
|
||||||
Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.
|
|
||||||
|
|
||||||
#### iconified(icon)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### focus(focus)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### resize(dimensions)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### suspended(sus)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouseenter()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouseleave()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### touchpress(touches)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### touchrelease(touches)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### touchmove(touches)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### clipboardpaste(str)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### quit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keys
|
|
||||||
**array**
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
#### keydown(key, repeat)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### keyup(key)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### droppedfile(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### textinput(c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mousemove(pos, dx)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mousescroll(dx)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mousedown(b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### mouseup(b)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### appupdate(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### update(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### physupdate(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### gui(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### debug(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### draw(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### screengui(...args)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user