Compare commits

...

305 Commits

Author SHA1 Message Date
Calvin Rose 7bae7d9efd Merge changes from bundle-tools branch:
- Update file IO to explicitly use 64bit ftell/fseek
- Add env argument to eval
- Allow naming function literals with keywords.
2024-05-26 12:04:35 -05:00
Calvin Rose ae2c5820a1 Fix janet_strerror when _GNU_SOURCE defined. 2024-05-25 13:24:01 -05:00
Calvin Rose 60e0c8ea92 Ignore gcov 2024-05-25 09:25:27 -05:00
Calvin Rose 7d3acc0ed6 Get rid of "extended locale" support.
While useful on some platforms, behavior seems to be different across
the board, making use difficult.
2024-05-19 18:01:20 -05:00
Calvin Rose 2637b33957 Include locale.h and xlocale.h on some platforms. 2024-05-19 17:40:39 -05:00
Calvin Rose 58ccb66659 Move janet_buffer_dtostr 2024-05-19 17:14:21 -05:00
Calvin Rose 634429cf61 Merge branch 'locales' 2024-05-19 17:05:49 -05:00
Calvin Rose 03166a745a Disallow nan and inf in jdn. 2024-05-19 13:25:25 -05:00
Calvin Rose ace60e1898 Add ev/with-*lock macros. 2024-05-18 17:55:47 -05:00
Calvin Rose 876b7f106f OpenBSD does not work with LC_*_MASK stuff. 2024-05-18 17:22:10 -05:00
Calvin Rose 809b6589a1 Put limits.h back. 2024-05-18 15:31:23 -05:00
Calvin Rose 02f53ca014 Formatting. 2024-05-18 15:21:37 -05:00
Calvin Rose 0b03ddb21b More work on setting locale for extended locale support. 2024-05-18 15:20:22 -05:00
Calvin Rose ea5d4fd3af JANET_BSD not defined on apple. 2024-05-18 14:24:51 -05:00
Calvin Rose e6b73f8cd1 BSD, use xlocale for thread safe functionality 2024-05-18 14:11:05 -05:00
Calvin Rose af232ef729 windows needs a distinct implementation from posix for thread safety.
I must say, the windows solution is a lot simpler.
2024-05-18 14:02:20 -05:00
Calvin Rose 2e2f8abfc0 Work on add locales.
Need to be careful not to mess with %j formatter, or
in some other places.
2024-05-18 13:23:33 -05:00
Calvin Rose 91a583db27
Merge pull request #1448 from znley/master
Add LoongArch64 support
2024-05-18 06:33:07 -05:00
znley c1647a74c5 Add LoongArch64 support 2024-05-18 07:18:59 +00:00
Calvin Rose 721f280966 Add `with-env`. 2024-05-16 21:52:49 -05:00
Calvin Rose e914eaf055 Update CHANGELOG.md 2024-05-16 21:37:08 -05:00
Calvin Rose fe54013679 Update naming *module-make-env* for #1447 2024-05-16 19:11:25 -05:00
Calvin Rose fdaf2e1594 Add *module/make-env* 2024-05-16 19:10:30 -05:00
Calvin Rose 9946f3bdf4 Add buffer/format-at
Move changes over from bundle-tools branch and add testing.
2024-05-15 20:16:42 -05:00
Calvin Rose c747e8d16c Address some compiler linter messages on openbsd 2024-05-15 18:20:20 -05:00
Calvin Rose 3e402d397e Use older openbsd build for CI. 2024-05-15 18:16:19 -05:00
Calvin Rose 0350834cd3 By default, require and import extend current env. 2024-05-15 07:40:21 -05:00
Calvin Rose 60e22d9703
Merge pull request #1445 from wishdev/defbind-alias
Add ffi/defbind-alias
2024-05-11 14:25:03 -05:00
John W Higgins ee7362e847 Add ffi/defbind-alias 2024-05-09 21:31:22 -07:00
Calvin Rose 369f96b80e Update README to prefer Zulip. 2024-05-03 07:51:35 -05:00
Calvin Rose 7c5ed04ab1 A few minor improvements.
- Add long-form CLI options
- Update changelog.
- Use snprintf instead of sprintf for linters.
2024-05-02 09:13:29 -05:00
Calvin Rose 4779a445e0 Fix BSD/Macos issue for #1431 2024-04-26 19:32:47 -05:00
Calvin Rose f0f1b7ce9e Address #1431 - level-trigger mode for net/accept-loop
In the edge-trigger mode before this change, if a socket
receives 2 connections before one can be handled, then only a single
connection is handle and 1 connection will never be handled in some
cases. Reverting to level-trigger mode makes this impossible.
2024-04-26 19:28:20 -05:00
Calvin Rose 7c9157a0ed Remove unneeded `string` functions. 2024-04-26 18:11:10 -05:00
Calvin Rose 522a6cb435
Merge pull request #1440 from ahgamut/cosmo-build
Build janet with Cosmopolitan Libc
2024-04-21 08:06:51 -05:00
Gautham d0d551d739 remove superconfigure recipe 2024-04-21 01:16:54 -05:00
Gautham 71a123fef7 apelink 2024-04-21 01:14:58 -05:00
Gautham 3f40c8d7fb fix typo 2024-04-21 01:12:59 -05:00
Gautham 983c2e5499 simplify build to use only cosmocc 2024-04-21 01:10:06 -05:00
Gautham eebb4c3ade remove logging 2024-04-20 22:35:04 -05:00
Gautham 50425eac72 typo 2024-04-20 22:23:29 -05:00
Gautham 382ff77bbe typo 2024-04-20 22:16:23 -05:00
Gautham bf680fb5d3 simplify janet APE build 2024-04-20 22:09:10 -05:00
Gautham 4ed7db4f91 simplify naming 2024-04-19 10:56:46 -05:00
Calvin Rose bf19920d65 Improve casting. 2024-04-18 03:29:45 -05:00
Gautham 174b5f6686 missing folder 2024-04-16 22:24:31 -05:00
Gautham 4173645b81 missing folder 2024-04-16 22:23:12 -05:00
Gautham af511f1f55 patch folder location 2024-04-16 22:15:54 -05:00
Gautham 83c6080380 yml config for building with Cosmopolitan Libc 2024-04-16 22:02:31 -05:00
Calvin Rose 2f0c789ea1 More work to address #1391
Properly set read_fiber and write_fiber to NULL when unused.
This was causing extra listening in the poll implemenation leading to
busy loops where a read was accidentally listening for POLLOUT.
2024-04-15 21:32:17 -05:00
Calvin Rose a9b8f8e8a9 Address #1391 - set fd to negative value if not used.
See https://groups.google.com/g/comp.unix.programmer/c/bNNadBIEpTo/m/G5gs1mqNhbIJ?pli=1 for a conversation and workaround.
2024-04-15 18:12:42 -05:00
Calvin Rose f92f3eb6fa Address #1434 - add dynamic bindings for module state. 2024-04-15 16:20:13 -05:00
Calvin Rose 89e74dca3e Update freebsd build 2024-04-15 16:02:34 -05:00
Calvin Rose f2e86d2f8d
Merge pull request #1432 from wishdev/os/clock
Add additional format options for os/clock
2024-04-15 07:34:02 -05:00
John W Higgins 623da131e5 os/clock docstring typos 2024-03-27 22:32:27 -07:00
John W Higgins e89ec31ae5 Add additional format options for os/clock 2024-03-27 22:32:27 -07:00
Calvin Rose 68a6ed208e
Merge pull request #1430 from pepe/fix-win-clean
Add exists test for dist directory on build command clean
2024-03-24 10:49:34 -07:00
Calvin Rose c01b32c4f3
Merge pull request #1429 from pepe/prepare-134 2024-03-22 06:52:40 -07:00
Josef Pospíšil ee11ff9da9 Move date and sort people 2024-03-22 07:54:23 +01:00
Josef Pospíšil ed56d5d6ff Add @llmII to docs 2024-03-20 10:40:30 +01:00
Josef Pospíšil b317ab755c One more commit 2024-03-20 10:34:30 +01:00
Josef Pospíšil 9819994999 Correct changelog 2024-03-20 10:32:13 +01:00
Josef Pospíšil e9dbaa81d2 Add exists test on clean 2024-03-20 10:18:42 +01:00
Josef Pospíšil 9f9146ffae Prepare for 1.34.0 release 2024-03-20 10:11:08 +01:00
Josef Pospíšil 9d9732af97 Update changelog for 1.34.0 2024-03-20 09:57:57 +01:00
Calvin Rose ebb8fa9787
Merge pull request #1410 from sogaiu/ev-deadline-and-friends-doc-tweaks
Doc tweaks for ev/deadline and ev/with-deadline
2024-03-12 06:18:40 -07:00
Calvin Rose 9e6abbf4d4 Fix asm roundtrip issue. 2024-03-10 09:07:11 -05:00
Calvin Rose 6032a6d658
Merge pull request #1414 from MaxGyver83/master
Fix documentation of peg/replace
2024-02-24 11:06:16 -08:00
Max Schillinger c29ab22e6d Fix documentation of peg/replace 2024-02-23 12:46:45 +01:00
sogaiu 592ac4904c Doc tweaks for ev/deadline and ev/with-deadline 2024-02-23 10:59:43 +09:00
Calvin Rose 03ae2ec153
Merge pull request #1394 from amano-kenji/master
Improve documentation on subprocess API
2024-02-19 11:25:17 -08:00
Calvin Rose 3bc42d0d37 Only re-register when using poll. 2024-02-19 13:19:23 -06:00
Calvin Rose 12630d3e54 Register stream on unmarshal 2024-02-19 13:16:45 -06:00
Calvin Rose c9897f99c3 Address #1405 - don't try and resume fibers that can't be resumed.
FOr fibers that _can_ be resumed and then get cancelled, the sched_id
will be incremented later prevent the spurious wake ups.
2024-02-19 08:37:49 -06:00
Calvin Rose e66dc14b3a Formatting. 2024-02-17 13:35:07 -06:00
Calvin Rose 7a2868c147 Fix macex1 to keep syntax location for all tuples - Address #1404 2024-02-17 13:34:23 -06:00
Calvin Rose 9e0daaee09 Address #1401 - restore if-let tail calls.
Changes to avoid multiple macro expansions of the "false" branch caused
a regression in this functionality.
2024-02-15 06:30:26 -06:00
Calvin Rose c293c7de93
Merge pull request #1402 from sogaiu/each-body-before-set
Swap set / body order for each (#1400)
2024-02-15 04:05:15 -08:00
Calvin Rose 49eb5f8563
Merge pull request #1403 from llmII/fix-os-proc-wait
Fix: make `proc_get_status` compliant to OS documentation.
2024-02-15 04:01:09 -08:00
amano.kenji 674b375b2c
Improve documentation on subprocess API 2024-02-13 05:34:52 +00:00
llmII 7e94c091eb
Fix: os/proc-wait
As discused over gitter, `WIFSIGNALED` macro must be checked before one
uses the WTERMSIG macro. This change reflects that necessity and adds a
final else clause which will panic if no status code could be
determined.
2024-02-12 23:06:08 -06:00
sogaiu 5885ccba61 Swap set / body order for each (#1400) 2024-02-13 11:12:18 +09:00
Calvin Rose 431ecd3d1a Abort on assert failure instead of exit 2024-02-03 14:12:10 -06:00
Calvin Rose f6df8ff935 Expose _exit to skip certain cleanup with os/exit 2024-02-03 14:12:10 -06:00
Calvin Rose 3fd70f0951 Update debug meson options. 2024-02-03 14:12:10 -06:00
Calvin Rose bebb635d4f
Merge pull request #1392 from sogaiu/propagate-docstring-additions
Add to propagate docstring (#1365)
2024-02-03 10:52:27 -08:00
sogaiu 354896bc4b Add to propagate docstring (#1365) 2024-02-03 15:48:19 +09:00
Calvin Rose 5ddefff27e
Merge pull request #1389 from sogaiu/fiber-last-value-doc-tweak 2024-02-02 08:42:48 -08:00
sogaiu 91827eef4f Tweak fiber/last-value docstring 2024-02-02 19:06:56 +09:00
Calvin Rose 9c14c09962 Add explicit stdatomic config setting for #1374
There was some hacky workaround code for development versions of TCC
that interfered with other compilers and technically was not legal
c99.
2024-01-28 15:53:41 -06:00
Calvin Rose e85a84171f Revert local change that removes stdatomic.h 2024-01-28 07:58:22 -06:00
Calvin Rose 3a4f86c3d7 Make host and port configurable for suite-ev.janet 2024-01-28 07:56:59 -06:00
Calvin Rose 5e75963312
Merge pull request #1367 from sogaiu/debug-stacktrace-doc-tweak
Tweak debug/stacktrace docstring (#1365)
2024-01-28 05:33:11 -08:00
Calvin Rose 184d9289b5
Merge pull request #1371 from pepe/destructuring-typo
Fix typo in destructuring
2024-01-28 05:33:05 -08:00
Calvin Rose b7ff9577c0
Merge pull request #1373 from sogaiu/module-expand-path-doc-suggestion
Address #1370
2024-01-28 05:32:50 -08:00
sogaiu 942a1aaac6 Address #1370 2024-01-27 21:20:27 +09:00
Josef Pospíšil 69f0fe004d Fix typo in destructuring 2024-01-26 14:36:56 +01:00
sogaiu 2a04347a42 Tweak debug/stacktrace docstring (#1365) 2024-01-24 16:52:37 +09:00
Calvin Rose 1394f1a5c0
Merge pull request #1364 from sogaiu/module-expand-path-doc-tweak
Cosmetically tweak module/expand-path docstring
2024-01-23 16:01:49 -08:00
sogaiu cf4d19a8ea Cosmetically tweak module/expand-path docstring 2024-01-22 22:16:14 +09:00
Calvin Rose 23b0fe9f8e
Merge pull request #1360 from pepe/patch-1 2024-01-17 11:51:59 -08:00
Josef Pospíšil 1ba718b15e
Update CHANGELOG.md 2024-01-17 13:58:00 +01:00
Calvin Rose df5f79ff35
Merge pull request #1359 from pnelson/binary
Add buffer/push-* sized int and float
2024-01-15 08:56:57 -08:00
Calvin Rose 6d7e8528ea
Merge pull request #1346 from ianthehenry/peg-split
add a new (split) PEG special
2024-01-15 08:16:06 -08:00
Philip Nelson 197bb73a62 Add buffer/push-* sized int and float 2024-01-14 15:32:13 -08:00
Calvin Rose f91e599451
Merge pull request #1351 from pepe/1.33 2024-01-07 13:30:08 -06:00
Josef Pospíšil 5b9aa9237c Prepare for 1.33.0 release 2024-01-07 16:26:20 +01:00
Ian Henry 61f38fab37
add a new (split) PEG special
This works similarly to string/split, but the separator is a PEG.
2024-01-05 22:02:52 -08:00
Calvin Rose 9142f38cbc Fix #1341. 2024-01-01 08:58:31 -06:00
Calvin Rose e8ed961572
Merge pull request #1344 from ianthehenry/peg-sub-special
Add a new (sub) PEG special
2023-12-31 18:40:47 -06:00
Calvin Rose be11a2a1ad Fix #1342 2023-12-31 18:36:55 -06:00
Ian Henry ea75086300
add a new (sub) PEG special
(sub) will first match one pattern, then match another pattern against the
text that the first pattern advanced over.
2023-12-28 22:15:54 -08:00
Calvin Rose 9eeefbd79a
Merge pull request #1340 from sogaiu/string-format-doc-tweak 2023-12-20 09:10:26 -06:00
sogaiu c573a98363 Cosmetically tweak string/format docstring 2023-12-19 18:33:47 +09:00
Calvin Rose 11d7af3f95 Work on addressing #1337 - fix valgrind case. 2023-12-18 08:56:27 -06:00
Calvin Rose a10b4f61d8 Address #1337 (leet!).
Changes a few scheduling details and adds a 0 byte explicitly to
symbols created via gensym.
2023-12-16 16:15:46 -06:00
Calvin Rose a0cb7514f1 Update Makefile for #1329
Add separate import library for libjanet.so and janet.exe with Mingw.
This was causing issues with linking.
2023-12-09 10:11:15 -06:00
Calvin Rose b066edc116
Merge pull request #1336 from pepe/peg-arity-typo 2023-12-07 11:31:13 -06:00
Josef Pospíšil 938f5a689e Fix arity typo in peg 2023-12-07 14:08:03 +01:00
Calvin Rose 772f4c26e8
Merge pull request #1334 from iacore/fix-0
fix (doc next)
2023-12-02 17:28:32 -06:00
Locria Cyber 6b5d151beb
fix typo in (doc next) 2023-12-02 15:38:35 +00:00
Calvin Rose a9176a77e6 Prevent bytecode optimization from remove mk* instructions.
These instructions read from the stack, and therefor have side effects.
Removing them without clearing the stack results in broken bytecode.
2023-11-22 08:18:23 -06:00
Calvin Rose 16f409c6a9 Typo for SIGALARM in os/proc-kill 2023-11-21 21:51:56 -06:00
Calvin Rose 9593c930de Address #1326 in a dynamic way that is fairly conservative.
Another optimization would be to keep track of immutable closure
captures (vs. mutable closure captures) and always detach them.
2023-11-14 21:13:21 -06:00
Calvin Rose 56f33f514b Fix regression #1327 2023-11-14 19:52:22 -06:00
Calvin Rose 1ccd544b94 Address #1326 - marshal_one_env w/ JANET_MARSHAL_UNSAFE.
This allows uses the precise closure state capture
when marshalling data between threads. This prevents
accidental state capture when using ev/do-thread or similar
with closures that reference the current state.
2023-11-10 15:36:45 -06:00
Calvin Rose 93c83a2ee2 Fix warnings w/ MSVC and format. 2023-11-10 15:02:10 -06:00
Calvin Rose f459e32ada
Merge pull request #1325 from zevv/zevv-connect-cleanup
net/ev: Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount
2023-11-10 15:01:43 -06:00
Ico Doornekamp 9b640c8e9c net/ev: Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount 2023-11-10 20:34:17 +01:00
Calvin Rose a3228f4997 Add changes and test cases for #1324 2023-11-09 11:18:03 -06:00
Calvin Rose 715eb69d92 Add more ipv6 feature detection. 2023-11-03 18:24:35 -05:00
Calvin Rose df2d5cb3d3 Add ipv6, shared, and cryptorand options to meosn.
Allows for builting with cosmopolitan, both with meson
and Makefile. Use:

CC=comsocc meson setup -Dipv6=false -Ddynamic_modules=false
-Dshared=false -Dos_name=cosmopolitan

to configure for cosmopolitan build.
2023-11-02 08:56:10 -05:00
Calvin Rose 3b189eab64 Fix #1321, poll event loop CPU usage issue
A stream may have a fiber attached for memory management purposes, but
not actually be waiting on anything. Be more seletive with poll, which
is not edge-triggered, to not poll for readiness on these streams.
2023-10-29 11:34:21 -05:00
Calvin Rose 609b629c22 Add support for atomic loads in Janet's atomic abstraction. 2023-10-21 10:40:57 -05:00
Calvin Rose e74365fe38 Be a bit safer with reference counting.
We might want to revisit some uses of refcounts in the
ev module to be more efficient if we care about signal atomicity
(where memory order isn't really important) or multithreading atomicity.
2023-10-21 09:55:00 -05:00
Calvin Rose 46b34833c2
Merge pull request #1314 from williewillus/pr1314
Use libc strlen in janet_buffer_push_cstring
2023-10-20 15:41:29 -07:00
Vincent Lee 045c80869d Use libc strlen in janet_buffer_push_cstring
Platform libc's often contains optimized assembly implementations of strlen, so take
advantage of them here instead of doing a naive count.
2023-10-19 23:30:28 -07:00
Calvin Rose 2ea2e72ddd
Merge pull request #1313 from sogaiu/default-peg-grammar-additions
Add more + and * keywords to default-peg-grammar
2023-10-19 19:26:10 -07:00
sogaiu 1b17e12fd6 Add more + and * keywords to default-peg-grammar 2023-10-19 18:45:20 +09:00
Calvin Rose cc5beda0d2 Update patch release. 2023-10-15 14:33:43 -05:00
Calvin Rose a363fd926d Update CHANGELOG.md 2023-10-15 14:32:56 -05:00
Calvin Rose 21ebede529 Move posix-fork inside correct if-def
Don't compile if processes are disabled.
2023-10-15 11:03:26 -05:00
Calvin Rose 15d67e9191
Merge pull request #1310 from Andriamanitra/patch-forward-word
Change Alt-f in the REPL move to next end of word instead of beginning
2023-10-14 18:36:05 -07:00
Calvin Rose b5996f5f02 Update for 1.32.0 2023-10-14 19:48:20 -05:00
Andriamanitra 83204dc293 Change Alt-f in the REPL move to next end of word instead of beginning 2023-10-14 14:21:16 +03:00
Calvin Rose e3f4142d2a Update result value from janet_do* functions. 2023-10-12 05:26:23 -05:00
Calvin Rose f18ad36b1b Rework #1306 - better default for pretty printing numbers.
Not perfect for serialization, but a representation that
plays well with both safe integers (z where abs(z) < 2^54) and
non-integer floats.
2023-10-11 00:59:57 -05:00
Calvin Rose cb25a2ecd6 Avoid using execvpe function. 2023-10-08 21:33:15 -05:00
Calvin Rose 741a5036e8 Add %D and %I for 64 bit formatting.
Instead of breaking old code with changing %i and %d.
2023-10-08 21:23:03 -05:00
Calvin Rose 549ee95f3d Add os/posix-exec (along os/posix-fork)
Useful for old-style unix daemons, start up scripts, and so on.
Easy to add on top of os/execute.

May want to consider allowing the same IO redirection as os/execute
and os/spawn.

May also want to put both fork and exec behind a config switch since I
suppose some systems may not support them, although I don't know of any
concrete examples.
2023-10-08 21:03:08 -05:00
Calvin Rose 6ae81058aa Be more consistent with va_arg types. 2023-10-08 19:09:35 -05:00
Calvin Rose 267c603824 Don't use full parallelism to avoid oom 2023-10-08 18:37:31 -05:00
Calvin Rose a8f583a372 CMD isn't bash 2023-10-08 18:34:04 -05:00
Calvin Rose 2b5d90f73a Disable amalgamation w/ mingw in CI due to memory limitations 2023-10-08 18:28:07 -05:00
Calvin Rose 4139e426fe Refine interface for janet's new event loop.
Infer the current root fiber and force user to
allocate state for async events.
2023-10-08 18:25:46 -05:00
Calvin Rose a775a89e01 Improve assert-no-error test helper. 2023-10-08 17:34:50 -05:00
Calvin Rose 990f6352e0 Allow for unregistered streams w/ kqueue. 2023-10-08 17:21:42 -05:00
Calvin Rose b344702304 Merge branch 'master' into ev-epoll-fewer-syscalls 2023-10-08 17:20:20 -05:00
Calvin Rose d497612bce Revert 2023-10-08 17:18:36 -05:00
Calvin Rose 2a3b101bd8 ktrace trace -c 2023-10-08 16:25:55 -05:00
Calvin Rose 63e93af421 Ktrace 2023-10-08 16:00:33 -05:00
Calvin Rose ab055b3ebe i dont know how ktrace works 2 2023-10-08 15:54:57 -05:00
Calvin Rose a9a013473f i dont know how ktrace works 2023-10-08 15:53:44 -05:00
Calvin Rose 87de1e5766 Quick experiment on macos 2023-10-08 15:51:44 -05:00
Calvin Rose 894aaef267 Mac please 2023-10-08 15:42:54 -05:00
Calvin Rose e209e54ffe bsds are very strict on C standards
No labels before declarations.
2023-10-08 15:37:23 -05:00
Calvin Rose 7511eadaa7 Update code for freebsd and ENODEV on stream register 2023-10-08 15:26:02 -05:00
Calvin Rose 6c4906605a Some bsds return ENODEV for devices like /dev/null 2023-10-08 15:09:53 -05:00
Calvin Rose 8a9be9d837 Make sure posted events are read from non-blocking socket. 2023-10-08 13:46:24 -05:00
Calvin Rose b72098cc71 remove extra decref 2023-10-08 13:24:42 -05:00
Calvin Rose defe60e08b Handle refcounts in posted events. 2023-10-08 13:18:08 -05:00
Calvin Rose 7f852b8af4 Handle refcounts in posted events. 2023-10-08 13:14:36 -05:00
Calvin Rose d71c100ca7 Revert "Add EV_EOF and EV_CLEAR to selfpipe for kqueue"
This reverts commit 5442c8e86d.
2023-10-08 13:13:58 -05:00
Calvin Rose 5442c8e86d Add EV_EOF and EV_CLEAR to selfpipe for kqueue 2023-10-08 12:34:13 -05:00
Calvin Rose cf4901e713 Update docstring for os/posix-fork 2023-10-08 11:54:25 -05:00
Calvin Rose 4b8c1ac2d2 Add os/posix-fork
Very simple fork function that returns a process object that can be
waited on.
2023-10-08 11:09:00 -05:00
Calvin Rose 555e0c0b85 Try to capture more events in kqueue. 2023-10-08 10:52:27 -05:00
Calvin Rose dc301305de Turn off verbose CI builds for BSD. 2023-10-07 21:35:38 -05:00
Calvin Rose f1111c135b Work on kq 2023-10-07 21:32:20 -05:00
Calvin Rose 3905e92965 kqueue mirror master branch implementation a bit better. 2023-10-07 12:53:59 -07:00
Calvin Rose 1418ada38f Remove duplicate code in kqueue event handling. 2023-10-07 12:40:43 -07:00
Calvin Rose 9256a66b76 Update ev.c for removing msvc warnings 2023-10-07 12:11:14 -07:00
Calvin Rose e8c013a778 Remove some extra fiber state and use a flag. 2023-10-07 12:07:05 -07:00
Calvin Rose fea8242ea7 Reuse overlapped overhead on windows for something useful. 2023-10-07 11:25:20 -07:00
Calvin Rose 7bfb17c209 Lots of work to make iocp work again.
Big issue with IOCP vs. poll variants is that the overlapped
structures have a longer lifetime than intermediate state needed
for epoll. One cannot free overlapped structures after closing a
handle/socket, like one can do with any intermediate state when using
readiness-based IO.
2023-10-07 11:18:43 -07:00
Calvin Rose e7e4341e70 Add EV_EOF for kqueue. 2023-10-06 17:39:52 -05:00
Calvin Rose 6186be4443 Run tests verbosely 2023-10-06 01:40:12 -05:00
Calvin Rose d07f01d7cb Update kqueue 2023-10-06 01:33:51 -05:00
Calvin Rose 73291a30a0 Update marsh.c 2023-10-06 01:26:07 -05:00
Calvin Rose a3b129845b Quick blind take on getting kqueue similar to poll and poll. 2023-10-06 00:48:05 -05:00
Calvin Rose 0ff8f58be8 Simpler async model that is better suited to epoll 2023-10-06 00:37:19 -05:00
Calvin Rose 66292beec9 Don't mess with async connect on BSDs. 2023-10-04 23:33:40 -05:00
Calvin Rose bf2af1051f Missing && 2023-10-04 23:29:54 -05:00
Calvin Rose b6e3020d4c Attempt 2 to fix bsd compilation errors. 2023-10-04 23:27:56 -05:00
Calvin Rose 8f516a1e28 Address some bsd issues. 2023-10-04 23:24:40 -05:00
Calvin Rose 5f2e287efd Make epoll event loop use EPOLLET and not reregister file descriptors.
This results in fewer system calls and presumably more effcient code. It
also brings the epoll (and kqueue) code more in line with how the
windows IOCP code works, incidentally.
2023-10-04 22:35:04 -05:00
Calvin Rose 8c0d65cf9f
Merge pull request #1301 from ianthehenry/typos 2023-10-03 19:25:01 -07:00
Ian Henry fa609a5079
fix some typos in docstrings 2023-10-03 19:17:18 -07:00
Calvin Rose c708ff9708 Allow for qemu and other simulator based testing. 2023-10-02 23:31:55 -07:00
Calvin Rose 2ea90334a3 Remove some checking code for iocp events.
Be more permissive abouts events we get.
2023-10-02 23:25:26 -07:00
Calvin Rose eea8aa555f Revert dccb60b to address #1299
This commit was to address issues with a use after free error in the
windows event loop, but the erroneous code was later reworked.
2023-10-03 00:14:25 -05:00
Calvin Rose 51a75e1872 Update janet_interpreter_interrupt to use new atomics 2023-10-01 10:52:05 -05:00
Calvin Rose af7ed4322e Get rid of req for 64 bit atomics 2023-10-01 10:27:51 -05:00
Calvin Rose 7cdd7cf6eb Expose atomic refcounts to be easier to port.
This code was duplicate in a few places.
2023-10-01 10:09:23 -05:00
Calvin Rose 26aa622afc Update CHANGELOG.md 2023-09-30 12:26:50 -05:00
Calvin Rose 84ad161f1e Add support for weak references in arrays.
Also change weak table syntax to not require keyword arguments.
2023-09-30 10:56:43 -05:00
Calvin Rose 6efb965dab Merge branch 'weak-tables' 2023-09-29 07:38:21 -05:00
Calvin Rose 8c90a12e0f More test cases. 2023-09-29 07:37:33 -05:00
Calvin Rose 2d54e88e74 Update CHANGELOG.md 2023-09-28 20:41:19 -05:00
Calvin Rose 16ea5323e0 More meson tweaks. 2023-09-28 20:32:14 -05:00
Calvin Rose 7a23ce2367 See if we can fix sr.ht build due to change in install. 2023-09-28 20:24:19 -05:00
Calvin Rose e05bc7eb54 Address compiler bug with `break`.
Using result from `break` expression could trigger code that would
work, yet contain invalid, dead code preventing good marshalling.
2023-09-28 20:14:22 -05:00
Calvin Rose b3a6e25ce0 Add weak references in the form of weak tables.
Any references exclusively held by a weak table may be collected
without the programmer needing to free references manually. A table
can be setup to have weak keys, weak values, or both.
2023-09-27 23:36:09 -05:00
Calvin Rose b63d41102e Fix bad merge. 2023-09-27 22:34:46 -05:00
Calvin Rose 964295b59d Merge branch 'net-reworkings' 2023-09-27 19:06:14 -05:00
Calvin Rose d19db30f3d Fix meson install test. 2023-09-27 00:19:35 -05:00
Calvin Rose d12464fc0e Make poll work by going back to array of listeners for gc keeping. 2023-09-26 23:02:06 -05:00
Calvin Rose a96971c8a7 More work on epoll implementation. 2023-09-26 12:05:06 -05:00
Calvin Rose f6f769503a Fix up ev.c to pass tests. 2023-09-26 11:11:29 -05:00
Calvin Rose 82917ac6e3 Kqueue fix. 2023-09-25 19:17:51 -07:00
Calvin Rose a6ffafb1a2 Patches to kqueue implementation. 2023-09-25 19:07:18 -07:00
Calvin Rose fb8c529f2e Partial work updating epoll reimplentation. 2023-09-25 18:52:15 -07:00
Calvin Rose 1ee98e1e66 Get IOCP reworked event loop passing tests. 2023-09-25 15:19:39 -07:00
Calvin Rose 81f35f5dd1 Redo state management for Janet listeners.
Make more use of the built in GC code for abstracts to
be sure things are more correct. Issue before was streams could
be freed before IOCP events arrived.
2023-09-25 00:43:36 -07:00
Calvin Rose 1b402347cd Work on debugging issue with server spawning. 2023-09-24 18:15:58 -07:00
Calvin Rose 7599656784 Update meson build once more. 2023-09-24 15:35:40 -07:00
Calvin Rose dccb60ba35 Ignore IOCP where the event failed to deque. 2023-09-24 12:53:06 -07:00
Calvin Rose ae642ceca0 Don't hide windows segfaults in build_win.bat. 2023-09-24 12:36:15 -07:00
Calvin Rose 471b6f9966 Add TOCLOSE back. 2023-09-24 12:28:35 -07:00
Calvin Rose 5dd18bac2c More fixups to gc.c 2023-09-24 11:51:22 -07:00
Calvin Rose 018f4e0891 Remove some old code. 2023-09-24 10:30:58 -07:00
Calvin Rose e85809a98a Remove remains of gc instrumentation code. 2023-09-24 10:11:24 -07:00
Calvin Rose e6e9bd8147 Redo async connect code to be moved out of ev.c.
Async connect is different than write.
2023-09-24 10:08:40 -07:00
Calvin Rose 221645d2ce More refinement of meson build. 2023-09-23 14:16:13 -07:00
Calvin Rose 2f4a6214a2 Make meson build work on windows.
By default, use more traditional linking pattern with meson.
The janet.exe will now link to janet-x.x.dll on windows (and
similar for linux/posix) when built with meson. This is slightly
less efficient and means that janet.exe built this way is no longer
standalone (you would need to move the dll along with the exe), but
plays better with most build systems.
2023-09-23 08:53:37 -07:00
Calvin Rose e00a461c26 Add optional buffer-size to file/open.
This calls setvbuf to change FILE buffering. A goal is
to be able to use the existing file/* functions for blocking
IO analogous to `read` and `write` system calls.
2023-09-23 09:40:17 -05:00
Calvin Rose c31314be38
Merge pull request #1296 from Andriamanitra/doc-loop-unless
add :unless modifier to (doc loop)
2023-09-22 05:41:06 -07:00
Andriamanitra ee142c4be0 truthy/falsey is more accurate than true/false 2023-09-22 03:04:41 +03:00
Andriamanitra aeacc0b31b add :unless modifier to (doc loop) 2023-09-21 19:23:40 +03:00
Calvin Rose 7b4c3bdbcc Address issues from #1294 on non-nanboxed platforms.
Underlying bug was obscured by nanbox implementation.
2023-09-21 07:36:53 -07:00
Calvin Rose 910b9cf1fd Distinguish between JANET_API and JANET_EXPORT
One is a way to export symbols, the other a way to reference
API functions. Also include prebuilt dlljanet.dll and dlljanet.lib
for windows to save people the trouble of compiling janet.c themselves.
2023-09-20 20:07:03 -07:00
Calvin Rose b10aaceab0 Work on dllimport option for janet. 2023-09-20 17:34:42 -07:00
Calvin Rose 169bd812c9 Update state.h to #include <windows.h>
Should fix usage with msvc in some pipelines.
2023-09-18 23:51:15 -05:00
Calvin Rose 34767f1e13
Merge pull request #1292 from sogaiu/tweak-janetconf-h 2023-09-18 18:50:46 -07:00
sogaiu 4f642c0843 Tweak janetconf.h 2023-09-19 10:34:50 +09:00
Calvin Rose 4e5889ed59 Prepare for 1.31.0 release. 2023-09-17 14:53:03 -05:00
Calvin Rose a1b848ad76
Merge pull request #1288 from sogaiu/more-error-loc-info-in-dobytes
Report line and col more in janet_dobytes
2023-09-14 08:21:52 -07:00
Calvin Rose dbcc1fad3e
Merge pull request #1289 from primo-ppcg/loop-unless
Add `:unless` loop modifier
2023-09-13 11:56:33 -07:00
primo-ppcg db366558e7 add `:unless` loop modifier 2023-09-13 15:21:46 +07:00
sogaiu a23c03fbd0 Report line and col more in janet_dobytes 2023-09-13 15:41:14 +09:00
Calvin Rose ff18b92eb0
Merge pull request #1287 from pepe/fix-arr-push-doc
Document array/push variadic argument
2023-09-12 07:07:42 -07:00
Josef Pospíšil 7f148522ab Document array/push variadic argument 2023-09-12 09:34:21 +02:00
Calvin Rose 159c612924 Update changelog.md 2023-09-09 11:03:14 -05:00
Calvin Rose b95dfd4bdf Update docstring. 2023-09-09 10:58:20 -05:00
Calvin Rose e69954af2f
Merge pull request #1283 from primo-ppcg/mean-partition
Update `partition`, `mean`
2023-09-09 10:30:11 -05:00
primo-ppcg a5ff26f602 add more test cases for `partition` and `mean` 2023-09-08 16:30:44 +07:00
primo-ppcg a7536268e1 update `partition` 2023-09-08 12:38:58 +07:00
primo-ppcg 541469371a update `mean` 2023-09-08 11:35:37 +07:00
Calvin Rose a13aeaf955
Merge pull request #1281 from primo-ppcg/interleave-interpose
Update `interleave`, `interpose`
2023-09-04 10:27:27 -05:00
primo-ppcg 9cf674cdcb update `interleave`, `interpose` 2023-09-04 17:09:53 +07:00
Calvin Rose 51c0cf97bc
Merge pull request #1280 from primo-ppcg/lengthable
Add `lengthable?`
2023-09-01 17:41:25 -05:00
primo-ppcg 4cb1f616c5 allow `reverse` on non-lengthable 2023-09-01 16:04:21 +07:00
primo-ppcg 645109048b update `keys`, `values`, `pairs` 2023-09-01 13:18:31 +07:00
primo-ppcg f969fb69e1 add `lengthable?` 2023-09-01 13:04:53 +07:00
Calvin Rose bfb60fdb84
Merge pull request #1278 from primo-ppcg/loop-range
Allow one-term `:range` and `:down` forms
2023-08-29 08:23:36 -05:00
primo-ppcg 2f43cb843e Allow one-term `:range` and `:down` forms 2023-08-29 10:59:16 +07:00
Calvin Rose 874fd2aba7 don't crash repl if someone sets a bad *pretty-format* 2023-08-27 13:09:22 -05:00
Calvin Rose 33d1371186 Update specials.c for formatting. 2023-08-25 16:28:41 -05:00
Calvin Rose d2dd241e6b
Merge pull request #1269 from primo-ppcg/check-nil-form
Optimize nil conditions for while and if
2023-08-24 09:30:58 -05:00
Calvin Rose 4ecadfabf4 Fix atomics - warnings on windows 2023-08-24 08:00:50 -05:00
Calvin Rose ffd79c6097 Allow multiple simultaneous interrupts cleanly for #1262
Instead of setting a flag, each interrupt increments an atomic
counter. When the interrupt is finally handled, either by scheduling
code to run on the event loop or executing some out of band code, the
user must now decrement the interrupt counter with
janet_interpreter_interrupt_handled. While this counter is non-zero, the
event loop will not enter the interpreter. This changes the API a bit but
makes it possible and easy to handle signals without race conditions
or scheduler hacks, as the runtime can ensure that high priority code is
run before re-entering possibly blocking interpreter code again.

Also included is a new function janet_schedule_soon, which prepends to
the task queue instead of appending, allowing interrupt handler to skip
ahead of all other scheduled fibers.

Lastly, also update meson default options to include the
interpreter_interrupt code and raise a runtime error if os/sigaction
is used with interpreter interrupt but that build option is not enabled.
2023-08-24 07:38:53 -05:00
primo-ppcg 35a8d2a519 Optimize nil conditions for while and if 2023-08-24 17:50:31 +07:00
Calvin Rose 21eab7e9cc Update sigaction to help address #1262.
Update example to have 4 cases - case 3 was previously broken but should
now work.
2023-08-23 09:16:59 -05:00
Calvin Rose d9605c2856 Allow iterating over generators with pairs, keys, and values. 2023-08-22 19:25:05 -05:00
Calvin Rose 70a467d469
Merge pull request #1266 from primo-ppcg/min-max
Speed up `min`, `max`
2023-08-21 22:32:03 -05:00
primo-ppcg 6e8979336d speed up `min`, `max` 2023-08-22 00:39:28 +07:00
Calvin Rose ee01045db5 Update CHANGELOG.md 2023-08-20 15:56:58 -05:00
Calvin Rose b7f8224588 Address #1263
Fix reference counting when cleaning up unused abstract types
per-thread.
2023-08-20 14:53:25 -05:00
Calvin Rose ca4c1e4259 Try to use atomics inside signal handler for ref count. 2023-08-20 08:49:49 -05:00
Calvin Rose 91712add3d Fix threaded abstracts in min build. 2023-08-19 20:19:05 -05:00
Calvin Rose 7198dcb416 Add sanboxing for signal handling. 2023-08-19 17:44:04 -05:00
Calvin Rose 08e20e912d Use pthread_sigmask when adding signal handlers. 2023-08-19 17:30:55 -05:00
Calvin Rose f45571033c Add os/sigaction for signal handling.
Also improve interrupts to work better with busy loops
and signals.
2023-08-19 13:26:29 -05:00
Calvin Rose 2ac36a0572
Merge pull request #1257 from primo-ppcg/any-every
Update `any?`, `every?`
2023-08-18 07:20:44 -05:00
Calvin Rose 3df1d54847
Merge pull request #1258 from primo-ppcg/each
Update `each` keys before body
2023-08-18 07:16:19 -05:00
Calvin Rose f3969b6066
Merge pull request #1259 from primo-ppcg/buffer-from-bytes
Add `buffer/from-bytes`
2023-08-18 07:15:08 -05:00
primo-ppcg 6222f35bc8 add `buffer/from-bytes` 2023-08-18 12:35:12 +07:00
primo-ppcg 2f178963c0 update `each` keys before body 2023-08-18 10:32:24 +07:00
primo-ppcg 15760b0950 update `any?`, `every?`
Updates `any?` and `every?` to be exact functional analogues to `or` and `and`.
2023-08-18 07:39:30 +07:00
Calvin Rose 43a6a70e1e
Merge pull request #1255 from primo-ppcg/sort
Special case common `sort` usages
2023-08-16 20:34:17 -05:00
Calvin Rose cd36f1ef5f Distinguish between threaded channels and non-threaded when marshalling.
Threaded channels _can_ be marshalled, just not for communication
between threads. This is a special case since the same abstract type
is used for both threaded and non-threaded channels.
2023-08-16 14:26:52 -05:00
primo-ppcg cdd7083c86 special case common `sort` usages 2023-08-15 11:58:22 +07:00
Calvin Rose 8df7364319 Quick fix for discussion #1253
Protect against garbage collection during connect.
2023-08-13 12:00:54 -05:00
Calvin Rose 63023722d1
Merge pull request #1246 from wooosh/use-object-typedefs
Use typedefs for strings, symbols, keywords, tuples, structs, and abstracts.
2023-08-13 08:33:19 -05:00
Calvin Rose 79c12e5116
Merge pull request #1252 from primo-ppcg/reverse
Rework `reverse`, again
2023-08-12 14:29:23 -05:00
primo-ppcg 53e16944a1 rework `reverse`, again 2023-08-13 00:54:17 +07:00
Calvin Rose 7475362c85
Merge pull request #1249 from primo-ppcg/compare
Speed up `compare` functions
2023-08-11 19:27:42 -05:00
primo-ppcg 9238b82cde speed up `compare` 2023-08-11 23:48:29 +07:00
Calvin Rose 7049f658ec
Merge pull request #1244 from primo-ppcg/bytes-indexed-dictionary
Move `bytes?`, `indexed?`, `dictionary?` to corelib
2023-08-09 17:56:35 -05:00
wooosh 701913fb19 Use typedefs for strings, symbols, keywords, tuples, structs, and abstracts. 2023-08-09 16:09:34 -04:00
primo-ppcg 831f41a62b move `bytes?`, `indexed?`, `dictionary?` to corelib 2023-08-08 10:00:05 +07:00
Calvin Rose 0ea1da80e7
Merge pull request #1242 from primo-ppcg/reverse
Rework `reverse`
2023-08-06 08:10:56 -05:00
Calvin Rose 06eea74b98
Merge pull request #1241 from primo-ppcg/keys-values-pairs
Rework `keys`, `values`, `pairs`
2023-08-06 08:10:43 -05:00
primo-ppcg c8c0e112bc rework `reverse` 2023-08-06 16:16:41 +07:00
primo-ppcg 7417e82c51 rework `keys`, `values`, `pairs` 2023-08-06 15:39:16 +07:00
70 changed files with 3134 additions and 1364 deletions

View File

@ -1,4 +1,4 @@
image: freebsd/12.x image: freebsd/14.x
sources: sources:
- https://git.sr.ht/~bakpakin/janet - https://git.sr.ht/~bakpakin/janet
packages: packages:
@ -9,3 +9,4 @@ tasks:
gmake gmake
gmake test gmake test
sudo gmake install sudo gmake install
sudo gmake uninstall

View File

@ -1,4 +1,4 @@
image: openbsd/latest image: openbsd/7.4
sources: sources:
- https://git.sr.ht/~bakpakin/janet - https://git.sr.ht/~bakpakin/janet
packages: packages:
@ -11,6 +11,7 @@ tasks:
gmake test gmake test
doas gmake install doas gmake install
gmake test-install gmake test-install
doas gmake uninstall
- meson_min: | - meson_min: |
cd janet cd janet
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
@ -29,4 +30,3 @@ tasks:
ninja ninja
ninja test ninja test
doas ninja install doas ninja install

38
.github/cosmo/build vendored Normal file
View File

@ -0,0 +1,38 @@
#!/bin/sh
set -eux
COSMO_DIR="/sc/cosmocc"
# build x86_64
X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc"
X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar"
mkdir -p /sc/cosmocc/x86_64
make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
cp build/janet /sc/cosmocc/x86_64/janet
make clean
# build aarch64
AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc"
AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar"
mkdir -p /sc/cosmocc/aarch64
make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
cp build/janet /sc/cosmocc/aarch64/janet
make clean
# fat binary
apefat () {
OUTPUT="$1"
OLDNAME_X86_64="$(basename -- "$2")"
OLDNAME_AARCH64="$(basename -- "$3")"
TARG_FOLD="$(dirname "$OUTPUT")"
"$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \
-l "$COSMO_DIR/bin/ape-aarch64.elf" \
-M "$COSMO_DIR/bin/ape-m1.c" \
-o "$OUTPUT" \
"$2" \
"$3"
cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64"
cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64"
}
apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet

21
.github/cosmo/setup vendored Normal file
View File

@ -0,0 +1,21 @@
#!/bin/sh
set -e
sudo apt update
sudo apt-get install -y ca-certificates libssl-dev\
qemu qemu-utils qemu-user-static\
texinfo groff\
cmake ninja-build bison zip\
pkg-config build-essential autoconf re2c
# download cosmocc
cd /sc
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
mkdir -p cosmocc
cd cosmocc
unzip ../cosmocc-3.3.3.zip
# register
cd /sc/cosmocc
sudo cp ./bin/ape-x86_64.elf /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"

View File

@ -60,3 +60,30 @@ jobs:
./dist/*.zip ./dist/*.zip
./*.zip ./*.zip
./*.msi ./*.msi
release-cosmo:
permissions:
contents: write # for softprops/action-gh-release to create GitHub release
name: Build release binaries for Cosmo
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: create build folder
run: |
sudo mkdir -p /sc
sudo chmod -R 0777 /sc
- name: setup Cosmopolitan Libc
run: bash ./.github/cosmo/setup
- name: Set the version
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- name: Set the platform
run: echo "platform=cosmo" >> $GITHUB_ENV
- name: build Janet APE binary
run: bash ./.github/cosmo/build
- name: push binary to github
uses: softprops/action-gh-release@v1
with:
draft: true
files: |
/sc/cosmocc/janet.com

View File

@ -56,7 +56,7 @@ jobs:
gcc gcc
- name: Build the project - name: Build the project
shell: cmd shell: cmd
run: make -j CC=gcc run: make -j4 CC=gcc JANET_NO_AMALG=1
test-mingw-linux: test-mingw-linux:
name: Build and test with Mingw on Linux + Wine name: Build and test with Mingw on Linux + Wine
@ -73,7 +73,7 @@ jobs:
- name: Compile the project - name: Compile the project
run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
- name: Test the project - name: Test the project
run: make test UNAME=MINGW RUN=wine run: make test UNAME=MINGW RUN=wine VERBOSE=1
test-arm-linux: test-arm-linux:
name: Build and test ARM32 cross compilation name: Build and test ARM32 cross compilation
@ -88,4 +88,4 @@ jobs:
- name: Compile the project - name: Compile the project
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
- name: Test the project - name: Test the project
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1

13
.gitignore vendored
View File

@ -34,8 +34,11 @@ local
# Common test files I use. # Common test files I use.
temp.janet temp.janet
temp*.janet temp.c
temp*janet
temp*.c
scratch.janet scratch.janet
scratch.c
# Emscripten # Emscripten
*.bc *.bc
@ -45,6 +48,7 @@ janet.wasm
# Generated files # Generated files
*.gen.h *.gen.h
*.gen.c *.gen.c
*.tmp
# Generate test files # Generate test files
*.out *.out
@ -57,6 +61,7 @@ xxd.exe
# VSCode # VSCode
.vs .vs
.clangd .clangd
.cache
# Swap files # Swap files
*.swp *.swp
@ -122,6 +127,9 @@ vgcore.*
*.idb *.idb
*.pdb *.pdb
# GGov
*.gcov
# Kernel Module Compile Results # Kernel Module Compile Results
*.mod* *.mod*
*.cmd *.cmd
@ -130,6 +138,9 @@ Module.symvers
Mkfile.old Mkfile.old
dkms.conf dkms.conf
# Coverage files
*.cov
# End of https://www.gitignore.io/api/c # End of https://www.gitignore.io/api/c
# Created by https://www.gitignore.io/api/cmake # Created by https://www.gitignore.io/api/cmake

View File

@ -1,6 +1,76 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Unreleased - ???
- Add extra optional `env` argument to `eval` and `eval-string`.
- Allow naming function literals with a keyword. This allows better stacktraces for macros without
accidentally adding new bindings.
- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks.
- Add `with-env`
- Add *module-make-env* dynamic binding
- Add buffer/format-at
- Add long form command line options for readable CLI usage
- Fix bug with `net/accept-loop` that would sometimes miss connections.
## 1.34.0 - 2024-03-22
- Add a new (split) PEG special by @ianthehenry
- Add buffer/push-* sized int and float by @pnelson
- Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu.
- Expose _exit to skip certain cleanup with os/exit.
- Swap set / body order for each by @sogaiu.
- Abort on assert failure instead of exit.
- Fix: os/proc-wait by @llmII.
- Fix macex1 to keep syntax location for all tuples.
- Restore if-let tail calls.
- Don't try and resume fibers that can't be resumed.
- Register stream on unmarshal.
- Fix asm roundtrip issue.
## 1.33.0 - 2024-01-07
- Add more + and * keywords to default-peg-grammar by @sogaiu.
- Use libc strlen in janet_buffer_push_cstring by @williewillus.
- Be a bit safer with reference counting.
- Add support for atomic loads in Janet's atomic abstraction.
- Fix poll event loop CPU usage issue.
- Add ipv6, shared, and cryptorand options to meson.
- Add more ipv6 feature detection.
- Fix loop for forever loop.
- Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv.
- Fix warnings w/ MSVC and format.
- Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE.
- Fix `(default)`.
- Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative.
- Fix typo for SIGALARM in os/proc-kill.
- Prevent bytecode optimization from remove mk* instructions.
- Fix arity typo in peg.c by @pepe.
- Update Makefile for MinGW.
- Fix canceling waiting fiber.
- Add a new (sub) PEG special by @ianthehenry.
- Fix if net/server's handler has incorrect arity.
- Fix macex raising on ().
## 1.32.1 - 2023-10-15
- Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop.
- Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`.
- Rework event loop to make fewer system calls on kqueue and epoll.
- Expose atomic refcount abstraction in janet.h
- Add `array/weak` for weak references in arrays
- Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`.
- Fix compiler bug with using the result of `(break x)` expression in some contexts.
- Rework internal event loop code to be better behaved on Windows
- Update meson build to work better on windows
## 1.31.0 - 2023-09-17
- Report line and column when using `janet_dobytes`
- Add `:unless` loop modifier
- Allow calling `reverse` on generators.
- Improve performance of a number of core functions including `partition`, `mean`, `keys`, `values`, `pairs`, `interleave`.
- Add `lengthable?`
- Add `os/sigaction`
- Change `every?` and `any?` to behave like the functional versions of the `and` and `or` macros.
- Fix bug with garbage collecting threaded abstract types.
- Add `:signal` to the `sandbox` function to allow intercepting signals.
## 1.30.0 - 2023-08-05 ## 1.30.0 - 2023-08-05
- Change indexing of `array/remove` to start from -1 at the end instead of -2. - Change indexing of `array/remove` to start from -1 at the end instead of -2.
- Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`. - Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`.

View File

@ -33,6 +33,7 @@ CLIBS=-lm -lpthread
JANET_TARGET=build/janet JANET_TARGET=build/janet
JANET_BOOT=build/janet_boot JANET_BOOT=build/janet_boot
JANET_IMPORT_LIB=build/janet.lib JANET_IMPORT_LIB=build/janet.lib
JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib
JANET_LIBRARY=build/libjanet.so JANET_LIBRARY=build/libjanet.so
JANET_STATIC_LIBRARY=build/libjanet.a JANET_STATIC_LIBRARY=build/libjanet.a
JANET_PATH?=$(LIBDIR)/janet JANET_PATH?=$(LIBDIR)/janet
@ -42,6 +43,7 @@ JANET_DIST_DIR?=janet-dist
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)' JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
JANET_TARGET_OBJECTS=build/janet.o build/shell.o JANET_TARGET_OBJECTS=build/janet.o build/shell.o
JPM_TAG?=master JPM_TAG?=master
HAS_SHARED?=1
DEBUGGER=gdb DEBUGGER=gdb
SONAME_SETTER=-Wl,-soname, SONAME_SETTER=-Wl,-soname,
@ -51,6 +53,7 @@ HOSTAR?=$(AR)
# Symbols are (optionally) removed later, keep -g as default! # Symbols are (optionally) removed later, keep -g as default!
CFLAGS?=-O2 -g CFLAGS?=-O2 -g
LDFLAGS?=-rdynamic LDFLAGS?=-rdynamic
LIBJANET_LDFLAGS?=$(LD_FLAGS)
RUN:=$(RUN) RUN:=$(RUN)
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
@ -93,12 +96,17 @@ endif
ifeq ($(findstring MINGW,$(UNAME)), MINGW) ifeq ($(findstring MINGW,$(UNAME)), MINGW)
CLIBS:=-lws2_32 -lpsapi -lwsock32 CLIBS:=-lws2_32 -lpsapi -lwsock32
LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB) LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
JANET_TARGET:=$(JANET_TARGET).exe JANET_TARGET:=$(JANET_TARGET).exe
JANET_BOOT:=$(JANET_BOOT).exe JANET_BOOT:=$(JANET_BOOT).exe
endif endif
$(shell mkdir -p build/core build/c build/boot build/mainclient) $(shell mkdir -p build/core build/c build/boot build/mainclient)
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h
ifeq ($(HAS_SHARED), 1)
all: $(JANET_LIBRARY)
endif
###################### ######################
##### Name Files ##### ##### Name Files #####
@ -196,9 +204,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
######################## ########################
ifeq ($(UNAME), Darwin) ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.30.dylib SONAME=libjanet.1.34.dylib
else else
SONAME=libjanet.so.1.30 SONAME=libjanet.so.1.34
endif endif
build/c/shell.c: src/mainclient/shell.c build/c/shell.c: src/mainclient/shell.c
@ -220,7 +228,7 @@ $(JANET_TARGET): $(JANET_TARGET_OBJECTS)
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS) $(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS) $(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) $(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS) $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
$(HOSTAR) rcs $@ $^ $(HOSTAR) rcs $@ $^
@ -263,7 +271,7 @@ dist: build/janet-dist.tar.gz
build/janet-%.tar.gz: $(JANET_TARGET) \ build/janet-%.tar.gz: $(JANET_TARGET) \
build/janet.h \ build/janet.h \
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \ janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \
README.md build/c/janet.c build/c/shell.c README.md build/c/janet.c build/c/shell.c
mkdir -p build/$(JANET_DIST_DIR)/bin mkdir -p build/$(JANET_DIST_DIR)/bin
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/ cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
@ -271,13 +279,17 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
mkdir -p build/$(JANET_DIST_DIR)/include mkdir -p build/$(JANET_DIST_DIR)/include
cp build/janet.h build/$(JANET_DIST_DIR)/include/ cp build/janet.h build/$(JANET_DIST_DIR)/include/
mkdir -p build/$(JANET_DIST_DIR)/lib/ mkdir -p build/$(JANET_DIST_DIR)/lib/
cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/ cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true
mkdir -p build/$(JANET_DIST_DIR)/man/man1/ mkdir -p build/$(JANET_DIST_DIR)/man/man1/
cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1 cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
mkdir -p build/$(JANET_DIST_DIR)/src/ mkdir -p build/$(JANET_DIST_DIR)/src/
cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/ cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/ cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR) cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR)
ifeq ($(HAS_SHARED), 1)
build/janet-%.tar.gz: $(JANET_LIBRARY)
endif
######################### #########################
##### Documentation ##### ##### Documentation #####
@ -331,6 +343,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)' mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc' cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)' cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs" [ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
install-jpm-git: $(JANET_TARGET) install-jpm-git: $(JANET_TARGET)

View File

@ -315,8 +315,7 @@ See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the w
## Discussion ## Discussion
Feel free to ask questions and join the discussion on the [Janet Gitter channel](https://gitter.im/janet-language/community). Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/)
Gitter provides Matrix and IRC bridges as well.
## FAQ ## FAQ
@ -383,7 +382,7 @@ Usually, one of a few reasons:
### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)? ### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)?
Probably, if that language has a good interface with C. But the programmer may need to do Probably, if that language has a good interface with C. But the programmer may need to do
some extra work to map Janet's internal memory model may need some to that of the bound language. Janet some extra work to map Janet's internal memory model to that of the bound language. Janet
also uses `setjmp`/`longjmp` for non-local returns internally. This also uses `setjmp`/`longjmp` for non-local returns internally. This
approach is out of favor with many programmers now and doesn't always play well with other languages approach is out of favor with many programmers now and doesn't always play well with other languages
that have exceptions or stack-unwinding. that have exceptions or stack-unwinding.

View File

@ -41,32 +41,32 @@ if not exist build\boot mkdir build\boot
@rem Build the bootstrap interpreter @rem Build the bootstrap interpreter
for %%f in (src\core\*.c) do ( for %%f in (src\core\*.c) do (
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
) )
for %%f in (src\boot\*.c) do ( for %%f in (src\boot\*.c) do (
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
) )
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj %JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
build\janet_boot . > build\c\janet.c build\janet_boot . > build\c\janet.c
@rem Build the sources @rem Build the sources
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c %JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c %JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
@rem Build the resources @rem Build the resources
rc /nologo /fobuild\janet_win.res janet_win.rc rc /nologo /fobuild\janet_win.res janet_win.rc
@rem Link everything to main client @rem Link everything to main client
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res %JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
@rem Build static library (libjanet.a) @rem Build static library (libjanet.lib)
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj %JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
@if errorlevel 1 goto :BUILDFAIL @if not errorlevel 0 goto :BUILDFAIL
echo === Successfully built janet.exe for Windows === echo === Successfully built janet.exe for Windows ===
echo === Run 'build_win test' to run tests. == echo === Run 'build_win test' to run tests. ==
@ -91,14 +91,16 @@ exit /b 0
:CLEAN :CLEAN
del *.exe *.lib *.exp del *.exe *.lib *.exp
rd /s /q build rd /s /q build
rd /s /q dist if exist dist (
rd /s /q dist
)
exit /b 0 exit /b 0
@rem Run tests @rem Run tests
:TEST :TEST
for %%f in (test/suite*.janet) do ( for %%f in (test/suite*.janet) do (
janet.exe test\%%f janet.exe test\%%f
@if errorlevel 1 goto TESTFAIL @if not errorlevel 0 goto TESTFAIL
) )
exit /b 0 exit /b 0
@ -117,6 +119,7 @@ copy README.md dist\README.md
copy janet.lib dist\janet.lib copy janet.lib dist\janet.lib
copy janet.exp dist\janet.exp copy janet.exp dist\janet.exp
copy janet.def dist\janet.def
janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h
copy build\janet.h dist\janet.h copy build\janet.h dist\janet.h

View File

@ -55,6 +55,7 @@
(ffi/defbind sixints-fn six-ints []) (ffi/defbind sixints-fn six-ints [])
(ffi/defbind sixints-fn-2 :int [x :int s six-ints]) (ffi/defbind sixints-fn-2 :int [x :int s six-ints])
(ffi/defbind sixints-fn-3 :int [s six-ints x :int]) (ffi/defbind sixints-fn-3 :int [s six-ints x :int])
(ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int])
# #
# Struct reading and writing # Struct reading and writing
@ -119,6 +120,7 @@
(tracev (return-struct 42)) (tracev (return-struct 42))
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10)) (tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
(tracev (struct-big 11 99.5)) (tracev (struct-big 11 99.5))
(tracev (int-fn-aliased 10 20))
(assert (= [10 10 12 12] (split-ret-fn 10 12))) (assert (= [10 10 12 12] (split-ret-fn 10 12)))
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12))) (assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))

View File

@ -0,0 +1,5 @@
# Switch to python
(print "running in Janet")
(os/posix-exec ["python"] :p)
(print "will not print")

41
examples/sigaction.janet Normal file
View File

@ -0,0 +1,41 @@
###
### Usage: janet examples/sigaction.janet 1|2|3|4 &
###
### Then at shell: kill -s SIGTERM $!
###
(defn action
[]
(print "Handled SIGTERM!")
(flush)
(os/exit 1))
(defn main1
[]
(os/sigaction :term action true)
(forever))
(defn main2
[]
(os/sigaction :term action)
(forever))
(defn main3
[]
(os/sigaction :term action true)
(forever (ev/sleep math/inf)))
(defn main4
[]
(os/sigaction :term action)
(forever (ev/sleep math/inf)))
(defn main
[& args]
(def which (scan-number (get args 1 "1")))
(case which
1 (main1) # should work
2 (main2) # will not work
3 (main3) # should work
4 (main4) # should work
(error "bad main")))

View File

@ -0,0 +1,20 @@
(def weak-k (table/weak-keys 10))
(def weak-v (table/weak-values 10))
(def weak-kv (table/weak 10))
(put weak-kv (gensym) 10)
(put weak-kv :hello :world)
(put weak-k :abc123zz77asda :stuff)
(put weak-k true :abc123zz77asda)
(put weak-k :zyzzyz false)
(put weak-v (gensym) 10)
(put weak-v 20 (gensym))
(print "before gc")
(tracev weak-k)
(tracev weak-v)
(tracev weak-kv)
(gccollect)
(print "after gc")
(tracev weak-k)
(tracev weak-v)
(tracev weak-kv)

View File

@ -20,7 +20,7 @@
project('janet', 'c', project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'], default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.30.0') version : '1.34.0')
# Global settings # Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet') janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@ -61,6 +61,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler')) conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
conf.set('JANET_NO_PEG', not get_option('peg')) conf.set('JANET_NO_PEG', not get_option('peg'))
conf.set('JANET_NO_NET', not get_option('net')) conf.set('JANET_NO_NET', not get_option('net'))
conf.set('JANET_NO_IPV6', not get_option('ipv6'))
conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded')) conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded'))
conf.set('JANET_REDUCED_OS', get_option('reduced_os')) conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
conf.set('JANET_NO_INT_TYPES', not get_option('int_types')) conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
@ -78,6 +79,7 @@ conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt')) conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
conf.set('JANET_NO_FFI', not get_option('ffi')) conf.set('JANET_NO_FFI', not get_option('ffi'))
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit')) conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
if get_option('os_name') != '' if get_option('os_name') != ''
conf.set('JANET_OS_NAME', get_option('os_name')) conf.set('JANET_OS_NAME', get_option('os_name'))
endif endif
@ -169,7 +171,7 @@ janet_boot = executable('janet-boot', core_src, boot_src,
# Build janet.c # Build janet.c
janetc = custom_target('janetc', janetc = custom_target('janetc',
input : [janet_boot], input : [janet_boot, 'src/boot/boot.janet'],
output : 'janet.c', output : 'janet.c',
capture : true, capture : true,
command : [ command : [
@ -182,25 +184,41 @@ if not get_option('single_threaded')
janet_dependencies += thread_dep janet_dependencies += thread_dep
endif endif
libjanet = library('janet', janetc, # Allow building with no shared library
include_directories : incdir, if cc.has_argument('-fvisibility=hidden')
dependencies : janet_dependencies, lib_cflags = ['-fvisibility=hidden']
version: meson.project_version(), else
soversion: version_parts[0] + '.' + version_parts[1], lib_cflags = []
install : true) endif
if get_option('shared')
libjanet = library('janet', janetc,
include_directories : incdir,
dependencies : janet_dependencies,
version: meson.project_version(),
soversion: version_parts[0] + '.' + version_parts[1],
c_args : lib_cflags,
install : true)
# Extra c flags - adding -fvisibility=hidden matches the Makefile and # Extra c flags - adding -fvisibility=hidden matches the Makefile and
# shaves off about 10k on linux x64, likely similar on other platforms. # shaves off about 10k on linux x64, likely similar on other platforms.
if cc.has_argument('-fvisibility=hidden') if cc.has_argument('-fvisibility=hidden')
extra_cflags = ['-fvisibility=hidden'] extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT']
else
extra_cflags = ['-DJANET_DLL_IMPORT']
endif
janet_mainclient = executable('janet', mainclient_src,
include_directories : incdir,
dependencies : janet_dependencies,
link_with: [libjanet],
c_args : extra_cflags,
install : true)
else else
extra_cflags = [] # No shared library
janet_mainclient = executable('janet', mainclient_src, janetc,
include_directories : incdir,
dependencies : janet_dependencies,
c_args : lib_cflags,
install : true)
endif endif
janet_mainclient = executable('janet', janetc, mainclient_src,
include_directories : incdir,
dependencies : janet_dependencies,
c_args : extra_cflags,
install : true)
if meson.is_cross_build() if meson.is_cross_build()
native_cc = meson.get_compiler('c', native: true) native_cc = meson.get_compiler('c', native: true)
@ -264,14 +282,15 @@ endforeach
run_target('repl', command : [janet_nativeclient]) run_target('repl', command : [janet_nativeclient])
# For use as meson subproject (wrap) # For use as meson subproject (wrap)
janet_dep = declare_dependency(include_directories : incdir, if get_option('shared')
link_with : libjanet) janet_dep = declare_dependency(include_directories : incdir,
link_with : libjanet)
# pkgconfig # pkgconfig
pkg = import('pkgconfig') pkg = import('pkgconfig')
pkg.generate(libjanet, pkg.generate(libjanet,
subdirs: 'janet', subdirs: 'janet',
description: 'Library for the Janet programming language.') description: 'Library for the Janet programming language.')
endif
# Installation # Installation
install_man('janet.1') install_man('janet.1')
@ -281,11 +300,12 @@ patched_janet = custom_target('patched-janeth',
install : true, install : true,
install_dir : join_paths(get_option('includedir'), 'janet'), install_dir : join_paths(get_option('includedir'), 'janet'),
build_by_default : true, build_by_default : true,
output : ['janet.h'], output : ['janet_' + meson.project_version() + '.h'],
command : [janet_nativeclient, '@INPUT@', '@OUTPUT@']) command : [janet_nativeclient, '@INPUT@', '@OUTPUT@'])
# Create a version of the janet.h header that matches what jpm often expects # Create a version of the janet.h header that matches what jpm often expects
if meson.version().version_compare('>=0.61') if meson.version().version_compare('>=0.61')
install_symlink('janet.h', pointing_to: 'janet/janet.h', install_dir: get_option('includedir')) install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir'))
install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet'))
endif endif

View File

@ -11,14 +11,15 @@ option('peg', type : 'boolean', value : true)
option('int_types', type : 'boolean', value : true) option('int_types', type : 'boolean', value : true)
option('prf', type : 'boolean', value : false) option('prf', type : 'boolean', value : false)
option('net', type : 'boolean', value : true) option('net', type : 'boolean', value : true)
option('ipv6', type : 'boolean', value : true)
option('ev', type : 'boolean', value : true) option('ev', type : 'boolean', value : true)
option('processes', type : 'boolean', value : true) option('processes', type : 'boolean', value : true)
option('umask', type : 'boolean', value : true) option('umask', type : 'boolean', value : true)
option('realpath', type : 'boolean', value : true) option('realpath', type : 'boolean', value : true)
option('simple_getline', type : 'boolean', value : false) option('simple_getline', type : 'boolean', value : false)
option('epoll', type : 'boolean', value : false) option('epoll', type : 'boolean', value : true)
option('kqueue', type : 'boolean', value : false) option('kqueue', type : 'boolean', value : true)
option('interpreter_interrupt', type : 'boolean', value : false) option('interpreter_interrupt', type : 'boolean', value : true)
option('ffi', type : 'boolean', value : true) option('ffi', type : 'boolean', value : true)
option('ffi_jit', type : 'boolean', value : true) option('ffi_jit', type : 'boolean', value : true)
@ -29,3 +30,5 @@ option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7f
option('arch_name', type : 'string', value: '') option('arch_name', type : 'string', value: '')
option('os_name', type : 'string', value: '') option('os_name', type : 'string', value: '')
option('shared', type : 'boolean', value: true)
option('cryptorand', type : 'boolean', value: true)

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,10 @@
#define JANETCONF_H #define JANETCONF_H
#define JANET_VERSION_MAJOR 1 #define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 30 #define JANET_VERSION_MINOR 34
#define JANET_VERSION_PATCH 0 #define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA "" #define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.30.0" #define JANET_VERSION "1.34.0"
/* #define JANET_BUILD "local" */ /* #define JANET_BUILD "local" */
@ -52,6 +52,9 @@
/* #define JANET_EV_NO_EPOLL */ /* #define JANET_EV_NO_EPOLL */
/* #define JANET_EV_NO_KQUEUE */ /* #define JANET_EV_NO_KQUEUE */
/* #define JANET_NO_INTERPRETER_INTERRUPT */ /* #define JANET_NO_INTERPRETER_INTERRUPT */
/* #define JANET_NO_IPV6 */
/* #define JANET_NO_CRYPTORAND */
/* #define JANET_USE_STDATOMIC */
/* Custom vm allocator support */ /* Custom vm allocator support */
/* #include <mimalloc.h> */ /* #include <mimalloc.h> */

View File

@ -31,8 +31,6 @@
#ifdef JANET_EV #ifdef JANET_EV
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#include <windows.h> #include <windows.h>
#else
#include <stdatomic.h>
#endif #endif
#endif #endif
@ -97,14 +95,6 @@ size_t janet_os_rwlock_size(void) {
return sizeof(void *); return sizeof(void *);
} }
static int32_t janet_incref(JanetAbstractHead *ab) {
return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount);
}
static int32_t janet_decref(JanetAbstractHead *ab) {
return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount);
}
void janet_os_mutex_init(JanetOSMutex *mutex) { void janet_os_mutex_init(JanetOSMutex *mutex) {
InitializeCriticalSection((CRITICAL_SECTION *) mutex); InitializeCriticalSection((CRITICAL_SECTION *) mutex);
} }
@ -157,14 +147,6 @@ size_t janet_os_rwlock_size(void) {
return sizeof(pthread_rwlock_t); return sizeof(pthread_rwlock_t);
} }
static int32_t janet_incref(JanetAbstractHead *ab) {
return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
}
static int32_t janet_decref(JanetAbstractHead *ab) {
return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED);
}
void janet_os_mutex_init(JanetOSMutex *mutex) { void janet_os_mutex_init(JanetOSMutex *mutex) {
pthread_mutexattr_t attr; pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); pthread_mutexattr_init(&attr);
@ -212,11 +194,11 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
#endif #endif
int32_t janet_abstract_incref(void *abst) { int32_t janet_abstract_incref(void *abst) {
return janet_incref(janet_abstract_head(abst)); return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount);
} }
int32_t janet_abstract_decref(void *abst) { int32_t janet_abstract_decref(void *abst) {
return janet_decref(janet_abstract_head(abst)); return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
} }
#endif #endif

View File

@ -30,9 +30,7 @@
#include <string.h> #include <string.h>
/* Creates a new array */ static void janet_array_impl(JanetArray *array, int32_t capacity) {
JanetArray *janet_array(int32_t capacity) {
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
Janet *data = NULL; Janet *data = NULL;
if (capacity > 0) { if (capacity > 0) {
janet_vm.next_collection += capacity * sizeof(Janet); janet_vm.next_collection += capacity * sizeof(Janet);
@ -44,6 +42,19 @@ JanetArray *janet_array(int32_t capacity) {
array->count = 0; array->count = 0;
array->capacity = capacity; array->capacity = capacity;
array->data = data; array->data = data;
}
/* Creates a new array */
JanetArray *janet_array(int32_t capacity) {
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
janet_array_impl(array, capacity);
return array;
}
/* Creates a new array with weak references */
JanetArray *janet_array_weak(int32_t capacity) {
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray));
janet_array_impl(array, capacity);
return array; return array;
} }
@ -132,6 +143,15 @@ JANET_CORE_FN(cfun_array_new,
return janet_wrap_array(array); return janet_wrap_array(array);
} }
JANET_CORE_FN(cfun_array_weak,
"(array/weak capacity)",
"Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getinteger(argv, 0);
JanetArray *array = janet_array_weak(cap);
return janet_wrap_array(array);
}
JANET_CORE_FN(cfun_array_new_filled, JANET_CORE_FN(cfun_array_new_filled,
"(array/new-filled count &opt value)", "(array/new-filled count &opt value)",
"Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") { "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
@ -177,8 +197,8 @@ JANET_CORE_FN(cfun_array_peek,
} }
JANET_CORE_FN(cfun_array_push, JANET_CORE_FN(cfun_array_push,
"(array/push arr x)", "(array/push arr & xs)",
"Insert an element in the end of an array. Modifies the input array and returns it.") { "Push all the elements of xs to the end of an array. Modifies the input array and returns it.") {
janet_arity(argc, 1, -1); janet_arity(argc, 1, -1);
JanetArray *array = janet_getarray(argv, 0); JanetArray *array = janet_getarray(argv, 0);
if (INT32_MAX - argc + 1 <= array->count) { if (INT32_MAX - argc + 1 <= array->count) {
@ -352,6 +372,7 @@ JANET_CORE_FN(cfun_array_clear,
void janet_lib_array(JanetTable *env) { void janet_lib_array(JanetTable *env) {
JanetRegExt array_cfuns[] = { JanetRegExt array_cfuns[] = {
JANET_CORE_REG("array/new", cfun_array_new), JANET_CORE_REG("array/new", cfun_array_new),
JANET_CORE_REG("array/weak", cfun_array_weak),
JANET_CORE_REG("array/new-filled", cfun_array_new_filled), JANET_CORE_REG("array/new-filled", cfun_array_new_filled),
JANET_CORE_REG("array/fill", cfun_array_fill), JANET_CORE_REG("array/fill", cfun_array_fill),
JANET_CORE_REG("array/pop", cfun_array_pop), JANET_CORE_REG("array/pop", cfun_array_pop),

View File

@ -560,6 +560,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
x = janet_get1(s, janet_ckeywordv("vararg")); x = janet_get1(s, janet_ckeywordv("vararg"));
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG; if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
/* Initialize slotcount */
def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
/* Check structarg */ /* Check structarg */
x = janet_get1(s, janet_ckeywordv("structarg")); x = janet_get1(s, janet_ckeywordv("structarg"));
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
@ -784,8 +787,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
} }
/* Verify the func def */ /* Verify the func def */
if (janet_verify(def)) { int verify_status = janet_verify(def);
janet_asm_error(&a, "invalid assembly"); if (verify_status) {
janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
} }
/* Add final flags */ /* Add final flags */

View File

@ -135,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
/* Push a cstring to buffer */ /* Push a cstring to buffer */
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) { void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
int32_t len = 0; int32_t len = (int32_t) strlen(cstring);
while (cstring[len]) ++len;
janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len); janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
} }
@ -221,6 +220,20 @@ JANET_CORE_FN(cfun_buffer_new_filled,
return janet_wrap_buffer(buffer); return janet_wrap_buffer(buffer);
} }
JANET_CORE_FN(cfun_buffer_frombytes,
"(buffer/from-bytes & byte-vals)",
"Creates a buffer from integer parameters with byte values. All integers "
"will be coerced to the range of 1 byte 0-255.") {
int32_t i;
JanetBuffer *buffer = janet_buffer(argc);
for (i = 0; i < argc; i++) {
int32_t c = janet_getinteger(argv, i);
buffer->data[i] = c & 0xFF;
}
buffer->count = argc;
return janet_wrap_buffer(buffer);
}
JANET_CORE_FN(cfun_buffer_fill, JANET_CORE_FN(cfun_buffer_fill,
"(buffer/fill buffer &opt byte)", "(buffer/fill buffer &opt byte)",
"Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. " "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
@ -307,6 +320,143 @@ JANET_CORE_FN(cfun_buffer_chars,
return argv[0]; return argv[0];
} }
static int should_reverse_bytes(const Janet *argv, int32_t argc) {
JanetKeyword order_kw = janet_getkeyword(argv, argc);
if (!janet_cstrcmp(order_kw, "le")) {
#if JANET_BIG_ENDIAN
return 1;
#endif
} else if (!janet_cstrcmp(order_kw, "be")) {
#if JANET_LITTLE_ENDIAN
return 1;
#endif
} else if (!janet_cstrcmp(order_kw, "native")) {
return 0;
} else {
janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]);
}
return 0;
}
static void reverse_u32(uint8_t bytes[4]) {
uint8_t temp;
temp = bytes[3];
bytes[3] = bytes[0];
bytes[0] = temp;
temp = bytes[2];
bytes[2] = bytes[1];
bytes[1] = temp;
}
static void reverse_u64(uint8_t bytes[8]) {
uint8_t temp;
temp = bytes[7];
bytes[7] = bytes[0];
bytes[0] = temp;
temp = bytes[6];
bytes[6] = bytes[1];
bytes[1] = temp;
temp = bytes[5];
bytes[5] = bytes[2];
bytes[2] = temp;
temp = bytes[4];
bytes[4] = bytes[3];
bytes[3] = temp;
}
JANET_CORE_FN(cfun_buffer_push_uint16,
"(buffer/push-uint16 buffer order data)",
"Push a 16 bit unsigned integer data onto the end of the buffer. "
"Returns the modified buffer.") {
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
union {
uint16_t data;
uint8_t bytes[2];
} u;
u.data = (uint16_t) janet_getinteger(argv, 2);
if (reverse) {
uint8_t temp = u.bytes[1];
u.bytes[1] = u.bytes[0];
u.bytes[0] = temp;
}
janet_buffer_push_u16(buffer, *(uint16_t *) u.bytes);
return argv[0];
}
JANET_CORE_FN(cfun_buffer_push_uint32,
"(buffer/push-uint32 buffer order data)",
"Push a 32 bit unsigned integer data onto the end of the buffer. "
"Returns the modified buffer.") {
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
union {
uint32_t data;
uint8_t bytes[4];
} u;
u.data = (uint32_t) janet_getinteger(argv, 2);
if (reverse)
reverse_u32(u.bytes);
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
return argv[0];
}
JANET_CORE_FN(cfun_buffer_push_uint64,
"(buffer/push-uint64 buffer order data)",
"Push a 64 bit unsigned integer data onto the end of the buffer. "
"Returns the modified buffer.") {
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
union {
uint64_t data;
uint8_t bytes[8];
} u;
u.data = (uint64_t) janet_getuinteger64(argv, 2);
if (reverse)
reverse_u64(u.bytes);
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
return argv[0];
}
JANET_CORE_FN(cfun_buffer_push_float32,
"(buffer/push-float32 buffer order data)",
"Push the underlying bytes of a 32 bit float data onto the end of the buffer. "
"Returns the modified buffer.") {
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
union {
float data;
uint8_t bytes[4];
} u;
u.data = (float) janet_getnumber(argv, 2);
if (reverse)
reverse_u32(u.bytes);
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
return argv[0];
}
JANET_CORE_FN(cfun_buffer_push_float64,
"(buffer/push-float64 buffer order data)",
"Push the underlying bytes of a 64 bit float data onto the end of the buffer. "
"Returns the modified buffer.") {
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
union {
double data;
uint8_t bytes[8];
} u;
u.data = janet_getnumber(argv, 2);
if (reverse)
reverse_u64(u.bytes);
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
return argv[0];
}
static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) { static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
for (int32_t i = argc_offset; i < argc; i++) { for (int32_t i = argc_offset; i < argc; i++) {
if (janet_checktype(argv[i], JANET_NUMBER)) { if (janet_checktype(argv[i], JANET_NUMBER)) {
@ -505,15 +655,42 @@ JANET_CORE_FN(cfun_buffer_format,
return argv[0]; return argv[0];
} }
JANET_CORE_FN(cfun_buffer_format_at,
"(buffer/format-at buffer at format & args)",
"Snprintf like functionality for printing values into a buffer. Returns "
"the modified buffer.") {
janet_arity(argc, 2, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int32_t at = janet_getinteger(argv, 1);
if (at < 0) {
at += buffer->count + 1;
}
if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at);
int32_t oldcount = buffer->count;
buffer->count = at;
const char *strfrmt = (const char *) janet_getstring(argv, 2);
janet_buffer_format(buffer, strfrmt, 2, argc, argv);
if (buffer->count < oldcount) {
buffer->count = oldcount;
}
return argv[0];
}
void janet_lib_buffer(JanetTable *env) { void janet_lib_buffer(JanetTable *env) {
JanetRegExt buffer_cfuns[] = { JanetRegExt buffer_cfuns[] = {
JANET_CORE_REG("buffer/new", cfun_buffer_new), JANET_CORE_REG("buffer/new", cfun_buffer_new),
JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled), JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled),
JANET_CORE_REG("buffer/from-bytes", cfun_buffer_frombytes),
JANET_CORE_REG("buffer/fill", cfun_buffer_fill), JANET_CORE_REG("buffer/fill", cfun_buffer_fill),
JANET_CORE_REG("buffer/trim", cfun_buffer_trim), JANET_CORE_REG("buffer/trim", cfun_buffer_trim),
JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8), JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8),
JANET_CORE_REG("buffer/push-word", cfun_buffer_word), JANET_CORE_REG("buffer/push-word", cfun_buffer_word),
JANET_CORE_REG("buffer/push-string", cfun_buffer_chars), JANET_CORE_REG("buffer/push-string", cfun_buffer_chars),
JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16),
JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32),
JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64),
JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32),
JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64),
JANET_CORE_REG("buffer/push", cfun_buffer_push), JANET_CORE_REG("buffer/push", cfun_buffer_push),
JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at), JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at),
JANET_CORE_REG("buffer/popn", cfun_buffer_popn), JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
@ -525,6 +702,7 @@ void janet_lib_buffer(JanetTable *env) {
JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle), JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
JANET_CORE_REG("buffer/blit", cfun_buffer_blit), JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
JANET_CORE_REG("buffer/format", cfun_buffer_format), JANET_CORE_REG("buffer/format", cfun_buffer_format),
JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, buffer_cfuns); janet_core_cfuns_ext(env, NULL, buffer_cfuns);

View File

@ -226,6 +226,7 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
case JOP_LOAD_TRUE: case JOP_LOAD_TRUE:
case JOP_LOAD_FALSE: case JOP_LOAD_FALSE:
case JOP_LOAD_SELF: case JOP_LOAD_SELF:
break;
case JOP_MAKE_ARRAY: case JOP_MAKE_ARRAY:
case JOP_MAKE_BUFFER: case JOP_MAKE_BUFFER:
case JOP_MAKE_STRING: case JOP_MAKE_STRING:
@ -233,6 +234,8 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
case JOP_MAKE_TABLE: case JOP_MAKE_TABLE:
case JOP_MAKE_TUPLE: case JOP_MAKE_TUPLE:
case JOP_MAKE_BRACKET_TUPLE: case JOP_MAKE_BRACKET_TUPLE:
/* Reads from the stack, don't remove */
janetc_regalloc_touch(&ra, DD);
break; break;
/* Read A */ /* Read A */

View File

@ -35,6 +35,13 @@
#endif #endif
#endif #endif
#ifdef JANET_USE_STDATOMIC
#include <stdatomic.h>
/* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations.
* Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of).
* For TCC and similar compilers, one would need -std=c11 or similar then to get access. */
#endif
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) { JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
#ifdef JANET_TOP_LEVEL_SIGNAL #ifdef JANET_TOP_LEVEL_SIGNAL
JANET_TOP_LEVEL_SIGNAL(msg); JANET_TOP_LEVEL_SIGNAL(msg);
@ -338,7 +345,7 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c
int32_t not_raw = raw; int32_t not_raw = raw;
if (not_raw < 0) not_raw += length + 1; if (not_raw < 0) not_raw += length + 1;
if (not_raw < 0 || not_raw > length) if (not_raw < 0 || not_raw > length)
janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length); janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length);
return not_raw; return not_raw;
} }
@ -361,7 +368,7 @@ int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const ch
int32_t not_raw = raw; int32_t not_raw = raw;
if (not_raw < 0) not_raw += length; if (not_raw < 0) not_raw += length;
if (not_raw < 0 || not_raw > length) if (not_raw < 0 || not_raw > length)
janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length); janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length);
return not_raw; return not_raw;
} }
@ -491,9 +498,41 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
return janet_getabstract(argv, n, at); return janet_getabstract(argv, n, at);
} }
/* Atomic refcounts */
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
#ifdef JANET_WINDOWS
return InterlockedIncrement(x);
#elif defined(JANET_USE_STDATOMIC)
return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
#else
return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
#endif
}
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
#ifdef JANET_WINDOWS
return InterlockedDecrement(x);
#elif defined(JANET_USE_STDATOMIC)
return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
#else
return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
#endif
}
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
#ifdef JANET_WINDOWS
return InterlockedOr(x, 0);
#elif defined(JANET_USE_STDATOMIC)
return atomic_load_explicit(x, memory_order_acquire);
#else
return __atomic_load_n(x, __ATOMIC_ACQUIRE);
#endif
}
/* Some definitions for function-like macros */ /* Some definitions for function-like macros */
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) { JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
return janet_struct_head(st); return janet_struct_head(st);
} }
@ -501,10 +540,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
return janet_abstract_head(abstract); return janet_abstract_head(abstract);
} }
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) { JANET_API JanetStringHead *(janet_string_head)(JanetString s) {
return janet_string_head(s); return janet_string_head(s);
} }
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) { JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) {
return janet_tuple_head(tuple); return janet_tuple_head(tuple);
} }

View File

@ -934,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
int32_t slotchunks = (def->slotcount + 31) >> 5; int32_t slotchunks = (def->slotcount + 31) >> 5;
/* numchunks is min of slotchunks and scope->ua.count */ /* numchunks is min of slotchunks and scope->ua.count */
int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks; int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks); uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
if (NULL == chunks) { if (NULL == chunks) {
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;
} }
@ -1056,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
if (c.result.status == JANET_COMPILE_OK) { if (c.result.status == JANET_COMPILE_OK) {
JanetFuncDef *def = janetc_pop_funcdef(&c); JanetFuncDef *def = janetc_pop_funcdef(&c);
def->name = janet_cstring("_thunk"); def->name = janet_cstring("thunk");
janet_def_addflags(def); janet_def_addflags(def);
c.result.funcdef = def; c.result.funcdef = def;
} else { } else {

View File

@ -69,15 +69,15 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
host.minor < modconf.minor || host.minor < modconf.minor ||
host.bits != modconf.bits) { host.bits != modconf.bits) {
char errbuf[128]; char errbuf[128];
sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)", snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
host.major, host.major,
host.minor, host.minor,
host.patch, host.patch,
host.bits, host.bits,
modconf.major, modconf.major,
modconf.minor, modconf.minor,
modconf.patch, modconf.patch,
modconf.bits); modconf.bits);
*error = janet_cstring(errbuf); *error = janet_cstring(errbuf);
return NULL; return NULL;
} }
@ -110,14 +110,14 @@ JANET_CORE_FN(janet_core_expand_path,
"(module/expand-path path template)", "(module/expand-path path template)",
"Expands a path template as found in `module/paths` for `module/find`. " "Expands a path template as found in `module/paths` for `module/find`. "
"This takes in a path (the argument to require) and a template string, " "This takes in a path (the argument to require) and a template string, "
"to expand the path to a path that can be " "to expand the path to a path that can be used for importing files. "
"used for importing files. The replacements are as follows:\n\n" "The replacements are as follows:\n\n"
"* :all: -- the value of path verbatim.\n\n" "* :all: -- the value of path verbatim.\n\n"
"* :@all: -- Same as :all:, but if `path` starts with the @ character,\n" "* :@all: -- Same as :all:, but if `path` starts with the @ character, "
" the first path segment is replaced with a dynamic binding\n" "the first path segment is replaced with a dynamic binding "
" `(dyn <first path segment as keyword>)`.\n\n" "`(dyn <first path segment as keyword>)`.\n\n"
"* :cur: -- the current file, or (dyn :current-file)\n\n" "* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n"
"* :dir: -- the directory containing the current file\n\n" "* :dir: -- the directory portion, if any, of the path argument\n\n"
"* :name: -- the name component of path, with extension if given\n\n" "* :name: -- the name component of path, with extension if given\n\n"
"* :native: -- the extension used to load natives, .so or .dll\n\n" "* :native: -- the extension used to load natives, .so or .dll\n\n"
"* :sys: -- the system path, or (dyn :syspath)") { "* :sys: -- the system path, or (dyn :syspath)") {
@ -659,6 +659,34 @@ ret_false:
return janet_wrap_false(); return janet_wrap_false();
} }
JANET_CORE_FN(janet_core_is_bytes,
"(bytes? x)",
"Check if x is a string, symbol, keyword, or buffer.") {
janet_fixarity(argc, 1);
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_BYTES));
}
JANET_CORE_FN(janet_core_is_indexed,
"(indexed? x)",
"Check if x is an array or tuple.") {
janet_fixarity(argc, 1);
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_INDEXED));
}
JANET_CORE_FN(janet_core_is_dictionary,
"(dictionary? x)",
"Check if x is a table or struct.") {
janet_fixarity(argc, 1);
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_DICTIONARY));
}
JANET_CORE_FN(janet_core_is_lengthable,
"(lengthable? x)",
"Check if x is a bytes, indexed, or dictionary.") {
janet_fixarity(argc, 1);
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_LENGTHABLE));
}
JANET_CORE_FN(janet_core_signal, JANET_CORE_FN(janet_core_signal,
"(signal what x)", "(signal what x)",
"Raise a signal with payload x. ") { "Raise a signal with payload x. ") {
@ -720,6 +748,7 @@ static const SandboxOption sandbox_options[] = {
{"net-connect", JANET_SANDBOX_NET_CONNECT}, {"net-connect", JANET_SANDBOX_NET_CONNECT},
{"net-listen", JANET_SANDBOX_NET_LISTEN}, {"net-listen", JANET_SANDBOX_NET_LISTEN},
{"sandbox", JANET_SANDBOX_SANDBOX}, {"sandbox", JANET_SANDBOX_SANDBOX},
{"signal", JANET_SANDBOX_SIGNAL},
{"subprocess", JANET_SANDBOX_SUBPROCESS}, {"subprocess", JANET_SANDBOX_SUBPROCESS},
{NULL, 0} {NULL, 0}
}; };
@ -744,6 +773,7 @@ JANET_CORE_FN(janet_core_sandbox,
"* :net-connect - disallow making outbound network connections\n" "* :net-connect - disallow making outbound network connections\n"
"* :net-listen - disallow accepting inbound network connections\n" "* :net-listen - disallow accepting inbound network connections\n"
"* :sandbox - disallow calling this function\n" "* :sandbox - disallow calling this function\n"
"* :signal - disallow adding or removing signal handlers\n"
"* :subprocess - disallow running subprocesses") { "* :subprocess - disallow running subprocesses") {
uint32_t flags = 0; uint32_t flags = 0;
for (int32_t i = 0; i < argc; i++) { for (int32_t i = 0; i < argc; i++) {
@ -1053,6 +1083,10 @@ static void janet_load_libs(JanetTable *env) {
JANET_CORE_REG("module/expand-path", janet_core_expand_path), JANET_CORE_REG("module/expand-path", janet_core_expand_path),
JANET_CORE_REG("int?", janet_core_check_int), JANET_CORE_REG("int?", janet_core_check_int),
JANET_CORE_REG("nat?", janet_core_check_nat), JANET_CORE_REG("nat?", janet_core_check_nat),
JANET_CORE_REG("bytes?", janet_core_is_bytes),
JANET_CORE_REG("indexed?", janet_core_is_indexed),
JANET_CORE_REG("dictionary?", janet_core_is_dictionary),
JANET_CORE_REG("lengthable?", janet_core_is_lengthable),
JANET_CORE_REG("slice", janet_core_slice), JANET_CORE_REG("slice", janet_core_slice),
JANET_CORE_REG("range", janet_core_range), JANET_CORE_REG("range", janet_core_range),
JANET_CORE_REG("signal", janet_core_signal), JANET_CORE_REG("signal", janet_core_signal),
@ -1110,17 +1144,20 @@ JanetTable *janet_core_env(JanetTable *replacements) {
JDOC("(next ds &opt key)\n\n" JDOC("(next ds &opt key)\n\n"
"Gets the next key in a data structure. Can be used to iterate through " "Gets the next key in a data structure. Can be used to iterate through "
"the keys of a data structure in an unspecified order. Keys are guaranteed " "the keys of a data structure in an unspecified order. Keys are guaranteed "
"to be seen only once per iteration if they data structure is not mutated " "to be seen only once per iteration if the data structure is not mutated "
"during iteration. If key is nil, next returns the first key. If next " "during iteration. If key is nil, next returns the first key. If next "
"returns nil, there are no more keys to iterate through.")); "returns nil, there are no more keys to iterate through."));
janet_quick_asm(env, JANET_FUN_PROP, janet_quick_asm(env, JANET_FUN_PROP,
"propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm), "propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
JDOC("(propagate x fiber)\n\n" JDOC("(propagate x fiber)\n\n"
"Propagate a signal from a fiber to the current fiber. The resulting " "Propagate a signal from a fiber to the current fiber and "
"stack trace from the current fiber will include frames from fiber. If " "set the last value of the current fiber to `x`. The signal "
"fiber is in a state that can be resumed, resuming the current fiber will " "value is then available as the status of the current fiber. "
"first resume fiber. This function can be used to re-raise an error without " "The resulting stack trace from the current fiber will include "
"losing the original stack trace.")); "frames from fiber. If fiber is in a state that can be resumed, "
"resuming the current fiber will first resume `fiber`. "
"This function can be used to re-raise an error without losing "
"the original stack trace."));
janet_quick_asm(env, JANET_FUN_DEBUG, janet_quick_asm(env, JANET_FUN_DEBUG,
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm), "debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
JDOC("(debug &opt x)\n\n" JDOC("(debug &opt x)\n\n"

View File

@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
} }
} }
if (frame->flags & JANET_STACKFRAME_TAILCALL) if (frame->flags & JANET_STACKFRAME_TAILCALL)
janet_eprintf(" (tailcall)"); janet_eprintf(" (tail call)");
if (frame->func && frame->pc) { if (frame->func && frame->pc) {
int32_t off = (int32_t)(frame->pc - def->bytecode); int32_t off = (int32_t)(frame->pc - def->bytecode);
if (def->sourcemap) { if (def->sourcemap) {
@ -180,6 +180,11 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
} }
} }
janet_eprintf("\n"); janet_eprintf("\n");
/* Print fiber points optionally. Clutters traces but provides info
if (i <= 0 && fi > 0) {
janet_eprintf(" in parent fiber\n");
}
*/
} }
} }
@ -388,8 +393,8 @@ JANET_CORE_FN(cfun_debug_stack,
JANET_CORE_FN(cfun_debug_stacktrace, JANET_CORE_FN(cfun_debug_stacktrace,
"(debug/stacktrace fiber &opt err prefix)", "(debug/stacktrace fiber &opt err prefix)",
"Prints a nice looking stacktrace for a fiber. Can optionally provide " "Prints a nice looking stacktrace for a fiber. Can optionally provide "
"an error value to print the stack trace with. If `err` is nil or not " "an error value to print the stack trace with. If `prefix` is nil or not "
"provided, and no prefix is given, will skip the error line. Returns the fiber.") { "provided, will skip the error line. Returns the fiber.") {
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
JanetFiber *fiber = janet_getfiber(argv, 0); JanetFiber *fiber = janet_getfiber(argv, 0);
Janet x = argc == 1 ? janet_wrap_nil() : argv[1]; Janet x = argc == 1 ? janet_wrap_nil() : argv[1];

View File

@ -26,6 +26,7 @@
#include "emit.h" #include "emit.h"
#include "vector.h" #include "vector.h"
#include "regalloc.h" #include "regalloc.h"
#include "util.h"
#endif #endif
/* Get a register */ /* Get a register */
@ -128,7 +129,8 @@ static void janetc_movenear(JanetCompiler *c,
((uint32_t)(src.envindex) << 16) | ((uint32_t)(src.envindex) << 16) |
((uint32_t)(dest) << 8) | ((uint32_t)(dest) << 8) |
JOP_LOAD_UPVALUE); JOP_LOAD_UPVALUE);
} else if (src.index > 0xFF || src.index != dest) { } else if (src.index != dest) {
janet_assert(src.index >= 0, "bad slot");
janetc_emit(c, janetc_emit(c,
((uint32_t)(src.index) << 16) | ((uint32_t)(src.index) << 16) |
((uint32_t)(dest) << 8) | ((uint32_t)(dest) << 8) |
@ -155,6 +157,7 @@ static void janetc_moveback(JanetCompiler *c,
((uint32_t)(src) << 8) | ((uint32_t)(src) << 8) |
JOP_SET_UPVALUE); JOP_SET_UPVALUE);
} else if (dest.index != src) { } else if (dest.index != src) {
janet_assert(dest.index >= 0, "bad slot");
janetc_emit(c, janetc_emit(c,
((uint32_t)(dest.index) << 16) | ((uint32_t)(dest.index) << 16) |
((uint32_t)(src) << 8) | ((uint32_t)(src) << 8) |

File diff suppressed because it is too large Load Diff

View File

@ -76,4 +76,6 @@
#define __BSD_VISIBLE 1 #define __BSD_VISIBLE 1
#endif #endif
#define _FILE_OFFSET_BITS 64
#endif #endif

View File

@ -1381,7 +1381,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
"(ffi/write ffi-type data &opt buffer index)", "(ffi/write ffi-type data &opt buffer index)",
"Append a native type to a buffer such as it would appear in memory. This can be used " "Append a native type to a buffer such as it would appear in memory. This can be used "
"to pass pointers to structs in the ffi, or send C/C++/native structs over the network " "to pass pointers to structs in the ffi, or send C/C++/native structs over the network "
"or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { "or to files. Returns a modified buffer or a new buffer if one is not supplied.") {
janet_sandbox_assert(JANET_SANDBOX_FFI_USE); janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
@ -1548,7 +1548,7 @@ JANET_CORE_FN(cfun_ffi_pointer_cfunction,
JANET_CORE_FN(cfun_ffi_supported_calling_conventions, JANET_CORE_FN(cfun_ffi_supported_calling_conventions,
"(ffi/calling-conventions)", "(ffi/calling-conventions)",
"Get an array of all supported calling conventions on the current arhcitecture. Some architectures may have some FFI " "Get an array of all supported calling conventions on the current architecture. Some architectures may have some FFI "
"functionality (ffi/malloc, ffi/free, ffi/read, ffi/write, etc.) but not support " "functionality (ffi/malloc, ffi/free, ffi/read, ffi/write, etc.) but not support "
"any calling conventions. This function can be used to get all supported calling conventions " "any calling conventions. This function can be used to get all supported calling conventions "
"that can be used on this architecture. All architectures support the :none calling " "that can be used on this architecture. All architectures support the :none calling "

View File

@ -39,8 +39,10 @@ static void fiber_reset(JanetFiber *fiber) {
fiber->env = NULL; fiber->env = NULL;
fiber->last_value = janet_wrap_nil(); fiber->last_value = janet_wrap_nil();
#ifdef JANET_EV #ifdef JANET_EV
fiber->waiting = NULL;
fiber->sched_id = 0; fiber->sched_id = 0;
fiber->ev_callback = NULL;
fiber->ev_state = NULL;
fiber->ev_stream = NULL;
fiber->supervisor_channel = NULL; fiber->supervisor_channel = NULL;
#endif #endif
janet_fiber_set_status(fiber, JANET_STATUS_NEW); janet_fiber_set_status(fiber, JANET_STATUS_NEW);
@ -85,7 +87,6 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
if (janet_fiber_funcframe(fiber, callee)) return NULL; if (janet_fiber_funcframe(fiber, callee)) return NULL;
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE; janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
#ifdef JANET_EV #ifdef JANET_EV
fiber->waiting = NULL;
fiber->supervisor_channel = NULL; fiber->supervisor_channel = NULL;
#endif #endif
return fiber; return fiber;
@ -238,8 +239,8 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
fiber->data + tuplehead, fiber->data + tuplehead,
oldtop - tuplehead) oldtop - tuplehead)
: janet_wrap_tuple(janet_tuple_n( : janet_wrap_tuple(janet_tuple_n(
fiber->data + tuplehead, fiber->data + tuplehead,
oldtop - tuplehead)); oldtop - tuplehead));
} }
} }
@ -369,8 +370,8 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
fiber->data + tuplehead, fiber->data + tuplehead,
fiber->stacktop - tuplehead) fiber->stacktop - tuplehead)
: janet_wrap_tuple(janet_tuple_n( : janet_wrap_tuple(janet_tuple_n(
fiber->data + tuplehead, fiber->data + tuplehead,
fiber->stacktop - tuplehead)); fiber->stacktop - tuplehead));
} }
stacksize = tuplehead - fiber->stackstart + 1; stacksize = tuplehead - fiber->stackstart + 1;
} else { } else {
@ -661,7 +662,7 @@ JANET_CORE_FN(cfun_fiber_can_resume,
} }
JANET_CORE_FN(cfun_fiber_last_value, JANET_CORE_FN(cfun_fiber_last_value,
"(fiber/last-value)", "(fiber/last-value fiber)",
"Get the last value returned or signaled from the fiber.") { "Get the last value returned or signaled from the fiber.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetFiber *fiber = janet_getfiber(argv, 0); JanetFiber *fiber = janet_getfiber(argv, 0);

View File

@ -59,6 +59,9 @@
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000 #define JANET_FIBER_EV_FLAG_CANCELED 0x10000
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000 #define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
#define JANET_FIBER_FLAG_ROOT 0x40000 #define JANET_FIBER_FLAG_ROOT 0x40000
#define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1
/* used only on windows, should otherwise be unset */
#define janet_fiber_set_status(f, s) do {\ #define janet_fiber_set_status(f, s) do {\
(f)->flags &= ~JANET_FIBER_STATUS_MASK;\ (f)->flags &= ~JANET_FIBER_STATUS_MASK;\

View File

@ -132,6 +132,24 @@ static void janet_mark_many(const Janet *values, int32_t n) {
} }
} }
/* Mark a bunch of key values items in memory */
static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n;
while (kvs < end) {
janet_mark(kvs->key);
kvs++;
}
}
/* Mark a bunch of key values items in memory */
static void janet_mark_values(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n;
while (kvs < end) {
janet_mark(kvs->value);
kvs++;
}
}
/* Mark a bunch of key values items in memory */ /* Mark a bunch of key values items in memory */
static void janet_mark_kvs(const JanetKV *kvs, int32_t n) { static void janet_mark_kvs(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n; const JanetKV *end = kvs + n;
@ -146,7 +164,9 @@ static void janet_mark_array(JanetArray *array) {
if (janet_gc_reachable(array)) if (janet_gc_reachable(array))
return; return;
janet_gc_mark(array); janet_gc_mark(array);
janet_mark_many(array->data, array->count); if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) {
janet_mark_many(array->data, array->count);
}
} }
static void janet_mark_table(JanetTable *table) { static void janet_mark_table(JanetTable *table) {
@ -154,7 +174,15 @@ recur: /* Manual tail recursion */
if (janet_gc_reachable(table)) if (janet_gc_reachable(table))
return; return;
janet_gc_mark(table); janet_gc_mark(table);
janet_mark_kvs(table->data, table->capacity); enum JanetMemoryType memtype = janet_gc_type(table);
if (memtype == JANET_MEMORY_TABLE_WEAKK) {
janet_mark_values(table->data, table->capacity);
} else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
janet_mark_keys(table->data, table->capacity);
} else if (memtype == JANET_MEMORY_TABLE) {
janet_mark_kvs(table->data, table->capacity);
}
/* do nothing for JANET_MEMORY_TABLE_WEAKKV */
if (table->proto) { if (table->proto) {
table = table->proto; table = table->proto;
goto recur; goto recur;
@ -268,6 +296,12 @@ recur:
if (fiber->supervisor_channel) { if (fiber->supervisor_channel) {
janet_mark_abstract(fiber->supervisor_channel); janet_mark_abstract(fiber->supervisor_channel);
} }
if (fiber->ev_stream) {
janet_mark_abstract(fiber->ev_stream);
}
if (fiber->ev_callback) {
fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK);
}
#endif #endif
/* Explicit tail recursion */ /* Explicit tail recursion */
@ -292,9 +326,17 @@ static void janet_deinit_block(JanetGCObject *mem) {
case JANET_MEMORY_TABLE: case JANET_MEMORY_TABLE:
janet_free(((JanetTable *) mem)->data); janet_free(((JanetTable *) mem)->data);
break; break;
case JANET_MEMORY_FIBER: case JANET_MEMORY_FIBER: {
janet_free(((JanetFiber *)mem)->data); JanetFiber *f = (JanetFiber *)mem;
break; #ifdef JANET_EV
if (f->ev_state && !(f->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) {
janet_ev_dec_refcount();
janet_free(f->ev_state);
}
#endif
janet_free(f->data);
}
break;
case JANET_MEMORY_BUFFER: case JANET_MEMORY_BUFFER:
janet_buffer_deinit((JanetBuffer *) mem); janet_buffer_deinit((JanetBuffer *) mem);
break; break;
@ -326,12 +368,98 @@ static void janet_deinit_block(JanetGCObject *mem) {
} }
} }
/* Check that a value x has been visited in the mark phase */
static int janet_check_liveref(Janet x) {
switch (janet_type(x)) {
default:
return 1;
case JANET_ARRAY:
case JANET_TABLE:
case JANET_FUNCTION:
case JANET_BUFFER:
case JANET_FIBER:
return janet_gc_reachable(janet_unwrap_pointer(x));
case JANET_STRING:
case JANET_SYMBOL:
case JANET_KEYWORD:
return janet_gc_reachable(janet_string_head(janet_unwrap_string(x)));
case JANET_ABSTRACT:
return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x)));
case JANET_TUPLE:
return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x)));
case JANET_STRUCT:
return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x)));
}
}
/* Iterate over all allocated memory, and free memory that is not /* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */ * marked as reachable. Flip the gc color flag for next sweep. */
void janet_sweep() { void janet_sweep() {
JanetGCObject *previous = NULL; JanetGCObject *previous = NULL;
JanetGCObject *current = janet_vm.blocks; JanetGCObject *current = janet_vm.weak_blocks;
JanetGCObject *next; JanetGCObject *next;
/* Sweep weak heap to drop weak refs */
while (NULL != current) {
next = current->data.next;
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
/* Check for dead references */
enum JanetMemoryType type = janet_gc_type(current);
if (type == JANET_MEMORY_ARRAY_WEAK) {
JanetArray *array = (JanetArray *) current;
for (uint32_t i = 0; i < (uint32_t) array->count; i++) {
if (!janet_check_liveref(array->data[i])) {
array->data[i] = janet_wrap_nil();
}
}
} else {
JanetTable *table = (JanetTable *) current;
int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV);
int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV);
JanetKV *end = table->data + table->capacity;
JanetKV *kvs = table->data;
while (kvs < end) {
int drop = 0;
if (check_keys && !janet_check_liveref(kvs->key)) drop = 1;
if (check_values && !janet_check_liveref(kvs->value)) drop = 1;
if (drop) {
/* Inlined from janet_table_remove without search */
table->count--;
table->deleted++;
kvs->key = janet_wrap_nil();
kvs->value = janet_wrap_false();
}
kvs++;
}
}
}
current = next;
}
/* Sweep weak heap to free blocks */
previous = NULL;
current = janet_vm.weak_blocks;
while (NULL != current) {
next = current->data.next;
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
previous = current;
current->flags &= ~JANET_MEM_REACHABLE;
} else {
janet_vm.block_count--;
janet_deinit_block(current);
if (NULL != previous) {
previous->data.next = next;
} else {
janet_vm.weak_blocks = next;
}
janet_free(current);
}
current = next;
}
/* Sweep main heap to free blocks */
previous = NULL;
current = janet_vm.blocks;
while (NULL != current) { while (NULL != current) {
next = current->data.next; next = current->data.next;
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) { if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
@ -349,6 +477,7 @@ void janet_sweep() {
} }
current = next; current = next;
} }
#ifdef JANET_EV #ifdef JANET_EV
/* Sweep threaded abstract types for references to decrement */ /* Sweep threaded abstract types for references to decrement */
JanetKV *items = janet_vm.threaded_abstracts.data; JanetKV *items = janet_vm.threaded_abstracts.data;
@ -370,14 +499,15 @@ void janet_sweep() {
if (head->type->gc) { if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed"); janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
} }
/* Mark as tombstone in place */
items[i].key = janet_wrap_nil();
items[i].value = janet_wrap_false();
janet_vm.threaded_abstracts.deleted++;
janet_vm.threaded_abstracts.count--;
/* Free memory */ /* Free memory */
janet_free(janet_abstract_head(abst)); janet_free(janet_abstract_head(abst));
} }
/* Mark as tombstone in place */
items[i].key = janet_wrap_nil();
items[i].value = janet_wrap_false();
janet_vm.threaded_abstracts.deleted++;
janet_vm.threaded_abstracts.count--;
} }
/* Reset for next sweep */ /* Reset for next sweep */
@ -405,8 +535,15 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
/* Prepend block to heap list */ /* Prepend block to heap list */
janet_vm.next_collection += size; janet_vm.next_collection += size;
mem->data.next = janet_vm.blocks; if (type < JANET_MEMORY_TABLE_WEAKK) {
janet_vm.blocks = mem; /* normal heap */
mem->data.next = janet_vm.blocks;
janet_vm.blocks = mem;
} else {
/* weak heap */
mem->data.next = janet_vm.weak_blocks;
janet_vm.weak_blocks = mem;
}
janet_vm.block_count++; janet_vm.block_count++;
return (void *)mem; return (void *)mem;
@ -437,7 +574,8 @@ void janet_collect(void) {
uint32_t i; uint32_t i;
if (janet_vm.gc_suspend) return; if (janet_vm.gc_suspend) return;
depth = JANET_RECURSION_GUARD; depth = JANET_RECURSION_GUARD;
/* Try and prevent many major collections back to back. janet_vm.gc_mark_phase = 1;
/* Try to prevent many major collections back to back.
* A full collection will take O(janet_vm.block_count) time. * A full collection will take O(janet_vm.block_count) time.
* If we have a large heap, make sure our interval is not too * If we have a large heap, make sure our interval is not too
* small so we won't make many collections over it. This is just a * small so we won't make many collections over it. This is just a
@ -456,6 +594,7 @@ void janet_collect(void) {
Janet x = janet_vm.roots[--janet_vm.root_count]; Janet x = janet_vm.roots[--janet_vm.root_count];
janet_mark(x); janet_mark(x);
} }
janet_vm.gc_mark_phase = 0;
janet_sweep(); janet_sweep();
janet_vm.next_collection = 0; janet_vm.next_collection = 0;
janet_free_all_scratch(); janet_free_all_scratch();
@ -559,7 +698,9 @@ void janet_gcunlock(int handle) {
janet_vm.gc_suspend = handle; janet_vm.gc_suspend = handle;
} }
/* Scratch memory API */ /* Scratch memory API
* Scratch memory allocations do not need to be free (but optionally can be), and will be automatically cleaned
* up in the next call to janet_collect. */
void *janet_smalloc(size_t size) { void *janet_smalloc(size_t size) {
JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size); JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size);

View File

@ -57,6 +57,10 @@ enum JanetMemoryType {
JANET_MEMORY_FUNCENV, JANET_MEMORY_FUNCENV,
JANET_MEMORY_FUNCDEF, JANET_MEMORY_FUNCDEF,
JANET_MEMORY_THREADED_ABSTRACT, JANET_MEMORY_THREADED_ABSTRACT,
JANET_MEMORY_TABLE_WEAKK,
JANET_MEMORY_TABLE_WEAKV,
JANET_MEMORY_TABLE_WEAKKV,
JANET_MEMORY_ARRAY_WEAK
}; };
/* To allocate collectable memory, one must call janet_alloc, initialize the memory, /* To allocate collectable memory, one must call janet_alloc, initialize the memory,

View File

@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) {
static void it_s64_tostring(void *p, JanetBuffer *buffer) { static void it_s64_tostring(void *p, JanetBuffer *buffer) {
char str[32]; char str[32];
sprintf(str, "%" PRId64, *((int64_t *)p)); snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p));
janet_buffer_push_cstring(buffer, str); janet_buffer_push_cstring(buffer, str);
} }
static void it_u64_tostring(void *p, JanetBuffer *buffer) { static void it_u64_tostring(void *p, JanetBuffer *buffer) {
char str[32]; char str[32];
sprintf(str, "%" PRIu64, *((uint64_t *)p)); snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p));
janet_buffer_push_cstring(buffer, str); janet_buffer_push_cstring(buffer, str);
} }
@ -239,7 +239,7 @@ JANET_CORE_FN(cfun_to_bytes,
"Write the bytes of an `int/s64` or `int/u64` into a buffer.\n" "Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
"The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n" "The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
"Returns the modified buffer.\n" "Returns the modified buffer.\n"
"The `endianness` paramater indicates the byte order:\n" "The `endianness` parameter indicates the byte order:\n"
"- `nil` (unset): system byte order\n" "- `nil` (unset): system byte order\n"
"- `:le`: little-endian, least significant byte first\n" "- `:le`: little-endian, least significant byte first\n"
"- `:be`: big-endian, most significant byte first\n") { "- `:be`: big-endian, most significant byte first\n") {

View File

@ -41,6 +41,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx);
static void *io_file_unmarshal(JanetMarshalContext *ctx); static void *io_file_unmarshal(JanetMarshalContext *ctx);
static Janet io_file_next(void *p, Janet key); static Janet io_file_next(void *p, Janet key);
#ifdef JANET_WINDOWS
#define ftell _ftelli64
#define fseek _fseeki64
#endif
const JanetAbstractType janet_file_type = { const JanetAbstractType janet_file_type = {
"core/file", "core/file",
cfun_io_gc, cfun_io_gc,
@ -126,12 +131,12 @@ JANET_CORE_FN(cfun_io_temp,
// XXX use mkostemp when we can to avoid CLOEXEC race. // XXX use mkostemp when we can to avoid CLOEXEC race.
FILE *tmp = tmpfile(); FILE *tmp = tmpfile();
if (!tmp) if (!tmp)
janet_panicf("unable to create temporary file - %s", strerror(errno)); janet_panicf("unable to create temporary file - %s", janet_strerror(errno));
return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY); return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
} }
JANET_CORE_FN(cfun_io_fopen, JANET_CORE_FN(cfun_io_fopen,
"(file/open path &opt mode)", "(file/open path &opt mode buffer-size)",
"Open a file. `path` is an absolute or relative path, and " "Open a file. `path` is an absolute or relative path, and "
"`mode` is a set of flags indicating the mode to open the file in. " "`mode` is a set of flags indicating the mode to open the file in. "
"`mode` is a keyword where each character represents a flag. If the file " "`mode` is a keyword where each character represents a flag. If the file "
@ -145,7 +150,7 @@ JANET_CORE_FN(cfun_io_fopen,
"* + - append to the file instead of overwriting it\n\n" "* + - append to the file instead of overwriting it\n\n"
"* n - error if the file cannot be opened instead of returning nil\n\n" "* n - error if the file cannot be opened instead of returning nil\n\n"
"See fopen (<stdio.h>, C99) for further details.") { "See fopen (<stdio.h>, C99) for further details.") {
janet_arity(argc, 1, 2); janet_arity(argc, 1, 3);
const uint8_t *fname = janet_getstring(argv, 0); const uint8_t *fname = janet_getstring(argv, 0);
const uint8_t *fmode; const uint8_t *fmode;
int32_t flags; int32_t flags;
@ -158,8 +163,17 @@ JANET_CORE_FN(cfun_io_fopen,
flags = JANET_FILE_READ; flags = JANET_FILE_READ;
} }
FILE *f = fopen((const char *)fname, (const char *)fmode); FILE *f = fopen((const char *)fname, (const char *)fmode);
if (f != NULL) {
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
if (bufsize != BUFSIZ) {
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
if (result) {
janet_panic("failed to set buffer size for file");
}
}
}
return f ? janet_makefile(f, flags) return f ? janet_makefile(f, flags)
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil()) : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
: janet_wrap_nil(); : janet_wrap_nil();
} }
@ -328,7 +342,7 @@ JANET_CORE_FN(cfun_io_fseek,
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED) if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed"); janet_panic("file is closed");
long int offset = 0; int64_t offset = 0;
int whence = SEEK_CUR; int whence = SEEK_CUR;
if (argc >= 2) { if (argc >= 2) {
const uint8_t *whence_sym = janet_getkeyword(argv, 1); const uint8_t *whence_sym = janet_getkeyword(argv, 1);
@ -342,7 +356,7 @@ JANET_CORE_FN(cfun_io_fseek,
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]); janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
} }
if (argc == 3) { if (argc == 3) {
offset = (long) janet_getinteger64(argv, 2); offset = (int64_t) janet_getinteger64(argv, 2);
} }
} }
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file"); if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
@ -356,7 +370,7 @@ JANET_CORE_FN(cfun_io_ftell,
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED) if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed"); janet_panic("file is closed");
long pos = ftell(iof->file); int64_t pos = ftell(iof->file);
if (pos == -1) janet_panic("error getting position in file"); if (pos == -1) janet_panic("error getting position in file");
return janet_wrap_number((double)pos); return janet_wrap_number((double)pos);
} }

View File

@ -185,6 +185,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags);
/* Prevent stack overflows */ /* Prevent stack overflows */
#define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow") #define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow")
/* Quick check if a fiber cannot be marshalled. This is will
* have no false positives, but may have false negatives. */
static int fiber_cannot_be_marshalled(JanetFiber *fiber) {
if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1;
int32_t i = fiber->frame;
while (i > 0) {
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
if (!frame->func) return 1; /* has cfunction on stack */
i = frame->prevframe;
}
return 0;
}
/* Marshal a function env */ /* Marshal a function env */
static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) { static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
MARSH_STACKCHECK; MARSH_STACKCHECK;
@ -197,7 +210,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
} }
janet_env_valid(env); janet_env_valid(env);
janet_v_push(st->seen_envs, env); janet_v_push(st->seen_envs, env);
if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) {
/* Special case for early detachment */
if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) {
pushint(st, 0); pushint(st, 0);
pushint(st, env->length); pushint(st, env->length);
Janet *values = env->as.fiber->data + env->offset; Janet *values = env->as.fiber->data + env->offset;
@ -328,7 +343,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
while (i > 0) { while (i > 0) {
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE); JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV; if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV;
if (!frame->func) janet_panic("cannot marshal fiber with c stackframe"); if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc));
pushint(st, frame->flags); pushint(st, frame->flags);
pushint(st, frame->prevframe); pushint(st, frame->prevframe);
int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode); int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode);
@ -1048,9 +1063,11 @@ static const uint8_t *unmarshal_one_fiber(
fiber->env = NULL; fiber->env = NULL;
fiber->last_value = janet_wrap_nil(); fiber->last_value = janet_wrap_nil();
#ifdef JANET_EV #ifdef JANET_EV
fiber->waiting = NULL;
fiber->sched_id = 0; fiber->sched_id = 0;
fiber->supervisor_channel = NULL; fiber->supervisor_channel = NULL;
fiber->ev_state = NULL;
fiber->ev_callback = NULL;
fiber->ev_stream = NULL;
#endif #endif
/* Push fiber to seen stack */ /* Push fiber to seen stack */
@ -1245,6 +1262,18 @@ void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
return p; return p;
} }
void *janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size) {
#ifdef JANET_THREADS
void *p = janet_abstract_threaded(ctx->at, size);
janet_unmarshal_abstract_reuse(ctx, p);
return p;
#else
(void) ctx;
(void) size;
janet_panic("threaded abstracts not supported");
#endif
}
static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) { static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) {
Janet key; Janet key;
data = unmarshal_one(st, data, &key, flags + 1); data = unmarshal_one(st, data, &key, flags + 1);

View File

@ -119,7 +119,7 @@ double janet_rng_double(JanetRNG *rng) {
JANET_CORE_FN(cfun_rng_make, JANET_CORE_FN(cfun_rng_make,
"(math/rng &opt seed)", "(math/rng &opt seed)",
"Creates a Psuedo-Random number generator, with an optional seed. " "Creates a Pseudo-Random number generator, with an optional seed. "
"The seed should be an unsigned 32 bit integer or a buffer. " "The seed should be an unsigned 32 bit integer or a buffer. "
"Do not use this for cryptography. Returns a core/rng abstract type." "Do not use this for cryptography. Returns a core/rng abstract type."
) { ) {
@ -349,6 +349,26 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
return janet_wrap_number(janet_lcm(x, y)); return janet_wrap_number(janet_lcm(x, y));
} }
JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)",
"Returns a tuple of (mantissa, exponent) from number.") {
janet_fixarity(argc, 1);
double x = janet_getnumber(argv, 0);
int exp;
x = frexp(x, &exp);
Janet *result = janet_tuple_begin(2);
result[0] = janet_wrap_number(x);
result[1] = janet_wrap_number((double) exp);
return janet_wrap_tuple(janet_tuple_end(result));
}
JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)",
"Creates a new number from a mantissa and an exponent.") {
janet_fixarity(argc, 2);
double x = janet_getnumber(argv, 0);
int32_t y = janet_getinteger(argv, 1);
return janet_wrap_number(ldexp(x, y));
}
/* Module entry point */ /* Module entry point */
void janet_lib_math(JanetTable *env) { void janet_lib_math(JanetTable *env) {
JanetRegExt math_cfuns[] = { JanetRegExt math_cfuns[] = {
@ -395,6 +415,8 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/next", janet_nextafter), JANET_CORE_REG("math/next", janet_nextafter),
JANET_CORE_REG("math/gcd", janet_cfun_gcd), JANET_CORE_REG("math/gcd", janet_cfun_gcd),
JANET_CORE_REG("math/lcm", janet_cfun_lcm), JANET_CORE_REG("math/lcm", janet_cfun_lcm),
JANET_CORE_REG("math/frexp", janet_cfun_frexp),
JANET_CORE_REG("math/ldexp", janet_cfun_ldexp),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, math_cfuns); janet_core_cfuns_ext(env, NULL, math_cfuns);
@ -411,11 +433,11 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN), JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
"The minimum contiguous integer representable by a 32 bit signed integer"); "The minimum contiguous integer representable by a 32 bit signed integer");
JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX), JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
"The maximum contiguous integer represtenable by a 32 bit signed integer"); "The maximum contiguous integer representable by a 32 bit signed integer");
JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE), JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
"The minimum contiguous integer representable by a double (2^53)"); "The minimum contiguous integer representable by a double (2^53)");
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE), JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
"The maximum contiguous integer represtenable by a double (-(2^53))"); "The maximum contiguous integer representable by a double (-(2^53))");
#ifdef NAN #ifdef NAN
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)"); JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
#else #else

View File

@ -24,6 +24,7 @@
#include "features.h" #include "features.h"
#include <janet.h> #include <janet.h>
#include "util.h" #include "util.h"
#include "fiber.h"
#endif #endif
#ifdef JANET_NET #ifdef JANET_NET
@ -78,12 +79,20 @@ const JanetAbstractType janet_address_type = {
/* maximum number of bytes in a socket address host (post name resolution) */ /* maximum number of bytes in a socket address host (post name resolution) */
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#ifdef JANET_NO_IPV6
#define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1)
#else
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1) #define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
#endif
typedef unsigned short in_port_t; typedef unsigned short in_port_t;
#else #else
#define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b)) #define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b))
#ifdef JANET_NO_IPV6
#define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
#else
#define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1) #define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
#endif #endif
#endif
static JanetStream *make_stream(JSock handle, uint32_t flags); static JanetStream *make_stream(JSock handle, uint32_t flags);
@ -111,12 +120,57 @@ static void janet_net_socknoblock(JSock s) {
#endif #endif
} }
/* State machine for async connect */
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
JanetStream *stream = fiber->ev_stream;
switch (event) {
default:
break;
#ifndef JANET_WINDOWS
/* Wait until we have an actual event before checking.
* Windows doesn't support async connect with this, just try immediately.*/
case JANET_ASYNC_EVENT_INIT:
#endif
case JANET_ASYNC_EVENT_DEINIT:
return;
case JANET_ASYNC_EVENT_CLOSE:
janet_cancel(fiber, janet_cstringv("stream closed"));
janet_async_end(fiber);
return;
}
#ifdef JANET_WINDOWS
int res = 0;
int size = sizeof(res);
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
#else
int res = 0;
socklen_t size = sizeof res;
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
#endif
if (r == 0) {
if (res == 0) {
janet_schedule(fiber, janet_wrap_abstract(stream));
} else {
janet_cancel(fiber, janet_cstringv(janet_strerror(res)));
stream->flags |= JANET_STREAM_TOCLOSE;
}
} else {
janet_cancel(fiber, janet_ev_lasterr());
stream->flags |= JANET_STREAM_TOCLOSE;
}
janet_async_end(fiber);
}
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
}
/* State machine for accepting connections. */ /* State machine for accepting connections. */
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
typedef struct { typedef struct {
JanetListenerState head;
WSAOVERLAPPED overlapped; WSAOVERLAPPED overlapped;
JanetFunction *function; JanetFunction *function;
JanetStream *lstream; JanetStream *lstream;
@ -124,10 +178,10 @@ typedef struct {
char buf[1024]; char buf[1024];
} NetStateAccept; } NetStateAccept;
static int net_sched_accept_impl(NetStateAccept *state, Janet *err); static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err);
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) { void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
NetStateAccept *state = (NetStateAccept *)s; NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
switch (event) { switch (event) {
default: default:
break; break;
@ -138,55 +192,60 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
break; break;
} }
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
case JANET_ASYNC_EVENT_COMPLETE: { case JANET_ASYNC_EVENT_COMPLETE: {
if (state->astream->flags & JANET_STREAM_CLOSED) { if (state->astream->flags & JANET_STREAM_CLOSED) {
janet_cancel(s->fiber, janet_cstringv("failed to accept connection")); janet_cancel(fiber, janet_cstringv("failed to accept connection"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
SOCKET lsock = (SOCKET) state->lstream->handle; SOCKET lsock = (SOCKET) state->lstream->handle;
if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char *) &lsock, sizeof(lsock))) { (char *) &lsock, sizeof(lsock))) {
janet_cancel(s->fiber, janet_cstringv("failed to accept connection")); janet_cancel(fiber, janet_cstringv("failed to accept connection"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
Janet streamv = janet_wrap_abstract(state->astream); Janet streamv = janet_wrap_abstract(state->astream);
if (state->function) { if (state->function) {
/* Schedule worker */ /* Schedule worker */
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv); JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
fiber->supervisor_channel = s->fiber->supervisor_channel; sub_fiber->supervisor_channel = fiber->supervisor_channel;
janet_schedule(fiber, janet_wrap_nil()); janet_schedule(sub_fiber, janet_wrap_nil());
/* Now listen again for next connection */ /* Now listen again for next connection */
Janet err; Janet err;
if (net_sched_accept_impl(state, &err)) { if (net_sched_accept_impl(state, fiber, &err)) {
janet_cancel(s->fiber, err); janet_cancel(fiber, err);
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
} else { } else {
janet_schedule(s->fiber, streamv); janet_schedule(fiber, streamv);
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
} }
} }
return JANET_ASYNC_STATUS_NOT_DONE;
} }
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) { JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
Janet err; Janet err;
JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
NetStateAccept *state = (NetStateAccept *)s;
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED)); memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
memset(&state->buf, 0, 1024); memset(&state->buf, 0, 1024);
state->function = fun; state->function = fun;
state->lstream = stream; state->lstream = stream;
s->tag = &state->overlapped; if (net_sched_accept_impl(state, janet_root_fiber(), &err)) {
if (net_sched_accept_impl(state, &err)) janet_panicv(err); janet_free(state);
janet_await(); janet_panicv(err);
}
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
} }
static int net_sched_accept_impl(NetStateAccept *state, Janet *err) { static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) {
SOCKET lsock = (SOCKET) state->lstream->handle; SOCKET lsock = (SOCKET) state->lstream->handle;
SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (asock == INVALID_SOCKET) { if (asock == INVALID_SOCKET) {
@ -198,7 +257,11 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
int socksize = sizeof(SOCKADDR_STORAGE) + 16; int socksize = sizeof(SOCKADDR_STORAGE) + 16;
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) { if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
int code = WSAGetLastError(); int code = WSAGetLastError();
if (code == WSA_IO_PENDING) return 0; /* indicates io is happening async */ if (code == WSA_IO_PENDING) {
/* indicates io is happening async */
janet_async_in_flight(fiber);
return 0;
}
*err = janet_ev_lasterr(); *err = janet_ev_lasterr();
return 1; return 1;
} }
@ -208,12 +271,12 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
#else #else
typedef struct { typedef struct {
JanetListenerState head;
JanetFunction *function; JanetFunction *function;
} NetStateAccept; } NetStateAccept;
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) { void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
NetStateAccept *state = (NetStateAccept *)s; JanetStream *stream = fiber->ev_stream;
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
switch (event) { switch (event) {
default: default:
break; break;
@ -222,38 +285,42 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
break; break;
} }
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
case JANET_ASYNC_EVENT_INIT:
case JANET_ASYNC_EVENT_READ: { case JANET_ASYNC_EVENT_READ: {
#if defined(JANET_LINUX) #if defined(JANET_LINUX)
JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC); JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
#else #else
/* On BSDs, CLOEXEC should be inherited from server socket */ /* On BSDs, CLOEXEC should be inherited from server socket */
JSock connfd = accept(s->stream->handle, NULL, NULL); JSock connfd = accept(stream->handle, NULL, NULL);
#endif #endif
if (JSOCKVALID(connfd)) { if (JSOCKVALID(connfd)) {
janet_net_socknoblock(connfd); janet_net_socknoblock(connfd);
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE); JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
Janet streamv = janet_wrap_abstract(stream); Janet streamv = janet_wrap_abstract(stream);
if (state->function) { if (state->function) {
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv); JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
fiber->supervisor_channel = s->fiber->supervisor_channel; sub_fiber->supervisor_channel = fiber->supervisor_channel;
janet_schedule(fiber, janet_wrap_nil()); janet_schedule(sub_fiber, janet_wrap_nil());
} else { } else {
janet_schedule(s->fiber, streamv); janet_schedule(fiber, streamv);
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
} }
break; break;
} }
} }
return JANET_ASYNC_STATUS_NOT_DONE;
} }
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) { JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
NetStateAccept *state = (NetStateAccept *) janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
memset(state, 0, sizeof(NetStateAccept));
state->function = fun; state->function = fun;
janet_await(); if (fun) janet_stream_level_triggered(stream);
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
} }
#endif #endif
@ -496,7 +563,7 @@ JANET_CORE_FN(cfun_net_connect,
} }
#endif #endif
if (status != 0) { if (status) {
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
if (err != WSAEWOULDBLOCK) { if (err != WSAEWOULDBLOCK) {
#else #else
@ -508,10 +575,7 @@ JANET_CORE_FN(cfun_net_connect,
} }
} }
/* Handle the connect() result in the event loop*/ net_sched_connect(stream);
janet_ev_connect(stream, MSG_NOSIGNAL);
janet_await();
} }
static const char *serverify_socket(JSock sfd) { static const char *serverify_socket(JSock sfd) {
@ -682,6 +746,7 @@ static Janet janet_so_getname(const void *sa_any) {
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))}; Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))};
return janet_wrap_tuple(janet_tuple_n(pair, 2)); return janet_wrap_tuple(janet_tuple_n(pair, 2));
} }
#ifndef JANET_NO_IPV6
case AF_INET6: { case AF_INET6: {
const struct sockaddr_in6 *sai6 = sa_any; const struct sockaddr_in6 *sai6 = sa_any;
if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) { if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) {
@ -690,6 +755,7 @@ static Janet janet_so_getname(const void *sa_any) {
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))}; Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))};
return janet_wrap_tuple(janet_tuple_n(pair, 2)); return janet_wrap_tuple(janet_tuple_n(pair, 2));
} }
#endif
#ifndef JANET_WINDOWS #ifndef JANET_WINDOWS
case AF_UNIX: { case AF_UNIX: {
const struct sockaddr_un *sun = sa_any; const struct sockaddr_un *sun = sa_any;
@ -756,6 +822,7 @@ JANET_CORE_FN(cfun_stream_accept_loop,
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type); JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET); janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET);
JanetFunction *fun = janet_getfunction(argv, 1); JanetFunction *fun = janet_getfunction(argv, 1);
if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument");
janet_sched_accept(stream, fun); janet_sched_accept(stream, fun);
} }
@ -792,7 +859,6 @@ JANET_CORE_FN(cfun_stream_read,
if (to != INFINITY) janet_addtimeout(to); if (to != INFINITY) janet_addtimeout(to);
janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL); janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL);
} }
janet_await();
} }
JANET_CORE_FN(cfun_stream_chunk, JANET_CORE_FN(cfun_stream_chunk,
@ -807,7 +873,6 @@ JANET_CORE_FN(cfun_stream_chunk,
double to = janet_optnumber(argv, argc, 3, INFINITY); double to = janet_optnumber(argv, argc, 3, INFINITY);
if (to != INFINITY) janet_addtimeout(to); if (to != INFINITY) janet_addtimeout(to);
janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL); janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL);
janet_await();
} }
JANET_CORE_FN(cfun_stream_recv_from, JANET_CORE_FN(cfun_stream_recv_from,
@ -822,7 +887,6 @@ JANET_CORE_FN(cfun_stream_recv_from,
double to = janet_optnumber(argv, argc, 3, INFINITY); double to = janet_optnumber(argv, argc, 3, INFINITY);
if (to != INFINITY) janet_addtimeout(to); if (to != INFINITY) janet_addtimeout(to);
janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL); janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL);
janet_await();
} }
JANET_CORE_FN(cfun_stream_write, JANET_CORE_FN(cfun_stream_write,
@ -842,7 +906,6 @@ JANET_CORE_FN(cfun_stream_write,
if (to != INFINITY) janet_addtimeout(to); if (to != INFINITY) janet_addtimeout(to);
janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL); janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL);
} }
janet_await();
} }
JANET_CORE_FN(cfun_stream_send_to, JANET_CORE_FN(cfun_stream_send_to,
@ -863,7 +926,6 @@ JANET_CORE_FN(cfun_stream_send_to,
if (to != INFINITY) janet_addtimeout(to); if (to != INFINITY) janet_addtimeout(to);
janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL); janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL);
} }
janet_await();
} }
JANET_CORE_FN(cfun_stream_flush, JANET_CORE_FN(cfun_stream_flush,
@ -897,8 +959,10 @@ static const struct sockopt_type sockopt_type_list[] = {
{ "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER }, { "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
{ "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER }, { "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
{ "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER }, { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
#ifndef JANET_NO_IPV6
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER }, { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER }, { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
#endif
{ NULL, 0, 0, JANET_POINTER } { NULL, 0, 0, JANET_POINTER }
}; };
@ -935,7 +999,9 @@ JANET_CORE_FN(cfun_net_setsockopt,
union { union {
int v_int; int v_int;
struct ip_mreq v_mreq; struct ip_mreq v_mreq;
#ifndef JANET_NO_IPV6
struct ipv6_mreq v_mreq6; struct ipv6_mreq v_mreq6;
#endif
} val; } val;
void *optval = (void *)&val; void *optval = (void *)&val;
@ -953,12 +1019,14 @@ JANET_CORE_FN(cfun_net_setsockopt,
val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY); val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr); inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
optlen = sizeof(val.v_mreq); optlen = sizeof(val.v_mreq);
#ifndef JANET_NO_IPV6
} else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) { } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
const char *addr = janet_getcstring(argv, 2); const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq6, 0, sizeof val.v_mreq6); memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
val.v_mreq6.ipv6mr_interface = 0; val.v_mreq6.ipv6mr_interface = 0;
inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr); inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
optlen = sizeof(val.v_mreq6); optlen = sizeof(val.v_mreq6);
#endif
} else { } else {
janet_panicf("invalid socket option type"); janet_panicf("invalid socket option type");
} }
@ -967,7 +1035,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen); int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
if (r == -1) { if (r == -1) {
janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno)); janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno));
} }
return janet_wrap_nil(); return janet_wrap_nil();

View File

@ -38,6 +38,7 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <signal.h> #include <signal.h>
#include <locale.h>
#ifdef JANET_BSD #ifdef JANET_BSD
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -229,10 +230,11 @@ JANET_CORE_FN(os_compiler,
#undef janet_stringify #undef janet_stringify
JANET_CORE_FN(os_exit, JANET_CORE_FN(os_exit,
"(os/exit &opt x)", "(os/exit &opt x force)",
"Exit from janet with an exit code equal to x. If x is not an integer, " "Exit from janet with an exit code equal to x. If x is not an integer, "
"the exit with status equal the hash of x.") { "the exit with status equal the hash of x. If `force` is truthy will exit immediately and "
janet_arity(argc, 0, 1); "skip cleanup code.") {
janet_arity(argc, 0, 2);
int status; int status;
if (argc == 0) { if (argc == 0) {
status = EXIT_SUCCESS; status = EXIT_SUCCESS;
@ -242,7 +244,11 @@ JANET_CORE_FN(os_exit,
status = EXIT_FAILURE; status = EXIT_FAILURE;
} }
janet_deinit(); janet_deinit();
exit(status); if (argc >= 2 && janet_truthy(argv[1])) {
_exit(status);
} else {
exit(status);
}
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -500,8 +506,11 @@ static int proc_get_status(JanetProc *proc) {
status = WEXITSTATUS(status); status = WEXITSTATUS(status);
} else if (WIFSTOPPED(status)) { } else if (WIFSTOPPED(status)) {
status = WSTOPSIG(status) + 128; status = WSTOPSIG(status) + 128;
} else { } else if (WIFSIGNALED(status)) {
status = WTERMSIG(status) + 128; status = WTERMSIG(status) + 128;
} else {
/* Could possibly return -1 but for now, just panic */
janet_panicf("Undefined status code for process termination, %d.", status);
} }
return status; return status;
} }
@ -517,7 +526,6 @@ static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) {
/* Callback that is called in main thread when subroutine completes. */ /* Callback that is called in main thread when subroutine completes. */
static void janet_proc_wait_cb(JanetEVGenericMessage args) { static void janet_proc_wait_cb(JanetEVGenericMessage args) {
janet_ev_dec_refcount();
JanetProc *proc = (JanetProc *) args.argp; JanetProc *proc = (JanetProc *) args.argp;
if (NULL != proc) { if (NULL != proc) {
int status = args.tag; int status = args.tag;
@ -530,7 +538,9 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
JanetString s = janet_formatc("command failed with non-zero exit code %d", status); JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
janet_cancel(args.fiber, janet_wrap_string(s)); janet_cancel(args.fiber, janet_wrap_string(s));
} else { } else {
janet_schedule(args.fiber, janet_wrap_integer(status)); if (janet_fiber_can_resume(args.fiber)) {
janet_schedule(args.fiber, janet_wrap_integer(status));
}
} }
} }
} }
@ -612,7 +622,11 @@ os_proc_wait_impl(JanetProc *proc) {
JANET_CORE_FN(os_proc_wait, JANET_CORE_FN(os_proc_wait,
"(os/proc-wait proc)", "(os/proc-wait proc)",
"Block until the subprocess completes. Returns the subprocess return code.") { "Suspend the current fiber until the subprocess completes. Returns the subprocess return code. "
"os/proc-wait cannot be called twice on the same process. If `ev/with-deadline` cancels `os/proc-wait` "
"with an error or os/proc-wait is cancelled with any error caused by anything else, os/proc-wait still "
"finishes in the background. Only after os/proc-wait finishes, a process is cleaned up by the operating "
"system. Thus, a process becomes a zombie process if os/proc-wait is not called.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
#ifdef JANET_EV #ifdef JANET_EV
@ -641,7 +655,7 @@ static const struct keyword_signal signal_keywords[] = {
#ifdef SIGTERM #ifdef SIGTERM
{"term", SIGTERM}, {"term", SIGTERM},
#endif #endif
#ifdef SIGARLM #ifdef SIGALRM
{"alrm", SIGALRM}, {"alrm", SIGALRM},
#endif #endif
#ifdef SIGHUP #ifdef SIGHUP
@ -706,15 +720,28 @@ static const struct keyword_signal signal_keywords[] = {
#endif #endif
{NULL, 0}, {NULL, 0},
}; };
static int get_signal_kw(const Janet *argv, int32_t n) {
JanetKeyword signal_kw = janet_getkeyword(argv, n);
const struct keyword_signal *ptr = signal_keywords;
while (ptr->keyword) {
if (!janet_cstrcmp(signal_kw, ptr->keyword)) {
return ptr->signal;
}
ptr++;
}
janet_panicf("undefined signal %v", argv[n]);
}
#endif #endif
JANET_CORE_FN(os_proc_kill, JANET_CORE_FN(os_proc_kill,
"(os/proc-kill proc &opt wait signal)", "(os/proc-kill proc &opt wait signal)",
"Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process " "Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
"handle on windows. If `wait` is truthy, will wait for the process to finish and " "handle on windows. If os/proc-wait already finished for proc, os/proc-kill raises an error. After "
"returns the exit code. Otherwise, returns `proc`. If signal is specified send it instead." "sending signal to proc, if `wait` is truthy, will wait for the process to finish and return the exit "
"Signal keywords are named after their C counterparts but in lowercase with the leading " "code by calling os/proc-wait. Otherwise, returns `proc`. If signal is specified, send it instead. "
"`SIG` stripped. Signals are ignored on windows.") { "Signal keywords are named after their C counterparts but in lowercase with the leading `SIG` stripped. "
"Signals are ignored on windows.") {
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
if (proc->flags & JANET_PROC_WAITED) { if (proc->flags & JANET_PROC_WAITED) {
@ -731,22 +758,11 @@ JANET_CORE_FN(os_proc_kill,
#else #else
int signal = -1; int signal = -1;
if (argc == 3) { if (argc == 3) {
JanetKeyword signal_kw = janet_getkeyword(argv, 2); signal = get_signal_kw(argv, 2);
const struct keyword_signal *ptr = signal_keywords;
while (ptr->keyword) {
if (!janet_cstrcmp(signal_kw, ptr->keyword)) {
signal = ptr->signal;
break;
}
ptr++;
}
if (signal == -1) {
janet_panic("undefined signal");
}
} }
int status = kill(proc->pid, signal == -1 ? SIGKILL : signal); int status = kill(proc->pid, signal == -1 ? SIGKILL : signal);
if (status) { if (status) {
janet_panic(strerror(errno)); janet_panic(janet_strerror(errno));
} }
#endif #endif
/* After killing process we wait on it. */ /* After killing process we wait on it. */
@ -764,8 +780,9 @@ JANET_CORE_FN(os_proc_kill,
JANET_CORE_FN(os_proc_close, JANET_CORE_FN(os_proc_close,
"(os/proc-close proc)", "(os/proc-close proc)",
"Wait on a process if it has not been waited on, and close pipes created by `os/spawn` " "Close pipes created by `os/spawn` if they have not been closed. Then, if os/proc-wait was not already "
"if they have not been closed. Returns nil.") { "called on proc, os/proc-wait is called on it, and it returns the exit code returned by os/proc-wait. "
"Otherwise, returns nil.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
#ifdef JANET_EV #ifdef JANET_EV
@ -803,6 +820,106 @@ static void close_handle(JanetHandle handle) {
#endif #endif
} }
#ifdef JANET_EV
#ifndef JANET_WINDOWS
static void janet_signal_callback(JanetEVGenericMessage msg) {
int sig = msg.tag;
if (msg.argi) janet_interpreter_interrupt_handled(NULL);
Janet handlerv = janet_table_get(&janet_vm.signal_handlers, janet_wrap_integer(sig));
if (!janet_checktype(handlerv, JANET_FUNCTION)) {
/* Let another thread/process try to handle this */
sigset_t set;
sigemptyset(&set);
sigaddset(&set, sig);
#ifdef JANET_THREADS
pthread_sigmask(SIG_BLOCK, &set, NULL);
#else
sigprocmask(SIG_BLOCK, &set, NULL);
#endif
raise(sig);
return;
}
JanetFunction *handler = janet_unwrap_function(handlerv);
JanetFiber *fiber = janet_fiber(handler, 64, 0, NULL);
janet_schedule_soon(fiber, janet_wrap_nil(), JANET_SIGNAL_OK);
}
static void janet_signal_trampoline_no_interrupt(int sig) {
/* Do not interact with global janet state here except for janet_ev_post_event, unsafe! */
JanetEVGenericMessage msg;
memset(&msg, 0, sizeof(msg));
msg.tag = sig;
janet_ev_post_event(&janet_vm, janet_signal_callback, msg);
}
static void janet_signal_trampoline(int sig) {
/* Do not interact with global janet state here except for janet_ev_post_event, unsafe! */
JanetEVGenericMessage msg;
memset(&msg, 0, sizeof(msg));
msg.tag = sig;
msg.argi = 1;
janet_interpreter_interrupt(NULL);
janet_ev_post_event(&janet_vm, janet_signal_callback, msg);
}
#endif
JANET_CORE_FN(os_sigaction,
"(os/sigaction which &opt handler interrupt-interpreter)",
"Add a signal handler for a given action. Use nil for the `handler` argument to remove a signal handler. "
"All signal handlers are the same as supported by `os/proc-kill`.") {
janet_sandbox_assert(JANET_SANDBOX_SIGNAL);
janet_arity(argc, 1, 3);
#ifdef JANET_WINDOWS
(void) argv;
janet_panic("unsupported on this platform");
#else
/* TODO - per thread signal masks */
int rc;
int sig = get_signal_kw(argv, 0);
JanetFunction *handler = janet_optfunction(argv, argc, 1, NULL);
int can_interrupt = janet_optboolean(argv, argc, 2, 0);
Janet oldhandler = janet_table_get(&janet_vm.signal_handlers, janet_wrap_integer(sig));
if (!janet_checktype(oldhandler, JANET_NIL)) {
janet_gcunroot(oldhandler);
}
if (NULL != handler) {
Janet handlerv = janet_wrap_function(handler);
janet_gcroot(handlerv);
janet_table_put(&janet_vm.signal_handlers, janet_wrap_integer(sig), handlerv);
} else {
janet_table_put(&janet_vm.signal_handlers, janet_wrap_integer(sig), janet_wrap_nil());
}
struct sigaction action;
sigset_t mask;
sigaddset(&mask, sig);
memset(&action, 0, sizeof(action));
action.sa_flags |= SA_RESTART;
if (can_interrupt) {
#ifdef JANET_NO_INTERPRETER_INTERRUPT
janet_panic("interpreter interrupt not enabled");
#else
action.sa_handler = janet_signal_trampoline;
#endif
} else {
action.sa_handler = janet_signal_trampoline_no_interrupt;
}
action.sa_mask = mask;
RETRY_EINTR(rc, sigaction(sig, &action, NULL));
sigset_t set;
sigemptyset(&set);
sigaddset(&set, sig);
#ifdef JANET_THREADS
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
#else
sigprocmask(SIG_UNBLOCK, &set, NULL);
#endif
return janet_wrap_nil();
#endif
}
#endif
/* Create piped file for os/execute and os/spawn. Need to be careful that we mark /* Create piped file for os/execute and os/spawn. Need to be careful that we mark
the error flag if we can't create pipe and don't leak handles. *handle will be cleaned the error flag if we can't create pipe and don't leak handles. *handle will be cleaned
up by the calling function. If everything goes well, *handle is owned by the calling function, up by the calling function. If everything goes well, *handle is owned by the calling function,
@ -982,11 +1099,18 @@ static JanetFile *get_stdio_for_handle(JanetHandle handle, void *orig, int iswri
} }
#endif #endif
static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { typedef enum {
JANET_EXECUTE_EXECUTE,
JANET_EXECUTE_SPAWN,
JANET_EXECUTE_EXEC
} JanetExecuteMode;
static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
/* Get flags */ /* Get flags */
int is_spawn = mode == JANET_EXECUTE_SPAWN;
uint64_t flags = 0; uint64_t flags = 0;
if (argc > 1) { if (argc > 1) {
flags = janet_getflags(argv, 1, "epxd"); flags = janet_getflags(argv, 1, "epxd");
@ -1010,7 +1134,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0; int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0;
/* Get optional redirections */ /* Get optional redirections */
if (argc > 2) { if (argc > 2 && (mode != JANET_EXECUTE_EXEC)) {
JanetDictView tab = janet_getdictionary(argv, 2); JanetDictView tab = janet_getdictionary(argv, 2);
Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in")); Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in"));
Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out")); Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out"));
@ -1131,12 +1255,32 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
* of posix_spawn would modify the argv array passed in. */ * of posix_spawn would modify the argv array passed in. */
char *const *cargv = (char *const *)child_argv; char *const *cargv = (char *const *)child_argv;
/* Use posix_spawn to spawn new process */
if (use_environ) { if (use_environ) {
janet_lock_environ(); janet_lock_environ();
} }
/* exec mode */
if (mode == JANET_EXECUTE_EXEC) {
#ifdef JANET_WINDOWS
janet_panic("not supported on windows");
#else
int status;
if (!use_environ) {
environ = envp;
}
do {
if (janet_flag_at(flags, 1)) {
status = execvp(cargv[0], cargv);
} else {
status = execv(cargv[0], cargv);
}
} while (status == -1 && errno == EINTR);
janet_panicf("%p: %s", cargv[0], janet_strerror(errno ? errno : ENOENT));
#endif
}
/* Use posix_spawn to spawn new process */
/* Posix spawn setup */ /* Posix spawn setup */
posix_spawn_file_actions_t actions; posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_init(&actions);
@ -1188,7 +1332,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
os_execute_cleanup(envp, child_argv); os_execute_cleanup(envp, child_argv);
if (status) { if (status) {
/* correct for macos bug where errno is not set */ /* correct for macos bug where errno is not set */
janet_panicf("%p: %s", argv[0], strerror(errno ? errno : ENOENT)); janet_panicf("%p: %s", argv[0], janet_strerror(errno ? errno : ENOENT));
} }
#endif #endif
@ -1243,22 +1387,63 @@ JANET_CORE_FN(os_execute,
"* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n" "* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n"
"`env` is a table or struct mapping environment variables to values. It can also " "`env` is a table or struct mapping environment variables to values. It can also "
"contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. " "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
"These arguments should be core/file values. " ":in, :out, and :err should be core/file values or core/stream values. core/file values and core/stream "
"Returns the exit status of the program.") { "values passed to :in, :out, and :err should be closed manually because os/execute doesn't close them. "
return os_execute_impl(argc, argv, 0); "Returns the exit code of the program.") {
return os_execute_impl(argc, argv, JANET_EXECUTE_EXECUTE);
} }
JANET_CORE_FN(os_spawn, JANET_CORE_FN(os_spawn,
"(os/spawn args &opt flags env)", "(os/spawn args &opt flags env)",
"Execute a program on the system and return a handle to the process. Otherwise, takes the " "Execute a program on the system and return a handle to the process. Otherwise, takes the "
"same arguments as `os/execute`. Does not wait for the process. " "same arguments as `os/execute`. Does not wait for the process. For each of the :in, :out, and :err keys "
"For each of the :in, :out, and :err keys to the `env` argument, one " "of the `env` argument, one can also pass in the keyword `:pipe` to get streams for standard IO of the "
"can also pass in the keyword `:pipe` " "subprocess that can be read from and written to. The returned value `proc` has the fields :in, :out, "
"to get streams for standard IO of the subprocess that can be read from and written to. " ":err, and the additional field :pid on unix-like platforms. `(os/proc-wait proc)` must be called to "
"The returned value `proc` has the fields :in, :out, :err, :return-code, and " "rejoin the subprocess. After `(os/proc-wait proc)` finishes, proc gains a new field, :return-code. "
"the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the " "If :x flag is passed to os/spawn, non-zero exit code will cause os/proc-wait to raise an error. "
"subprocess or `(os/proc-kill proc)`.") { "If pipe streams created with :pipe keyword are not closed in time, janet can run out of file "
return os_execute_impl(argc, argv, 1); "descriptors. They can be closed individually, or `os/proc-close` can close all pipe streams on proc. "
"If pipe streams aren't read before `os/proc-wait` finishes, then pipe buffers become full, and the "
"process cannot finish because the process cannot print more on pipe buffers which are already full. "
"If the process cannot finish, os/proc-wait cannot finish, either.") {
return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN);
}
JANET_CORE_FN(os_posix_exec,
"(os/posix-exec args &opt flags env)",
"Use the execvpe or execve system calls to replace the current process with an interface similar to os/execute. "
"Hoever, instead of creating a subprocess, the current process is replaced. Is not supported on windows, and "
"does not allow redirection of stdio.") {
return os_execute_impl(argc, argv, JANET_EXECUTE_EXEC);
}
JANET_CORE_FN(os_posix_fork,
"(os/posix-fork)",
"Make a `fork` system call and create a new process. Return nil if in the new process, otherwise a core/process object (as returned by os/spawn). "
"Not supported on all systems (POSIX only).") {
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
janet_fixarity(argc, 0);
(void) argv;
#ifdef JANET_WINDOWS
janet_panic("not supported");
#else
pid_t result;
do {
result = fork();
} while (result == -1 && errno == EINTR);
if (result == -1) {
janet_panic(janet_strerror(errno));
}
if (result) {
JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
memset(proc, 0, sizeof(JanetProc));
proc->pid = result;
proc->flags = JANET_PROC_ALLOW_ZOMBIE;
return janet_wrap_abstract(proc);
}
return janet_wrap_nil();
#endif
} }
#ifdef JANET_EV #ifdef JANET_EV
@ -1334,8 +1519,8 @@ JANET_CORE_FN(os_getenv,
janet_sandbox_assert(JANET_SANDBOX_ENV); janet_sandbox_assert(JANET_SANDBOX_ENV);
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
const char *cstr = janet_getcstring(argv, 0); const char *cstr = janet_getcstring(argv, 0);
const char *res = getenv(cstr);
janet_lock_environ(); janet_lock_environ();
const char *res = getenv(cstr);
Janet ret = res Janet ret = res
? janet_cstringv(res) ? janet_cstringv(res)
: argc == 2 : argc == 2
@ -1380,34 +1565,51 @@ JANET_CORE_FN(os_time,
} }
JANET_CORE_FN(os_clock, JANET_CORE_FN(os_clock,
"(os/clock &opt source)", "(os/clock &opt source format)",
"Return the number of whole + fractional seconds of the requested clock source.\n\n" "Return the current time of the requested clock source.\n\n"
"The `source` argument selects the clock source to use, when not specified the default " "The `source` argument selects the clock source to use, when not specified the default "
"is `:realtime`:\n" "is `:realtime`:\n"
"- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous " "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous "
" jumps in the system time\n" " jumps in the system time\n"
"- :monotonic: Return the number of whole + fractional seconds since some fixed point in " "- :monotonic: Return the number of whole + fractional seconds since some fixed point in "
" time. The clock is guaranteed to be non-decreasing in real time.\n" " time. The clock is guaranteed to be non-decreasing in real time.\n"
"- :cputime: Return the CPU time consumed by this process (i.e. all threads in the process)\n") { "- :cputime: Return the CPU time consumed by this process (i.e. all threads in the process)\n"
"The `format` argument selects the type of output, when not specified the default is `:double`:\n"
"- :double: Return the number of seconds + fractional seconds as a double\n"
"- :int: Return the number of seconds as an integer\n"
"- :tuple: Return a 2 integer tuple [seconds, nanoseconds]\n") {
enum JanetTimeSource source;
janet_sandbox_assert(JANET_SANDBOX_HRTIME); janet_sandbox_assert(JANET_SANDBOX_HRTIME);
janet_arity(argc, 0, 1); janet_arity(argc, 0, 2);
enum JanetTimeSource source = JANET_TIME_REALTIME;
if (argc == 1) { JanetKeyword sourcestr = janet_optkeyword(argv, argc, 0, (const uint8_t *) "realtime");
JanetKeyword sourcestr = janet_getkeyword(argv, 0); if (janet_cstrcmp(sourcestr, "realtime") == 0) {
if (janet_cstrcmp(sourcestr, "realtime") == 0) { source = JANET_TIME_REALTIME;
source = JANET_TIME_REALTIME; } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) {
} else if (janet_cstrcmp(sourcestr, "monotonic") == 0) { source = JANET_TIME_MONOTONIC;
source = JANET_TIME_MONOTONIC; } else if (janet_cstrcmp(sourcestr, "cputime") == 0) {
} else if (janet_cstrcmp(sourcestr, "cputime") == 0) { source = JANET_TIME_CPUTIME;
source = JANET_TIME_CPUTIME; } else {
} else { janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]);
janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]);
}
} }
struct timespec tv; struct timespec tv;
if (janet_gettime(&tv, source)) janet_panic("could not get time"); if (janet_gettime(&tv, source)) janet_panic("could not get time");
double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
return janet_wrap_number(dtime); JanetKeyword formatstr = janet_optkeyword(argv, argc, 1, (const uint8_t *) "double");
if (janet_cstrcmp(formatstr, "double") == 0) {
double dtime = (double)(tv.tv_sec + (tv.tv_nsec / 1E9));
return janet_wrap_number(dtime);
} else if (janet_cstrcmp(formatstr, "int") == 0) {
return janet_wrap_number((double)(tv.tv_sec));
} else if (janet_cstrcmp(formatstr, "tuple") == 0) {
Janet tup[2] = {janet_wrap_number((double)tv.tv_sec),
janet_wrap_number((double)tv.tv_nsec)
};
return janet_wrap_tuple(janet_tuple_n(tup, 2));
} else {
janet_panicf("expected :double, :int, or :tuple, got %v", argv[1]);
}
} }
JANET_CORE_FN(os_sleep, JANET_CORE_FN(os_sleep,
@ -1443,7 +1645,7 @@ JANET_CORE_FN(os_isatty,
return janet_wrap_boolean(_isatty(fd)); return janet_wrap_boolean(_isatty(fd));
#else #else
int fd = fileno(f); int fd = fileno(f);
if (fd == -1) janet_panic(strerror(errno)); if (fd == -1) janet_panic(janet_strerror(errno));
return janet_wrap_boolean(isatty(fd)); return janet_wrap_boolean(isatty(fd));
#endif #endif
} }
@ -1678,7 +1880,7 @@ JANET_CORE_FN(os_mktime,
} }
if (t == (time_t) -1) { if (t == (time_t) -1) {
janet_panicf("%s", strerror(errno)); janet_panicf("%s", janet_strerror(errno));
} }
return janet_wrap_number((double)t); return janet_wrap_number((double)t);
@ -1690,6 +1892,43 @@ JANET_CORE_FN(os_mktime,
#define j_symlink symlink #define j_symlink symlink
#endif #endif
JANET_CORE_FN(os_setlocale,
"(os/setlocale &opt locale category)",
"Set the system locale, which affects how dates and numbers are formatted. "
"Passing nil to locale will return the current locale. Category can be one of:\n\n"
" * :all (default)\n"
" * :collate\n"
" * :ctype\n"
" * :monetary\n"
" * :numeric\n"
" * :time\n\n"
"Returns the new locale if set successfully, otherwise nil. Note that this will affect "
"other functions such as `os/strftime` and even `printf`.") {
janet_arity(argc, 0, 2);
const char *locale_name = janet_optcstring(argv, argc, 0, NULL);
int category_int = LC_ALL;
if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) {
if (janet_keyeq(argv[1], "all")) {
category_int = LC_ALL;
} else if (janet_keyeq(argv[1], "collate")) {
category_int = LC_COLLATE;
} else if (janet_keyeq(argv[1], "ctype")) {
category_int = LC_CTYPE;
} else if (janet_keyeq(argv[1], "monetary")) {
category_int = LC_MONETARY;
} else if (janet_keyeq(argv[1], "numeric")) {
category_int = LC_NUMERIC;
} else if (janet_keyeq(argv[1], "time")) {
category_int = LC_TIME;
} else {
janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[1]);
}
}
const char *old = setlocale(category_int, locale_name);
if (old == NULL) return janet_wrap_nil();
return janet_cstringv(old);
}
JANET_CORE_FN(os_link, JANET_CORE_FN(os_link,
"(os/link oldpath newpath &opt symlink)", "(os/link oldpath newpath &opt symlink)",
"Create a link at newpath that points to oldpath and returns nil. " "Create a link at newpath that points to oldpath and returns nil. "
@ -1707,7 +1946,7 @@ JANET_CORE_FN(os_link,
const char *oldpath = janet_getcstring(argv, 0); const char *oldpath = janet_getcstring(argv, 0);
const char *newpath = janet_getcstring(argv, 1); const char *newpath = janet_getcstring(argv, 1);
int res = ((argc == 3 && janet_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath); int res = ((argc == 3 && janet_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath);
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath);
return janet_wrap_nil(); return janet_wrap_nil();
#endif #endif
} }
@ -1726,7 +1965,7 @@ JANET_CORE_FN(os_symlink,
const char *oldpath = janet_getcstring(argv, 0); const char *oldpath = janet_getcstring(argv, 0);
const char *newpath = janet_getcstring(argv, 1); const char *newpath = janet_getcstring(argv, 1);
int res = j_symlink(oldpath, newpath); int res = j_symlink(oldpath, newpath);
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath);
return janet_wrap_nil(); return janet_wrap_nil();
#endif #endif
} }
@ -1748,7 +1987,7 @@ JANET_CORE_FN(os_mkdir,
#endif #endif
if (res == 0) return janet_wrap_true(); if (res == 0) return janet_wrap_true();
if (errno == EEXIST) return janet_wrap_false(); if (errno == EEXIST) return janet_wrap_false();
janet_panicf("%s: %s", strerror(errno), path); janet_panicf("%s: %s", janet_strerror(errno), path);
} }
JANET_CORE_FN(os_rmdir, JANET_CORE_FN(os_rmdir,
@ -1762,7 +2001,7 @@ JANET_CORE_FN(os_rmdir,
#else #else
int res = rmdir(path); int res = rmdir(path);
#endif #endif
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -1777,7 +2016,7 @@ JANET_CORE_FN(os_cd,
#else #else
int res = chdir(path); int res = chdir(path);
#endif #endif
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -1801,7 +2040,7 @@ JANET_CORE_FN(os_touch,
bufp = NULL; bufp = NULL;
} }
int res = utime(path, bufp); int res = utime(path, bufp);
if (-1 == res) janet_panic(strerror(errno)); if (-1 == res) janet_panic(janet_strerror(errno));
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -1811,7 +2050,7 @@ JANET_CORE_FN(os_remove,
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
int status = remove(path); int status = remove(path);
if (-1 == status) janet_panicf("%s: %s", strerror(errno), path); if (-1 == status) janet_panicf("%s: %s", janet_strerror(errno), path);
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -1830,7 +2069,7 @@ JANET_CORE_FN(os_readlink,
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
ssize_t len = readlink(path, buffer, sizeof buffer); ssize_t len = readlink(path, buffer, sizeof buffer);
if (len < 0 || (size_t)len >= sizeof buffer) if (len < 0 || (size_t)len >= sizeof buffer)
janet_panicf("%s: %s", strerror(errno), path); janet_panicf("%s: %s", janet_strerror(errno), path);
return janet_stringv((const uint8_t *)buffer, len); return janet_stringv((const uint8_t *)buffer, len);
#endif #endif
} }
@ -2125,7 +2364,7 @@ JANET_CORE_FN(os_chmod,
#else #else
int res = chmod(path, os_getmode(argv, 1)); int res = chmod(path, os_getmode(argv, 1));
#endif #endif
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -2161,7 +2400,7 @@ JANET_CORE_FN(os_dir,
janet_panicf("path too long: %s", dir); janet_panicf("path too long: %s", dir);
sprintf(pattern, "%s/*", dir); sprintf(pattern, "%s/*", dir);
intptr_t res = _findfirst(pattern, &afile); intptr_t res = _findfirst(pattern, &afile);
if (-1 == res) janet_panicv(janet_cstringv(strerror(errno))); if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno)));
do { do {
if (strcmp(".", afile.name) && strcmp("..", afile.name)) { if (strcmp(".", afile.name) && strcmp("..", afile.name)) {
janet_array_push(paths, janet_cstringv(afile.name)); janet_array_push(paths, janet_cstringv(afile.name));
@ -2172,8 +2411,18 @@ JANET_CORE_FN(os_dir,
/* Read directory items with opendir / readdir / closedir */ /* Read directory items with opendir / readdir / closedir */
struct dirent *dp; struct dirent *dp;
DIR *dfd = opendir(dir); DIR *dfd = opendir(dir);
if (dfd == NULL) janet_panicf("cannot open directory %s", dir); if (dfd == NULL) janet_panicf("cannot open directory %s: %s", dir, janet_strerror(errno));
while ((dp = readdir(dfd)) != NULL) { for (;;) {
errno = 0;
dp = readdir(dfd);
if (dp == NULL) {
if (errno) {
int olderr = errno;
closedir(dfd);
janet_panicf("failed to read directory %s: %s", dir, janet_strerror(olderr));
}
break;
}
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
continue; continue;
} }
@ -2193,7 +2442,7 @@ JANET_CORE_FN(os_rename,
const char *dest = janet_getcstring(argv, 1); const char *dest = janet_getcstring(argv, 1);
int status = rename(src, dest); int status = rename(src, dest);
if (status) { if (status) {
janet_panic(strerror(errno)); janet_panic(janet_strerror(errno));
} }
return janet_wrap_nil(); return janet_wrap_nil();
} }
@ -2213,7 +2462,7 @@ JANET_CORE_FN(os_realpath,
#else #else
char *dest = realpath(src, NULL); char *dest = realpath(src, NULL);
#endif #endif
if (NULL == dest) janet_panicf("%s: %s", strerror(errno), src); if (NULL == dest) janet_panicf("%s: %s", janet_strerror(errno), src);
Janet ret = janet_cstringv(dest); Janet ret = janet_cstringv(dest);
janet_free(dest); janet_free(dest);
return ret; return ret;
@ -2487,6 +2736,7 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/strftime", os_strftime), JANET_CORE_REG("os/strftime", os_strftime),
JANET_CORE_REG("os/sleep", os_sleep), JANET_CORE_REG("os/sleep", os_sleep),
JANET_CORE_REG("os/isatty", os_isatty), JANET_CORE_REG("os/isatty", os_isatty),
JANET_CORE_REG("os/setlocale", os_setlocale),
/* env functions */ /* env functions */
JANET_CORE_REG("os/environ", os_environ), JANET_CORE_REG("os/environ", os_environ),
@ -2523,6 +2773,8 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/execute", os_execute), JANET_CORE_REG("os/execute", os_execute),
JANET_CORE_REG("os/spawn", os_spawn), JANET_CORE_REG("os/spawn", os_spawn),
JANET_CORE_REG("os/shell", os_shell), JANET_CORE_REG("os/shell", os_shell),
JANET_CORE_REG("os/posix-fork", os_posix_fork),
JANET_CORE_REG("os/posix-exec", os_posix_exec),
/* no need to sandbox process management if you can't create processes /* no need to sandbox process management if you can't create processes
* (allows for limited functionality if use exposes C-functions to create specific processes) */ * (allows for limited functionality if use exposes C-functions to create specific processes) */
JANET_CORE_REG("os/proc-wait", os_proc_wait), JANET_CORE_REG("os/proc-wait", os_proc_wait),
@ -2536,6 +2788,7 @@ void janet_lib_os(JanetTable *env) {
#ifdef JANET_EV #ifdef JANET_EV
JANET_CORE_REG("os/open", os_open), /* fs read and write */ JANET_CORE_REG("os/open", os_open), /* fs read and write */
JANET_CORE_REG("os/pipe", os_pipe), JANET_CORE_REG("os/pipe", os_pipe),
JANET_CORE_REG("os/sigaction", os_sigaction),
#endif #endif
#endif #endif
JANET_REG_END JANET_REG_END

View File

@ -39,6 +39,10 @@
typedef struct { typedef struct {
const uint8_t *text_start; const uint8_t *text_start;
const uint8_t *text_end; const uint8_t *text_end;
/* text_end can be restricted by some rules, but
outer_text_end will always contain the real end of
input, which we need to generate a line mapping */
const uint8_t *outer_text_end;
const uint32_t *bytecode; const uint32_t *bytecode;
const Janet *constants; const Janet *constants;
JanetArray *captures; JanetArray *captures;
@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
/* Generate if not made yet */ /* Generate if not made yet */
if (s->linemaplen < 0) { if (s->linemaplen < 0) {
int32_t newline_count = 0; int32_t newline_count = 0;
for (const uint8_t *c = s->text_start; c < s->text_end; c++) { for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
if (*c == '\n') newline_count++; if (*c == '\n') newline_count++;
} }
int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count); int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count);
size_t index = 0; size_t index = 0;
for (const uint8_t *c = s->text_start; c < s->text_end; c++) { for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start); if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
} }
s->linemaplen = newline_count; s->linemaplen = newline_count;
@ -179,7 +183,7 @@ static const uint8_t *peg_rule(
const uint32_t *rule, const uint32_t *rule,
const uint8_t *text) { const uint8_t *text) {
tail: tail:
switch (*rule & 0x1F) { switch (*rule) {
default: default:
janet_panic("unexpected opcode"); janet_panic("unexpected opcode");
return NULL; return NULL;
@ -482,6 +486,68 @@ tail:
return result; return result;
} }
case RULE_SUB: {
const uint8_t *text_start = text;
const uint32_t *rule_window = s->bytecode + rule[1];
const uint32_t *rule_subpattern = s->bytecode + rule[2];
down1(s);
const uint8_t *window_end = peg_rule(s, rule_window, text);
up1(s);
if (!window_end) {
return NULL;
}
const uint8_t *saved_end = s->text_end;
s->text_end = window_end;
down1(s);
const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start);
up1(s);
s->text_end = saved_end;
if (!next_text) {
return NULL;
}
return window_end;
}
case RULE_SPLIT: {
const uint8_t *saved_end = s->text_end;
const uint32_t *rule_separator = s->bytecode + rule[1];
const uint32_t *rule_subpattern = s->bytecode + rule[2];
const uint8_t *separator_end = NULL;
do {
const uint8_t *text_start = text;
CapState cs = cap_save(s);
down1(s);
while (text <= s->text_end) {
separator_end = peg_rule(s, rule_separator, text);
cap_load(s, cs);
if (separator_end) {
break;
}
text++;
}
up1(s);
if (separator_end) {
s->text_end = text;
text = separator_end;
}
down1(s);
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
up1(s);
s->text_end = saved_end;
if (!subpattern_end) {
return NULL;
}
} while (separator_end);
return s->text_end;
}
case RULE_REPLACE: case RULE_REPLACE:
case RULE_MATCHTIME: { case RULE_MATCHTIME: {
uint32_t tag = rule[3]; uint32_t tag = rule[3];
@ -1107,6 +1173,22 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag); emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
} }
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 2);
Reserve r = reserve(b, 3);
uint32_t subrule1 = peg_compile1(b, argv[0]);
uint32_t subrule2 = peg_compile1(b, argv[1]);
emit_2(r, RULE_SUB, subrule1, subrule2);
}
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 2);
Reserve r = reserve(b, 3);
uint32_t subrule1 = peg_compile1(b, argv[0]);
uint32_t subrule2 = peg_compile1(b, argv[1]);
emit_2(r, RULE_SPLIT, subrule1, subrule2);
}
#ifdef JANET_INT_TYPES #ifdef JANET_INT_TYPES
#define JANET_MAX_READINT_WIDTH 8 #define JANET_MAX_READINT_WIDTH 8
#else #else
@ -1190,6 +1272,8 @@ static const SpecialPair peg_specials[] = {
{"sequence", spec_sequence}, {"sequence", spec_sequence},
{"set", spec_set}, {"set", spec_set},
{"some", spec_some}, {"some", spec_some},
{"split", spec_split},
{"sub", spec_sub},
{"thru", spec_thru}, {"thru", spec_thru},
{"to", spec_to}, {"to", spec_to},
{"uint", spec_uint_le}, {"uint", spec_uint_le},
@ -1431,7 +1515,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
uint32_t instr = bytecode[i]; uint32_t instr = bytecode[i];
uint32_t *rule = bytecode + i; uint32_t *rule = bytecode + i;
op_flags[i] |= 0x02; op_flags[i] |= 0x02;
switch (instr & 0x1F) { switch (instr) {
case RULE_LITERAL: case RULE_LITERAL:
i += 2 + ((rule[1] + 3) >> 2); i += 2 + ((rule[1] + 3) >> 2);
break; break;
@ -1524,6 +1608,15 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
op_flags[rule[1]] |= 0x01; op_flags[rule[1]] |= 0x01;
i += 4; i += 4;
break; break;
case RULE_SUB:
case RULE_SPLIT:
/* [rule, rule] */
if (rule[1] >= blen) goto bad;
if (rule[2] >= blen) goto bad;
op_flags[rule[1]] |= 0x01;
op_flags[rule[2]] |= 0x01;
i += 3;
break;
case RULE_ERROR: case RULE_ERROR:
case RULE_DROP: case RULE_DROP:
case RULE_NOT: case RULE_NOT:
@ -1652,7 +1745,7 @@ typedef struct {
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) { static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
PegCall ret; PegCall ret;
int32_t min = get_replace ? 3 : 2; int32_t min = get_replace ? 3 : 2;
janet_arity(argc, get_replace, -1); janet_arity(argc, min, -1);
if (janet_checktype(argv[0], JANET_ABSTRACT) && if (janet_checktype(argv[0], JANET_ABSTRACT) &&
janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) { janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
ret.peg = janet_unwrap_abstract(argv[0]); ret.peg = janet_unwrap_abstract(argv[0]);
@ -1677,6 +1770,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
ret.s.mode = PEG_MODE_NORMAL; ret.s.mode = PEG_MODE_NORMAL;
ret.s.text_start = ret.bytes.bytes; ret.s.text_start = ret.bytes.bytes;
ret.s.text_end = ret.bytes.bytes + ret.bytes.len; ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
ret.s.outer_text_end = ret.s.text_end;
ret.s.depth = JANET_RECURSION_GUARD; ret.s.depth = JANET_RECURSION_GUARD;
ret.s.captures = janet_array(0); ret.s.captures = janet_array(0);
ret.s.tagged_captures = janet_array(0); ret.s.tagged_captures = janet_array(0);
@ -1771,7 +1865,7 @@ JANET_CORE_FN(cfun_peg_replace_all,
} }
JANET_CORE_FN(cfun_peg_replace, JANET_CORE_FN(cfun_peg_replace,
"(peg/replace peg repl text &opt start & args)", "(peg/replace peg subst text &opt start & args)",
"Replace first match of `peg` in `text` with `subst`, returning a new buffer. " "Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
"The peg does not need to make captures to do replacement. " "The peg does not need to make captures to do replacement. "
"If `subst` is a function, it will be called with the " "If `subst` is a function, it will be called with the "

View File

@ -31,6 +31,7 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <inttypes.h> #include <inttypes.h>
#include <float.h>
/* Implements a pretty printer for Janet. The pretty printer /* Implements a pretty printer for Janet. The pretty printer
* is simple and not that flexible, but fast. */ * is simple and not that flexible, but fast. */
@ -38,11 +39,15 @@
/* Temporary buffer size */ /* Temporary buffer size */
#define BUFSIZE 64 #define BUFSIZE 64
/* Preprocessor hacks */
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
static void number_to_string_b(JanetBuffer *buffer, double x) { static void number_to_string_b(JanetBuffer *buffer, double x) {
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2); janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
const char *fmt = (x == floor(x) && const char *fmt = (x == floor(x) &&
x <= JANET_INTMAX_DOUBLE && x <= JANET_INTMAX_DOUBLE &&
x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g"; x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g");
int count; int count;
if (x == 0.0) { if (x == 0.0) {
/* Prevent printing of '-0' */ /* Prevent printing of '-0' */
@ -374,8 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
break; break;
case JANET_NUMBER: case JANET_NUMBER:
janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2); janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x)); double num = janet_unwrap_number(x);
S->buffer->count += count; if (isnan(num)) return 1;
if (isinf(num)) return 1;
janet_buffer_dtostr(S->buffer, num);
break; break;
case JANET_SYMBOL: case JANET_SYMBOL:
case JANET_KEYWORD: case JANET_KEYWORD:
@ -772,6 +779,8 @@ struct FmtMapping {
/* Janet uses fixed width integer types for most things, so map /* Janet uses fixed width integer types for most things, so map
* format specifiers to these fixed sizes */ * format specifiers to these fixed sizes */
static const struct FmtMapping format_mappings[] = { static const struct FmtMapping format_mappings[] = {
{'D', PRId64},
{'I', PRIi64},
{'d', PRId64}, {'d', PRId64},
{'i', PRIi64}, {'i', PRIi64},
{'o', PRIo64}, {'o', PRIo64},
@ -823,7 +832,7 @@ static const char *scanformat(
if (loc != NULL && *loc != '\0') { if (loc != NULL && *loc != '\0') {
const char *mapping = get_fmt_mapping(*p2++); const char *mapping = get_fmt_mapping(*p2++);
size_t len = strlen(mapping); size_t len = strlen(mapping);
strcpy(form, mapping); memcpy(form, mapping, len);
form += len; form += len;
} else { } else {
*(form++) = *(p2++); *(form++) = *(p2++);
@ -850,13 +859,19 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
c = scanformat(c, form, width, precision); c = scanformat(c, form, width, precision);
switch (*c++) { switch (*c++) {
case 'c': { case 'c': {
int n = va_arg(args, long); int n = va_arg(args, int);
nb = snprintf(item, MAX_ITEM, form, n); nb = snprintf(item, MAX_ITEM, form, n);
break; break;
} }
case 'd': case 'd':
case 'i': { case 'i': {
int64_t n = va_arg(args, int); int64_t n = (int64_t) va_arg(args, int32_t);
nb = snprintf(item, MAX_ITEM, form, n);
break;
}
case 'D':
case 'I': {
int64_t n = va_arg(args, int64_t);
nb = snprintf(item, MAX_ITEM, form, n); nb = snprintf(item, MAX_ITEM, form, n);
break; break;
} }
@ -864,7 +879,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
case 'X': case 'X':
case 'o': case 'o':
case 'u': { case 'u': {
uint64_t n = va_arg(args, unsigned int); uint64_t n = va_arg(args, uint64_t);
nb = snprintf(item, MAX_ITEM, form, n); nb = snprintf(item, MAX_ITEM, form, n);
break; break;
} }
@ -908,7 +923,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
janet_buffer_push_cstring(b, typestr(va_arg(args, Janet))); janet_buffer_push_cstring(b, typestr(va_arg(args, Janet)));
break; break;
case 'T': { case 'T': {
int types = va_arg(args, long); int types = va_arg(args, int);
pushtypes(b, types); pushtypes(b, types);
break; break;
} }
@ -1017,6 +1032,8 @@ void janet_buffer_format(
janet_getinteger(argv, arg)); janet_getinteger(argv, arg));
break; break;
} }
case 'D':
case 'I':
case 'd': case 'd':
case 'i': { case 'i': {
int64_t n = janet_getinteger64(argv, arg); int64_t n = janet_getinteger64(argv, arg);

View File

@ -32,6 +32,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
int errflags = 0, done = 0; int errflags = 0, done = 0;
int32_t index = 0; int32_t index = 0;
Janet ret = janet_wrap_nil(); Janet ret = janet_wrap_nil();
JanetFiber *fiber = NULL;
const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL; const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
if (where) janet_gcroot(janet_wrap_string(where)); if (where) janet_gcroot(janet_wrap_string(where));
@ -47,7 +48,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
JanetCompileResult cres = janet_compile(form, env, where); JanetCompileResult cres = janet_compile(form, env, where);
if (cres.status == JANET_COMPILE_OK) { if (cres.status == JANET_COMPILE_OK) {
JanetFunction *f = janet_thunk(cres.funcdef); JanetFunction *f = janet_thunk(cres.funcdef);
JanetFiber *fiber = janet_fiber(f, 64, 0, NULL); fiber = janet_fiber(f, 64, 0, NULL);
fiber->env = env; fiber->env = env;
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret); JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) { if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
@ -57,12 +58,20 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
} }
} else { } else {
ret = janet_wrap_string(cres.error); ret = janet_wrap_string(cres.error);
int32_t line = (int32_t) parser.line;
int32_t col = (int32_t) parser.column;
if ((cres.error_mapping.line > 0) &&
(cres.error_mapping.column > 0)) {
line = cres.error_mapping.line;
col = cres.error_mapping.column;
}
if (cres.macrofiber) { if (cres.macrofiber) {
janet_eprintf("compile error in %s: ", sourcePath); janet_eprintf("%s:%d:%d: compile error", sourcePath,
line, col);
janet_stacktrace_ext(cres.macrofiber, ret, ""); janet_stacktrace_ext(cres.macrofiber, ret, "");
} else { } else {
janet_eprintf("compile error in %s: %s\n", sourcePath, janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
(const char *)cres.error); line, col, (const char *)cres.error);
} }
errflags |= 0x02; errflags |= 0x02;
done = 1; done = 1;
@ -104,9 +113,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
#ifdef JANET_EV #ifdef JANET_EV
/* Enter the event loop if we are not already in it */ /* Enter the event loop if we are not already in it */
if (janet_vm.stackn == 0) { if (janet_vm.stackn == 0) {
janet_gcroot(ret); if (fiber) {
janet_gcroot(janet_wrap_fiber(fiber));
}
janet_loop(); janet_loop();
janet_gcunroot(ret); if (fiber) {
janet_gcunroot(janet_wrap_fiber(fiber));
ret = fiber->last_value;
}
} }
#endif #endif
if (out) *out = ret; if (out) *out = ret;

View File

@ -149,7 +149,7 @@ static int destructure(JanetCompiler *c,
JanetTable *attr) { JanetTable *attr) {
switch (janet_type(left)) { switch (janet_type(left)) {
default: default:
janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left)); janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left));
return 1; return 1;
case JANET_SYMBOL: case JANET_SYMBOL:
/* Leaf, assign right to left */ /* Leaf, assign right to left */
@ -530,6 +530,26 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
return ret; return ret;
} }
/* Check if a form matches the pattern (= nil _) or (not= nil _) */
static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) {
if (!janet_checktype(x, JANET_TUPLE)) return 0;
JanetTuple tup = janet_unwrap_tuple(x);
if (3 != janet_tuple_length(tup)) return 0;
Janet op1 = tup[0];
if (!janet_checktype(op1, JANET_FUNCTION)) return 0;
JanetFunction *fun = janet_unwrap_function(op1);
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
if (tag != fun_tag) return 0;
if (janet_checktype(tup[1], JANET_NIL)) {
*capture = tup[2];
return 1;
} else if (janet_checktype(tup[2], JANET_NIL)) {
*capture = tup[1];
return 1;
}
return 0;
}
/* /*
* :condition * :condition
* ... * ...
@ -550,6 +570,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
JanetScope condscope, tempscope; JanetScope condscope, tempscope;
const int tail = opts.flags & JANET_FOPTS_TAIL; const int tail = opts.flags & JANET_FOPTS_TAIL;
const int drop = opts.flags & JANET_FOPTS_DROP; const int drop = opts.flags & JANET_FOPTS_DROP;
uint8_t ifnjmp = JOP_JUMP_IF_NOT;
if (argn < 2 || argn > 3) { if (argn < 2 || argn > 3) {
janetc_cerror(c, "expected 2 or 3 arguments to if"); janetc_cerror(c, "expected 2 or 3 arguments to if");
@ -572,12 +593,24 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
/* Compile condition */ /* Compile condition */
janetc_scope(&condscope, c, 0, "if"); janetc_scope(&condscope, c, 0, "if");
cond = janetc_value(condopts, argv[0]);
Janet condform = argv[0];
if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
ifnjmp = JOP_JUMP_IF_NOT_NIL;
} else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
ifnjmp = JOP_JUMP_IF_NIL;
}
cond = janetc_value(condopts, condform);
/* Check constant condition. */ /* Check constant condition. */
/* TODO: Use type info for more short circuits */ /* TODO: Use type info for more short circuits */
if (cond.flags & JANET_SLOT_CONSTANT) { if (cond.flags & JANET_SLOT_CONSTANT) {
if (!janet_truthy(cond.constant)) { int swap_condition = 0;
if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1;
if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
if (swap_condition) {
/* Swap the true and false bodies */ /* Swap the true and false bodies */
Janet temp = falsebody; Janet temp = falsebody;
falsebody = truebody; falsebody = truebody;
@ -595,7 +628,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
} }
/* Compile jump to right */ /* Compile jump to right */
labeljr = janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0); labeljr = janetc_emit_si(c, ifnjmp, cond, 0, 0);
/* Condition left body */ /* Condition left body */
janetc_scope(&tempscope, c, 0, "if-true"); janetc_scope(&tempscope, c, 0, "if-true");
@ -605,7 +638,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
/* Compile jump to done */ /* Compile jump to done */
labeljd = janet_v_count(c->buffer); labeljd = janet_v_count(c->buffer);
if (!tail) janetc_emit(c, JOP_JUMP); if (!tail && !(drop && janet_checktype(falsebody, JANET_NIL))) janetc_emit(c, JOP_JUMP);
/* Compile right body */ /* Compile right body */
labelr = janet_v_count(c->buffer); labelr = janet_v_count(c->buffer);
@ -716,9 +749,8 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
if (!(scope->flags & JANET_SCOPE_WHILE) && argn) { if (!(scope->flags & JANET_SCOPE_WHILE) && argn) {
/* Closure body with return argument */ /* Closure body with return argument */
subopts.flags |= JANET_FOPTS_TAIL; subopts.flags |= JANET_FOPTS_TAIL;
JanetSlot ret = janetc_value(subopts, argv[0]); janetc_value(subopts, argv[0]);
ret.flags |= JANET_SLOT_RETURNED; return janetc_cslot(janet_wrap_nil());
return ret;
} else { } else {
/* while loop IIFE or no argument */ /* while loop IIFE or no argument */
if (argn) { if (argn) {
@ -726,9 +758,7 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
janetc_value(subopts, argv[0]); janetc_value(subopts, argv[0]);
} }
janetc_emit(c, JOP_RETURN_NIL); janetc_emit(c, JOP_RETURN_NIL);
JanetSlot s = janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
s.flags |= JANET_SLOT_RETURNED;
return s;
} }
} else { } else {
if (argn) { if (argn) {
@ -741,20 +771,6 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
} }
} }
/* Check if a form matches the pattern (not= nil _) */
static int janetc_check_notnil_form(Janet x, Janet *capture) {
if (!janet_checktype(x, JANET_TUPLE)) return 0;
JanetTuple tup = janet_unwrap_tuple(x);
if (!janet_checktype(tup[0], JANET_FUNCTION)) return 0;
if (3 != janet_tuple_length(tup)) return 0;
JanetFunction *fun = janet_unwrap_function(tup[0]);
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
if (tag != JANET_FUN_NEQ) return 0;
if (!janet_checktype(tup[1], JANET_NIL)) return 0;
*capture = tup[2];
return 1;
}
/* /*
* :whiletop * :whiletop
* ... * ...
@ -771,6 +787,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
JanetScope tempscope; JanetScope tempscope;
int32_t labelwt, labeld, labeljt, labelc, i; int32_t labelwt, labeld, labeljt, labelc, i;
int infinite = 0; int infinite = 0;
int is_nil_form = 0;
int is_notnil_form = 0; int is_notnil_form = 0;
uint8_t ifjmp = JOP_JUMP_IF; uint8_t ifjmp = JOP_JUMP_IF;
uint8_t ifnjmp = JOP_JUMP_IF_NOT; uint8_t ifnjmp = JOP_JUMP_IF_NOT;
@ -784,11 +801,16 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while"); janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while");
/* Check for `(not= nil _)` in condition, and if so, use the /* Check for `(= nil _)` or `(not= nil _)` in condition, and if so, use the
* jmpnl or jmpnn instructions. This let's us implement `(each ...)` * jmpnl or jmpnn instructions. This let's us implement `(each ...)`
* more efficiently. */ * more efficiently. */
Janet condform = argv[0]; Janet condform = argv[0];
if (janetc_check_notnil_form(condform, &condform)) { if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
is_nil_form = 1;
ifjmp = JOP_JUMP_IF_NIL;
ifnjmp = JOP_JUMP_IF_NOT_NIL;
}
if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
is_notnil_form = 1; is_notnil_form = 1;
ifjmp = JOP_JUMP_IF_NOT_NIL; ifjmp = JOP_JUMP_IF_NOT_NIL;
ifnjmp = JOP_JUMP_IF_NIL; ifnjmp = JOP_JUMP_IF_NIL;
@ -800,7 +822,9 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
/* Check for constant condition */ /* Check for constant condition */
if (cond.flags & JANET_SLOT_CONSTANT) { if (cond.flags & JANET_SLOT_CONSTANT) {
/* Loop never executes */ /* Loop never executes */
int never_executes = is_notnil_form int never_executes = is_nil_form
? !janet_checktype(cond.constant, JANET_NIL)
: is_notnil_form
? janet_checktype(cond.constant, JANET_NIL) ? janet_checktype(cond.constant, JANET_NIL)
: !janet_truthy(cond.constant); : !janet_truthy(cond.constant);
if (never_executes) { if (never_executes) {
@ -901,6 +925,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
int structarg = 0; int structarg = 0;
int allow_extra = 0; int allow_extra = 0;
int selfref = 0; int selfref = 0;
int hasname = 0;
int seenamp = 0; int seenamp = 0;
int seenopt = 0; int seenopt = 0;
int namedargs = 0; int namedargs = 0;
@ -919,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
head = argv[0]; head = argv[0];
if (janet_checktype(head, JANET_SYMBOL)) { if (janet_checktype(head, JANET_SYMBOL)) {
selfref = 1; selfref = 1;
hasname = 1;
parami = 1;
} else if (janet_checktype(head, JANET_KEYWORD)) {
hasname = 1;
parami = 1; parami = 1;
} }
if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) { if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) {
@ -1079,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG; if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
if (selfref) def->name = janet_unwrap_symbol(head); if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
janet_def_addflags(def); janet_def_addflags(def);
defindex = janetc_addfuncdef(c, def); defindex = janetc_addfuncdef(c, def);

View File

@ -24,6 +24,11 @@
#include "features.h" #include "features.h"
#include <janet.h> #include <janet.h>
#include "state.h" #include "state.h"
#include "util.h"
#endif
#ifdef JANET_WINDOWS
#include <windows.h>
#endif #endif
JANET_THREAD_LOCAL JanetVM janet_vm; JANET_THREAD_LOCAL JanetVM janet_vm;
@ -57,5 +62,10 @@ void janet_vm_load(JanetVM *from) {
* use NULL to interrupt the current VM when convenient */ * use NULL to interrupt the current VM when convenient */
void janet_interpreter_interrupt(JanetVM *vm) { void janet_interpreter_interrupt(JanetVM *vm) {
vm = vm ? vm : &janet_vm; vm = vm ? vm : &janet_vm;
vm->auto_suspend = 1; janet_atomic_inc(&vm->auto_suspend);
}
void janet_interpreter_interrupt_handled(JanetVM *vm) {
vm = vm ? vm : &janet_vm;
janet_atomic_dec(&vm->auto_suspend);
} }

View File

@ -89,7 +89,7 @@ struct JanetVM {
/* If this flag is true, suspend on function calls and backwards jumps. /* If this flag is true, suspend on function calls and backwards jumps.
* When this occurs, this flag will be reset to 0. */ * When this occurs, this flag will be reset to 0. */
int auto_suspend; volatile JanetAtomicInt auto_suspend;
/* The current running fiber on the current thread. /* The current running fiber on the current thread.
* Set and unset by functions in vm.c */ * Set and unset by functions in vm.c */
@ -121,10 +121,12 @@ struct JanetVM {
/* Garbage collection */ /* Garbage collection */
void *blocks; void *blocks;
void *weak_blocks;
size_t gc_interval; size_t gc_interval;
size_t next_collection; size_t next_collection;
size_t block_count; size_t block_count;
int gc_suspend; int gc_suspend;
int gc_mark_phase;
/* GC roots */ /* GC roots */
Janet *roots; Janet *roots;
@ -147,6 +149,11 @@ struct JanetVM {
JanetTraversalNode *traversal_top; JanetTraversalNode *traversal_top;
JanetTraversalNode *traversal_base; JanetTraversalNode *traversal_base;
/* Thread safe strerror error buffer - for janet_strerror */
#ifndef JANET_WINDOWS
char strerror_buf[256];
#endif
/* Event loop and scheduler globals */ /* Event loop and scheduler globals */
#ifdef JANET_EV #ifdef JANET_EV
size_t tq_count; size_t tq_count;
@ -154,12 +161,10 @@ struct JanetVM {
JanetQueue spawn; JanetQueue spawn;
JanetTimeout *tq; JanetTimeout *tq;
JanetRNG ev_rng; JanetRNG ev_rng;
JanetListenerState **listeners; volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */
size_t listener_count;
size_t listener_cap;
size_t extra_listeners;
JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */ JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */
JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */ JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */
JanetTable signal_handlers;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
void **iocp; void **iocp;
#elif defined(JANET_EV_EPOLL) #elif defined(JANET_EV_EPOLL)
@ -175,6 +180,9 @@ struct JanetVM {
int timer; int timer;
int timer_enabled; int timer_enabled;
#else #else
JanetStream **streams;
size_t stream_count;
size_t stream_capacity;
pthread_attr_t new_thread_attr; pthread_attr_t new_thread_attr;
JanetHandle selfpipe[2]; JanetHandle selfpipe[2];
struct pollfd *fds; struct pollfd *fds;

View File

@ -549,12 +549,12 @@ JANET_CORE_FN(cfun_string_format,
"- `a`, `A`: floating point number, formatted as a hexadecimal number.\n" "- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
"- `s`: formatted as a string, precision indicates padding and maximum length.\n" "- `s`: formatted as a string, precision indicates padding and maximum length.\n"
"- `t`: emit the type of the given value.\n" "- `t`: emit the type of the given value.\n"
"- `v`: format with (describe x)" "- `v`: format with (describe x)\n"
"- `V`: format with (string x)" "- `V`: format with (string x)\n"
"- `j`: format to jdn (Janet data notation).\n" "- `j`: format to jdn (Janet data notation).\n"
"\n" "\n"
"The following conversion specifiers are used for \"pretty-printing\", where the upper-case " "The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
"variants generate colored output. These speficiers can take a precision " "variants generate colored output. These specifiers can take a precision "
"argument to specify the maximum nesting depth to print.\n" "argument to specify the maximum nesting depth to print.\n"
"- `p`, `P`: pretty format, truncating if necessary\n" "- `p`, `P`: pretty format, truncating if necessary\n"
"- `m`, `M`: pretty format without truncating.\n" "- `m`, `M`: pretty format without truncating.\n"

View File

@ -490,3 +490,18 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
} }
#endif #endif
void janet_buffer_dtostr(JanetBuffer *buffer, double x) {
#define BUFSIZE 32
janet_buffer_extra(buffer, BUFSIZE);
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x);
#undef BUFSIZE
/* fix locale issues with commas */
for (int i = 0; i < count; i++) {
char c = buffer->data[buffer->count + i];
if (c == ',') {
buffer->data[buffer->count + i] = '.';
}
}
buffer->count += count;
}

View File

@ -234,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) {
head->hash = hash; head->hash = hash;
sym = (uint8_t *)(head->data); sym = (uint8_t *)(head->data);
memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter)); memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
sym[head->length] = 0;
janet_symcache_put((const uint8_t *)sym, bucket); janet_symcache_put((const uint8_t *)sym, bucket);
return (const uint8_t *)sym; return (const uint8_t *)sym;
} }

View File

@ -87,11 +87,27 @@ void janet_table_deinit(JanetTable *table) {
} }
/* Create a new table */ /* Create a new table */
JanetTable *janet_table(int32_t capacity) { JanetTable *janet_table(int32_t capacity) {
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable)); JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
return janet_table_init_impl(table, capacity, 0); return janet_table_init_impl(table, capacity, 0);
} }
JanetTable *janet_table_weakk(int32_t capacity) {
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKK, sizeof(JanetTable));
return janet_table_init_impl(table, capacity, 0);
}
JanetTable *janet_table_weakv(int32_t capacity) {
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKV, sizeof(JanetTable));
return janet_table_init_impl(table, capacity, 0);
}
JanetTable *janet_table_weakkv(int32_t capacity) {
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKKV, sizeof(JanetTable));
return janet_table_init_impl(table, capacity, 0);
}
/* Find the bucket that contains the given key. Will also return /* Find the bucket that contains the given key. Will also return
* bucket where key should go if not in the table. */ * bucket where key should go if not in the table. */
JanetKV *janet_table_find(JanetTable *t, Janet key) { JanetKV *janet_table_find(JanetTable *t, Janet key) {
@ -111,12 +127,11 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;
} }
} }
int32_t i, oldcapacity; int32_t oldcapacity = t->capacity;
oldcapacity = t->capacity;
t->data = newdata; t->data = newdata;
t->capacity = size; t->capacity = size;
t->deleted = 0; t->deleted = 0;
for (i = 0; i < oldcapacity; i++) { for (int32_t i = 0; i < oldcapacity; i++) {
JanetKV *kv = olddata + i; JanetKV *kv = olddata + i;
if (!janet_checktype(kv->key, JANET_NIL)) { if (!janet_checktype(kv->key, JANET_NIL)) {
JanetKV *newkv = janet_table_find(t, kv->key); JanetKV *newkv = janet_table_find(t, kv->key);
@ -298,11 +313,46 @@ JANET_CORE_FN(cfun_table_new,
"Creates a new empty table with pre-allocated memory " "Creates a new empty table with pre-allocated memory "
"for `capacity` entries. This means that if one knows the number of " "for `capacity` entries. This means that if one knows the number of "
"entries going into a table on creation, extra memory allocation " "entries going into a table on creation, extra memory allocation "
"can be avoided. Returns the new table.") { "can be avoided. "
"Returns the new table.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
int32_t cap = janet_getnat(argv, 0); int32_t cap = janet_getnat(argv, 0);
return janet_wrap_table(janet_table(cap)); return janet_wrap_table(janet_table(cap));
} }
/*
uint32_t flags = janet_getflags(argv, 1, "kv");
if (flags == 0) return janet_wrap_table(janet_table(cap));
if (flags == 1) return janet_wrap_table(janet_table_weakk(cap));
if (flags == 2) return janet_wrap_table(janet_table_weakv(cap));
return janet_wrap_table(janet_table_weakkv(cap));
*/
JANET_CORE_FN(cfun_table_weak,
"(table/weak capacity)",
"Creates a new empty table with weak references to keys and values. Similar to `table/new`. "
"Returns the new table.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getnat(argv, 0);
return janet_wrap_table(janet_table_weakkv(cap));
}
JANET_CORE_FN(cfun_table_weak_keys,
"(table/weak-keys capacity)",
"Creates a new empty table with weak references to keys and normal references to values. Similar to `table/new`. "
"Returns the new table.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getnat(argv, 0);
return janet_wrap_table(janet_table_weakk(cap));
}
JANET_CORE_FN(cfun_table_weak_values,
"(table/weak-values capacity)",
"Creates a new empty table with normal references to keys and weak references to values. Similar to `table/new`. "
"Returns the new table.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getnat(argv, 0);
return janet_wrap_table(janet_table_weakv(cap));
}
JANET_CORE_FN(cfun_table_getproto, JANET_CORE_FN(cfun_table_getproto,
"(table/getproto tab)", "(table/getproto tab)",
@ -377,6 +427,9 @@ JANET_CORE_FN(cfun_table_proto_flatten,
void janet_lib_table(JanetTable *env) { void janet_lib_table(JanetTable *env) {
JanetRegExt table_cfuns[] = { JanetRegExt table_cfuns[] = {
JANET_CORE_REG("table/new", cfun_table_new), JANET_CORE_REG("table/new", cfun_table_new),
JANET_CORE_REG("table/weak", cfun_table_weak),
JANET_CORE_REG("table/weak-keys", cfun_table_weak_keys),
JANET_CORE_REG("table/weak-values", cfun_table_weak_values),
JANET_CORE_REG("table/to-struct", cfun_table_tostruct), JANET_CORE_REG("table/to-struct", cfun_table_tostruct),
JANET_CORE_REG("table/getproto", cfun_table_getproto), JANET_CORE_REG("table/getproto", cfun_table_getproto),
JANET_CORE_REG("table/setproto", cfun_table_setproto), JANET_CORE_REG("table/setproto", cfun_table_setproto),

View File

@ -953,6 +953,20 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
#endif #endif
#endif #endif
/* Better strerror (thread-safe if available) */
const char *janet_strerror(int e) {
#ifdef JANET_WINDOWS
/* Microsoft strerror seems sane here and is thread safe by default */
return strerror(e);
#elif defined(_GNU_SOURCE)
/* See https://linux.die.net/man/3/strerror_r */
return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
#else
strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
return janet_vm.strerror_buf;
#endif
}
/* Setting C99 standard makes this not available, but it should /* Setting C99 standard makes this not available, but it should
* work/link properly if we detect a BSD */ * work/link properly if we detect a BSD */
#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) #if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
@ -960,6 +974,7 @@ void arc4random_buf(void *buf, size_t nbytes);
#endif #endif
int janet_cryptorand(uint8_t *out, size_t n) { int janet_cryptorand(uint8_t *out, size_t n) {
#ifndef JANET_NO_CRYPTORAND
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
for (size_t i = 0; i < n; i += sizeof(unsigned int)) { for (size_t i = 0; i < n; i += sizeof(unsigned int)) {
unsigned int v; unsigned int v;
@ -971,7 +986,10 @@ int janet_cryptorand(uint8_t *out, size_t n) {
} }
} }
return 0; return 0;
#elif defined(JANET_LINUX) || defined(JANET_CYGWIN) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) ) #elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
arc4random_buf(out, n);
return 0;
#else
/* We should be able to call getrandom on linux, but it doesn't seem /* We should be able to call getrandom on linux, but it doesn't seem
to be uniformly supported on linux distros. to be uniformly supported on linux distros.
On Mac, arc4random_buf wasn't available on until 10.7. On Mac, arc4random_buf wasn't available on until 10.7.
@ -993,12 +1011,10 @@ int janet_cryptorand(uint8_t *out, size_t n) {
} }
RETRY_EINTR(rc, close(randfd)); RETRY_EINTR(rc, close(randfd));
return 0; return 0;
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) #endif
arc4random_buf(out, n);
return 0;
#else #else
(void) n;
(void) out; (void) out;
(void) n;
return -1; return -1;
#endif #endif
} }

View File

@ -49,11 +49,11 @@
#ifndef JANET_EXIT #ifndef JANET_EXIT
#include <stdio.h> #include <stdio.h>
#define JANET_EXIT(m) do { \ #define JANET_EXIT(m) do { \
fprintf(stderr, "C runtime error at line %d in file %s: %s\n",\ fprintf(stderr, "janet internal error at line %d in file %s: %s\n",\
__LINE__,\ __LINE__,\
__FILE__,\ __FILE__,\
(m));\ (m));\
exit(1);\ abort();\
} while (0) } while (0)
#endif #endif
@ -80,6 +80,8 @@ void janet_memempty(JanetKV *mem, int32_t count);
void *janet_memalloc_empty(int32_t count); void *janet_memalloc_empty(int32_t count);
JanetTable *janet_get_core_table(const char *name); JanetTable *janet_get_core_table(const char *name);
void janet_def_addflags(JanetFuncDef *def); void janet_def_addflags(JanetFuncDef *def);
void janet_buffer_dtostr(JanetBuffer *buffer, double x);
const char *janet_strerror(int e);
const void *janet_strbinsearch( const void *janet_strbinsearch(
const void *tab, const void *tab,
size_t tabcount, size_t tabcount,

View File

@ -116,7 +116,6 @@
#else #else
#define vm_maybe_auto_suspend(COND) do { \ #define vm_maybe_auto_suspend(COND) do { \
if ((COND) && janet_vm.auto_suspend) { \ if ((COND) && janet_vm.auto_suspend) { \
janet_vm.auto_suspend = 0; \
fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \ fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \ vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
} \ } \
@ -319,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
Janet lr = janet_method_lookup(rhs, rmethod); Janet lr = janet_method_lookup(rhs, rmethod);
Janet argv[2] = { rhs, lhs }; Janet argv[2] = { rhs, lhs };
if (janet_checktype(lr, JANET_NIL)) { if (janet_checktype(lr, JANET_NIL)) {
janet_panicf("could not find method :%s for %v, or :%s for %v", janet_panicf("could not find method :%s for %v or :%s for %v",
lmethod, lhs, lmethod, lhs,
rmethod, rhs); rmethod, rhs);
} }
@ -800,13 +799,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
VM_OP(JOP_JUMP) VM_OP(JOP_JUMP)
pc += DS; pc += DS;
vm_maybe_auto_suspend(DS < 0); vm_maybe_auto_suspend(DS <= 0);
vm_next(); vm_next();
VM_OP(JOP_JUMP_IF) VM_OP(JOP_JUMP_IF)
if (janet_truthy(stack[A])) { if (janet_truthy(stack[A])) {
pc += ES; pc += ES;
vm_maybe_auto_suspend(ES < 0); vm_maybe_auto_suspend(ES <= 0);
} else { } else {
pc++; pc++;
} }
@ -817,14 +816,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
pc++; pc++;
} else { } else {
pc += ES; pc += ES;
vm_maybe_auto_suspend(ES < 0); vm_maybe_auto_suspend(ES <= 0);
} }
vm_next(); vm_next();
VM_OP(JOP_JUMP_IF_NIL) VM_OP(JOP_JUMP_IF_NIL)
if (janet_checktype(stack[A], JANET_NIL)) { if (janet_checktype(stack[A], JANET_NIL)) {
pc += ES; pc += ES;
vm_maybe_auto_suspend(ES < 0); vm_maybe_auto_suspend(ES <= 0);
} else { } else {
pc++; pc++;
} }
@ -835,7 +834,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
pc++; pc++;
} else { } else {
pc += ES; pc += ES;
vm_maybe_auto_suspend(ES < 0); vm_maybe_auto_suspend(ES <= 0);
} }
vm_next(); vm_next();
@ -862,7 +861,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext(); vm_pcnext();
VM_OP(JOP_EQUALS_IMMEDIATE) VM_OP(JOP_EQUALS_IMMEDIATE)
stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) == (double) CS); stack[A] = janet_wrap_boolean(janet_checktype(stack[B], JANET_NUMBER) && (janet_unwrap_number(stack[B]) == (double) CS));
vm_pcnext(); vm_pcnext();
VM_OP(JOP_NOT_EQUALS) VM_OP(JOP_NOT_EQUALS)
@ -870,7 +869,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext(); vm_pcnext();
VM_OP(JOP_NOT_EQUALS_IMMEDIATE) VM_OP(JOP_NOT_EQUALS_IMMEDIATE)
stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) != (double) CS); stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
vm_pcnext(); vm_pcnext();
VM_OP(JOP_COMPARE) VM_OP(JOP_COMPARE)
@ -1586,9 +1585,11 @@ int janet_init(void) {
/* Garbage collection */ /* Garbage collection */
janet_vm.blocks = NULL; janet_vm.blocks = NULL;
janet_vm.weak_blocks = NULL;
janet_vm.next_collection = 0; janet_vm.next_collection = 0;
janet_vm.gc_interval = 0x400000; janet_vm.gc_interval = 0x400000;
janet_vm.block_count = 0; janet_vm.block_count = 0;
janet_vm.gc_mark_phase = 0;
janet_symcache_init(); janet_symcache_init();

View File

@ -43,10 +43,10 @@ int (janet_truthy)(Janet x) {
return janet_truthy(x); return janet_truthy(x);
} }
const JanetKV *(janet_unwrap_struct)(Janet x) { JanetStruct(janet_unwrap_struct)(Janet x) {
return janet_unwrap_struct(x); return janet_unwrap_struct(x);
} }
const Janet *(janet_unwrap_tuple)(Janet x) { JanetTuple(janet_unwrap_tuple)(Janet x) {
return janet_unwrap_tuple(x); return janet_unwrap_tuple(x);
} }
JanetFiber *(janet_unwrap_fiber)(Janet x) { JanetFiber *(janet_unwrap_fiber)(Janet x) {
@ -61,16 +61,16 @@ JanetTable *(janet_unwrap_table)(Janet x) {
JanetBuffer *(janet_unwrap_buffer)(Janet x) { JanetBuffer *(janet_unwrap_buffer)(Janet x) {
return janet_unwrap_buffer(x); return janet_unwrap_buffer(x);
} }
const uint8_t *(janet_unwrap_string)(Janet x) { JanetString(janet_unwrap_string)(Janet x) {
return janet_unwrap_string(x); return janet_unwrap_string(x);
} }
const uint8_t *(janet_unwrap_symbol)(Janet x) { JanetSymbol(janet_unwrap_symbol)(Janet x) {
return janet_unwrap_symbol(x); return janet_unwrap_symbol(x);
} }
const uint8_t *(janet_unwrap_keyword)(Janet x) { JanetKeyword(janet_unwrap_keyword)(Janet x) {
return janet_unwrap_keyword(x); return janet_unwrap_keyword(x);
} }
void *(janet_unwrap_abstract)(Janet x) { JanetAbstract(janet_unwrap_abstract)(Janet x) {
return janet_unwrap_abstract(x); return janet_unwrap_abstract(x);
} }
void *(janet_unwrap_pointer)(Janet x) { void *(janet_unwrap_pointer)(Janet x) {
@ -102,22 +102,22 @@ Janet(janet_wrap_false)(void) {
Janet(janet_wrap_boolean)(int x) { Janet(janet_wrap_boolean)(int x) {
return janet_wrap_boolean(x); return janet_wrap_boolean(x);
} }
Janet(janet_wrap_string)(const uint8_t *x) { Janet(janet_wrap_string)(JanetString x) {
return janet_wrap_string(x); return janet_wrap_string(x);
} }
Janet(janet_wrap_symbol)(const uint8_t *x) { Janet(janet_wrap_symbol)(JanetSymbol x) {
return janet_wrap_symbol(x); return janet_wrap_symbol(x);
} }
Janet(janet_wrap_keyword)(const uint8_t *x) { Janet(janet_wrap_keyword)(JanetKeyword x) {
return janet_wrap_keyword(x); return janet_wrap_keyword(x);
} }
Janet(janet_wrap_array)(JanetArray *x) { Janet(janet_wrap_array)(JanetArray *x) {
return janet_wrap_array(x); return janet_wrap_array(x);
} }
Janet(janet_wrap_tuple)(const Janet *x) { Janet(janet_wrap_tuple)(JanetTuple x) {
return janet_wrap_tuple(x); return janet_wrap_tuple(x);
} }
Janet(janet_wrap_struct)(const JanetKV *x) { Janet(janet_wrap_struct)(JanetStruct x) {
return janet_wrap_struct(x); return janet_wrap_struct(x);
} }
Janet(janet_wrap_fiber)(JanetFiber *x) { Janet(janet_wrap_fiber)(JanetFiber *x) {
@ -135,7 +135,7 @@ Janet(janet_wrap_cfunction)(JanetCFunction x) {
Janet(janet_wrap_table)(JanetTable *x) { Janet(janet_wrap_table)(JanetTable *x) {
return janet_wrap_table(x); return janet_wrap_table(x);
} }
Janet(janet_wrap_abstract)(void *x) { Janet(janet_wrap_abstract)(JanetAbstract x) {
return janet_wrap_abstract(x); return janet_wrap_abstract(x);
} }
Janet(janet_wrap_pointer)(void *x) { Janet(janet_wrap_pointer)(void *x) {
@ -317,4 +317,3 @@ JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer)
#undef JANET_WRAP_DEFINE #undef JANET_WRAP_DEFINE
#endif #endif

View File

@ -112,7 +112,8 @@ extern "C" {
|| defined(__s390x__) /* S390 64-bit (BE) */ \ || defined(__s390x__) /* S390 64-bit (BE) */ \
|| (defined(__ppc64__) || defined(__PPC64__)) \ || (defined(__ppc64__) || defined(__PPC64__)) \
|| defined(__aarch64__) /* ARM 64-bit */ \ || defined(__aarch64__) /* ARM 64-bit */ \
|| (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \
|| defined(__loongarch64) /* LoongArch64 64-bit */
#define JANET_64 1 #define JANET_64 1
#else #else
#define JANET_32 1 #define JANET_32 1
@ -234,10 +235,28 @@ extern "C" {
#define JANET_EV_KQUEUE #define JANET_EV_KQUEUE
#endif #endif
/* Use poll as last resort */
#if !defined(JANET_WINDOWS) && !defined(JANET_EV_EPOLL) && !defined(JANET_EV_KQUEUE)
#define JANET_EV_POLL
#endif
/* How to export symbols */ /* How to export symbols */
#ifndef JANET_EXPORT
#ifdef JANET_WINDOWS
#define JANET_EXPORT __declspec(dllexport)
#else
#define JANET_EXPORT __attribute__((visibility ("default")))
#endif
#endif
/* How declare API functions */
#ifndef JANET_API #ifndef JANET_API
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#ifdef JANET_DLL_IMPORT
#define JANET_API __declspec(dllimport)
#else
#define JANET_API __declspec(dllexport) #define JANET_API __declspec(dllexport)
#endif
#else #else
#define JANET_API __attribute__((visibility ("default"))) #define JANET_API __attribute__((visibility ("default")))
#endif #endif
@ -393,12 +412,11 @@ typedef enum {
JANET_SIGNAL_USER6, JANET_SIGNAL_USER6,
JANET_SIGNAL_USER7, JANET_SIGNAL_USER7,
JANET_SIGNAL_USER8, JANET_SIGNAL_USER8,
JANET_SIGNAL_USER9 JANET_SIGNAL_USER9,
JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8,
JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9,
} JanetSignal; } JanetSignal;
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
#define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8
/* Fiber statuses - mostly corresponds to signals. */ /* Fiber statuses - mostly corresponds to signals. */
typedef enum { typedef enum {
JANET_STATUS_DEAD, JANET_STATUS_DEAD,
@ -562,7 +580,7 @@ typedef void *JanetAbstract;
#define JANET_STREAM_CLOSED 0x1 #define JANET_STREAM_CLOSED 0x1
#define JANET_STREAM_SOCKET 0x2 #define JANET_STREAM_SOCKET 0x2
#define JANET_STREAM_IOCP 0x4 #define JANET_STREAM_UNREGISTERED 0x4
#define JANET_STREAM_READABLE 0x200 #define JANET_STREAM_READABLE 0x200
#define JANET_STREAM_WRITABLE 0x400 #define JANET_STREAM_WRITABLE 0x400
#define JANET_STREAM_ACCEPTABLE 0x800 #define JANET_STREAM_ACCEPTABLE 0x800
@ -570,62 +588,73 @@ typedef void *JanetAbstract;
#define JANET_STREAM_TOCLOSE 0x10000 #define JANET_STREAM_TOCLOSE 0x10000
typedef enum { typedef enum {
JANET_ASYNC_EVENT_INIT, JANET_ASYNC_EVENT_INIT = 0,
JANET_ASYNC_EVENT_MARK, JANET_ASYNC_EVENT_MARK = 1,
JANET_ASYNC_EVENT_DEINIT, JANET_ASYNC_EVENT_DEINIT = 2,
JANET_ASYNC_EVENT_CLOSE, JANET_ASYNC_EVENT_CLOSE = 3,
JANET_ASYNC_EVENT_ERR, JANET_ASYNC_EVENT_ERR = 4,
JANET_ASYNC_EVENT_HUP, JANET_ASYNC_EVENT_HUP = 5,
JANET_ASYNC_EVENT_READ, JANET_ASYNC_EVENT_READ = 6,
JANET_ASYNC_EVENT_WRITE, JANET_ASYNC_EVENT_WRITE = 7,
JANET_ASYNC_EVENT_CANCEL, JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */
JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */ JANET_ASYNC_EVENT_FAILED = 9 /* Used on windows for IOCP */
JANET_ASYNC_EVENT_USER
} JanetAsyncEvent; } JanetAsyncEvent;
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
typedef enum { typedef enum {
JANET_ASYNC_STATUS_NOT_DONE, JANET_ASYNC_LISTEN_READ = 1,
JANET_ASYNC_STATUS_DONE JANET_ASYNC_LISTEN_WRITE,
} JanetAsyncStatus; JANET_ASYNC_LISTEN_BOTH
} JanetAsyncMode;
/* Typedefs */
typedef struct JanetListenerState JanetListenerState;
typedef struct JanetStream JanetStream; typedef struct JanetStream JanetStream;
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event);
/* Wrapper around file descriptors and HANDLEs that can be polled. */ /* Wrapper around file descriptors and HANDLEs that can be polled. */
struct JanetStream { struct JanetStream {
JanetHandle handle; JanetHandle handle;
uint32_t flags; uint32_t flags;
/* Linked list of all in-flight IO routines for this stream */ uint32_t index;
JanetListenerState *state; JanetFiber *read_fiber;
JanetFiber *write_fiber;
const void *methods; /* Methods for this stream */ const void *methods; /* Methods for this stream */
/* internal - used to disallow multiple concurrent reads / writes on the same stream.
* this constraint may be lifted later but allowing such would require more internal book keeping
* for some implementations. You can read and write at the same time on the same stream, though. */
int _mask;
}; };
/* Interface for state machine based event loop */ typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
struct JanetListenerState {
JanetListener machine; /* Start listening for events from a stream on the current root fiber. After
JanetFiber *fiber; * calling this, users should call janet_await() before returning from the
JanetStream *stream; * current C Function. This also will call janet_await.
void *event; /* Used to pass data from asynchronous IO event. Contents depend on both * mode is which events to listen for, and callback is the function pointer to
implementation of the event loop and the particular event. */ * call when ever an event is sent from the event loop. state is an optional (can be NULL)
* pointer to data allocated with janet_malloc. This pointer will be passed to callback as
* fiber->ev_state. It will also be freed for you by the runtime when the event loop determines
* it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct. */
JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
/* Do not send any more events to the given callback. Call this after scheduling fiber to be resume
* or canceled. */
JANET_API void janet_async_end(JanetFiber *fiber);
/* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */
JANET_API void janet_async_in_flight(JanetFiber *fiber);
/* On some platforms, it is important to be able to control if a stream is edge-trigger or level triggered.
* For example, a server that is accepting connections might want to be level triggered or edge-triggered
* depending on expected service. */
JANET_API void janet_stream_edge_triggered(JanetStream *stream);
JANET_API void janet_stream_level_triggered(JanetStream *stream);
#endif
/* Janet uses atomic integers in several places for synchronization between threads and
* signals. Define them here */
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
void *tag; /* Used to associate listeners with an overlapped structure */ typedef long JanetAtomicInt;
int bytes; /* Used to track how many bytes were transfered. */ #else
#endif typedef int32_t JanetAtomicInt;
/* internal */
size_t _index;
int _mask;
JanetListenerState *_next;
};
#endif #endif
JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x);
JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x);
JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x);
/* We provide three possible implementations of Janets. The preferred /* We provide three possible implementations of Janets. The preferred
* nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
@ -653,10 +682,10 @@ struct JanetListenerState {
* external bindings, we should prefer using the Head structs directly, and * external bindings, we should prefer using the Head structs directly, and
* use the host language to add sugar around the manipulation of the Janet types. */ * use the host language to add sugar around the manipulation of the Janet types. */
JANET_API JanetStructHead *janet_struct_head(const JanetKV *st); JANET_API JanetStructHead *janet_struct_head(JanetStruct st);
JANET_API JanetAbstractHead *janet_abstract_head(const void *abstract); JANET_API JanetAbstractHead *janet_abstract_head(const void *abstract);
JANET_API JanetStringHead *janet_string_head(const uint8_t *s); JANET_API JanetStringHead *janet_string_head(JanetString s);
JANET_API JanetTupleHead *janet_tuple_head(const Janet *tuple); JANET_API JanetTupleHead *janet_tuple_head(JanetTuple tuple);
/* Some language bindings won't have access to the macro versions. */ /* Some language bindings won't have access to the macro versions. */
@ -665,16 +694,16 @@ JANET_API int janet_checktype(Janet x, JanetType type);
JANET_API int janet_checktypes(Janet x, int typeflags); JANET_API int janet_checktypes(Janet x, int typeflags);
JANET_API int janet_truthy(Janet x); JANET_API int janet_truthy(Janet x);
JANET_API const JanetKV *janet_unwrap_struct(Janet x); JANET_API JanetStruct janet_unwrap_struct(Janet x);
JANET_API const Janet *janet_unwrap_tuple(Janet x); JANET_API JanetTuple janet_unwrap_tuple(Janet x);
JANET_API JanetFiber *janet_unwrap_fiber(Janet x); JANET_API JanetFiber *janet_unwrap_fiber(Janet x);
JANET_API JanetArray *janet_unwrap_array(Janet x); JANET_API JanetArray *janet_unwrap_array(Janet x);
JANET_API JanetTable *janet_unwrap_table(Janet x); JANET_API JanetTable *janet_unwrap_table(Janet x);
JANET_API JanetBuffer *janet_unwrap_buffer(Janet x); JANET_API JanetBuffer *janet_unwrap_buffer(Janet x);
JANET_API const uint8_t *janet_unwrap_string(Janet x); JANET_API JanetString janet_unwrap_string(Janet x);
JANET_API const uint8_t *janet_unwrap_symbol(Janet x); JANET_API JanetSymbol janet_unwrap_symbol(Janet x);
JANET_API const uint8_t *janet_unwrap_keyword(Janet x); JANET_API JanetKeyword janet_unwrap_keyword(Janet x);
JANET_API void *janet_unwrap_abstract(Janet x); JANET_API JanetAbstract janet_unwrap_abstract(Janet x);
JANET_API void *janet_unwrap_pointer(Janet x); JANET_API void *janet_unwrap_pointer(Janet x);
JANET_API JanetFunction *janet_unwrap_function(Janet x); JANET_API JanetFunction *janet_unwrap_function(Janet x);
JANET_API JanetCFunction janet_unwrap_cfunction(Janet x); JANET_API JanetCFunction janet_unwrap_cfunction(Janet x);
@ -687,18 +716,18 @@ JANET_API Janet janet_wrap_number(double x);
JANET_API Janet janet_wrap_true(void); JANET_API Janet janet_wrap_true(void);
JANET_API Janet janet_wrap_false(void); JANET_API Janet janet_wrap_false(void);
JANET_API Janet janet_wrap_boolean(int x); JANET_API Janet janet_wrap_boolean(int x);
JANET_API Janet janet_wrap_string(const uint8_t *x); JANET_API Janet janet_wrap_string(JanetString x);
JANET_API Janet janet_wrap_symbol(const uint8_t *x); JANET_API Janet janet_wrap_symbol(JanetSymbol x);
JANET_API Janet janet_wrap_keyword(const uint8_t *x); JANET_API Janet janet_wrap_keyword(JanetKeyword x);
JANET_API Janet janet_wrap_array(JanetArray *x); JANET_API Janet janet_wrap_array(JanetArray *x);
JANET_API Janet janet_wrap_tuple(const Janet *x); JANET_API Janet janet_wrap_tuple(JanetTuple x);
JANET_API Janet janet_wrap_struct(const JanetKV *x); JANET_API Janet janet_wrap_struct(JanetStruct x);
JANET_API Janet janet_wrap_fiber(JanetFiber *x); JANET_API Janet janet_wrap_fiber(JanetFiber *x);
JANET_API Janet janet_wrap_buffer(JanetBuffer *x); JANET_API Janet janet_wrap_buffer(JanetBuffer *x);
JANET_API Janet janet_wrap_function(JanetFunction *x); JANET_API Janet janet_wrap_function(JanetFunction *x);
JANET_API Janet janet_wrap_cfunction(JanetCFunction x); JANET_API Janet janet_wrap_cfunction(JanetCFunction x);
JANET_API Janet janet_wrap_table(JanetTable *x); JANET_API Janet janet_wrap_table(JanetTable *x);
JANET_API Janet janet_wrap_abstract(void *x); JANET_API Janet janet_wrap_abstract(JanetAbstract x);
JANET_API Janet janet_wrap_pointer(void *x); JANET_API Janet janet_wrap_pointer(void *x);
JANET_API Janet janet_wrap_integer(int32_t x); JANET_API Janet janet_wrap_integer(int32_t x);
@ -730,6 +759,7 @@ JANET_API Janet janet_wrap_integer(int32_t x);
? janet_nanbox_isnumber(x) \ ? janet_nanbox_isnumber(x) \
: janet_nanbox_checkauxtype((x), (t))) : janet_nanbox_checkauxtype((x), (t)))
/* Use JANET_API so that modules will use a local version of these functions if possible */
JANET_API void *janet_nanbox_to_pointer(Janet x); JANET_API void *janet_nanbox_to_pointer(Janet x);
JANET_API Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask); JANET_API Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask);
JANET_API Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask); JANET_API Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask);
@ -776,14 +806,14 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
#define janet_wrap_pointer(s) janet_nanbox_wrap_((s), JANET_POINTER) #define janet_wrap_pointer(s) janet_nanbox_wrap_((s), JANET_POINTER)
/* Unwrap the pointer types */ /* Unwrap the pointer types */
#define janet_unwrap_struct(x) ((const JanetKV *)janet_nanbox_to_pointer(x)) #define janet_unwrap_struct(x) ((JanetStruct)janet_nanbox_to_pointer(x))
#define janet_unwrap_tuple(x) ((const Janet *)janet_nanbox_to_pointer(x)) #define janet_unwrap_tuple(x) ((JanetTuple)janet_nanbox_to_pointer(x))
#define janet_unwrap_fiber(x) ((JanetFiber *)janet_nanbox_to_pointer(x)) #define janet_unwrap_fiber(x) ((JanetFiber *)janet_nanbox_to_pointer(x))
#define janet_unwrap_array(x) ((JanetArray *)janet_nanbox_to_pointer(x)) #define janet_unwrap_array(x) ((JanetArray *)janet_nanbox_to_pointer(x))
#define janet_unwrap_table(x) ((JanetTable *)janet_nanbox_to_pointer(x)) #define janet_unwrap_table(x) ((JanetTable *)janet_nanbox_to_pointer(x))
#define janet_unwrap_buffer(x) ((JanetBuffer *)janet_nanbox_to_pointer(x)) #define janet_unwrap_buffer(x) ((JanetBuffer *)janet_nanbox_to_pointer(x))
#define janet_unwrap_string(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) #define janet_unwrap_string(x) ((JanetString)janet_nanbox_to_pointer(x))
#define janet_unwrap_symbol(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) #define janet_unwrap_symbol(x) ((JanetSymbol)janet_nanbox_to_pointer(x))
#define janet_unwrap_keyword(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) #define janet_unwrap_keyword(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
#define janet_unwrap_abstract(x) (janet_nanbox_to_pointer(x)) #define janet_unwrap_abstract(x) (janet_nanbox_to_pointer(x))
#define janet_unwrap_pointer(x) (janet_nanbox_to_pointer(x)) #define janet_unwrap_pointer(x) (janet_nanbox_to_pointer(x))
@ -825,15 +855,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
#define janet_wrap_cfunction(s) janet_nanbox32_from_tagp(JANET_CFUNCTION, (void *)(s)) #define janet_wrap_cfunction(s) janet_nanbox32_from_tagp(JANET_CFUNCTION, (void *)(s))
#define janet_wrap_pointer(s) janet_nanbox32_from_tagp(JANET_POINTER, (void *)(s)) #define janet_wrap_pointer(s) janet_nanbox32_from_tagp(JANET_POINTER, (void *)(s))
#define janet_unwrap_struct(x) ((const JanetKV *)(x).tagged.payload.pointer) #define janet_unwrap_struct(x) ((JanetStruct)(x).tagged.payload.pointer)
#define janet_unwrap_tuple(x) ((const Janet *)(x).tagged.payload.pointer) #define janet_unwrap_tuple(x) ((JanetTuple)(x).tagged.payload.pointer)
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).tagged.payload.pointer) #define janet_unwrap_fiber(x) ((JanetFiber *)(x).tagged.payload.pointer)
#define janet_unwrap_array(x) ((JanetArray *)(x).tagged.payload.pointer) #define janet_unwrap_array(x) ((JanetArray *)(x).tagged.payload.pointer)
#define janet_unwrap_table(x) ((JanetTable *)(x).tagged.payload.pointer) #define janet_unwrap_table(x) ((JanetTable *)(x).tagged.payload.pointer)
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).tagged.payload.pointer) #define janet_unwrap_buffer(x) ((JanetBuffer *)(x).tagged.payload.pointer)
#define janet_unwrap_string(x) ((const uint8_t *)(x).tagged.payload.pointer) #define janet_unwrap_string(x) ((JanetString)(x).tagged.payload.pointer)
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).tagged.payload.pointer) #define janet_unwrap_symbol(x) ((JanetSymbol)(x).tagged.payload.pointer)
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).tagged.payload.pointer) #define janet_unwrap_keyword(x) ((JanetKeyword)(x).tagged.payload.pointer)
#define janet_unwrap_abstract(x) ((x).tagged.payload.pointer) #define janet_unwrap_abstract(x) ((x).tagged.payload.pointer)
#define janet_unwrap_pointer(x) ((x).tagged.payload.pointer) #define janet_unwrap_pointer(x) ((x).tagged.payload.pointer)
#define janet_unwrap_function(x) ((JanetFunction *)(x).tagged.payload.pointer) #define janet_unwrap_function(x) ((JanetFunction *)(x).tagged.payload.pointer)
@ -848,15 +878,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
#define janet_truthy(x) \ #define janet_truthy(x) \
((x).type != JANET_NIL && ((x).type != JANET_BOOLEAN || ((x).as.u64 & 0x1))) ((x).type != JANET_NIL && ((x).type != JANET_BOOLEAN || ((x).as.u64 & 0x1)))
#define janet_unwrap_struct(x) ((const JanetKV *)(x).as.pointer) #define janet_unwrap_struct(x) ((JanetStruct)(x).as.pointer)
#define janet_unwrap_tuple(x) ((const Janet *)(x).as.pointer) #define janet_unwrap_tuple(x) ((JanetTuple)(x).as.pointer)
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).as.pointer) #define janet_unwrap_fiber(x) ((JanetFiber *)(x).as.pointer)
#define janet_unwrap_array(x) ((JanetArray *)(x).as.pointer) #define janet_unwrap_array(x) ((JanetArray *)(x).as.pointer)
#define janet_unwrap_table(x) ((JanetTable *)(x).as.pointer) #define janet_unwrap_table(x) ((JanetTable *)(x).as.pointer)
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).as.pointer) #define janet_unwrap_buffer(x) ((JanetBuffer *)(x).as.pointer)
#define janet_unwrap_string(x) ((const uint8_t *)(x).as.pointer) #define janet_unwrap_string(x) ((JanetString)(x).as.pointer)
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer) #define janet_unwrap_symbol(x) ((JanetSymbol)(x).as.pointer)
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).as.pointer) #define janet_unwrap_keyword(x) ((JanetKeyword)(x).as.pointer)
#define janet_unwrap_abstract(x) ((x).as.pointer) #define janet_unwrap_abstract(x) ((x).as.pointer)
#define janet_unwrap_pointer(x) ((x).as.pointer) #define janet_unwrap_pointer(x) ((x).as.pointer)
#define janet_unwrap_function(x) ((JanetFunction *)(x).as.pointer) #define janet_unwrap_function(x) ((JanetFunction *)(x).as.pointer)
@ -889,7 +919,7 @@ struct JanetGCObject {
int32_t flags; int32_t flags;
union { union {
JanetGCObject *next; JanetGCObject *next;
int32_t refcount; /* For threaded abstract types */ volatile JanetAtomicInt refcount; /* For threaded abstract types */
} data; } data;
}; };
@ -912,8 +942,10 @@ struct JanetFiber {
* that is, fibers that are scheduled on the event loop and behave much like threads * that is, fibers that are scheduled on the event loop and behave much like threads
* in a multi-tasking system. It would be possible to move these fields to a new * in a multi-tasking system. It would be possible to move these fields to a new
* type, say "JanetTask", that as separate from fibers to save a bit of space. */ * type, say "JanetTask", that as separate from fibers to save a bit of space. */
JanetListenerState *waiting;
uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */ uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
JanetEVCallback ev_callback; /* Call this before starting scheduled fibers */
JanetStream *ev_stream; /* which stream we are waiting on */
void *ev_state; /* Extra data for ev callback state. On windows, first element must be OVERLAPPED. */
void *supervisor_channel; /* Channel to push self to when complete */ void *supervisor_channel; /* Channel to push self to when complete */
#endif #endif
}; };
@ -1388,9 +1420,7 @@ JANET_API void janet_stream_flags(JanetStream *stream, uint32_t flags);
JANET_API void janet_schedule(JanetFiber *fiber, Janet value); JANET_API void janet_schedule(JanetFiber *fiber, Janet value);
JANET_API void janet_cancel(JanetFiber *fiber, Janet value); JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig); JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig);
/* Start a state machine listening for events from a stream */
JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user);
/* Shorthand for yielding to event loop in C */ /* Shorthand for yielding to event loop in C */
JANET_NO_RETURN JANET_API void janet_await(void); JANET_NO_RETURN JANET_API void janet_await(void);
@ -1478,23 +1508,22 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value); JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
/* Read async from a stream */ /* Read async from a stream */
JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); JANET_NO_RETURN JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); JANET_NO_RETURN JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
#ifdef JANET_NET #ifdef JANET_NET
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); JANET_NO_RETURN JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
JANET_API void janet_ev_connect(JanetStream *stream, int flags);
#endif #endif
/* Write async to a stream */ /* Write async to a stream */
JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf); JANET_NO_RETURN JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str); JANET_NO_RETURN JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
#ifdef JANET_NET #ifdef JANET_NET
JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags); JANET_NO_RETURN JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags); JANET_NO_RETURN JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags); JANET_NO_RETURN JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags); JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
#endif #endif
#endif #endif
@ -1583,6 +1612,7 @@ JANET_API double janet_rng_double(JanetRNG *rng);
/* Array functions */ /* Array functions */
JANET_API JanetArray *janet_array(int32_t capacity); JANET_API JanetArray *janet_array(int32_t capacity);
JANET_API JanetArray *janet_array_weak(int32_t capacity);
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n); JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth); JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth);
JANET_API void janet_array_setcount(JanetArray *array, int32_t count); JANET_API void janet_array_setcount(JanetArray *array, int32_t count);
@ -1612,7 +1642,7 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
#define JANET_TUPLE_FLAG_BRACKETCTOR 0x10000 #define JANET_TUPLE_FLAG_BRACKETCTOR 0x10000
#define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data))) #define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data)))
#define janet_tuple_from_head(gcobject) ((const Janet *)((char *)gcobject + offsetof(JanetTupleHead, data))) #define janet_tuple_from_head(gcobject) ((JanetTuple)((char *)gcobject + offsetof(JanetTupleHead, data)))
#define janet_tuple_length(t) (janet_tuple_head(t)->length) #define janet_tuple_length(t) (janet_tuple_head(t)->length)
#define janet_tuple_hash(t) (janet_tuple_head(t)->hash) #define janet_tuple_hash(t) (janet_tuple_head(t)->hash)
#define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line) #define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line)
@ -1658,7 +1688,7 @@ JANET_API JanetSymbol janet_symbol_gen(void);
/* Structs */ /* Structs */
#define janet_struct_head(t) ((JanetStructHead *)((char *)t - offsetof(JanetStructHead, data))) #define janet_struct_head(t) ((JanetStructHead *)((char *)t - offsetof(JanetStructHead, data)))
#define janet_struct_from_head(t) ((const JanetKV *)((char *)gcobject + offsetof(JanetStructHead, data))) #define janet_struct_from_head(t) ((JanetStruct)((char *)gcobject + offsetof(JanetStructHead, data)))
#define janet_struct_length(t) (janet_struct_head(t)->length) #define janet_struct_length(t) (janet_struct_head(t)->length)
#define janet_struct_capacity(t) (janet_struct_head(t)->capacity) #define janet_struct_capacity(t) (janet_struct_head(t)->capacity)
#define janet_struct_hash(t) (janet_struct_head(t)->hash) #define janet_struct_hash(t) (janet_struct_head(t)->hash)
@ -1799,6 +1829,7 @@ JANET_API void janet_vm_free(JanetVM *vm);
JANET_API void janet_vm_save(JanetVM *into); JANET_API void janet_vm_save(JanetVM *into);
JANET_API void janet_vm_load(JanetVM *from); JANET_API void janet_vm_load(JanetVM *from);
JANET_API void janet_interpreter_interrupt(JanetVM *vm); JANET_API void janet_interpreter_interrupt(JanetVM *vm);
JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm);
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out); JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig); JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig);
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f); JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
@ -1822,6 +1853,7 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
#define JANET_SANDBOX_FS_TEMP 1024 #define JANET_SANDBOX_FS_TEMP 1024
#define JANET_SANDBOX_FFI_USE 2048 #define JANET_SANDBOX_FFI_USE 2048
#define JANET_SANDBOX_FFI_JIT 4096 #define JANET_SANDBOX_FFI_JIT 4096
#define JANET_SANDBOX_SIGNAL 8192
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT) #define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP) #define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN) #define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)
@ -1945,10 +1977,10 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun);
#endif #endif
#ifndef JANET_ENTRY_NAME #ifndef JANET_ENTRY_NAME
#define JANET_MODULE_ENTRY \ #define JANET_MODULE_ENTRY \
JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \ JANET_MODULE_PREFIX JANET_EXPORT JanetBuildConfig _janet_mod_config(void) { \
return janet_config_current(); \ return janet_config_current(); \
} \ } \
JANET_MODULE_PREFIX JANET_API void _janet_init JANET_MODULE_PREFIX JANET_EXPORT void _janet_init
#else #else
#define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME #define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME
#endif #endif
@ -2072,6 +2104,7 @@ JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx);
JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len); JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len);
JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx); JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx);
JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size); JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size);
JANET_API JanetAbstract janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size);
JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p); JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p);
JANET_API void janet_register_abstract_type(const JanetAbstractType *at); JANET_API void janet_register_abstract_type(const JanetAbstractType *at);
@ -2114,7 +2147,9 @@ typedef enum {
RULE_LINE, /* [tag] */ RULE_LINE, /* [tag] */
RULE_COLUMN, /* [tag] */ RULE_COLUMN, /* [tag] */
RULE_UNREF, /* [rule, tag] */ RULE_UNREF, /* [rule, tag] */
RULE_CAPTURE_NUM /* [rule, tag] */ RULE_CAPTURE_NUM, /* [rule, tag] */
RULE_SUB, /* [rule, rule] */
RULE_SPLIT /* [rule, rule] */
} JanetPegOpcod; } JanetPegOpcod;
typedef struct { typedef struct {

View File

@ -502,10 +502,10 @@ static void kright(void) {
} }
static void krightw(void) { static void krightw(void) {
while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) { while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
gbl_pos++; gbl_pos++;
} }
while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) { while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
gbl_pos++; gbl_pos++;
} }
refresh(); refresh();

View File

@ -19,7 +19,7 @@
(frame :source) (frame :source-line))) (frame :source) (frame :source-line)))
(if x (if x
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x)) (when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x)) (do (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
x) x)
(defmacro assert-error (defmacro assert-error
@ -34,17 +34,17 @@
(defmacro assert-no-error (defmacro assert-no-error
[msg & forms] [msg & forms]
(def errsym (keyword (gensym))) (def e (gensym))
~(assert (not= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg)) (def f (gensym))
(if is-verbose
~(try (do ,;forms (,assert true ,msg)) ([,e ,f] (,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m ")))
~(try (do ,;forms (,assert true ,msg)) ([_] (,assert false ,msg)))))
(defn start-suite [&opt x] (defn start-suite [&opt x]
(default x (dyn :current-file)) (default x (dyn :current-file))
(set suite-name (set suite-name
(cond (cond
(number? x) (string x) (number? x) (string x)
(string? x) (string/slice x
(length "test/suite-")
(- (inc (length ".janet"))))
(string x))) (string x)))
(set start-time (os/clock)) (set start-time (os/clock))
(eprint "Starting suite " suite-name "...")) (eprint "Starting suite " suite-name "..."))

View File

@ -51,5 +51,13 @@
(def f (asm (disasm (fn [x] (fn [y] (+ x y)))))) (def f (asm (disasm (fn [x] (fn [y] (+ x y))))))
(assert (= ((f 10) 37) 47) "asm environment tables") (assert (= ((f 10) 37) 47) "asm environment tables")
# issue #1424
(assert-no-error "arity > used slots (issue #1424)"
(asm
(disasm
(fn []
(def foo (fn [one two] one))
(foo 100 200)))))
(end-suite) (end-suite)

View File

@ -113,13 +113,22 @@
# 7478ad11 # 7478ad11
(assert (= nil (any? [])) "any? 1") (assert (= nil (any? [])) "any? 1")
(assert (= nil (any? [false nil])) "any? 2") (assert (= nil (any? [false nil])) "any? 2")
(assert (= nil (any? [nil false])) "any? 3") (assert (= false (any? [nil false])) "any? 3")
(assert (= 1 (any? [1])) "any? 4") (assert (= 1 (any? [1])) "any? 4")
(assert (nan? (any? [nil math/nan nil])) "any? 5") (assert (nan? (any? [nil math/nan nil])) "any? 5")
(assert (= true (assert (= true
(any? [nil nil false nil nil true nil nil nil nil false :a nil])) (any? [nil nil false nil nil true nil nil nil nil false :a nil]))
"any? 6") "any? 6")
(assert (= true (every? [])) "every? 1")
(assert (= true (every? [1 true])) "every? 2")
(assert (= 1 (every? [true 1])) "every? 3")
(assert (= nil (every? [nil])) "every? 4")
(assert (= 2 (every? [1 math/nan 2])) "every? 5")
(assert (= false
(every? [1 1 true 1 1 false 1 1 1 1 true :a nil]))
"every? 6")
# Some higher order functions and macros # Some higher order functions and macros
# 5e2de33 # 5e2de33
(def my-array @[1 2 3 4 5 6]) (def my-array @[1 2 3 4 5 6])
@ -177,6 +186,11 @@
(assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) (assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]])
"nested seq") "nested seq")
# :unless modifier
(assert (deep= (seq [i :range [0 10] :unless (odd? i)] i)
@[0 2 4 6 8])
":unless modifier")
# 515891b03 # 515891b03
(assert (deep= (tabseq [i :in (range 3)] i (* 3 i)) (assert (deep= (tabseq [i :in (range 3)] i (* 3 i))
@{0 0 1 3 2 6})) @{0 0 1 3 2 6}))
@ -195,6 +209,12 @@
(assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) (assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x))
"loop :down-to") "loop :down-to")
# one-term :range forms
(assert (deep= (seq [x :range [10]] x) (seq [x :range [0 10]] x))
"one-term :range")
(assert (deep= (seq [x :down [10]] x) (seq [x :down [10 0]] x))
"one-term :down")
# 7880d7320 # 7880d7320
(def res @{}) (def res @{})
(loop [[k v] :pairs @{1 2 3 4 5 6}] (loop [[k v] :pairs @{1 2 3 4 5 6}]
@ -221,6 +241,16 @@
(assert (pos? (% x 4)) "generate in loop")) (assert (pos? (% x 4)) "generate in loop"))
(assert (= gencount 75) "generate loop count") (assert (= gencount 75) "generate loop count")
# more loop checks
(assert (deep= (seq [i :range [0 10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 1")
(assert (deep= (seq [i :range [0 10 2]] i) @[0 2 4 6 8]) "seq 2")
(assert (deep= (seq [i :range [10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 3")
(assert (deep= (seq [i :range-to [10]] i) @[0 1 2 3 4 5 6 7 8 9 10]) "seq 4")
(def gen (generate [x :range-to [0 nil 2]] x))
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit")
(def gen (generate [x :range [0 nil 2]] x))
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit 2")
# Even and odd # Even and odd
# ff163a5ae # ff163a5ae
(assert (odd? 9) "odd? 1") (assert (odd? 9) "odd? 1")
@ -334,6 +364,13 @@
"sort 5") "sort 5")
(assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6") (assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6")
# #1283
(assert (deep=
(partition 2 (generate [ i :in [:a :b :c :d :e]] i))
'@[(:a :b) (:c :d) (:e)]))
(assert (= (mean (generate [i :in [2 3 5 7 11]] i))
5.6))
# And and or # And and or
# c16a9d846 # c16a9d846
(assert (= (and true true) true) "and true true") (assert (= (and true true) true) "and true true")
@ -897,4 +934,49 @@
[:strict 3 4 "bar-oops"]]) [:strict 3 4 "bar-oops"]])
"maclintf 2") "maclintf 2")
# Bad bytecode wrt. using result from break expression
(defn bytecode-roundtrip
[f]
(assert-no-error "bytecode round-trip" (unmarshal (marshal f make-image-dict))))
(defn case-1 [&] (def x (break 1)))
(bytecode-roundtrip case-1)
(defn foo [&])
(defn case-2 [&]
(foo (break (foo)))
(foo))
(bytecode-roundtrip case-2)
(defn case-3 [&]
(def x (break (do (foo)))))
(bytecode-roundtrip case-3)
(defn case-4 [&]
(def x (break (break (foo)))))
(bytecode-roundtrip case-4)
(defn case-4 [&]
(def x (break (break (break)))))
(bytecode-roundtrip case-4)
(defn case-5 []
(def foo (fn [one two] one))
(foo 100 200))
(bytecode-roundtrip case-5)
# Debug bytecode of these functions
# (pp (disasm case-1))
# (pp (disasm case-2))
# (pp (disasm case-3))
# Regression #1330
(defn regress-1330 [&]
(def a [1 2 3])
(def b [;a])
(identity a))
(assert (= [1 2 3] (regress-1330)) "regression 1330")
# Issue 1341
(assert (= () '() (macex '())) "macex ()")
(assert (= '[] (macex '[])) "macex []")
(assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn")
(assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2))))
(end-suite) (end-suite)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2023 Calvin Rose # Copyright (c) 2024 Calvin Rose
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to # of this software and associated documentation files (the "Software"), to
@ -77,6 +77,54 @@
(buffer/push-string b5 "456" @"789") (buffer/push-string b5 "456" @"789")
(assert (= "123456789" (string b5)) "buffer/push-buffer 2") (assert (= "123456789" (string b5)) "buffer/push-buffer 2")
(def buffer-uint16-be @"")
(buffer/push-uint16 buffer-uint16-be :be 0x0102)
(assert (= "\x01\x02" (string buffer-uint16-be)) "buffer/push-uint16 big endian")
(def buffer-uint16-le @"")
(buffer/push-uint16 buffer-uint16-le :le 0x0102)
(assert (= "\x02\x01" (string buffer-uint16-le)) "buffer/push-uint16 little endian")
(def buffer-uint16-negative @"")
(buffer/push-uint16 buffer-uint16-negative :be -1)
(assert (= "\xff\xff" (string buffer-uint16-negative)) "buffer/push-uint16 negative")
(def buffer-uint32-be @"")
(buffer/push-uint32 buffer-uint32-be :be 0x01020304)
(assert (= "\x01\x02\x03\x04" (string buffer-uint32-be)) "buffer/push-uint32 big endian")
(def buffer-uint32-le @"")
(buffer/push-uint32 buffer-uint32-le :le 0x01020304)
(assert (= "\x04\x03\x02\x01" (string buffer-uint32-le)) "buffer/push-uint32 little endian")
(def buffer-uint32-negative @"")
(buffer/push-uint32 buffer-uint32-negative :be -1)
(assert (= "\xff\xff\xff\xff" (string buffer-uint32-negative)) "buffer/push-uint32 negative")
(def buffer-float32-be @"")
(buffer/push-float32 buffer-float32-be :be 1.234)
(assert (= "\x3f\x9d\xf3\xb6" (string buffer-float32-be)) "buffer/push-float32 big endian")
(def buffer-float32-le @"")
(buffer/push-float32 buffer-float32-le :le 1.234)
(assert (= "\xb6\xf3\x9d\x3f" (string buffer-float32-le)) "buffer/push-float32 little endian")
(def buffer-float64-be @"")
(buffer/push-float64 buffer-float64-be :be 1.234)
(assert (= "\x3f\xf3\xbe\x76\xc8\xb4\x39\x58" (string buffer-float64-be)) "buffer/push-float64 big endian")
(def buffer-float64-le @"")
(buffer/push-float64 buffer-float64-le :le 1.234)
(assert (= "\x58\x39\xb4\xc8\x76\xbe\xf3\x3f" (string buffer-float64-le)) "buffer/push-float64 little endian")
# Buffer from bytes
(assert (deep= @"" (buffer/from-bytes)) "buffer/from-bytes 1")
(assert (deep= @"ABC" (buffer/from-bytes 65 66 67)) "buffer/from-bytes 2")
(assert (deep= @"0123456789" (buffer/from-bytes ;(range 48 58))) "buffer/from-bytes 3")
(assert (= 0 (length (buffer/from-bytes))) "buffer/from-bytes 4")
(assert (= 5 (length (buffer/from-bytes ;(range 5)))) "buffer/from-bytes 5")
(assert-error "bad slot #1, expected 32 bit signed integer" (buffer/from-bytes :abc))
# some tests for buffer/format # some tests for buffer/format
# 029394d # 029394d
(assert (= (string (buffer/format @"" "pi = %6.3f" math/pi)) "pi = 3.142") (assert (= (string (buffer/format @"" "pi = %6.3f" math/pi)) "pi = 3.142")
@ -114,8 +162,20 @@
(assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4")) (assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4"))
"buffer/push-at 3") "buffer/push-at 3")
# 4782a76 # buffer/format-at
(assert (= 10 (do (var x 10) (def y x) (++ x) y)) "no invalid aliasing") (def start-buf (buffer/new-filled 100 (chr "x")))
(buffer/format-at start-buf 50 "aa%dbb" 32)
(assert (= (string start-buf) (string (string/repeat "x" 50) "aa32bb" (string/repeat "x" 44)))
"buffer/format-at 1")
(assert
(deep=
(buffer/format @"" "%j" [1 2 3 :a :b :c])
(buffer/format-at @"" 0 "%j" [1 2 3 :a :b :c]))
"buffer/format-at empty buffer")
(def buf @"xxxyyy")
(buffer/format-at buf -4 "xxx")
(assert (= (string buf) "xxxxxx") "buffer/format-at negative index")
(assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc"))
(end-suite) (end-suite)

View File

@ -172,5 +172,10 @@
(assert (= (length (range -10)) 0) "(range -10)") (assert (= (length (range -10)) 0) "(range -10)")
(assert (= (length (range 1 10)) 9) "(range 1 10)") (assert (= (length (range 1 10)) 9) "(range 1 10)")
# iterating over generator
(assert-no-error "iterate over coro 1" (values (generate [x :range [0 10]] x)))
(assert-no-error "iterate over coro 2" (keys (generate [x :range [0 10]] x)))
(assert-no-error "iterate over coro 3" (pairs (generate [x :range [0 10]] x)))
(end-suite) (end-suite)

View File

@ -21,9 +21,12 @@
(import ./helper :prefix "" :exit true) (import ./helper :prefix "" :exit true)
(start-suite) (start-suite)
(def test-port (os/getenv "JANET_TEST_PORT" "8761"))
(def test-host (os/getenv "JANET_TEST_HOST" "127.0.0.1"))
# Subprocess # Subprocess
# 5e1a8c86f # 5e1a8c86f
(def janet (dyn :executable)) (def janet (dyn *executable*))
# Subprocess should inherit the "RUN" parameter for fancy testing # Subprocess should inherit the "RUN" parameter for fancy testing
(def run (filter next (string/split " " (os/getenv "SUBRUN" "")))) (def run (filter next (string/split " " (os/getenv "SUBRUN" ""))))
@ -192,11 +195,11 @@
(net/write stream b) (net/write stream b)
(buffer/clear b))) (buffer/clear b)))
(def s (net/server "127.0.0.1" "8000" handler)) (def s (net/server test-host test-port handler))
(assert s "made server 1") (assert s "made server 1")
(defn test-echo [msg] (defn test-echo [msg]
(with [conn (net/connect "127.0.0.1" "8000")] (with [conn (net/connect test-host test-port)]
(net/write conn msg) (net/write conn msg)
(def res (net/read conn 1024)) (def res (net/read conn 1024))
(assert (= (string res) msg) (string "echo " msg)))) (assert (= (string res) msg) (string "echo " msg))))
@ -205,7 +208,8 @@
(test-echo "world") (test-echo "world")
(test-echo (string/repeat "abcd" 200)) (test-echo (string/repeat "abcd" 200))
(:close s)) (:close s)
(gccollect))
# Test on both server and client # Test on both server and client
# 504411e # 504411e
@ -215,18 +219,18 @@
# prevent immediate close # prevent immediate close
(ev/read stream 1) (ev/read stream 1)
(def [host port] (net/localname stream)) (def [host port] (net/localname stream))
(assert (= host "127.0.0.1") "localname host server") (assert (= host test-host) "localname host server")
(assert (= port 8000) "localname port server"))) (assert (= port (scan-number test-port)) "localname port server")))
# Test localname and peername # Test localname and peername
# 077bf5eba # 077bf5eba
(repeat 10 (repeat 10
(with [s (net/server "127.0.0.1" "8000" names-handler)] (with [s (net/server test-host test-port names-handler)]
(repeat 10 (repeat 10
(with [conn (net/connect "127.0.0.1" "8000")] (with [conn (net/connect test-host test-port)]
(def [host port] (net/peername conn)) (def [host port] (net/peername conn))
(assert (= host "127.0.0.1") "peername host client ") (assert (= host test-host) "peername host client ")
(assert (= port 8000) "peername port client") (assert (= port (scan-number test-port)) "peername port client")
# let server close # let server close
(ev/write conn " ")))) (ev/write conn " "))))
(gccollect)) (gccollect))
@ -344,5 +348,31 @@
(ev/go |(ev/chan-close ch)) (ev/go |(ev/chan-close ch))
(assert (= (ev/select [ch 1]) [:close ch])) (assert (= (ev/select [ch 1]) [:close ch]))
(end-suite) # ev/gather check
(defn exec-slurp
"Read stdout of subprocess and return it trimmed in a string."
[& args]
(def env (os/environ))
(put env :out :pipe)
(def proc (os/spawn args :epx env))
(def out (get proc :out))
(def buf @"")
(ev/gather
(:read out :all buf)
(:wait proc))
(string/trimr buf))
(assert-no-error
"ev/with-deadline 1"
(assert (= "hi"
(ev/with-deadline
10
(exec-slurp ;run janet "-e" "(print :hi)")))
"exec-slurp 1"))
# valgrind-able check for #1337
(def superv (ev/chan 10))
(def f (ev/go |(ev/sleep 1e9) nil superv))
(ev/cancel f (gensym))
(ev/take superv)
(end-suite)

View File

@ -126,7 +126,7 @@
(assert (deep= (int/to-bytes (u64 300) :be buf2) (assert (deep= (int/to-bytes (u64 300) :be buf2)
@"abcd\x00\x00\x00\x00\x00\x00\x01\x2C"))) @"abcd\x00\x00\x00\x00\x00\x00\x01\x2C")))
# int/s64 and int/u64 paramater type checking # int/s64 and int/u64 parameter type checking
# 6aea7c7f7 # 6aea7c7f7
(assert-error (assert-error
"bad value passed to int/to-bytes" "bad value passed to int/to-bytes"

View File

@ -96,11 +96,23 @@
(assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer") (assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer")
(assert (= (length buf) 2) "cryptorand appends to buffer")) (assert (= (length buf) 2) "cryptorand appends to buffer"))
(assert-no-error "realtime clock" (os/clock))
(assert-no-error "realtime clock" (os/clock nil))
(assert-no-error "realtime clock" (os/clock nil nil))
# 80db68210 # 80db68210
(assert-no-error "realtime clock" (os/clock :realtime)) (assert-no-error "realtime clock" (os/clock :realtime))
(assert-no-error "cputime clock" (os/clock :cputime)) (assert-no-error "cputime clock" (os/clock :cputime))
(assert-no-error "monotonic clock" (os/clock :monotonic)) (assert-no-error "monotonic clock" (os/clock :monotonic))
(assert-no-error "realtime clock double output" (os/clock nil :double))
(assert-no-error "realtime clock int output" (os/clock nil :int))
(assert-no-error "realtime clock tuple output" (os/clock nil :tuple))
(assert-error "invalid clock" (os/clock :a))
(assert-error "invalid output" (os/clock :realtime :b))
(assert-error "invalid clock and output" (os/clock :a :b))
(def before (os/clock :monotonic)) (def before (os/clock :monotonic))
(def after (os/clock :monotonic)) (def after (os/clock :monotonic))
(assert (>= after before) "monotonic clock is monotonic") (assert (>= after before) "monotonic clock is monotonic")
@ -148,4 +160,3 @@
{:out dn :err dn}))) {:out dn :err dn})))
(end-suite) (end-suite)

View File

@ -263,6 +263,9 @@
(marshpeg '(if-not "abcdf" 123)) (marshpeg '(if-not "abcdf" 123))
(marshpeg ~(cmt "abcdf" ,identity)) (marshpeg ~(cmt "abcdf" ,identity))
(marshpeg '(group "abc")) (marshpeg '(group "abc"))
(marshpeg '(sub "abcdf" "abc"))
(marshpeg '(* (sub 1 1)))
(marshpeg '(split "," (+ "a" "b" "c")))
# Peg swallowing errors # Peg swallowing errors
# 159651117 # 159651117
@ -660,5 +663,98 @@
(peg/match '(if (not (* (constant 7) "a")) "hello") "hello") (peg/match '(if (not (* (constant 7) "a")) "hello") "hello")
@[]) "peg if not") @[]) "peg if not")
(defn test [name peg input expected]
(assert (deep= (peg/match peg input) expected) name))
(test "sub: matches the same input twice"
~(sub "abcd" "abc")
"abcdef"
@[])
(test "sub: second pattern cannot match more than the first pattern"
~(sub "abcd" "abcde")
"abcdef"
nil)
(test "sub: fails if first pattern fails"
~(sub "x" "abc")
"abcdef"
nil)
(test "sub: fails if second pattern fails"
~(sub "abc" "x")
"abcdef"
nil)
(test "sub: keeps captures from both patterns"
~(sub '"abcd" '"abc")
"abcdef"
@["abcd" "abc"])
(test "sub: second pattern can reference captures from first"
~(* (constant 5 :tag) (sub (capture "abc" :tag) (backref :tag)))
"abcdef"
@[5 "abc" "abc"])
(test "sub: second pattern can't see past what the first pattern matches"
~(sub "abc" (* "abc" -1))
"abcdef"
@[])
(test "sub: positions inside second match are still relative to the entire input"
~(* "one\ntw" (sub "o" (* ($) (line) (column))))
"one\ntwo\nthree\n"
@[6 2 3])
(test "sub: advances to the end of the first pattern's match"
~(* (sub "abc" "ab") "d")
"abcdef"
@[])
(test "split: basic functionality"
~(split "," '1)
"a,b,c"
@["a" "b" "c"])
(test "split: drops captures from separator pattern"
~(split '"," '1)
"a,b,c"
@["a" "b" "c"])
(test "split: can match empty subpatterns"
~(split "," ':w*)
",a,,bar,,,c,,"
@["" "a" "" "bar" "" "" "c" "" ""])
(test "split: subpattern is limited to only text before the separator"
~(split "," '(to -1))
"a,,bar,c"
@["a" "" "bar" "c"])
(test "split: fails if any subpattern fails"
~(split "," '"a")
"a,a,b"
nil)
(test "split: separator does not have to match anything"
~(split "x" '(to -1))
"a,a,b"
@["a,a,b"])
(test "split: always consumes entire input"
~(split 1 '"")
"abc"
@["" "" "" ""])
(test "split: separator can be an arbitrary PEG"
~(split :s+ '(to -1))
"a b c"
@["a" "b" "c"])
(test "split: does not advance past the end of the input"
~(* (split "," ':w+) 0)
"a,b,c"
@["a" "b" "c"])
(end-suite) (end-suite)

View File

@ -198,5 +198,9 @@
(assert (= (test) '(1 ())) "issue #919") (assert (= (test) '(1 ())) "issue #919")
(end-suite) # Regression #1327
(def x "A")
(def x (if (= nil x) "B" x))
(assert (= x "A"))
(end-suite)

View File

@ -292,5 +292,8 @@
[2 6 4 'z]]) [2 6 4 'z]])
"arg & inner symbolmap") "arg & inner symbolmap")
# 4782a76
(assert (= 10 (do (var x 10) (def y x) (++ x) y)) "no invalid aliasing")
(end-suite) (end-suite)

View File

@ -42,7 +42,7 @@
(defn buffer-factory (defn buffer-factory
[] []
@"im am a buffer") @"i am a buffer")
(assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation") (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation")