1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-26 12:14:49 +00:00

Compare commits

..

198 Commits

Author SHA1 Message Date
Calvin Rose
b099bd97f2 Merge branch 'master' into bytecode_opt 2023-05-30 18:13:02 -05:00
Calvin Rose
9c97d8f648 Merge pull request #1171 from zevv/zevv-net-connect
Fixed net/connect binding address
2023-05-30 16:53:24 -05:00
Ico Doornekamp
40080b23ae Fixed net/connect binding address 2023-05-30 16:57:17 +02:00
Calvin Rose
7acb5c63e0 Remove bad windows10 check. 2023-05-29 18:17:22 -05:00
Calvin Rose
fcca9bbab3 Add recursion to the pruning optimization. 2023-05-29 18:05:14 -05:00
Calvin Rose
dbb2187425 Merge pull request #1167 from zevv/janet-formatbf-fix
Fix janet_formatbv() type when handling %d %u int specifiers
2023-05-29 18:03:13 -05:00
Calvin Rose
82e51f9e81 Merge pull request #1169 from zevv/fix-buffer-push-at-doc
Updated documentation for buffer/push-at
2023-05-29 18:02:05 -05:00
Calvin Rose
4782a76bca Add inital bytecode optimizations for #1163
This removes unnecessary movn, movf, lds, and a few other instructions.
Any instructions that has not side effects and writes to a slot that
isn't used can be removed. A number of other optimizations can follow
from this:

- Implement the def-aliasing-var optimization better
- This function can be iterated as a fix point until no more
  instructions are removed.
- If we implement slot renaming, then we no longer need to free slots
  and can simplify the initial code generation a lot.
2023-05-29 16:10:48 -05:00
Ico Doornekamp
d13788a4ed Updated documentation for buffer/push-at 2023-05-29 22:03:37 +02:00
Ico Doornekamp
e64a0175b1 change janet_formatbv() to handle int/unsigned int instead of long/unsigned long on '%d' and '%u' format specifiers. 2023-05-29 19:50:14 +02:00
Calvin Rose
4aca94154f Be more selective when testing FFI.
In the future, we really should get more FFI testing for
partially supported FFI on various platforms.
2023-05-28 15:28:17 -05:00
Calvin Rose
ac5f118dac Merge pull request #1164 from dressupgeekout/janet_h_symlink
More portable method of installing janet.h -> janet/janet.h symlink
2023-05-28 15:22:12 -05:00
Charlotte Koch
a2812ec5eb More portable method of installing janet.h -> janet/janet.h symlink 2023-05-27 14:22:11 -07:00
Calvin Rose
70f13f1b62 Merge pull request #1157 from zevv/file-lines
Add file/lines iterator
2023-05-26 18:16:14 -05:00
Calvin Rose
77e62a25cb Merge pull request #1160 from primo-ppcg/mapcat-et-al
Allow mapcat et al to accept multiple iterable arguments
2023-05-26 18:15:09 -05:00
Ico Doornekamp
09345ec786 file/linex now only acceps a file, not a path name 2023-05-26 17:50:26 +02:00
primo-ppcg
bad73baf98 Add test cases for variadic arguments to map-like functions 2023-05-26 19:08:00 +07:00
primo-ppcg
3602f5aa5d Update boot.janet
`kvs` is not yet defined at this point.
2023-05-25 18:27:31 +07:00
primo-ppcg
672b705faf Allow mapcat et al to accept multiple iterable arguments
#1159
2023-05-25 18:12:38 +07:00
Ico Doornekamp
64e3cdeb2b Add file/lines iterator 2023-05-24 16:54:04 +02:00
Calvin Rose
909c906080 Fix yields inside nested fibers. 2023-05-23 20:09:46 -05:00
Calvin Rose
71bde11e95 Allow one argument to while. 2023-05-23 20:09:46 -05:00
Calvin Rose
fc20fbed92 Merge pull request #1151 from zevv/document-string-format
Add docstring to string/format
2023-05-23 18:57:55 -05:00
Calvin Rose
e6b7c85c37 Merge pull request #1152 from zevv/error-messages
Improved various error messages when handling unexpected types.
2023-05-23 18:57:20 -05:00
Ico Doornekamp
b3a92363f8 Add docstring to string/format 2023-05-23 07:21:26 +02:00
Ico Doornekamp
e9f2d1aca7 changed some error messages 'x|y' -> 'x or y' 2023-05-23 06:58:52 +02:00
Ico Doornekamp
b4e3dbf331 Improved various error messages when handling unexpected types.
error: bad slot #1, expected string|symbol|keyword|buffer, got ...
error: bad slot #1, expected a string, symbol, keyword or buffer, got ...

bad s64 initializer: "donkey"
can not convert string "donkey" to s64
2023-05-23 06:57:12 +02:00
Calvin Rose
c3620786cf Merge branch 'master' of github.com:janet-lang/janet 2023-05-22 20:41:05 -05:00
Calvin Rose
41943746e4 Fix #1149 - deep-not= should only return true/false.
Also improve perf at same time.
2023-05-22 20:40:30 -05:00
Calvin Rose
176e816b8c Merge pull request #1153 from zevv/fix-warning
Fix warning in janet_gettime()
2023-05-22 18:46:55 -05:00
Ico Doornekamp
50a19bd870 Fix warning in janet_gettime() 2023-05-22 20:53:03 +02:00
Calvin Rose
57b751b994 Merge branch 'master' of github.com:janet-lang/janet 2023-05-21 16:23:44 -05:00
Calvin Rose
77732a8f44 inet_test change. 2023-05-21 13:36:11 -05:00
Calvin Rose
c47c2e538d Merge pull request #1137 from tionis/master
os/proc-kill now accepts an optional signal to send
2023-05-21 13:33:24 -05:00
Calvin Rose
cc5545277d Merge pull request #1147 from zevv/error-messages
improved error messages for special forms
2023-05-21 13:31:06 -05:00
Ico Doornekamp
63353b98cd improved error messages for special forms 2023-05-21 20:18:32 +02:00
tionis
4dfc869b8a fixed formatting in src/core/os.c 2023-05-21 15:55:11 +02:00
tionis
b4b1c7d80b Merge branch 'janet-lang:master' into master 2023-05-21 13:51:24 +00:00
tionis
e53c03028f ignoring signals on windows in os/proc-kill again 2023-05-21 15:50:15 +02:00
Calvin Rose
8680aef42f Merge pull request #1146 from zevv/os-clock
Add  clock sources to os/clock (:realtime, :monotonic, :cputime)
2023-05-21 08:35:24 -05:00
Calvin Rose
c3fd71d643 Merge pull request #1142 from tionis/thaw
added thaw to complement freeze
2023-05-21 08:09:47 -05:00
Ico Doornekamp
30c47d685d Fixed :cputime because msdn does not implement clock() properly 2023-05-21 07:29:27 +02:00
Ico Doornekamp
80db682109 Added tests for os/clock 2023-05-21 07:29:27 +02:00
Ico Doornekamp
e8e5f66f4c Implement janet_gettime() for win32 and macos; need testing 2023-05-21 07:29:27 +02:00
Ico Doornekamp
aaf3d08bcd Added 'source' argument to os/clock to select the clock source 2023-05-21 07:29:27 +02:00
Ico Doornekamp
61132d6c40 os/time and janet_gettime now use CLOCK_MONOTONIC instead of CLOCK_REALTIM, this matches the description from the documentation of os/clock. Fixes issue #1144 2023-05-21 07:29:27 +02:00
tionis
9cc0645a1e added test for thaw and freeze 2023-05-20 17:35:25 +02:00
Calvin Rose
fc8c6a429e Modulo should not be variadic. 2023-05-20 07:45:18 -05:00
Calvin Rose
2f966883d9 Fix #1145 - variadic imperative arith. macros.
Also update CHANGELOG
2023-05-20 07:42:50 -05:00
tionis
320ba80ca1 added support for tables/structs with prototypes in thaw 2023-05-20 14:00:33 +02:00
Calvin Rose
b621d4dd2e Merge pull request #1139 from zevv/async-connect
changed net/connect to be non-blocking / asynchronous
2023-05-19 21:12:16 -05:00
tionis
56d927c72d added thaw to complement freeze 2023-05-19 21:18:54 +02:00
tionis
53afc2e50a Merge branch 'janet-lang:master' into master 2023-05-19 19:14:12 +00:00
Ico Doornekamp
89debac8f6 Fixed janet_loop1_impl stream use after dealloc 2023-05-19 20:00:59 +02:00
Calvin Rose
f2197fa2d8 Merge pull request #1141 from zevv/mingw-test
Add CI test for mingw/wine on linux
2023-05-19 07:25:47 -05:00
Ico Doornekamp
a6a097c111 Add CI test for mingw/wine on linux 2023-05-18 15:15:41 +02:00
Ico Doornekamp
c3e28bc924 added deferred closing of streams after async connect() fails 2023-05-18 14:10:22 +02:00
Ico Doornekamp
8d78fb1f6b changed net/connect to be non-blocking / asynchronous 2023-05-18 10:55:48 +02:00
Calvin Rose
148917d4ca Move -g to CFLAGS to make it easier to remove/customize 2023-05-16 21:10:18 -05:00
Calvin Rose
d8cf9bf942 Merge pull request #1140 from zevv/debug-symbols
Enable debug symbols in janet binary; strip target at 'make install'
2023-05-16 21:08:12 -05:00
Calvin Rose
d6f5a060ed Squashed commit of the following:
commit 725b8749464895e21c761f1c5479692335282f62
Author: Calvin Rose <calsrose@gmail.com>
Date:   Tue May 16 20:58:34 2023 -0500

    Update header file.

commit 38bf2a5131
Author: Calvin Rose <calsrose@gmail.com>
Date:   Tue May 16 19:43:22 2023 -0500

    Run experiment on bsd.
2023-05-16 21:00:31 -05:00
Calvin Rose
692b6ef8ac Merge pull request #1138 from zevv/setsockopt
add net/setsockopt
2023-05-16 19:29:25 -05:00
Ico Doornekamp
ac5f1fe1be enable debug symbols in janet binary; strip target at 'make instal' 2023-05-16 19:48:18 +02:00
tionis
0f35acade1 use SIGTERM for os/proc-kill signal test 2023-05-16 18:47:38 +02:00
tionis
56d72ec4c5 support sending signals to processes on windows 2023-05-16 17:07:51 +02:00
tionis
71d51c160d added simple test for signal handling in os/proc-kill using :kill 2023-05-16 13:27:52 +02:00
tionis
0b58e505ee os/proc-kill now accepts an optional signal to send 2023-05-16 00:44:19 +02:00
Ico Doornekamp
2a6c615bec features.h: define _DARWIN_C_SOURCE for __APPLE__ 2023-05-15 16:55:09 +02:00
Ico Doornekamp
ab8c5a0b5f net/setsockopt optname symbols are now lower case 2023-05-15 15:25:09 +02:00
Ico Doornekamp
68c35feaea Formatting 2023-05-15 12:33:37 +02:00
Ico Doornekamp
88d0c2ca0f add net/setsockopt 2023-05-15 12:15:36 +02:00
Calvin Rose
398833ebe3 Enable FFI module unconditionally. 2023-05-14 09:18:54 -05:00
Calvin Rose
358f5a03bf Version bump to 1.28.0 2023-05-13 09:59:55 -05:00
Calvin Rose
fba1fdabe4 Update short-fn to fix #1123
Symbols are renamed on expansion to avoid the issue.
2023-05-13 09:44:30 -05:00
Calvin Rose
d42afd21e5 Merge branch 'master' of github.com:janet-lang/janet 2023-05-12 19:08:35 -05:00
Calvin Rose
20ada86761 Fix NAN typo. 2023-05-12 19:08:26 -05:00
Calvin Rose
3b353f1855 Merge pull request #1133 from zevv/cross
Updated Makefile for better cross-compilation support.
2023-05-12 08:41:54 -05:00
Calvin Rose
1467ab4f93 Copy paste error. 2023-05-11 20:56:12 -05:00
Calvin Rose
7e65c2bdad Fix #1130 - mod flipped for signed integers. 2023-05-11 18:15:37 -05:00
Calvin Rose
84a4e3e98a Update CHANGELOG.
and format.
2023-05-11 18:03:38 -05:00
Calvin Rose
bcbeedb001 Merge pull request #1128 from zevv/master
Added os.strftime()
2023-05-11 18:01:39 -05:00
Calvin Rose
e04b103b5d Merge pull request #1134 from CosmicToast/const_sourceline
Make JANET_FN_S* sourceline const
2023-05-11 17:59:27 -05:00
Chloe Kudryavtsev
ac75b94679 Make JANET_FN_S* sourceline const
Otherwise attempts to use it on some platforms cause the following error
`error: initializer element is not a compile-time constant`
when attempting to use the corresponding `JANET_REG`.
2023-05-11 16:07:34 -04:00
Ico Doornekamp
d3bb06cfd6 Updated Makefile for better cross-compilation support.
Building janet requires janet_boot to be run on the host at build time;

- $(UNAME) can now be overridden from the make cmdline
- Added $(RUN) variable to allow a emulator to be specified
- Added ".exe" extension to binaries when using MINGW

Examples:

Cross compiling for win32 and running under wine:

```
make test \
       CC=i686-w64-mingw32-gcc \
       LD=i686-w64-mingw32-gcc \
       UNAME=MINGW \
       RUN=wine

Janet 1.27.0-ad7c3bed mingw/x86/gcc - '(doc)' for help
```

Cross compiling for aarch64 and running under qemu:

```
make repl \
        CC=aarch64-none-linux-gnu-gcc \
        LD=aarch64-none-linux-gnu-gcc \
        RUN="qemu-aarch64 -L /tmp/aarch64/"

Janet 1.27.0-ad7c3bed linux/aarch64/gcc - '(doc)' for help
```
2023-05-11 07:46:36 +02:00
Ico Doornekamp
5cd729c4c1 Added os.strftime() 2023-05-11 06:08:19 +02:00
Calvin Rose
c9fd2bdf39 Merge branch 'master' of github.com:janet-lang/janet 2023-05-10 18:46:42 -05:00
Calvin Rose
e4be5992b3 Address issue with #1131 2023-05-10 18:43:33 -05:00
Calvin Rose
2ac4988f1b Merge pull request #1124 from sogaiu/tweak-ev-select-docstring
Tweak ev/select docstring
2023-05-08 04:17:25 -05:00
Calvin Rose
19f14adb9e Update changelog. 2023-05-07 21:07:22 -05:00
Calvin Rose
86de039492 Merge branch 'master' of github.com:janet-lang/janet 2023-05-07 11:54:20 -05:00
Calvin Rose
2360164e4f Address #1125 - fix ev/select to only take and release locks once.
By take and releasing locks twice per channel in the case where nothing
is reading, there was an opportunity for ev/select to hang in the
multithreaded case. Also silence valgrind/helgrind errors.
2023-05-07 11:54:06 -05:00
Calvin Rose
c93ddceadb Merge pull request #1122 from CosmicToast/getcbytes
Add get/opt cbytes
2023-05-07 10:33:09 -05:00
sogaiu
cd19dec44a Tweak ev/select docstring 2023-05-07 22:22:33 +09:00
Chloe Kudryavtsev
53ba9c800a Add get/opt cbytes
Like getcstring, but operates on a byteview.
When writing bindings (i.e what capi.c is primarily used for), it's
common to want to accept a buffer *or* a string rather than just
a string.
For this, a byteview is perfect (and why not accept keywords while
you're at it?).
However, there's no built-in function for getting a cstring out of
a byteview, this adds one.
This also reformulates getcstring to be an edge-case of getcbytes
(simply adding an explicit check for stringness).
2023-05-06 22:13:53 -04:00
Calvin Rose
cabbaded68 Add support for the NO_COLOR environment variable. 2023-05-06 16:33:45 -05:00
Calvin Rose
9bb589f827 update readme 2023-05-06 15:56:27 -05:00
Calvin Rose
c3a06686c2 Merge branch 'shell-win-feature-check' 2023-05-06 15:53:02 -05:00
Calvin Rose
7d57f87007 Address #1121 - disallow extra splices.
This turns splices that are ignored into compiler errors. Other
alternatives here should also be considered, for example making this
a compiler warning rather than an error. For example, the latest
spork as of a3ee63c137ee3234987dbbca71b566994ff8ae8c has an error of this
kind, but the resulting program does work correctly.

Also disallow splice propagation - code of the
form (+ 1 (do ;[2 3 4]) 5).
2023-05-06 13:12:31 -05:00
Calvin Rose
4cc4a9d38b (and ... <truthy-value> true) will return true as per docs. 2023-05-06 10:16:05 -05:00
Calvin Rose
02c7cd0194 Merge pull request #1116 from sogaiu/tweak-long-string-peg
Tweak long-string|bytes peg in test suite files
2023-04-30 16:21:12 -05:00
Calvin Rose
696efcb9e2 Add header file. 2023-04-30 12:19:55 -05:00
Calvin Rose
6e9cde8ac1 Add feature check for windows version shell.c
Tried to get console working on windows 7 and below
2023-04-30 10:36:42 -05:00
sogaiu
a9fae49671 Tweak long-string|bytes peg in test suite files 2023-04-30 21:55:43 +09:00
Calvin Rose
440af9fd64 Remove extra allocation in drop. 2023-04-24 09:41:33 -05:00
Calvin Rose
347721ae40 Fix macos behavior - Closes #1097, Fixes #1015 2023-04-24 09:37:49 -05:00
Calvin Rose
daea91044c Give different names to the user9 and user8 fiber statuses.
These now have semantic menaings that are pretty difficult to
work around. Code that tries to maniuplate user8 and user9 signals
right now may be affected
2023-04-24 09:19:15 -05:00
Calvin Rose
4ed3f2c662 Merge pull request #1114 from ianthehenry/drop-from-end
drop with a negative count now drops from the end
2023-04-24 09:16:10 -05:00
Calvin Rose
3641c8f60a Merge pull request #1108 from wackbyte/read
README grammar and formatting changes
2023-04-24 09:15:07 -05:00
Calvin Rose
e4b68cd940 Merge pull request #1109 from wackbyte/remove-double-space
Remove double space in help message
2023-04-24 09:11:45 -05:00
Calvin Rose
b8c936e2fe Merge pull request #1113 from ianthehenry/catseq
add catseq
2023-04-24 09:07:38 -05:00
Calvin Rose
83cd519702 Merge pull request #1112 from ianthehenry/dynamic-replace
string and peg replacement functions can now take functions
2023-04-24 09:07:03 -05:00
Ian Henry
54b54f85f3 drop with a negative count now drops from the end 2023-04-23 21:39:14 -07:00
Ian Henry
ccd874fe4e add catseq 2023-04-23 21:20:01 -07:00
Ian Henry
9dc7e8ed3a peg replacement functions have access to captures
When peg/replace or peg/replace-all are given a function to serve as the text
replacement, any captures produced by the PEG are passed as additional
arguments to that function.
2023-04-23 09:15:46 -07:00
Ian Henry
485099fd6e string and peg replacement functions can now take functions
Functions will be invoked with the matched text, and their result will be
coerced to a string and used as the new replacement text.

This also allows passing non-function, non-byteviewable values, which will be
converted into strings during replacement (only once, and only if at least
one match is found).
2023-04-23 08:36:17 -07:00
wackbyte
d359c6b43e Remove double space in help message 2023-04-22 21:46:24 -04:00
Calvin Rose
d9ed7a77f8 Merge pull request #1106 from CosmicToast/1105
handle null-byte case in scanformat (fixes #1105)
2023-04-22 15:25:48 -05:00
wackbyte
4238a4ca6a README grammar and formatting changes 2023-04-20 21:06:33 -04:00
Chloe Kudryavtsev
0902a5a981 improve null format handling
there was a request to improve the error message, but the whole function
has non-informative errors. (both functions, actually, since the code is
duplicated)
as such, instead of catching it directly, address the assumption that
led to the SIGSEGV and let it be caught by the functions themselves,
thus reusing existing error messages (which can then be improved
separately).
2023-04-20 11:51:11 -04:00
Chloe Kudryavtsev
f3192303ab check for NULL in get_fmt_mapping (fixes #1105)
When there is no format to be found after a %, get_fmt_mapping returns
NULL. It then gets called against strlen, which is a typical SEGV.
Check for NULL aginst mapping, which signals a null format being
specified.
2023-04-19 12:55:25 -04:00
Calvin Rose
bef5bd72c2 Merge pull request #1095 from ml-2/ml
Add `keep-syntax` and `keep-syntax!` functions
2023-04-08 10:46:47 -05:00
ML
b6175e4296 Add keep-syntax and keep-syntax! functions
These functions are designed to make it easier to properly preserve the
sourcemap and tuple type in macros. This commit also modifies the threading
macros to make use of these functions.
2023-04-07 21:09:52 -04:00
Calvin Rose
3858b2e177 Add ev/all-tasks to get running and pending root fibers. 2023-04-01 18:57:13 -05:00
Calvin Rose
9a76e77981 Update for undefined behavior sanitizer. 2023-03-24 18:49:21 -05:00
Calvin Rose
8182d640cd Merge branch 'master' of github.com:janet-lang/janet 2023-03-12 10:33:06 -05:00
Calvin Rose
1c6fda1a5c Address #1076 - unexpected shadowing behavior
While the old behavior was reasonable, it is not spelled out anywhere
in the documentation and was incidental rather than intentional.
Parameters of the same name of the function should probably take
precedence on name collision, following the principle of least surprise.
2023-03-12 10:30:59 -05:00
Calvin Rose
c51db1cf2f Merge pull request #1081 from ianthehenry/log-gamma-docstring
remove quotes in math/log-gamma docstring
2023-03-12 10:17:51 -05:00
Ian Henry
4e7930fc4c remove quotes around log-gamma 2023-03-11 12:26:45 -08:00
Calvin Rose
3563f8ccdb Merge pull request #1079 from dressupgeekout/charlotte_dragonfly
Some basic fixes for DragonFly
2023-03-08 15:05:51 -06:00
Charlotte Koch
575af763f6 os/which can return :dragonfly
While here, document that :bsd is another possible return value.
2023-03-07 06:45:31 -08:00
Charlotte Koch
8b16b9b246 Need to enable __BSD_VISIBLE on DragonFly in conjunction with -std=c99 2023-03-07 06:40:58 -08:00
Calvin Rose
01aab66667 Prepare for 1.27.0 release. 2023-03-05 09:48:25 -06:00
Calvin Rose
aa5c987a94 Change semantics of bracketed tuple equality.
Comparison between different bracket and normal tuples
will now take into account the delimiter type. This solves strange
non-locality issues in the compiler due to this false equality, and is
more consistent with Janet's otherwise strong equality philosophy.
2023-03-03 18:24:02 -06:00
Calvin Rose
75229332c8 Merge pull request #1075 from sogaiu/math-rng-int-docs
Tweak math/rng-int docs
2023-03-03 18:09:11 -06:00
sogaiu
9d5b1ba838 Tweak math/rng-int docs 2023-02-24 19:55:47 +09:00
Calvin Rose
f27b225b34 Merge pull request #1072 from sogaiu/ftell
Add file/tell
2023-02-21 07:13:00 -06:00
sogaiu
3c523d66e9 Add file/tell 2023-02-21 20:19:17 +09:00
Calvin Rose
1144c27c54 Merge pull request #1071 from ianthehenry/number-pegfault
fix a segfault in the (number) special
2023-02-20 21:01:33 -06:00
Ian Henry
b442b21d3f fix a segfault in the (number) special
This was an error about the base argument that referenced the tag argument,
which might not exist.
2023-02-20 17:07:42 -08:00
Calvin Rose
746ff5307d Update for more minimal builds. 2023-02-12 12:14:11 -06:00
Calvin Rose
ef85b24d8f Add optional offset to ffi/write. 2023-02-12 12:08:28 -06:00
Calvin Rose
c55d93512b Add buffer/push-at for easier manipulation of buffers
buffer/blit is difficult to use, and while buffer/push is the easiet
buffer manipulation function to use it only appends to the buffer.
buffer/push-at lets users manipulate buffers at any index - useful
for buffers used as an in-memory databases, for example.
2023-02-12 11:26:00 -06:00
Calvin Rose
2e38f9ba61 Allow passing pointer-buffers to other threads. 2023-02-12 11:07:45 -06:00
Calvin Rose
1cadff8e58 Add ffi/pointer-buffer for easier memory manipulation in FFI.
Added underlying buffer support for buffer instances that cannot
reallocated underlying memory - useful for (small) memory mapped
files and other FFI utilties.
2023-02-12 09:20:05 -06:00
Calvin Rose
d1eba60ba8 Add separate sandbox flag for file/temp
Doesn't really impart (much) file systtem information when used, and
can be used for a lot of things where file functions are used to process
in a stream.
2023-02-09 08:57:53 -06:00
Calvin Rose
057dccad8f Turn on nanboxing by default for risc-v 64. 2023-02-09 03:09:53 -06:00
Calvin Rose
4285200b4b Add a sandbox option to disable native modules.
Also sort the sandbox docstring list.
2023-02-09 00:19:56 -06:00
Calvin Rose
73c2fbbc2a Check sandbox capabilities instead of ignoring unknown ones. 2023-02-06 17:38:00 -06:00
Calvin Rose
37b7e170fa Update changelist. 2023-02-06 12:31:49 -06:00
Calvin Rose
b032d94877 Add sandboxing API.
The sandboxing API is meant to make janet a bit more attractive
for certain application embedding use cases. The sandboxing API
puts limits on what system resources the interpreter can access.
2023-02-06 09:05:57 -06:00
Calvin Rose
9476016741 Update asm.c 2023-02-05 23:49:18 -06:00
Calvin Rose
7a1c9c7798 Add support for debugging upvalues.
Upvalues are stored in the symbol slots structure as well, but
since they are always live, we repurpose the death_pc field to
refer to the environment index that we want to look at at runtime.
2023-02-05 15:30:01 -06:00
Calvin Rose
c7fb7b4451 Merge branch 'master' into localbindings 2023-02-05 11:36:57 -06:00
Calvin Rose
67c474fc7a More fixes to ev/gather (tested on httpf server). 2023-02-05 10:21:37 -06:00
Calvin Rose
4e8154cf8a Fix ev/gather to cancel children on cancellation.
Otherwise, we would be leaving zombie fibers around.
2023-02-05 09:43:16 -06:00
Calvin Rose
9582d3c623 Allow infinite wait to work without issues. 2023-02-05 09:29:39 -06:00
Calvin Rose
0079500713 Merge branch 'master' into localbindings 2023-02-04 13:39:24 -06:00
Calvin Rose
55af6ce834 Fix write after free with printing to files. 2023-02-04 13:36:30 -06:00
Calvin Rose
3e82fdc125 Update symbolmapping code with marshal/unmarshal. 2023-02-03 17:33:11 -06:00
Calvin Rose
7344a6cfc0 Fix null check. 2023-02-03 16:24:50 -06:00
Calvin Rose
0aded71343 Fix issue with environments in asm.c 2023-02-03 16:24:50 -06:00
Calvin Rose
7663b1e703 Fix null check. 2023-02-02 22:03:18 -06:00
Calvin Rose
282546c03f Fix issue with environments in asm.c 2023-02-02 21:12:17 -06:00
Calvin Rose
f4bc89d1c0 Progress. 2023-02-02 21:08:48 -06:00
Jona Ekenberg
fa277c3797 added future test for upvalues and symbolslots 2023-02-01 21:26:29 +01:00
Jona Ekenberg
c0c8ab25e6 added symbolslots to asm 2023-02-01 21:12:42 +01:00
Jona Ekenberg
b685bf3026 updated symbolslots-test 2023-02-01 11:46:36 +01:00
Jona Ekenberg
ce31db09e4 symbolslots should be gc'd, local_symbols always pushed in case *debug* is set in middle of function 2023-02-01 11:45:13 +01:00
Jona Ekenberg
624a6cf619 symbolslots nil when there are no symbols, changed debugger to not have special case 2023-02-01 11:25:52 +01:00
Jona Ekenberg
587aa87d28 symbolslots now use janet_v vectors, flat structure 2023-02-01 11:06:33 +01:00
Jona Ekenberg
88813c4f87 initial slotsyms implementation 2023-02-01 09:39:24 +01:00
Calvin Rose
dacbe29771 Allow round-tripping more functions with disasm and asm.
Nested functions that captured with environments didn't
work well with asm.
2023-01-30 09:04:42 -06:00
Calvin Rose
244833cfa1 Merge pull request #1040 from lgtm-migrator/codeql
Add CodeQL workflow for GitHub code scanning
2023-01-29 09:04:18 -06:00
Calvin Rose
05e7f974e3 Add os/compiler to the core.
Allows querying what compiler was used to compile Janet.
2023-01-28 14:00:02 -06:00
Calvin Rose
0dbef65a73 Merge pull request #1065 from sogaiu/comment-tweaks
Misc comment tweaks
2023-01-27 11:26:05 -06:00
Calvin Rose
9106228787 Add :riscv32 and :riscv32 values for os/arch. 2023-01-27 11:23:57 -06:00
Calvin Rose
6ae3bdb25c Add RISC-V 64 bit detection in janet.h 2023-01-26 22:40:05 -06:00
sogaiu
310bcec260 Misc comment tweaks 2023-01-25 18:45:19 +09:00
Calvin Rose
8c4cc4e671 Merge pull request #1064 from sogaiu/realpath-doc-tweak
Update os/realpath docstring
2023-01-24 09:50:44 -06:00
Calvin Rose
c6eaaa83ed Buffer initialized with janet_buffer_init will not be gced.
Set an internal flag that disables garbage collection on such
buffers. For all currently correct usage, this should have no effect,
and will fix use cases where buffers are initialized this way and then
passed to the interpreter.
2023-01-23 20:57:30 -06:00
sogaiu
8f598d6f96 Update os/realpath docstring 2023-01-23 12:55:04 +09:00
Calvin Rose
20bc323d17 Use gcc explicitly in mingw build. 2023-01-22 11:21:28 -06:00
Calvin Rose
8b0bcf4db9 Add the mingw toolchain. 2023-01-22 11:10:04 -06:00
Calvin Rose
8955e6f536 Merge branch 'master' of github.com:janet-lang/janet 2023-01-22 11:05:39 -06:00
Calvin Rose
f8ddea6452 Add msys2 testing with github actions. 2023-01-22 11:05:27 -06:00
Calvin Rose
987e04086d Merge pull request #1063 from AlecTroemel/1062-io-flag-types
use int32_t type for file flags, just like header
2023-01-22 10:56:19 -06:00
Calvin Rose
85f2acbf52 Fix tools/format.sh file permissions. 2023-01-22 10:47:33 -06:00
alectroemel
1acf4c3ab7 add int32_t type for file flags, just like header
Signed-off-by: alectroemel <alectroemel@hotmail.com>
Signed-off-by: alectroemel <alec@mirusresearch.com>
2023-01-22 10:24:46 -06:00
Calvin Rose
07a3158fba Merge pull request #1060 from ianthehenry/doc-typos
Fix some docstring typos
2023-01-21 16:21:57 -06:00
bakpakin
2f8bed9d82 Build and install an import library on mingw 2023-01-21 14:31:48 -06:00
bakpakin
a490937cd9 Add :mingw value when getting the OS setting when compiled with mingw. 2023-01-21 11:50:03 -06:00
bakpakin
8ee5942481 Fix windows build with JANET_NO_NET - #1055 2023-01-21 10:56:20 -06:00
bakpakin
93b469885a Initial Mingw support with Makefile.
Also add a macro JANET_MSVC to distinguish between
a windows build (JANET_WINDOWS) and a build with msvc.
2023-01-21 10:37:34 -06:00
Calvin Rose
d8d1de2dcb Don't compile library loading code on windows if it is disabled. 2023-01-21 09:36:03 -06:00
Ian Henry
ab224514f0 Fix some docstring typos. 2023-01-18 19:41:56 -08:00
Calvin Rose
75179de8da Merge pull request #1056 from fd00/use-dev-urandom-on-cygwin
Use `/dev/urandom` for `janet_cryptorand` on cygwin
2023-01-08 09:29:52 -06:00
Daisuke Fujimura (fd0)
3254c2c477 Use /dev/urandom for janet_cryptorand on cygwin 2023-01-07 08:58:35 +09:00
LGTM Migrator
4067f883a2 Add CodeQL workflow for GitHub code scanning 2022-11-05 10:22:49 +00:00
56 changed files with 2569 additions and 564 deletions

41
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "2 7 * * 4"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View File

@@ -35,3 +35,42 @@ jobs:
- name: Test the project - name: Test the project
shell: cmd shell: cmd
run: build_win test run: build_win test
test-mingw:
name: Build on Windows with Mingw (no test yet)
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Setup Mingw
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
base-devel
git
gcc
- name: Build the project
shell: cmd
run: make -j CC=gcc
test-mingw-linux:
name: Build and test with Mingw on Linux + Wine
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Setup Mingw and wine
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libstdc++6:i386 libgcc-s1:i386
sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64
- name: Compile the project
run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
- name: Test the project
run: make test UNAME=MINGW RUN=wine

View File

@@ -1,6 +1,40 @@
# 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
- Make imperative arithmetic macros variadic
- `ev/connect` now yields to the event loop instead of blocking while waiting for an ACK.
## 1.28.0 - 2023-05-13
- Various bug fixes
- Make nested short-fn's behave a bit more predictably (it is still not recommended to nest short-fns).
- Add `os/strftime` for date formatting.
- Fix `ev/select` on threaded channels sometimes live-locking.
- Support the `NO_COLOR` environment variable to turn off VT100 color codes in repl (and in scripts).
See http://no-color.org/
- Disallow using `(splice x)` in contexts where it doesn't make sense rather than silently coercing to `x`.
Instead, raise a compiler error.
- Change the names of `:user8` and `:user9` sigals to `:interrupt` and `:await`
- Change the names of `:user8` and `:user9` fiber statuses to `:interrupted` and `:suspended`.
- Add `ev/all-tasks` to see all currently suspended fibers.
- Add `keep-syntax` and `keep-syntax!` functions to make writing macros easier.
## 1.27.0 - 2023-03-05
- Change semantics around bracket tuples to no longer be equal to regular tuples.
- Add `index` argument to `ffi/write` for symmetry with `ffi/read`.
- Add `buffer/push-at`
- Add `ffi/pointer-buffer` to convert pointers to buffers the cannot be reallocated. This
allows easier manipulation of FFI memory, memory mapped files, and buffer memory shared between threads.
- Calling `ev/cancel` on a fiber waiting on `ev/gather` will correctly
cancel the child fibers.
- Add `(sandbox ...)` function to core for permission based security. Also add `janet_sandbox` to C API.
The sandbox allows limiting access to the file system, network, ffi, and OS resources at runtime.
- Add `(.locals)` function to debugger to see currently bound local symbols.
- Track symbol -> slot mapping so debugger can get symbolic information. This exposes local bindings
in `debug/stack` and `disasm`.
- Add `os/compiler` to detect what host compiler was used to compile the interpreter
- Add support for mingw and cygwin builds (mingw support also added in jpm).
## 1.26.0 - 2023-01-07 ## 1.26.0 - 2023-01-07
- Add `ffi/malloc` and `ffi/free`. Useful as tools of last resort. - Add `ffi/malloc` and `ffi/free`. Useful as tools of last resort.
- Add `ffi/jitfn` to allow calling function pointers generated at runtime from machine code. - Add `ffi/jitfn` to allow calling function pointers generated at runtime from machine code.

View File

@@ -31,6 +31,8 @@ LIBDIR?=$(PREFIX)/lib
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1 2> /dev/null || echo local)\"" JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1 2> /dev/null || echo local)\""
CLIBS=-lm -lpthread CLIBS=-lm -lpthread
JANET_TARGET=build/janet JANET_TARGET=build/janet
JANET_BOOT=build/janet_boot
JANET_IMPORT_LIB=build/janet.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
@@ -44,18 +46,19 @@ SONAME_SETTER=-Wl,-soname,
# For cross compilation # For cross compilation
HOSTCC?=$(CC) HOSTCC?=$(CC)
HOSTAR?=$(AR) HOSTAR?=$(AR)
CFLAGS?=-O2 CFLAGS?=-O2 -g
LDFLAGS?=-rdynamic LDFLAGS?=-rdynamic
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
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS) BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS) BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
# For installation # For installation
LDCONFIG:=ldconfig "$(LIBDIR)" LDCONFIG:=ldconfig "$(LIBDIR)"
# Check OS # Check OS
UNAME:=$(shell uname -s) UNAME?=$(shell uname -s)
ifeq ($(UNAME), Darwin) ifeq ($(UNAME), Darwin)
CLIBS:=$(CLIBS) -ldl CLIBS:=$(CLIBS) -ldl
SONAME_SETTER:=-Wl,-install_name, SONAME_SETTER:=-Wl,-install_name,
@@ -77,6 +80,14 @@ ifeq ($(shell uname -o), Android)
endif endif
endif endif
# Mingw
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
CLIBS:=-lws2_32 -lpsapi -lwsock32
LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
JANET_TARGET:=$(JANET_TARGET).exe
JANET_BOOT:=$(JANET_BOOT).exe
endif
$(shell mkdir -p build/core build/c build/boot) $(shell mkdir -p build/core build/c build/boot)
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
@@ -156,12 +167,12 @@ $(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
$(CC) $(BOOT_CFLAGS) -o $@ -c $< $(CC) $(BOOT_CFLAGS) -o $@ -c $<
build/janet_boot: $(JANET_BOOT_OBJECTS) $(JANET_BOOT): $(JANET_BOOT_OBJECTS)
$(CC) $(BOOT_CFLAGS) -o $@ $(JANET_BOOT_OBJECTS) $(CLIBS) $(CC) $(BOOT_CFLAGS) -o $@ $(JANET_BOOT_OBJECTS) $(CLIBS)
# Now the reason we bootstrap in the first place # Now the reason we bootstrap in the first place
build/c/janet.c: build/janet_boot src/boot/boot.janet build/c/janet.c: $(JANET_BOOT) src/boot/boot.janet
build/janet_boot . JANET_PATH '$(JANET_PATH)' > $@ $(RUN) $(JANET_BOOT) . JANET_PATH '$(JANET_PATH)' > $@
cksum $@ cksum $@
######################## ########################
@@ -169,16 +180,16 @@ build/c/janet.c: build/janet_boot src/boot/boot.janet
######################## ########################
ifeq ($(UNAME), Darwin) ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.26.dylib SONAME=libjanet.1.28.dylib
else else
SONAME=libjanet.so.1.26 SONAME=libjanet.so.1.28
endif endif
build/c/shell.c: src/mainclient/shell.c build/c/shell.c: src/mainclient/shell.c
cp $< $@ cp $< $@
build/janet.h: $(JANET_TARGET) src/include/janet.h $(JANETCONF_HEADER) build/janet.h: $(JANET_TARGET) src/include/janet.h $(JANETCONF_HEADER)
./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h $(JANETCONF_HEADER) $@ $(RUN) ./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h $(JANETCONF_HEADER) $@
build/janetconf.h: $(JANETCONF_HEADER) build/janetconf.h: $(JANETCONF_HEADER)
cp $< $@ cp $< $@
@@ -207,19 +218,19 @@ $(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
TEST_SCRIPTS=$(wildcard test/suite*.janet) TEST_SCRIPTS=$(wildcard test/suite*.janet)
repl: $(JANET_TARGET) repl: $(JANET_TARGET)
./$(JANET_TARGET) $(RUN) ./$(JANET_TARGET)
debug: $(JANET_TARGET) debug: $(JANET_TARGET)
$(DEBUGGER) ./$(JANET_TARGET) $(DEBUGGER) ./$(JANET_TARGET)
VALGRIND_COMMAND=valgrind --leak-check=full VALGRIND_COMMAND=valgrind --leak-check=full --quiet
valgrind: $(JANET_TARGET) valgrind: $(JANET_TARGET)
$(VALGRIND_COMMAND) ./$(JANET_TARGET) $(VALGRIND_COMMAND) ./$(JANET_TARGET)
test: $(JANET_TARGET) $(TEST_PROGRAMS) test: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do ./$(JANET_TARGET) "$$f" || exit; done for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
valtest: $(JANET_TARGET) $(TEST_PROGRAMS) valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
@@ -258,7 +269,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
docs: build/doc.html docs: build/doc.html
build/doc.html: $(JANET_TARGET) tools/gendoc.janet build/doc.html: $(JANET_TARGET) tools/gendoc.janet
$(JANET_TARGET) tools/gendoc.janet > build/doc.html $(RUN) $(JANET_TARGET) tools/gendoc.janet > build/doc.html
######################## ########################
##### Installation ##### ##### Installation #####
@@ -274,7 +285,7 @@ build/janet.pc: $(JANET_TARGET)
echo "Name: janet" >> $@ echo "Name: janet" >> $@
echo "Url: https://janet-lang.org" >> $@ echo "Url: https://janet-lang.org" >> $@
echo "Description: Library for the Janet programming language." >> $@ echo "Description: Library for the Janet programming language." >> $@
$(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@ $(RUN) $(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
echo 'Cflags: -I$${includedir}' >> $@ echo 'Cflags: -I$${includedir}' >> $@
echo 'Libs: -L$${libdir} -ljanet' >> $@ echo 'Libs: -L$${libdir} -ljanet' >> $@
echo 'Libs.private: $(CLIBS)' >> $@ echo 'Libs.private: $(CLIBS)' >> $@
@@ -282,9 +293,10 @@ build/janet.pc: $(JANET_TARGET)
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
mkdir -p '$(DESTDIR)$(BINDIR)' mkdir -p '$(DESTDIR)$(BINDIR)'
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet' cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
strip '$(DESTDIR)$(BINDIR)/janet'
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet' mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet' cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
mkdir -p '$(DESTDIR)$(JANET_PATH)' mkdir -p '$(DESTDIR)$(JANET_PATH)'
mkdir -p '$(DESTDIR)$(LIBDIR)' mkdir -p '$(DESTDIR)$(LIBDIR)'
if test $(UNAME) = Darwin ; then \ if test $(UNAME) = Darwin ; then \
@@ -301,6 +313,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
cp janet.1 '$(DESTDIR)$(JANET_MANPATH)' cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
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)'
[ -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)
@@ -313,7 +326,7 @@ install-jpm-git: $(JANET_TARGET)
JANET_HEADERPATH='$(INCLUDEDIR)/janet' \ JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
JANET_BINPATH='$(BINDIR)' \ JANET_BINPATH='$(BINDIR)' \
JANET_LIBPATH='$(LIBDIR)' \ JANET_LIBPATH='$(LIBDIR)' \
../../$(JANET_TARGET) ./bootstrap.janet $(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
uninstall: uninstall:
-rm '$(DESTDIR)$(BINDIR)/janet' -rm '$(DESTDIR)$(BINDIR)/janet'
@@ -333,7 +346,7 @@ format:
grammar: build/janet.tmLanguage grammar: build/janet.tmLanguage
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET) build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
$(JANET_TARGET) $< > $@ $(RUN) $(JANET_TARGET) $< > $@
compile-commands: compile-commands:
# Requires pip install copmiledb # Requires pip install copmiledb

View File

@@ -7,14 +7,14 @@
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left"> <img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a **Janet** is a functional and imperative programming language and bytecode interpreter. It is a
lisp-like language, but lists are replaced Lisp-like language, but lists are replaced
by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples). by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples).
The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly. The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
There is a REPL for trying out the language, as well as the ability There is a REPL for trying out the language, as well as the ability
to run script files. This client program is separate from the core runtime, so to run script files. This client program is separate from the core runtime, so
Janet can be embedded in other programs. Try Janet in your browser at Janet can be embedded in other programs. Try Janet in your browser at
[https://janet-lang.org](https://janet-lang.org). <https://janet-lang.org>.
If you'd like to financially support the ongoing development of Janet, consider If you'd like to financially support the ongoing development of Janet, consider
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub. [sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
@@ -41,8 +41,8 @@ Lua, but smaller than GNU Guile or Python.
* Macros * Macros
* Multithreading * Multithreading
* Per-thread event loop for efficient evented IO * Per-thread event loop for efficient evented IO
* Byte code interpreter with an assembly interface, as well as bytecode verification * Bytecode interpreter with an assembly interface, as well as bytecode verification
* Tail call Optimization * Tail-call optimization
* Direct interop with C via abstract types and C functions * Direct interop with C via abstract types and C functions
* Dynamically load C libraries * Dynamically load C libraries
* Functional and imperative standard library * Functional and imperative standard library
@@ -57,7 +57,7 @@ Lua, but smaller than GNU Guile or Python.
## Documentation ## Documentation
* For a quick tutorial, see [the introduction](https://janet-lang.org/docs/index.html) for more details. * For a quick tutorial, see [the introduction](https://janet-lang.org/docs/index.html) for more details.
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html) * For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html).
Documentation is also available locally in the REPL. Documentation is also available locally in the REPL.
Use the `(doc symbol-name)` macro to get API Use the `(doc symbol-name)` macro to get API
@@ -65,7 +65,7 @@ documentation for symbols in the core library. For example,
``` ```
(doc apply) (doc apply)
``` ```
Shows documentation for the `apply` function. shows documentation for the `apply` function.
To get a list of all bindings in the default To get a list of all bindings in the default
environment, use the `(all-bindings)` function. You environment, use the `(all-bindings)` function. You
@@ -84,7 +84,7 @@ the SourceHut mirror is actively maintained.
The Makefile is non-portable and requires GNU-flavored make. The Makefile is non-portable and requires GNU-flavored make.
``` ```sh
cd somewhere/my/projects/janet cd somewhere/my/projects/janet
make make
make test make test
@@ -100,7 +100,7 @@ Find out more about the available make targets by running `make help`.
32-bit Haiku build instructions are the same as the UNIX-like build instructions, 32-bit Haiku build instructions are the same as the UNIX-like build instructions,
but you need to specify an alternative compiler, such as `gcc-x86`. but you need to specify an alternative compiler, such as `gcc-x86`.
``` ```sh
cd somewhere/my/projects/janet cd somewhere/my/projects/janet
make CC=gcc-x86 make CC=gcc-x86
make test make test
@@ -112,10 +112,9 @@ make install-jpm-git
### FreeBSD ### FreeBSD
FreeBSD build instructions are the same as the UNIX-like build instructions, FreeBSD build instructions are the same as the UNIX-like build instructions,
but you need `gmake` to compile. Alternatively, install directly from but you need `gmake` to compile. Alternatively, install the package directly with `pkg install lang/janet`.
packages, using `pkg install lang/janet`.
``` ```sh
cd somewhere/my/projects/janet cd somewhere/my/projects/janet
gmake gmake
gmake test gmake test
@@ -127,19 +126,19 @@ gmake install-jpm-git
### NetBSD ### NetBSD
NetBSD build instructions are the same as the FreeBSD build instructions. NetBSD build instructions are the same as the FreeBSD build instructions.
Alternatively, install directly from packages, using `pkgin install janet`. Alternatively, install the package directly with `pkgin install janet`.
### Windows ### Windows
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#) 1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
2. Run a Visual Studio Command Prompt (cl.exe and link.exe need to be on the PATH) and cd to the directory with janet. 2. Run a Visual Studio Command Prompt (`cl.exe` and `link.exe` need to be on your PATH) and `cd` to the directory with Janet.
3. Run `build_win` to compile janet. 3. Run `build_win` to compile Janet.
4. Run `build_win test` to make sure everything is working. 4. Run `build_win test` to make sure everything is working.
To build an `.msi` installer executable, in addition to the above steps, you will have to: To build an `.msi` installer executable, in addition to the above steps, you will have to:
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases) 5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases).
6. run `build_win dist` 6. Run `build_win dist`.
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself. Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
@@ -175,9 +174,9 @@ ninja -C build install
Janet can be hacked on with pretty much any environment you like, but for IDE Janet can be hacked on with pretty much any environment you like, but for IDE
lovers, [Gnome Builder](https://wiki.gnome.org/Apps/Builder) is probably the lovers, [Gnome Builder](https://wiki.gnome.org/Apps/Builder) is probably the
best option, as it has excellent meson integration. It also offers code completion best option, as it has excellent Meson integration. It also offers code completion
for Janet's C API right out of the box, which is very useful for exploring. VSCode, Vim, for Janet's C API right out of the box, which is very useful for exploring. VSCode, Vim,
Emacs, and Atom will have syntax packages for the Janet language, though. Emacs, and Atom each have syntax packages for the Janet language, though.
## Installation ## Installation
@@ -186,8 +185,8 @@ to try out the language, you don't need to install anything. You can also move t
## Usage ## Usage
A REPL is launched when the binary is invoked with no arguments. Pass the -h flag A REPL is launched when the binary is invoked with no arguments. Pass the `-h` flag
to display the usage information. Individual scripts can be run with `./janet myscript.janet` to display the usage information. Individual scripts can be run with `./janet myscript.janet`.
If you are looking to explore, you can print a list of all available macros, functions, and constants If you are looking to explore, you can print a list of all available macros, functions, and constants
by entering the command `(all-bindings)` into the REPL. by entering the command `(all-bindings)` into the REPL.
@@ -202,20 +201,26 @@ Hello, World!
nil nil
janet:3:> (os/exit) janet:3:> (os/exit)
$ janet -h $ janet -h
usage: build/janet [options] script args... usage: janet [options] script args...
Options are: Options are:
-h : Show this help -h : Show this help
-v : Print the version string -v : Print the version string
-s : Use raw stdin instead of getline like functionality -s : Use raw stdin instead of getline like functionality
-e code : Execute a string of janet -e code : Execute a string of janet
-E code arguments... : Evaluate an expression as a short-fn with arguments
-d : Set the debug flag in the REPL
-r : Enter the REPL after running all scripts -r : Enter the REPL after running all scripts
-R : Disables loading profile.janet when JANET_PROFILE is present
-p : Keep on executing if there is a top-level error (persistent) -p : Keep on executing if there is a top-level error (persistent)
-q : Hide prompt, logo, and REPL output (quiet) -q : Hide logo (quiet)
-k : Compile scripts but do not execute (flycheck) -k : Compile scripts but do not execute (flycheck)
-m syspath : Set system path for loading global modules -m syspath : Set system path for loading global modules
-c source output : Compile janet source code into an image -c source output : Compile janet source code into an image
-i : Load the script argument as an image file instead of source code
-n : Disable ANSI color output in the REPL -n : Disable ANSI color output in the REPL
-l path : Execute code in a file before running the main script -l lib : Use a module before processing more arguments
-w level : Set the lint warning level - default is "normal"
-x level : Set the lint error level - default is "none"
-- : Stop handling options -- : Stop handling options
``` ```
@@ -226,8 +231,8 @@ If installed, you can also run `man janet` to get usage information.
Janet can be embedded in a host program very easily. The normal build Janet can be embedded in a host program very easily. The normal build
will create a file `build/janet.c`, which is a single C file will create a file `build/janet.c`, which is a single C file
that contains all the source to Janet. This file, along with that contains all the source to Janet. This file, along with
`src/include/janet.h` and `src/conf/janetconf.h` can be dragged into any C `src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
project and compiled into the project. Janet should be compiled with `-std=c99` project and compiled into it. Janet should be compiled with `-std=c99`
on most compilers, and will need to be linked to the math library, `-lm`, and on most compilers, and will need to be linked to the math library, `-lm`, and
the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If
there is no need for dynamic modules, add the define there is no need for dynamic modules, add the define
@@ -237,24 +242,24 @@ See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the w
## Examples ## Examples
See the examples directory for some example janet code. See the examples directory for some example Janet code.
## 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 Gitter channel](https://gitter.im/janet-language/community).
Gitter provides Matrix and irc bridges as well. Gitter provides Matrix and IRC bridges as well.
## FAQ ## FAQ
### Where is (favorite feature from other language)? ### Where is (favorite feature from other language)?
It may exist, it may not. If you want to propose major language features, go ahead and open an issue, but It may exist, it may not. If you want to propose a major language feature, go ahead and open an issue, but
they will likely by closed as "will not implement". Often, such features make one usecase simpler at the expense it will likely be closed as "will not implement". Often, such features make one usecase simpler at the expense
of 5 others by making the language more complicated. of 5 others by making the language more complicated.
### Is there a language spec? ### Is there a language spec?
There is not currently a spec besides the documentation at https://janet-lang.org. There is not currently a spec besides the documentation at <https://janet-lang.org>.
### Is this Scheme/Common Lisp? Where are the cons cells? ### Is this Scheme/Common Lisp? Where are the cons cells?
@@ -270,13 +275,13 @@ Internally, Janet is not at all like Clojure.
No. They are immutable arrays and hash tables. Don't try and use them like Clojure's vectors No. They are immutable arrays and hash tables. Don't try and use them like Clojure's vectors
and maps, instead they work well as table keys or other identifiers. and maps, instead they work well as table keys or other identifiers.
### Can I do Object Oriented programming with Janet? ### Can I do object-oriented programming with Janet?
To some extent, yes. However, it is not the recommended method of abstraction, and performance may suffer. To some extent, yes. However, it is not the recommended method of abstraction, and performance may suffer.
That said, tables can be used to make mutable objects with inheritance and polymorphism, where object That said, tables can be used to make mutable objects with inheritance and polymorphism, where object
methods are implemeted with keywords. methods are implemented with keywords.
``` ```clj
(def Car @{:honk (fn [self msg] (print "car " self " goes " msg)) }) (def Car @{:honk (fn [self msg] (print "car " self " goes " msg)) })
(def my-car (table/setproto @{} Car)) (def my-car (table/setproto @{} Car))
(:honk my-car "Beep!") (:honk my-car "Beep!")
@@ -287,9 +292,9 @@ methods are implemeted with keywords.
Usually, one of a few reasons: Usually, one of a few reasons:
- Often, it already exists in a different form and the Clojure port would be redundant. - Often, it already exists in a different form and the Clojure port would be redundant.
- Clojure programs often generate a lot of garbage and rely on the JVM to clean it up. - Clojure programs often generate a lot of garbage and rely on the JVM to clean it up.
Janet does not run on the JVM, and has a more primitive garbage collector. Janet does not run on the JVM and has a more primitive garbage collector.
- We want to keep the Janet core small. With Lisps, usually a feature can be added as a library - We want to keep the Janet core small. With Lisps, a feature can usually be added as a library
without feeling "bolted on", especially when compared to ALGOL like languages. Adding features without feeling "bolted on", especially when compared to ALGOL-like languages. Adding features
to the core also makes it a bit more difficult to keep Janet maximally portable. to the core also makes it a bit more difficult to keep Janet maximally portable.
### Why is my terminal spitting out junk when I run the REPL? ### Why is my terminal spitting out junk when I run the REPL?
@@ -297,7 +302,7 @@ Usually, one of a few reasons:
Make sure your terminal supports ANSI escape codes. Most modern terminals will Make sure your terminal supports ANSI escape codes. Most modern terminals will
support these, but some older terminals, Windows consoles, or embedded terminals support these, but some older terminals, Windows consoles, or embedded terminals
will not. If your terminal does not support ANSI escape codes, run the REPL with will not. If your terminal does not support ANSI escape codes, run the REPL with
the `-n` flag, which disables color output. You can also try the `-s` if further issues the `-n` flag, which disables color output. You can also try the `-s` flag if further issues
ensue. ensue.
## Why is it called "Janet"? ## Why is it called "Janet"?

10
janet.1
View File

@@ -183,6 +183,10 @@ default repl.
.BR \-n .BR \-n
Disable ANSI colors in the repl. Has no effect if no repl is run. Disable ANSI colors in the repl. Has no effect if no repl is run.
.TP
.BR \-N
Enable ANSI colors in the repl. Has no effect if no repl is run.
.TP .TP
.BR \-r .BR \-r
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no
@@ -268,5 +272,11 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
cannot be defined for this variable to have an effect. cannot be defined for this variable to have an effect.
.RE .RE
.B NO_COLOR
.RS
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime
via dynamic bindings *err-color* and *pretty-format*, or via the command line parameters -n and -N.
.RE
.SH AUTHOR .SH AUTHOR
Written by Calvin Rose <calsrose@gmail.com> Written by Calvin Rose <calsrose@gmail.com>

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.26.0') version : '1.28.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')

View File

@@ -129,30 +129,28 @@
# For macros, we define an imcomplete odd? function that will be overriden. # For macros, we define an imcomplete odd? function that will be overriden.
(defn odd? [x] (= 1 (mod x 2))) (defn odd? [x] (= 1 (mod x 2)))
(def idempotent? (def- non-atomic-types
``` {:array true
(idempotent? x) :tuple true
:table true
:buffer true
:symbol true
:struct true})
Check if x is a value that evaluates to itself when compiled. (defn idempotent?
``` "Check if x is a value that evaluates to itself when compiled."
(do [x]
(def non-atomic-types (not (in non-atomic-types (type x))))
{:array true
:tuple true
:table true
:buffer true
:struct true})
(fn idempotent? [x] (not (in non-atomic-types (type x))))))
# C style macros and functions for imperative sugar. No bitwise though. # C style macros and functions for imperative sugar. No bitwise though.
(defn inc "Returns x + 1." [x] (+ x 1)) (defn inc "Returns x + 1." [x] (+ x 1))
(defn dec "Returns x - 1." [x] (- x 1)) (defn dec "Returns x - 1." [x] (- x 1))
(defmacro ++ "Increments the var x by 1." [x] ~(set ,x (,+ ,x ,1))) (defmacro ++ "Increments the var x by 1." [x] ~(set ,x (,+ ,x ,1)))
(defmacro -- "Decrements the var x by 1." [x] ~(set ,x (,- ,x ,1))) (defmacro -- "Decrements the var x by 1." [x] ~(set ,x (,- ,x ,1)))
(defmacro += "Increments the var x by n." [x n] ~(set ,x (,+ ,x ,n))) (defmacro += "Increments the var x by n." [x & ns] ~(set ,x (,+ ,x ,;ns)))
(defmacro -= "Decrements the var x by n." [x n] ~(set ,x (,- ,x ,n))) (defmacro -= "Decrements the var x by n." [x & ns] ~(set ,x (,- ,x ,;ns)))
(defmacro *= "Shorthand for (set x (\\* x n))." [x n] ~(set ,x (,* ,x ,n))) (defmacro *= "Shorthand for (set x (\\* x n))." [x & ns] ~(set ,x (,* ,x ,;ns)))
(defmacro /= "Shorthand for (set x (/ x n))." [x n] ~(set ,x (,/ ,x ,n))) (defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns)))
(defmacro %= "Shorthand for (set x (% x n))." [x n] ~(set ,x (,% ,x ,n))) (defmacro %= "Shorthand for (set x (% x n))." [x n] ~(set ,x (,% ,x ,n)))
(defmacro assert (defmacro assert
@@ -282,7 +280,7 @@
(while (> i 0) (while (> i 0)
(-- i) (-- i)
(def v (in forms i)) (def v (in forms i))
(set ret (if (= ret true) (set ret (if (= i (- len 1))
v v
(if (idempotent? v) (if (idempotent? v)
['if v ret v] ['if v ret v]
@@ -613,6 +611,13 @@
(def $accum (gensym)) (def $accum (gensym))
~(do (def ,$accum @[]) (loop ,head (,array/push ,$accum (do ,;body))) ,$accum)) ~(do (def ,$accum @[]) (loop ,head (,array/push ,$accum (do ,;body))) ,$accum))
(defmacro catseq
``Similar to `loop`, but concatenates each element from the loop body into an array and returns that.
See `loop` for details.``
[head & body]
(def $accum (gensym))
~(do (def ,$accum @[]) (loop ,head (,array/concat ,$accum (do ,;body))) ,$accum))
(defmacro tabseq (defmacro tabseq
``Similar to `loop`, but accumulates key value pairs into a table. ``Similar to `loop`, but accumulates key value pairs into a table.
See `loop` for details.`` See `loop` for details.``
@@ -918,67 +923,68 @@
(set k (next ind k))) (set k (next ind k)))
ret) ret)
(defmacro- map-aggregator
`Aggregation logic for various map functions.`
[maptype res val]
(case maptype
:map ~(array/push ,res ,val)
:mapcat ~(array/concat ,res ,val)
:keep ~(if (def y ,val) (array/push ,res y))
:count ~(if ,val (++ ,res))
:some ~(if (def y ,val) (do (set ,res y) (break)))
:all ~(if (def y ,val) nil (do (set ,res y) (break)))))
(defmacro- map-n
`Generates efficient map logic for a specific number of
indexed beyond the first.`
[n maptype res f ind inds]
~(do
(def ,(seq [k :range [0 n]] (symbol 'ind k)) ,inds)
,;(seq [k :range [0 n]] ~(var ,(symbol 'key k) nil))
(each x ,ind
,;(seq [k :range [0 n]]
~(if (= nil (set ,(symbol 'key k) (next ,(symbol 'ind k) ,(symbol 'key k)))) (break)))
(map-aggregator ,maptype ,res (,f x ,;(seq [k :range [0 n]] ~(in ,(symbol 'ind k) ,(symbol 'key k))))))))
(defmacro- map-template
[maptype res f ind inds]
~(do
(def ninds (length ,inds))
(case ninds
0 (each x ,ind (map-aggregator ,maptype ,res (,f x)))
1 (map-n 1 ,maptype ,res ,f ,ind ,inds)
2 (map-n 2 ,maptype ,res ,f ,ind ,inds)
3 (map-n 3 ,maptype ,res ,f ,ind ,inds)
4 (map-n 4 ,maptype ,res ,f ,ind ,inds)
(do
(def iter-keys (array/new-filled ninds))
(def call-buffer (array/new-filled ninds))
(var done false)
(each x ,ind
(forv i 0 ninds
(let [old-key (in iter-keys i)
ii (in ,inds i)
new-key (next ii old-key)]
(if (= nil new-key)
(do (set done true) (break))
(do (set (iter-keys i) new-key) (set (call-buffer i) (in ii new-key))))))
(if done (break))
(map-aggregator ,maptype ,res (,f x ;call-buffer)))))))
(defn map (defn map
`Map a function over every value in a data structure and `Map a function over every value in a data structure and
return an array of the results.` return an array of the results.`
[f & inds] [f ind & inds]
(def ninds (length inds))
(if (= 0 ninds) (error "expected at least 1 indexed collection"))
(def res @[]) (def res @[])
(def [i1 i2 i3 i4] inds) (map-template :map res f ind inds)
(case ninds
1 (each x i1 (array/push res (f x)))
2 (do
(var k1 nil)
(var k2 nil)
(while true
(if (= nil (set k1 (next i1 k1))) (break))
(if (= nil (set k2 (next i2 k2))) (break))
(array/push res (f (in i1 k1) (in i2 k2)))))
3 (do
(var k1 nil)
(var k2 nil)
(var k3 nil)
(while true
(if (= nil (set k1 (next i1 k1))) (break))
(if (= nil (set k2 (next i2 k2))) (break))
(if (= nil (set k3 (next i3 k3))) (break))
(array/push res (f (in i1 k1) (in i2 k2) (in i3 k3)))))
4 (do
(var k1 nil)
(var k2 nil)
(var k3 nil)
(var k4 nil)
(while true
(if (= nil (set k1 (next i1 k1))) (break))
(if (= nil (set k2 (next i2 k2))) (break))
(if (= nil (set k3 (next i3 k3))) (break))
(if (= nil (set k4 (next i4 k4))) (break))
(array/push res (f (in i1 k1) (in i2 k2) (in i3 k3) (in i4 k4)))))
(do
(def iterkeys (array/new-filled ninds))
(var done false)
(def call-buffer @[])
(while true
(forv i 0 ninds
(let [old-key (in iterkeys i)
ii (in inds i)
new-key (next ii old-key)]
(if (= nil new-key)
(do (set done true) (break))
(do (set (iterkeys i) new-key) (array/push call-buffer (in ii new-key))))))
(if done (break))
(array/push res (f ;call-buffer))
(array/clear call-buffer))))
res) res)
(defn mapcat (defn mapcat
``Map a function over every element in an array or tuple and ``Map a function over every element in an array or tuple and
use `array/concat` to concatenate the results.`` use `array/concat` to concatenate the results.``
[f ind] [f ind & inds]
(def res @[]) (def res @[])
(each x ind (map-template :mapcat res f ind inds)
(array/concat res (f x)))
res) res)
(defn filter (defn filter
@@ -994,23 +1000,19 @@
(defn count (defn count
``Count the number of items in `ind` for which `(pred item)` ``Count the number of items in `ind` for which `(pred item)`
is true.`` is true.``
[pred ind] [pred ind & inds]
(var counter 0) (var res 0)
(each item ind (map-template :count res pred ind inds)
(if (pred item) res)
(++ counter)))
counter)
(defn keep (defn keep
``Given a predicate `pred`, return a new array containing the truthy results ``Given a predicate `pred`, return a new array containing the truthy results
of applying `pred` to each element in the indexed collection `ind`. This is of applying `pred` to each element in the indexed collection `ind`. This is
different from `filter` which returns an array of the original elements where different from `filter` which returns an array of the original elements where
the predicate is truthy.`` the predicate is truthy.``
[pred ind] [pred ind & inds]
(def res @[]) (def res @[])
(each item ind (map-template :keep res pred ind inds)
(if-let [y (pred item)]
(array/push res y)))
res) res)
(defn range (defn range
@@ -1135,16 +1137,16 @@
(take-until (complement pred) ind)) (take-until (complement pred) ind))
(defn drop (defn drop
``Drop the first n elements in an indexed or bytes type. Returns a new tuple or string ``Drop the first `n elements in an indexed or bytes type. Returns a new tuple or string
instance, respectively.`` instance, respectively. If `n` is negative, drops the last `n` elements instead.``
[n ind] [n ind]
(def use-str (bytes? ind)) (def use-str (bytes? ind))
(def f (if use-str string/slice tuple/slice)) (def f (if use-str string/slice tuple/slice))
(def len (length ind)) (def len (length ind))
# make sure start is in [0, len] (def negn (>= n 0))
(def m (if (> n 0) n 0)) (def start (if negn (min n len) 0))
(def start (if (> m len) len m)) (def end (if negn len (max 0 (+ len n))))
(f ind start -1)) (f ind start end))
(defn drop-until (defn drop-until
"Same as `(drop-while (complement pred) ind)`." "Same as `(drop-while (complement pred) ind)`."
@@ -1234,6 +1236,29 @@
(,eprintf (,dyn :pretty-format "%q") ,s) (,eprintf (,dyn :pretty-format "%q") ,s)
,s)) ,s))
(defn keep-syntax
``Creates a tuple with the tuple type and sourcemap of `before` but the
elements of `after`. If either one of its argements is not a tuple, returns
`after` unmodified. Useful to preserve syntactic information when transforming
an ast in macros.``
[before after]
(if (and (= :tuple (type before))
(= :tuple (type after)))
(do
(def res (if (= :parens (tuple/type before))
(tuple/slice after)
(tuple/brackets ;after)))
(tuple/setmap res ;(tuple/sourcemap before)))
after))
(defn keep-syntax!
``Like `keep-syntax`, but if `after` is an array, it is coerced into a tuple.
Useful to preserve syntactic information when transforming an ast in macros.``
[before after]
(keep-syntax before (if (= :array (type after))
(tuple/slice after)
after)))
(defmacro -> (defmacro ->
``Threading macro. Inserts x as the second value in the first form ``Threading macro. Inserts x as the second value in the first form
in `forms`, and inserts the modified first form into the second form in `forms`, and inserts the modified first form into the second form
@@ -1244,7 +1269,7 @@
(tuple (in n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def parts (array/concat @[h last] t)) (def parts (array/concat @[h last] t))
(tuple/slice parts 0)) (keep-syntax! n parts))
(reduce fop x forms)) (reduce fop x forms))
(defmacro ->> (defmacro ->>
@@ -1257,7 +1282,7 @@
(tuple (in n 0) (array/slice n 1)) (tuple (in n 0) (array/slice n 1))
(tuple n @[]))) (tuple n @[])))
(def parts (array/concat @[h] t @[last])) (def parts (array/concat @[h] t @[last]))
(tuple/slice parts 0)) (keep-syntax! n parts))
(reduce fop x forms)) (reduce fop x forms))
(defmacro -?> (defmacro -?>
@@ -1273,7 +1298,7 @@
(tuple n @[]))) (tuple n @[])))
(def sym (gensym)) (def sym (gensym))
(def parts (array/concat @[h sym] t)) (def parts (array/concat @[h sym] t))
~(let [,sym ,last] (if ,sym ,(tuple/slice parts 0)))) ~(let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
(reduce fop x forms)) (reduce fop x forms))
(defmacro -?>> (defmacro -?>>
@@ -1289,7 +1314,7 @@
(tuple n @[]))) (tuple n @[])))
(def sym (gensym)) (def sym (gensym))
(def parts (array/concat @[h] t @[sym])) (def parts (array/concat @[h] t @[sym]))
~(let [,sym ,last] (if ,sym ,(tuple/slice parts 0)))) ~(let [,sym ,last] (if ,sym ,(keep-syntax! n parts))))
(reduce fop x forms)) (reduce fop x forms))
(defn- walk-ind [f form] (defn- walk-ind [f form]
@@ -1313,10 +1338,7 @@
:table (walk-dict f form) :table (walk-dict f form)
:struct (table/to-struct (walk-dict f form)) :struct (table/to-struct (walk-dict f form))
:array (walk-ind f form) :array (walk-ind f form)
:tuple (let [x (walk-ind f form)] :tuple (keep-syntax! form (walk-ind f form))
(if (= :parens (tuple/type form))
(tuple/slice x)
(tuple/brackets ;x)))
form)) form))
(defn postwalk (defn postwalk
@@ -1724,6 +1746,14 @@
(printf (dyn *pretty-format* "%q") x) (printf (dyn *pretty-format* "%q") x)
(flush)) (flush))
(defn file/lines
"Return an iterator over the lines of a file."
[file]
(coro
(while (def line (file/read file :line))
(yield line))))
### ###
### ###
### Pattern Matching ### Pattern Matching
@@ -2065,21 +2095,21 @@
ret) ret)
(defn all (defn all
``Returns true if `(pred item)` returns a truthy value for every item in `xs`. ``Returns true if `(pred item)` is truthy for every item in `ind`.
Otherwise, returns the first falsey `(pred item)` result encountered. Otherwise, returns the first falsey result encountered.
Returns true if `xs` is empty.`` Returns true if `ind` is empty.``
[pred xs] [pred ind & inds]
(var ret true) (var res true)
(loop [x :in xs :while ret] (set ret (pred x))) (map-template :all res pred ind inds)
ret) res)
(defn some (defn some
``Returns nil if all `xs` are false or nil, otherwise returns the result of the ``Returns nil if `(pred item)` is false or nil for every item in `ind`.
first truthy predicate, `(pred x)`.`` Otherwise, returns the first truthy result encountered.``
[pred xs] [pred ind & inds]
(var ret nil) (var res nil)
(loop [x :in xs :while (not ret)] (if-let [y (pred x)] (set ret y))) (map-template :some res pred ind inds)
ret) res)
(defn deep-not= (defn deep-not=
``Like `not=`, but mutable types (arrays, tables, buffers) are considered ``Like `not=`, but mutable types (arrays, tables, buffers) are considered
@@ -2089,8 +2119,24 @@
(or (or
(not= tx (type y)) (not= tx (type y))
(case tx (case tx
:tuple (or (not= (length x) (length y)) (some identity (map deep-not= x y))) :tuple (or (not= (length x) (length y))
:array (or (not= (length x) (length y)) (some identity (map deep-not= x y))) (do
(var ret false)
(forv i 0 (length x)
(def xx (in x i))
(def yy (in y i))
(if (deep-not= xx yy)
(break (set ret true))))
ret))
:array (or (not= (length x) (length y))
(do
(var ret false)
(forv i 0 (length x)
(def xx (in x i))
(def yy (in y i))
(if (deep-not= xx yy)
(break (set ret true))))
ret))
:struct (deep-not= (kvs x) (kvs y)) :struct (deep-not= (kvs x) (kvs y))
:table (deep-not= (table/to-struct x) (table/to-struct y)) :table (deep-not= (table/to-struct x) (table/to-struct y))
:buffer (not= (string x) (string y)) :buffer (not= (string x) (string y))
@@ -2117,6 +2163,19 @@
:buffer (string x) :buffer (string x)
x)) x))
(defn thaw
`Thaw an object (make it mutable) and do a deep copy, making
child value also mutable. Closures, fibers, and abstract
types will not be recursively thawed, but all other types will`
[ds]
(case (type ds)
:array (walk-ind thaw ds)
:tuple (walk-ind thaw ds)
:table (walk-dict thaw (table/proto-flatten ds))
:struct (walk-dict thaw (struct/proto-flatten ds))
:string (buffer ds)
ds))
(defn macex (defn macex
``Expand macros completely. ``Expand macros completely.
`on-binding` is an optional callback for whenever a normal symbolic binding `on-binding` is an optional callback for whenever a normal symbolic binding
@@ -2184,6 +2243,7 @@
(defn saw-special-arg (defn saw-special-arg
[num] [num]
(set max-param-seen (max max-param-seen num))) (set max-param-seen (max max-param-seen num)))
(def prefix (gensym))
(defn on-binding (defn on-binding
[x] [x]
(if (string/has-prefix? '$ x) (if (string/has-prefix? '$ x)
@@ -2191,22 +2251,24 @@
(= '$ x) (= '$ x)
(do (do
(saw-special-arg 0) (saw-special-arg 0)
'$0) (symbol prefix '$0))
(= '$& x) (= '$& x)
(do (do
(set vararg true) (set vararg true)
x) (symbol prefix x))
:else :else
(do (do
(def num (scan-number (string/slice x 1))) (def num (scan-number (string/slice x 1)))
(if (nat? num) (if (nat? num)
(saw-special-arg num)) (do
x)) (saw-special-arg num)
(symbol prefix x))
x)))
x)) x))
(def expanded (macex arg on-binding)) (def expanded (macex arg on-binding))
(def name-splice (if name [name] [])) (def name-splice (if name [name] []))
(def fn-args (seq [i :range [0 (+ 1 max-param-seen)]] (symbol '$ i))) (def fn-args (seq [i :range [0 (+ 1 max-param-seen)]] (symbol prefix '$ i)))
~(fn ,;name-splice [,;fn-args ,;(if vararg ['& '$&] [])] ,expanded)) ~(fn ,;name-splice [,;fn-args ,;(if vararg ['& (symbol prefix '$&)] [])] ,expanded))
### ###
### ###
@@ -2766,6 +2828,7 @@
(put nextenv :fiber fiber) (put nextenv :fiber fiber)
(put nextenv :debug-level level) (put nextenv :debug-level level)
(put nextenv :signal (fiber/last-value fiber)) (put nextenv :signal (fiber/last-value fiber))
(merge-into nextenv debugger-env) (merge-into nextenv debugger-env)
(defn debugger-chunks [buf p] (defn debugger-chunks [buf p]
(def status (:state p :delimiters)) (def status (:state p :delimiters))
@@ -3374,11 +3437,16 @@
(print)) (print))
(defn .frame (defn .frame
"Show a stack frame." "Show a stack frame"
[&opt n] [&opt n]
(def stack (debug/stack (.fiber))) (def stack (debug/stack (.fiber)))
(in stack (or n 0))) (in stack (or n 0)))
(defn .locals
"Show local bindings"
[&opt n]
(get (.frame n) :locals))
(defn .fn (defn .fn
"Get the current function." "Get the current function."
[&opt n] [&opt n]
@@ -3583,13 +3651,18 @@
(,ev/deadline ,deadline nil ,f) (,ev/deadline ,deadline nil ,f)
(,resume ,f)))) (,resume ,f))))
(defn- cancel-all [fibers reason] (each f fibers (ev/cancel f reason) (put fibers f nil)))
(defn- wait-for-fibers (defn- wait-for-fibers
[chan fibers] [chan fibers]
(repeat (length fibers) (defer (cancel-all fibers "parent canceled")
(def [sig fiber] (ev/take chan)) (repeat (length fibers)
(unless (= sig :ok) (def [sig fiber] (ev/take chan))
(each f fibers (ev/cancel f "sibling canceled")) (if (= sig :ok)
(propagate (fiber/last-value fiber) fiber)))) (put fibers fiber nil)
(do
(cancel-all fibers "sibling canceled")
(propagate (fiber/last-value fiber) fiber))))))
(defmacro ev/gather (defmacro ev/gather
`` ``
@@ -3597,13 +3670,16 @@
Returns the gathered results in an array. Returns the gathered results in an array.
`` ``
[& bodies] [& bodies]
(with-syms [chan res] (with-syms [chan res fset ftemp]
~(do ~(do
(def ,fset @{})
(def ,chan (,ev/chan)) (def ,chan (,ev/chan))
(def ,res @[]) (def ,res @[])
(,wait-for-fibers ,chan ,;(seq [[i body] :pairs bodies]
,(seq [[i body] :pairs bodies] ~(do
~(,ev/go (fn [] (put ,res ,i ,body)) nil ,chan))) (def ,ftemp (,ev/go (fn [] (put ,res ,i ,body)) nil ,chan))
(,put ,fset ,ftemp ,ftemp)))
(,wait-for-fibers ,chan ,fset)
,res)))) ,res))))
(compwhen (dyn 'net/listen) (compwhen (dyn 'net/listen)
@@ -3816,6 +3892,7 @@
(if-let [jp (getenv-alias "JANET_PATH")] (setdyn *syspath* jp)) (if-let [jp (getenv-alias "JANET_PATH")] (setdyn *syspath* jp))
(if-let [jprofile (getenv-alias "JANET_PROFILE")] (setdyn *profilepath* jprofile)) (if-let [jprofile (getenv-alias "JANET_PROFILE")] (setdyn *profilepath* jprofile))
(set colorize (not (getenv-alias "NO_COLOR")))
(defn- get-lint-level (defn- get-lint-level
[i] [i]
@@ -3833,7 +3910,7 @@
-v : Print the version string -v : Print the version string
-s : Use raw stdin instead of getline like functionality -s : Use raw stdin instead of getline like functionality
-e code : Execute a string of janet -e code : Execute a string of janet
-E code arguments... : Evaluate an expression as a short-fn with arguments -E code arguments... : Evaluate an expression as a short-fn with arguments
-d : Set the debug flag in the REPL -d : Set the debug flag in the REPL
-r : Enter the REPL after running all scripts -r : Enter the REPL after running all scripts
-R : Disables loading profile.janet when JANET_PROFILE is present -R : Disables loading profile.janet when JANET_PROFILE is present
@@ -3844,6 +3921,7 @@
-c source output : Compile janet source code into an image -c source output : Compile janet source code into an image
-i : Load the script argument as an image file instead of source code -i : Load the script argument as an image file instead of source code
-n : Disable ANSI color output in the REPL -n : Disable ANSI color output in the REPL
-N : Enable ANSI color output in the REPL
-l lib : Use a module before processing more arguments -l lib : Use a module before processing more arguments
-w level : Set the lint warning level - default is "normal" -w level : Set the lint warning level - default is "normal"
-x level : Set the lint error level - default is "none" -x level : Set the lint error level - default is "none"
@@ -3859,6 +3937,7 @@
"i" (fn [&] (set expect-image true) 1) "i" (fn [&] (set expect-image true) 1)
"k" (fn [&] (set compile-only true) (set exit-on-error false) 1) "k" (fn [&] (set compile-only true) (set exit-on-error false) 1)
"n" (fn [&] (set colorize false) 1) "n" (fn [&] (set colorize false) 1)
"N" (fn [&] (set colorize true) 1)
"m" (fn [i &] (setdyn *syspath* (in args (+ i 1))) 2) "m" (fn [i &] (setdyn *syspath* (in args (+ i 1))) 2)
"c" (fn c-switch [i &] "c" (fn c-switch [i &]
(def path (in args (+ i 1))) (def path (in args (+ i 1)))
@@ -3933,7 +4012,7 @@
compile-only (flycheck stdin :source :stdin :exit exit-on-error) compile-only (flycheck stdin :source :stdin :exit exit-on-error)
(do (do
(if-not quiet (if-not quiet
(print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch) " - '(doc)' for help")) (print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch) "/" (os/compiler) " - '(doc)' for help"))
(flush) (flush)
(defn getprompt [p] (defn getprompt [p]
(def [line] (parser/where p)) (def [line] (parser/where p))

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 26 #define JANET_VERSION_MINOR 28
#define JANET_VERSION_PATCH 0 #define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA "" #define JANET_VERSION_EXTRA "-dev"
#define JANET_VERSION "1.26.0" #define JANET_VERSION "1.28.0-dev"
/* #define JANET_BUILD "local" */ /* #define JANET_BUILD "local" */

View File

@@ -98,11 +98,11 @@ size_t janet_os_rwlock_size(void) {
} }
static int32_t janet_incref(JanetAbstractHead *ab) { static int32_t janet_incref(JanetAbstractHead *ab) {
return InterlockedIncrement(&ab->gc.data.refcount); return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount);
} }
static int32_t janet_decref(JanetAbstractHead *ab) { static int32_t janet_decref(JanetAbstractHead *ab) {
return InterlockedDecrement(&ab->gc.data.refcount); return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount);
} }
void janet_os_mutex_init(JanetOSMutex *mutex) { void janet_os_mutex_init(JanetOSMutex *mutex) {

View File

@@ -187,7 +187,11 @@ static void janet_asm_longjmp(JanetAssembler *a) {
/* Throw some kind of assembly error */ /* Throw some kind of assembly error */
static void janet_asm_error(JanetAssembler *a, const char *message) { static void janet_asm_error(JanetAssembler *a, const char *message) {
a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex); if (a->errindex < 0) {
a->errmessage = janet_formatc("%s", message);
} else {
a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
}
janet_asm_longjmp(a); janet_asm_longjmp(a);
} }
#define janet_asm_assert(a, c, m) do { if (!(c)) janet_asm_error((a), (m)); } while (0) #define janet_asm_assert(a, c, m) do { if (!(c)) janet_asm_error((a), (m)); } while (0)
@@ -516,6 +520,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
#endif #endif
if (NULL != a.parent) { if (NULL != a.parent) {
janet_asm_deinit(&a); janet_asm_deinit(&a);
a.parent->errmessage = a.errmessage;
janet_asm_longjmp(a.parent); janet_asm_longjmp(a.parent);
} }
result.funcdef = NULL; result.funcdef = NULL;
@@ -601,6 +606,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
/* Parse sub funcdefs */ /* Parse sub funcdefs */
x = janet_get1(s, janet_ckeywordv("closures")); x = janet_get1(s, janet_ckeywordv("closures"));
if (janet_checktype(x, JANET_NIL)) {
x = janet_get1(s, janet_ckeywordv("defs"));
}
if (janet_indexed_view(x, &arr, &count)) { if (janet_indexed_view(x, &arr, &count)) {
int32_t i; int32_t i;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@@ -713,10 +721,63 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
} }
} }
/* Set symbolmap */
def->symbolmap = NULL;
def->symbolmap_length = 0;
x = janet_get1(s, janet_ckeywordv("symbolmap"));
if (janet_indexed_view(x, &arr, &count)) {
def->symbolmap_length = count;
def->symbolmap = janet_malloc(sizeof(JanetSymbolMap) * (size_t)count);
if (NULL == def->symbolmap) {
JANET_OUT_OF_MEMORY;
}
for (i = 0; i < count; i++) {
const Janet *tup;
Janet entry = arr[i];
JanetSymbolMap ss;
if (!janet_checktype(entry, JANET_TUPLE)) {
janet_asm_error(&a, "expected tuple");
}
tup = janet_unwrap_tuple(entry);
if (janet_keyeq(tup[0], "upvalue")) {
ss.birth_pc = UINT32_MAX;
} else if (!janet_checkint(tup[0])) {
janet_asm_error(&a, "expected integer");
} else {
ss.birth_pc = janet_unwrap_integer(tup[0]);
}
if (!janet_checkint(tup[1])) {
janet_asm_error(&a, "expected integer");
}
if (!janet_checkint(tup[2])) {
janet_asm_error(&a, "expected integer");
}
if (!janet_checktype(tup[3], JANET_SYMBOL)) {
janet_asm_error(&a, "expected symbol");
}
ss.death_pc = janet_unwrap_integer(tup[1]);
ss.slot_index = janet_unwrap_integer(tup[2]);
ss.symbol = janet_unwrap_symbol(tup[3]);
def->symbolmap[i] = ss;
}
}
if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
/* Set environments */ /* Set environments */
def->environments = x = janet_get1(s, janet_ckeywordv("environments"));
janet_realloc(def->environments, def->environments_length * sizeof(int32_t)); if (janet_indexed_view(x, &arr, &count)) {
if (NULL == def->environments) { def->environments_length = count;
if (def->environments_length) {
def->environments = janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
}
for (int32_t i = 0; i < count; i++) {
if (!janet_checkint(arr[i])) {
janet_asm_error(&a, "expected integer");
}
def->environments[i] = janet_unwrap_integer(arr[i]);
}
}
if (def->environments_length && NULL == def->environments) {
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;
} }
@@ -865,6 +926,30 @@ static Janet janet_disasm_slotcount(JanetFuncDef *def) {
return janet_wrap_integer(def->slotcount); return janet_wrap_integer(def->slotcount);
} }
static Janet janet_disasm_symbolslots(JanetFuncDef *def) {
if (def->symbolmap == NULL) {
return janet_wrap_nil();
}
JanetArray *symbolslots = janet_array(def->symbolmap_length);
Janet upvaluekw = janet_ckeywordv("upvalue");
for (int32_t i = 0; i < def->symbolmap_length; i++) {
JanetSymbolMap ss = def->symbolmap[i];
Janet *t = janet_tuple_begin(4);
if (ss.birth_pc == UINT32_MAX) {
t[0] = upvaluekw;
} else {
t[0] = janet_wrap_integer(ss.birth_pc);
}
t[1] = janet_wrap_integer(ss.death_pc);
t[2] = janet_wrap_integer(ss.slot_index);
t[3] = janet_wrap_symbol(ss.symbol);
symbolslots->data[i] = janet_wrap_tuple(janet_tuple_end(t));
}
symbolslots->count = def->symbolmap_length;
return janet_wrap_array(symbolslots);
}
static Janet janet_disasm_bytecode(JanetFuncDef *def) { static Janet janet_disasm_bytecode(JanetFuncDef *def) {
JanetArray *bcode = janet_array(def->bytecode_length); JanetArray *bcode = janet_array(def->bytecode_length);
for (int32_t i = 0; i < def->bytecode_length; i++) { for (int32_t i = 0; i < def->bytecode_length; i++) {
@@ -944,6 +1029,7 @@ Janet janet_disasm(JanetFuncDef *def) {
janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def)); janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def)); janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def)); janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def)); janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def)); janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def));
janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def)); janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def));
@@ -961,7 +1047,7 @@ JANET_CORE_FN(cfun_asm,
JanetAssembleResult res; JanetAssembleResult res;
res = janet_asm(argv[0], 0); res = janet_asm(argv[0], 0);
if (res.status != JANET_ASSEMBLE_OK) { if (res.status != JANET_ASSEMBLE_OK) {
janet_panics(res.error); janet_panics(res.error ? res.error : janet_cstring("invalid assembly"));
} }
return janet_wrap_function(janet_thunk(res.funcdef)); return janet_wrap_function(janet_thunk(res.funcdef));
} }
@@ -980,6 +1066,7 @@ JANET_CORE_FN(cfun_disasm,
"* :source - name of source file that this function was compiled from.\n" "* :source - name of source file that this function was compiled from.\n"
"* :name - name of function.\n" "* :name - name of function.\n"
"* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n" "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
"* :symbolmap - all symbols and their slots.\n"
"* :constants - an array of constants referenced by this function.\n" "* :constants - an array of constants referenced by this function.\n"
"* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n" "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
"* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n" "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"

View File

@@ -28,8 +28,15 @@
#include "state.h" #include "state.h"
#endif #endif
/* Allow for managed buffers that cannot realloc/free their backing memory */
static void janet_buffer_can_realloc(JanetBuffer *buffer) {
if (buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) {
janet_panic("buffer cannot reallocate foreign memory");
}
}
/* Initialize a buffer */ /* Initialize a buffer */
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) { static JanetBuffer *janet_buffer_init_impl(JanetBuffer *buffer, int32_t capacity) {
uint8_t *data = NULL; uint8_t *data = NULL;
if (capacity < 4) capacity = 4; if (capacity < 4) capacity = 4;
janet_gcpressure(capacity); janet_gcpressure(capacity);
@@ -43,15 +50,37 @@ JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
return buffer; return buffer;
} }
/* Initialize a buffer */
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
janet_buffer_init_impl(buffer, capacity);
buffer->gc.data.next = NULL;
buffer->gc.flags = JANET_MEM_DISABLED;
return buffer;
}
/* Initialize an unmanaged buffer */
JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count) {
if (count < 0) janet_panic("count < 0");
if (capacity < count) janet_panic("capacity < count");
JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
buffer->gc.flags |= JANET_BUFFER_FLAG_NO_REALLOC;
buffer->capacity = capacity;
buffer->count = count;
buffer->data = (uint8_t *) memory;
return buffer;
}
/* Deinitialize a buffer (free data memory) */ /* Deinitialize a buffer (free data memory) */
void janet_buffer_deinit(JanetBuffer *buffer) { void janet_buffer_deinit(JanetBuffer *buffer) {
janet_free(buffer->data); if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
janet_free(buffer->data);
}
} }
/* Initialize a buffer */ /* Initialize a buffer */
JanetBuffer *janet_buffer(int32_t capacity) { JanetBuffer *janet_buffer(int32_t capacity) {
JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer)); JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
return janet_buffer_init(buffer, capacity); return janet_buffer_init_impl(buffer, capacity);
} }
/* Ensure that the buffer has enough internal capacity */ /* Ensure that the buffer has enough internal capacity */
@@ -59,6 +88,7 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
uint8_t *new_data; uint8_t *new_data;
uint8_t *old = buffer->data; uint8_t *old = buffer->data;
if (capacity <= buffer->capacity) return; if (capacity <= buffer->capacity) return;
janet_buffer_can_realloc(buffer);
int64_t big_capacity = ((int64_t) capacity) * growth; int64_t big_capacity = ((int64_t) capacity) * growth;
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity; capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
janet_gcpressure(capacity - buffer->capacity); janet_gcpressure(capacity - buffer->capacity);
@@ -91,6 +121,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
} }
int32_t new_size = buffer->count + n; int32_t new_size = buffer->count + n;
if (new_size > buffer->capacity) { if (new_size > buffer->capacity) {
janet_buffer_can_realloc(buffer);
int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2); int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2);
uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t)); uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t));
janet_gcpressure(new_capacity - buffer->capacity); janet_gcpressure(new_capacity - buffer->capacity);
@@ -212,6 +243,7 @@ JANET_CORE_FN(cfun_buffer_trim,
"modified buffer.") { "modified buffer.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetBuffer *buffer = janet_getbuffer(argv, 0); JanetBuffer *buffer = janet_getbuffer(argv, 0);
janet_buffer_can_realloc(buffer);
if (buffer->count < buffer->capacity) { if (buffer->count < buffer->capacity) {
int32_t newcap = buffer->count > 4 ? buffer->count : 4; int32_t newcap = buffer->count > 4 ? buffer->count : 4;
uint8_t *newData = janet_realloc(buffer->data, newcap); uint8_t *newData = janet_realloc(buffer->data, newcap);
@@ -275,17 +307,8 @@ JANET_CORE_FN(cfun_buffer_chars,
return argv[0]; return argv[0];
} }
JANET_CORE_FN(cfun_buffer_push, static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
"(buffer/push buffer & xs)", for (int32_t i = argc_offset; i < argc; i++) {
"Push both individual bytes and byte sequences to a buffer. For each x in xs, "
"push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
"Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
"Returns the modified buffer. "
"Will throw an error if the buffer overflows.") {
int32_t i;
janet_arity(argc, 1, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
for (i = 1; i < argc; i++) {
if (janet_checktype(argv[i], JANET_NUMBER)) { if (janet_checktype(argv[i], JANET_NUMBER)) {
janet_buffer_push_u8(buffer, (uint8_t)(janet_getinteger(argv, i) & 0xFF)); janet_buffer_push_u8(buffer, (uint8_t)(janet_getinteger(argv, i) & 0xFF));
} else { } else {
@@ -297,6 +320,37 @@ JANET_CORE_FN(cfun_buffer_push,
janet_buffer_push_bytes(buffer, view.bytes, view.len); janet_buffer_push_bytes(buffer, view.bytes, view.len);
} }
} }
}
JANET_CORE_FN(cfun_buffer_push_at,
"(buffer/push-at buffer index & xs)",
"Same as buffer/push, but copies the new data into the buffer "
" at index `index`.") {
janet_arity(argc, 2, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int32_t index = janet_getinteger(argv, 1);
int32_t old_count = buffer->count;
if (index < 0 || index > old_count) {
janet_panicf("index out of range [0, %d)", old_count);
}
buffer->count = index;
buffer_push_impl(buffer, argv, 2, argc);
if (buffer->count < old_count) {
buffer->count = old_count;
}
return argv[0];
}
JANET_CORE_FN(cfun_buffer_push,
"(buffer/push buffer & xs)",
"Push both individual bytes and byte sequences to a buffer. For each x in xs, "
"push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
"Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
"Returns the modified buffer. "
"Will throw an error if the buffer overflows.") {
janet_arity(argc, 1, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
buffer_push_impl(buffer, argv, 1, argc);
return argv[0]; return argv[0];
} }
@@ -442,7 +496,7 @@ JANET_CORE_FN(cfun_buffer_blit,
JANET_CORE_FN(cfun_buffer_format, JANET_CORE_FN(cfun_buffer_format,
"(buffer/format buffer format & args)", "(buffer/format buffer format & args)",
"Snprintf like functionality for printing values into a buffer. Returns " "Snprintf like functionality for printing values into a buffer. Returns "
" the modified buffer.") { "the modified buffer.") {
janet_arity(argc, 2, -1); janet_arity(argc, 2, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0); JanetBuffer *buffer = janet_getbuffer(argv, 0);
const char *strfrmt = (const char *) janet_getstring(argv, 1); const char *strfrmt = (const char *) janet_getstring(argv, 1);
@@ -460,6 +514,7 @@ void janet_lib_buffer(JanetTable *env) {
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", 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/popn", cfun_buffer_popn), JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
JANET_CORE_REG("buffer/clear", cfun_buffer_clear), JANET_CORE_REG("buffer/clear", cfun_buffer_clear),
JANET_CORE_REG("buffer/slice", cfun_buffer_slice), JANET_CORE_REG("buffer/slice", cfun_buffer_slice),

View File

@@ -25,6 +25,7 @@
#include <janet.h> #include <janet.h>
#include "gc.h" #include "gc.h"
#include "util.h" #include "util.h"
#include "regalloc.h"
#endif #endif
/* Look up table for instructions */ /* Look up table for instructions */
@@ -106,6 +107,288 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
JINT_SSS /* JOP_CANCEL, */ JINT_SSS /* JOP_CANCEL, */
}; };
/* Remove all noops while preserving jumps and debugging information.
* Useful as part of a filtering compiler pass. */
void janet_bytecode_remove_noops(JanetFuncDef *def) {
/* Get an instruction rewrite map so we can rewrite jumps */
uint32_t *pc_map = janet_smalloc(sizeof(uint32_t) * (1 + def->bytecode_length));
uint32_t new_bytecode_length = 0;
for (int32_t i = 0; i < def->bytecode_length; i++) {
uint32_t instr = def->bytecode[i];
uint32_t opcode = instr & 0x7F;
pc_map[i] = new_bytecode_length;
if (opcode != JOP_NOOP) {
new_bytecode_length++;
}
}
pc_map[def->bytecode_length] = new_bytecode_length;
/* Linear scan rewrite bytecode and sourcemap. Also fix jumps. */
int32_t j = 0;
for (int32_t i = 0; i < def->bytecode_length; i++) {
uint32_t instr = def->bytecode[i];
uint32_t opcode = instr & 0x7F;
int32_t old_jump_target = 0;
int32_t new_jump_target = 0;
switch (opcode) {
case JOP_NOOP:
continue;
case JOP_JUMP:
/* relative pc is in DS field of instruction */
old_jump_target = i + (((int32_t)instr) >> 8);
new_jump_target = pc_map[old_jump_target];
instr += (new_jump_target - old_jump_target + (i - j)) << 8;
break;
case JOP_JUMP_IF:
case JOP_JUMP_IF_NIL:
case JOP_JUMP_IF_NOT:
case JOP_JUMP_IF_NOT_NIL:
/* relative pc is in ES field of instruction */
old_jump_target = i + (((int32_t)instr) >> 16);
new_jump_target = pc_map[old_jump_target];
instr += (new_jump_target - old_jump_target + (i - j)) << 16;
break;
default:
break;
}
def->bytecode[j] = instr;
if (def->sourcemap != NULL) {
def->sourcemap[j] = def->sourcemap[i];
}
j++;
}
/* Rewrite symbolmap */
for (int32_t i = 0; i < def->symbolmap_length; i++) {
JanetSymbolMap *sm = def->symbolmap + i;
/* Don't rewrite upvalue mappings */
if (sm->birth_pc < UINT32_MAX) {
sm->birth_pc = pc_map[sm->birth_pc];
sm->death_pc = pc_map[sm->death_pc];
}
}
def->bytecode_length = new_bytecode_length;
janet_sfree(pc_map);
}
/* Remove redundant loads, moves and other instructions if possible and convert them to
* noops. Input is assumed valid bytecode. */
void janet_bytecode_movopt(JanetFuncDef *def) {
JanetcRegisterAllocator ra;
int recur = 1;
/* Iterate this until no more instructions can be removed. */
while (recur) {
janetc_regalloc_init(&ra);
/* Look for slots that have writes but no reads (and aren't in the closure bitset). */
if (def->closure_bitset != NULL) {
for (int32_t i = 0; i < def->slotcount; i++) {
int32_t index = i >> 5;
uint32_t mask = 1U << (((uint32_t) i) & 31);
if (def->closure_bitset[index] & mask) {
janetc_regalloc_touch(&ra, i);
}
}
}
#define AA ((instr >> 8) & 0xFF)
#define BB ((instr >> 16) & 0xFF)
#define CC (instr >> 24)
#define DD (instr >> 8)
#define EE (instr >> 16)
/* Check reads and writes */
for (int32_t i = 0; i < def->bytecode_length; i++) {
uint32_t instr = def->bytecode[i];
switch (instr & 0x7F) {
/* Group instructions my how they read from slots */
/* No reads or writes */
default:
janet_assert(0, "unhandled instruction");
case JOP_JUMP:
case JOP_NOOP:
case JOP_RETURN_NIL:
/* Write A */
case JOP_LOAD_INTEGER:
case JOP_LOAD_CONSTANT:
case JOP_LOAD_UPVALUE:
case JOP_CLOSURE:
/* Write D */
case JOP_LOAD_NIL:
case JOP_LOAD_TRUE:
case JOP_LOAD_FALSE:
case JOP_LOAD_SELF:
case JOP_MAKE_ARRAY:
case JOP_MAKE_BUFFER:
case JOP_MAKE_STRING:
case JOP_MAKE_STRUCT:
case JOP_MAKE_TABLE:
case JOP_MAKE_TUPLE:
case JOP_MAKE_BRACKET_TUPLE:
break;
/* Read A */
case JOP_ERROR:
case JOP_TYPECHECK:
case JOP_JUMP_IF:
case JOP_JUMP_IF_NOT:
case JOP_JUMP_IF_NIL:
case JOP_JUMP_IF_NOT_NIL:
case JOP_SET_UPVALUE:
/* Write E, Read A */
case JOP_MOVE_FAR:
janetc_regalloc_touch(&ra, AA);
break;
/* Read B */
case JOP_SIGNAL:
/* Write A, Read B */
case JOP_ADD_IMMEDIATE:
case JOP_MULTIPLY_IMMEDIATE:
case JOP_DIVIDE_IMMEDIATE:
case JOP_SHIFT_LEFT_IMMEDIATE:
case JOP_SHIFT_RIGHT_IMMEDIATE:
case JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE:
case JOP_GREATER_THAN_IMMEDIATE:
case JOP_LESS_THAN_IMMEDIATE:
case JOP_EQUALS_IMMEDIATE:
case JOP_NOT_EQUALS_IMMEDIATE:
case JOP_GET_INDEX:
janetc_regalloc_touch(&ra, BB);
break;
/* Read D */
case JOP_RETURN:
case JOP_PUSH:
case JOP_PUSH_ARRAY:
case JOP_TAILCALL:
janetc_regalloc_touch(&ra, DD);
break;
/* Write A, Read E */
case JOP_MOVE_NEAR:
case JOP_LENGTH:
case JOP_BNOT:
case JOP_CALL:
janetc_regalloc_touch(&ra, EE);
break;
/* Read A, B */
case JOP_PUT_INDEX:
janetc_regalloc_touch(&ra, AA);
janetc_regalloc_touch(&ra, BB);
break;
/* Read A, E */
case JOP_PUSH_2:
janetc_regalloc_touch(&ra, AA);
janetc_regalloc_touch(&ra, EE);
break;
/* Read B, C */
case JOP_PROPAGATE:
/* Write A, Read B and C */
case JOP_BAND:
case JOP_BOR:
case JOP_BXOR:
case JOP_ADD:
case JOP_SUBTRACT:
case JOP_MULTIPLY:
case JOP_DIVIDE:
case JOP_MODULO:
case JOP_REMAINDER:
case JOP_SHIFT_LEFT:
case JOP_SHIFT_RIGHT:
case JOP_SHIFT_RIGHT_UNSIGNED:
case JOP_GREATER_THAN:
case JOP_LESS_THAN:
case JOP_EQUALS:
case JOP_COMPARE:
case JOP_IN:
case JOP_GET:
case JOP_GREATER_THAN_EQUAL:
case JOP_LESS_THAN_EQUAL:
case JOP_NOT_EQUALS:
case JOP_CANCEL:
case JOP_RESUME:
case JOP_NEXT:
janetc_regalloc_touch(&ra, BB);
janetc_regalloc_touch(&ra, CC);
break;
/* Read A, B, C */
case JOP_PUT:
case JOP_PUSH_3:
janetc_regalloc_touch(&ra, AA);
janetc_regalloc_touch(&ra, BB);
janetc_regalloc_touch(&ra, CC);
break;
}
}
/* Iterate and set noops on instructions that make writes that no one ever reads.
* Only set noops for instructions with no side effects - moves, loads, etc. that can't
* raise errors (outside of systemic errors like oom or stack overflow). */
recur = 0;
for (int32_t i = 0; i < def->bytecode_length; i++) {
uint32_t instr = def->bytecode[i];
switch (instr & 0x7F) {
default:
break;
/* Write D */
case JOP_LOAD_NIL:
case JOP_LOAD_TRUE:
case JOP_LOAD_FALSE:
case JOP_LOAD_SELF:
case JOP_MAKE_ARRAY:
case JOP_MAKE_TUPLE:
case JOP_MAKE_BRACKET_TUPLE: {
if (!janetc_regalloc_check(&ra, DD)) {
def->bytecode[i] = JOP_NOOP;
recur = 1;
}
}
break;
/* Write E, Read A */
case JOP_MOVE_FAR: {
if (!janetc_regalloc_check(&ra, EE)) {
def->bytecode[i] = JOP_NOOP;
recur = 1;
}
}
break;
/* Write A, Read E */
case JOP_MOVE_NEAR:
/* Write A, Read B */
case JOP_GET_INDEX:
/* Write A */
case JOP_LOAD_INTEGER:
case JOP_LOAD_CONSTANT:
case JOP_LOAD_UPVALUE:
case JOP_CLOSURE: {
if (!janetc_regalloc_check(&ra, AA)) {
def->bytecode[i] = JOP_NOOP;
recur = 1;
}
}
break;
}
}
janetc_regalloc_deinit(&ra);
#undef AA
#undef BB
#undef CC
#undef DD
#undef EE
}
}
/* Verify some bytecode */ /* Verify some bytecode */
int janet_verify(JanetFuncDef *def) { int janet_verify(JanetFuncDef *def) {
int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG); int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG);
@@ -218,6 +501,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
def->closure_bitset = NULL; def->closure_bitset = NULL;
def->flags = 0; def->flags = 0;
def->slotcount = 0; def->slotcount = 0;
def->symbolmap = NULL;
def->arity = 0; def->arity = 0;
def->min_arity = 0; def->min_arity = 0;
def->max_arity = INT32_MAX; def->max_arity = INT32_MAX;
@@ -229,6 +513,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
def->constants_length = 0; def->constants_length = 0;
def->bytecode_length = 0; def->bytecode_length = 0;
def->environments_length = 0; def->environments_length = 0;
def->symbolmap_length = 0;
return def; return def;
} }

View File

@@ -209,14 +209,28 @@ const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const c
#undef DEFINE_OPTLEN #undef DEFINE_OPTLEN
const char *janet_getcstring(const Janet *argv, int32_t n) { const char *janet_getcstring(const Janet *argv, int32_t n) {
const uint8_t *jstr = janet_getstring(argv, n); if (!janet_checktype(argv[n], JANET_STRING)) {
const char *cstr = (const char *)jstr; janet_panic_type(argv[n], n, JANET_TFLAG_STRING);
if (strlen(cstr) != (size_t) janet_string_length(jstr)) { }
janet_panic("string contains embedded 0s"); return janet_getcbytes(argv, n);
}
const char *janet_getcbytes(const Janet *argv, int32_t n) {
JanetByteView view = janet_getbytes(argv, n);
const char *cstr = (const char *)view.bytes;
if (strlen(cstr) != (size_t) view.len) {
janet_panic("bytes contain embedded 0s");
} }
return cstr; return cstr;
} }
const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) {
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
return dflt;
}
return janet_getcbytes(argv, n);
}
int32_t janet_getnat(const Janet *argv, int32_t n) { int32_t janet_getnat(const Janet *argv, int32_t n) {
Janet x = argv[n]; Janet x = argv[n];
if (!janet_checkint(x)) goto bad; if (!janet_checkint(x)) goto bad;

View File

@@ -93,10 +93,14 @@ void janetc_freeslot(JanetCompiler *c, JanetSlot s) {
/* Add a slot to a scope with a symbol associated with it (def or var). */ /* Add a slot to a scope with a symbol associated with it (def or var). */
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) { void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
SymPair sp; SymPair sp;
int32_t cnt = janet_v_count(c->buffer);
sp.sym = sym; sp.sym = sym;
sp.sym2 = sym;
sp.slot = s; sp.slot = s;
sp.keep = 0; sp.keep = 0;
sp.slot.flags |= JANET_SLOT_NAMED; sp.slot.flags |= JANET_SLOT_NAMED;
sp.birth_pc = cnt ? cnt - 1 : 0;
sp.death_pc = UINT32_MAX;
janet_v_push(c->scope->syms, sp); janet_v_push(c->scope->syms, sp);
} }
@@ -159,21 +163,27 @@ void janetc_popscope(JanetCompiler *c) {
if (oldscope->flags & JANET_SCOPE_CLOSURE) { if (oldscope->flags & JANET_SCOPE_CLOSURE) {
newscope->flags |= JANET_SCOPE_CLOSURE; newscope->flags |= JANET_SCOPE_CLOSURE;
} }
if (newscope->ra.max < oldscope->ra.max) if (newscope->ra.max < oldscope->ra.max) {
newscope->ra.max = oldscope->ra.max; newscope->ra.max = oldscope->ra.max;
/* Keep upvalue slots */
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
SymPair pair = oldscope->syms[i];
if (pair.keep) {
/* The variable should not be lexically accessible */
pair.sym = NULL;
janet_v_push(newscope->syms, pair);
janetc_regalloc_touch(&newscope->ra, pair.slot.index);
}
} }
/* Keep upvalue slots and symbols for debugging. */
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
SymPair pair = oldscope->syms[i];
/* The variable should not be lexically accessible */
pair.sym = NULL;
if (pair.death_pc == UINT32_MAX) {
pair.death_pc = (uint32_t) janet_v_count(c->buffer);
}
if (pair.keep) {
/* The variable should also not be included in the locals */
pair.sym2 = NULL;
janetc_regalloc_touch(&newscope->ra, pair.slot.index);
}
janet_v_push(newscope->syms, pair);
}
} }
/* Free the old scope */ /* Free the old scope */
janet_v_free(oldscope->consts); janet_v_free(oldscope->consts);
janet_v_free(oldscope->syms); janet_v_free(oldscope->syms);
@@ -334,6 +344,7 @@ found:
} }
/* non-local scope needs to expose its environment */ /* non-local scope needs to expose its environment */
JanetScope *original_scope = scope;
pair->keep = 1; pair->keep = 1;
while (scope && !(scope->flags & JANET_SCOPE_FUNCTION)) while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
scope = scope->parent; scope = scope->parent;
@@ -355,7 +366,7 @@ found:
/* Check if scope already has env. If so, break */ /* Check if scope already has env. If so, break */
len = janet_v_count(scope->envs); len = janet_v_count(scope->envs);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
if (scope->envs[j] == envindex) { if (scope->envs[j].envindex == envindex) {
scopefound = 1; scopefound = 1;
envindex = j; envindex = j;
break; break;
@@ -364,7 +375,10 @@ found:
/* Add the environment if it is not already referenced */ /* Add the environment if it is not already referenced */
if (!scopefound) { if (!scopefound) {
len = janet_v_count(scope->envs); len = janet_v_count(scope->envs);
janet_v_push(scope->envs, envindex); JanetEnvRef ref;
ref.envindex = envindex;
ref.scope = original_scope;
janet_v_push(scope->envs, ref);
envindex = len; envindex = len;
} }
} }
@@ -408,6 +422,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
int32_t i; int32_t i;
JanetSlot *ret = NULL; JanetSlot *ret = NULL;
JanetFopts subopts = janetc_fopts_default(c); JanetFopts subopts = janetc_fopts_default(c);
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
janet_v_push(ret, janetc_value(subopts, vals[i])); janet_v_push(ret, janetc_value(subopts, vals[i]));
} }
@@ -418,6 +433,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) { JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
JanetSlot *ret = NULL; JanetSlot *ret = NULL;
JanetFopts subopts = janetc_fopts_default(c); JanetFopts subopts = janetc_fopts_default(c);
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
const JanetKV *kvs = NULL; const JanetKV *kvs = NULL;
int32_t cap = 0, len = 0; int32_t cap = 0, len = 0;
janet_dictionary_view(ds, &kvs, &len, &cap); janet_dictionary_view(ds, &kvs, &len, &cap);
@@ -868,7 +884,10 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
/* Copy envs */ /* Copy envs */
def->environments_length = janet_v_count(scope->envs); def->environments_length = janet_v_count(scope->envs);
def->environments = janet_v_flatten(scope->envs); def->environments = janet_malloc(sizeof(int32_t) * def->environments_length);
for (int32_t i = 0; i < def->environments_length; i++) {
def->environments[i] = scope->envs[i].envindex;
}
def->constants_length = janet_v_count(scope->consts); def->constants_length = janet_v_count(scope->consts);
def->constants = janet_v_flatten(scope->consts); def->constants = janet_v_flatten(scope->consts);
@@ -923,9 +942,57 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
def->closure_bitset = chunks; def->closure_bitset = chunks;
} }
/* Capture symbol to local mapping */
JanetSymbolMap *locals = NULL;
/* Symbol -> upvalue mapping */
JanetScope *top = c->scope;
while (top->parent) top = top->parent;
for (JanetScope *s = top; s != NULL; s = s->child) {
for (int32_t j = 0; j < janet_v_count(scope->envs); j++) {
JanetEnvRef ref = scope->envs[j];
JanetScope *upscope = ref.scope;
if (upscope != s) continue;
for (int32_t i = 0; i < janet_v_count(upscope->syms); i++) {
SymPair pair = upscope->syms[i];
if (pair.sym2) {
JanetSymbolMap jsm;
jsm.birth_pc = UINT32_MAX;
jsm.death_pc = j;
jsm.slot_index = pair.slot.index;
jsm.symbol = pair.sym2;
janet_v_push(locals, jsm);
}
}
}
}
/* Symbol -> slot mapping */
for (int32_t i = 0; i < janet_v_count(scope->syms); i++) {
SymPair pair = scope->syms[i];
if (pair.sym2) {
if (pair.death_pc == UINT32_MAX) {
pair.death_pc = def->bytecode_length;
}
JanetSymbolMap jsm;
jsm.birth_pc = pair.birth_pc;
jsm.death_pc = pair.death_pc;
jsm.slot_index = pair.slot.index;
jsm.symbol = pair.sym2;
janet_v_push(locals, jsm);
}
}
def->symbolmap_length = janet_v_count(locals);
def->symbolmap = janet_v_flatten(locals);
if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
/* Pop the scope */ /* Pop the scope */
janetc_popscope(c); janetc_popscope(c);
/* Do basic optimization */
janet_bytecode_movopt(def);
janet_bytecode_remove_noops(def);
return def; return def;
} }

View File

@@ -111,13 +111,21 @@ struct JanetSlot {
typedef struct SymPair { typedef struct SymPair {
JanetSlot slot; JanetSlot slot;
const uint8_t *sym; const uint8_t *sym;
const uint8_t *sym2;
int keep; int keep;
uint32_t birth_pc;
uint32_t death_pc;
} SymPair; } SymPair;
typedef struct JanetEnvRef {
int32_t envindex;
JanetScope *scope;
} JanetEnvRef;
/* A lexical scope during compilation */ /* A lexical scope during compilation */
struct JanetScope { struct JanetScope {
/* For debugging */ /* For debugging the compiler */
const char *name; const char *name;
/* Scopes are doubly linked list */ /* Scopes are doubly linked list */
@@ -133,7 +141,7 @@ struct JanetScope {
/* FuncDefs */ /* FuncDefs */
JanetFuncDef **defs; JanetFuncDef **defs;
/* Regsiter allocator */ /* Register allocator */
JanetcRegisterAllocator ra; JanetcRegisterAllocator ra;
/* Upvalue allocator */ /* Upvalue allocator */
@@ -142,7 +150,7 @@ struct JanetScope {
/* Referenced closure environments. The values at each index correspond /* Referenced closure environments. The values at each index correspond
* to which index to get the environment from in the parent. The environment * to which index to get the environment from in the parent. The environment
* that corresponds to the direct parent's stack will always have value 0. */ * that corresponds to the direct parent's stack will always have value 0. */
int32_t *envs; JanetEnvRef *envs;
int32_t bytecode_start; int32_t bytecode_start;
int flags; int flags;
@@ -179,6 +187,7 @@ struct JanetCompiler {
#define JANET_FOPTS_TAIL 0x10000 #define JANET_FOPTS_TAIL 0x10000
#define JANET_FOPTS_HINT 0x20000 #define JANET_FOPTS_HINT 0x20000
#define JANET_FOPTS_DROP 0x40000 #define JANET_FOPTS_DROP 0x40000
#define JANET_FOPTS_ACCEPT_SPLICE 0x80000
/* Options for compiling a single form */ /* Options for compiling a single form */
struct JanetFopts { struct JanetFopts {
@@ -227,7 +236,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len);
/* Get a bunch of slots for function arguments */ /* Get a bunch of slots for function arguments */
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds); JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds);
/* Push slots load via janetc_toslots. */ /* Push slots loaded via janetc_toslots. */
int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots); int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots);
/* Free slots loaded via janetc_toslots */ /* Free slots loaded via janetc_toslots */
@@ -258,4 +267,8 @@ JanetSlot janetc_cslot(Janet x);
/* Search for a symbol */ /* Search for a symbol */
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym); JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
/* Bytecode optimization */
void janet_bytecode_movopt(JanetFuncDef *def);
void janet_bytecode_remove_noops(JanetFuncDef *def);
#endif #endif

View File

@@ -43,6 +43,7 @@ extern size_t janet_core_image_size;
#endif #endif
JanetModule janet_native(const char *name, const uint8_t **error) { JanetModule janet_native(const char *name, const uint8_t **error) {
janet_sandbox_assert(JANET_SANDBOX_DYNAMIC_MODULES);
char *processed_name = get_processed_name(name); char *processed_name = get_processed_name(name);
Clib lib = load_clib(processed_name); Clib lib = load_clib(processed_name);
JanetModule init; JanetModule init;
@@ -457,7 +458,7 @@ JANET_CORE_FN(janet_core_getproto,
? janet_wrap_struct(janet_struct_proto(st)) ? janet_wrap_struct(janet_struct_proto(st))
: janet_wrap_nil(); : janet_wrap_nil();
} }
janet_panicf("expected struct|table, got %v", argv[0]); janet_panicf("expected struct or table, got %v", argv[0]);
} }
JANET_CORE_FN(janet_core_struct, JANET_CORE_FN(janet_core_struct,
@@ -652,9 +653,9 @@ JANET_CORE_FN(janet_core_signal,
JANET_CORE_FN(janet_core_memcmp, JANET_CORE_FN(janet_core_memcmp,
"(memcmp a b &opt len offset-a offset-b)", "(memcmp a b &opt len offset-a offset-b)",
"Compare memory. Takes to byte sequences `a` and `b`, and " "Compare memory. Takes two byte sequences `a` and `b`, and "
"return 0 if they have identical contents, a negative integer if a is less than b, " "return 0 if they have identical contents, a negative integer if a is less than b, "
"and a positive integer if a is greather than b. Optionally take a length and offsets " "and a positive integer if a is greater than b. Optionally take a length and offsets "
"to compare slices of the bytes sequences.") { "to compare slices of the bytes sequences.") {
janet_arity(argc, 2, 5); janet_arity(argc, 2, 5);
JanetByteView a = janet_getbytes(argv, 0); JanetByteView a = janet_getbytes(argv, 0);
@@ -667,6 +668,64 @@ JANET_CORE_FN(janet_core_memcmp,
return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len)); return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
} }
typedef struct SandboxOption {
const char *name;
uint32_t flag;
} SandboxOption;
static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL},
{"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI},
{"fs", JANET_SANDBOX_FS},
{"fs-read", JANET_SANDBOX_FS_READ},
{"fs-temp", JANET_SANDBOX_FS_TEMP},
{"fs-write", JANET_SANDBOX_FS_WRITE},
{"hrtime", JANET_SANDBOX_HRTIME},
{"modules", JANET_SANDBOX_DYNAMIC_MODULES},
{"net", JANET_SANDBOX_NET},
{"net-connect", JANET_SANDBOX_NET_CONNECT},
{"net-listen", JANET_SANDBOX_NET_LISTEN},
{"sandbox", JANET_SANDBOX_SANDBOX},
{"subprocess", JANET_SANDBOX_SUBPROCESS},
{NULL, 0}
};
JANET_CORE_FN(janet_core_sandbox,
"(sandbox & forbidden-capabilities)",
"Disable feature sets to prevent the interpreter from using certain system resources. "
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
"* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :fs - disallow access to the file system\n"
"* :fs-read - disallow read access to the file system\n"
"* :fs-temp - disallow creating temporary files\n"
"* :fs-write - disallow write access to the file system\n"
"* :hrtime - disallow high-resolution timers\n"
"* :modules - disallow load dynamic modules (natives)\n"
"* :net - disallow network access\n"
"* :net-connect - disallow making outbound network connections\n"
"* :net-listen - disallow accepting inbound network connections\n"
"* :sandbox - disallow calling this function\n"
"* :subprocess - disallow running subprocesses") {
uint32_t flags = 0;
for (int32_t i = 0; i < argc; i++) {
JanetKeyword kw = janet_getkeyword(argv, i);
const SandboxOption *opt = sandbox_options;
while (opt->name != NULL) {
if (janet_cstrcmp(kw, opt->name) == 0) {
flags |= opt->flag;
break;
}
opt++;
}
if (opt->name == NULL) janet_panicf("unknown capability %v", argv[i]);
}
janet_sandbox(flags);
return janet_wrap_nil();
}
#ifdef JANET_BOOTSTRAP #ifdef JANET_BOOTSTRAP
/* Utility for inline assembly */ /* Utility for inline assembly */
@@ -970,6 +1029,7 @@ static void janet_load_libs(JanetTable *env) {
JANET_CORE_REG("signal", janet_core_signal), JANET_CORE_REG("signal", janet_core_signal),
JANET_CORE_REG("memcmp", janet_core_memcmp), JANET_CORE_REG("memcmp", janet_core_memcmp),
JANET_CORE_REG("getproto", janet_core_getproto), JANET_CORE_REG("getproto", janet_core_getproto),
JANET_CORE_REG("sandbox", janet_core_sandbox),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, corelib_cfuns); janet_core_cfuns_ext(env, NULL, corelib_cfuns);

View File

@@ -131,9 +131,9 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
if (!wrote_error) { if (!wrote_error) {
JanetFiberStatus status = janet_fiber_status(fiber); JanetFiberStatus status = janet_fiber_status(fiber);
janet_eprintf("%s%s: %s\n", janet_eprintf("%s%s: %s\n",
prefix, prefix ? prefix : "",
janet_status_names[status], janet_status_names[status],
errstr); errstr ? errstr : janet_status_names[status]);
wrote_error = 1; wrote_error = 1;
} }
@@ -329,6 +329,27 @@ static Janet doframe(JanetStackFrame *frame) {
safe_memcpy(slots->data, stack, sizeof(Janet) * def->slotcount); safe_memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
slots->count = def->slotcount; slots->count = def->slotcount;
janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots)); janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots));
/* Add local bindings */
if (def->symbolmap) {
JanetTable *local_bindings = janet_table(0);
for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
JanetSymbolMap jsm = def->symbolmap[i];
Janet value = janet_wrap_nil();
uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
if (jsm.birth_pc == UINT32_MAX) {
JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
if (env->offset > 0) {
value = env->as.fiber->data[env->offset + jsm.slot_index];
} else {
value = env->as.values[jsm.slot_index];
}
} else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
value = stack[jsm.slot_index];
}
janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
}
janet_table_put(t, janet_ckeywordv("locals"), janet_wrap_table(local_bindings));
}
} }
return janet_wrap_table(t); return janet_wrap_table(t);
} }

View File

@@ -172,6 +172,9 @@ static JanetTimestamp ts_now(void);
/* Get current timestamp + an interval (millisecond precision) */ /* Get current timestamp + an interval (millisecond precision) */
static JanetTimestamp ts_delta(JanetTimestamp ts, double delta) { static JanetTimestamp ts_delta(JanetTimestamp ts, double delta) {
if (isinf(delta)) {
return delta < 0 ? ts : INT64_MAX;
}
ts += (int64_t)round(delta * 1000); ts += (int64_t)round(delta * 1000);
return ts; return ts;
} }
@@ -469,8 +472,12 @@ const JanetAbstractType janet_stream_type = {
/* Register a fiber to resume with value */ /* Register a fiber to resume with value */
void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig) { void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig) {
if (fiber->gc.flags & JANET_FIBER_EV_FLAG_CANCELED) return; if (fiber->gc.flags & JANET_FIBER_EV_FLAG_CANCELED) return;
fiber->gc.flags |= JANET_FIBER_FLAG_ROOT; if (!(fiber->gc.flags & JANET_FIBER_FLAG_ROOT)) {
Janet task_element = janet_wrap_fiber(fiber);
janet_table_put(&janet_vm.active_tasks, task_element, janet_wrap_true());
}
JanetTask t = { fiber, value, sig, ++fiber->sched_id }; JanetTask t = { fiber, value, sig, ++fiber->sched_id };
fiber->gc.flags |= JANET_FIBER_FLAG_ROOT;
if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED; if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED;
janet_q_push(&janet_vm.spawn, &t, sizeof(t)); janet_q_push(&janet_vm.spawn, &t, sizeof(t));
} }
@@ -556,6 +563,7 @@ void janet_ev_init_common(void) {
janet_vm.tq_count = 0; janet_vm.tq_count = 0;
janet_vm.tq_capacity = 0; janet_vm.tq_capacity = 0;
janet_table_init_raw(&janet_vm.threaded_abstracts, 0); janet_table_init_raw(&janet_vm.threaded_abstracts, 0);
janet_table_init_raw(&janet_vm.active_tasks, 0);
janet_rng_seed(&janet_vm.ev_rng, 0); janet_rng_seed(&janet_vm.ev_rng, 0);
#ifndef JANET_WINDOWS #ifndef JANET_WINDOWS
pthread_attr_init(&janet_vm.new_thread_attr); pthread_attr_init(&janet_vm.new_thread_attr);
@@ -570,13 +578,15 @@ void janet_ev_deinit_common(void) {
janet_free(janet_vm.listeners); janet_free(janet_vm.listeners);
janet_vm.listeners = NULL; janet_vm.listeners = NULL;
janet_table_deinit(&janet_vm.threaded_abstracts); janet_table_deinit(&janet_vm.threaded_abstracts);
janet_table_deinit(&janet_vm.active_tasks);
#ifndef JANET_WINDOWS #ifndef JANET_WINDOWS
pthread_attr_destroy(&janet_vm.new_thread_attr); pthread_attr_destroy(&janet_vm.new_thread_attr);
#endif #endif
} }
/* Short hand to yield to event loop */ /* Shorthand to yield to event loop */
void janet_await(void) { void janet_await(void) {
/* Store the fiber in a gobal table */
janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil()); janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil());
} }
@@ -662,19 +672,6 @@ static void janet_chan_init(JanetChannel *chan, int32_t limit, int threaded) {
janet_os_mutex_init((JanetOSMutex *) &chan->lock); janet_os_mutex_init((JanetOSMutex *) &chan->lock);
} }
static void janet_chan_deinit(JanetChannel *chan) {
janet_q_deinit(&chan->read_pending);
janet_q_deinit(&chan->write_pending);
if (janet_chan_is_threaded(chan)) {
Janet item;
while (!janet_q_pop(&chan->items, &item, sizeof(item))) {
janet_chan_unpack(chan, &item, 1);
}
}
janet_q_deinit(&chan->items);
janet_os_mutex_deinit((JanetOSMutex *) &chan->lock);
}
static void janet_chan_lock(JanetChannel *chan) { static void janet_chan_lock(JanetChannel *chan) {
if (!janet_chan_is_threaded(chan)) return; if (!janet_chan_is_threaded(chan)) return;
janet_os_mutex_lock((JanetOSMutex *) &chan->lock); janet_os_mutex_lock((JanetOSMutex *) &chan->lock);
@@ -685,6 +682,25 @@ static void janet_chan_unlock(JanetChannel *chan) {
janet_os_mutex_unlock((JanetOSMutex *) &chan->lock); janet_os_mutex_unlock((JanetOSMutex *) &chan->lock);
} }
static void janet_chan_deinit(JanetChannel *chan) {
if (janet_chan_is_threaded(chan)) {
Janet item;
janet_chan_lock(chan);
janet_q_deinit(&chan->read_pending);
janet_q_deinit(&chan->write_pending);
while (!janet_q_pop(&chan->items, &item, sizeof(item))) {
janet_chan_unpack(chan, &item, 1);
}
janet_q_deinit(&chan->items);
janet_chan_unlock(chan);
} else {
janet_q_deinit(&chan->read_pending);
janet_q_deinit(&chan->write_pending);
janet_q_deinit(&chan->items);
}
janet_os_mutex_deinit((JanetOSMutex *) &chan->lock);
}
/* /*
* Janet Channel abstract type * Janet Channel abstract type
*/ */
@@ -761,6 +777,7 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
int mode = msg.tag; int mode = msg.tag;
JanetChannel *channel = (JanetChannel *) msg.argp; JanetChannel *channel = (JanetChannel *) msg.argp;
Janet x = msg.argj; Janet x = msg.argj;
janet_chan_lock(channel);
if (fiber->sched_id == sched_id) { if (fiber->sched_id == sched_id) {
if (mode == JANET_CP_MODE_CHOICE_READ) { if (mode == JANET_CP_MODE_CHOICE_READ) {
janet_assert(!janet_chan_unpack(channel, &x, 0), "packing error"); janet_assert(!janet_chan_unpack(channel, &x, 0), "packing error");
@@ -781,7 +798,6 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ); int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ);
if (is_read) { if (is_read) {
JanetChannelPending reader; JanetChannelPending reader;
janet_chan_lock(channel);
if (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) { if (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
JanetVM *vm = reader.thread; JanetVM *vm = reader.thread;
JanetEVGenericMessage msg; JanetEVGenericMessage msg;
@@ -792,10 +808,8 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
msg.argj = x; msg.argj = x;
janet_ev_post_event(vm, janet_thread_chan_cb, msg); janet_ev_post_event(vm, janet_thread_chan_cb, msg);
} }
janet_chan_unlock(channel);
} else { } else {
JanetChannelPending writer; JanetChannelPending writer;
janet_chan_lock(channel);
if (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) { if (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
JanetVM *vm = writer.thread; JanetVM *vm = writer.thread;
JanetEVGenericMessage msg; JanetEVGenericMessage msg;
@@ -806,21 +820,21 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
msg.argj = janet_wrap_nil(); msg.argj = janet_wrap_nil();
janet_ev_post_event(vm, janet_thread_chan_cb, msg); janet_ev_post_event(vm, janet_thread_chan_cb, msg);
} }
janet_chan_unlock(channel);
} }
} }
janet_chan_unlock(channel);
} }
/* Push a value to a channel, and return 1 if channel should block, zero otherwise. /* Push a value to a channel, and return 1 if channel should block, zero otherwise.
* If the push would block, will add to the write_pending queue in the channel. * If the push would block, will add to the write_pending queue in the channel.
* Handles both threaded and unthreaded channels. */ * Handles both threaded and unthreaded channels. */
static int janet_channel_push(JanetChannel *channel, Janet x, int mode) { static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode) {
JanetChannelPending reader; JanetChannelPending reader;
int is_empty; int is_empty;
if (janet_chan_pack(channel, &x)) { if (janet_chan_pack(channel, &x)) {
janet_chan_unlock(channel);
janet_panicf("failed to pack value for channel: %v", x); janet_panicf("failed to pack value for channel: %v", x);
} }
janet_chan_lock(channel);
if (channel->closed) { if (channel->closed) {
janet_chan_unlock(channel); janet_chan_unlock(channel);
janet_panic("cannot write to closed channel"); janet_panic("cannot write to closed channel");
@@ -881,12 +895,16 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode) {
return 0; return 0;
} }
static int janet_channel_push(JanetChannel *channel, Janet x, int mode) {
janet_chan_lock(channel);
return janet_channel_push_with_lock(channel, x, mode);
}
/* Pop from a channel - returns 1 if item was obtained, 0 otherwise. The item /* Pop from a channel - returns 1 if item was obtained, 0 otherwise. The item
* is returned by reference. If the pop would block, will add to the read_pending * is returned by reference. If the pop would block, will add to the read_pending
* queue in the channel. */ * queue in the channel. */
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice) { static int janet_channel_pop_with_lock(JanetChannel *channel, Janet *item, int is_choice) {
JanetChannelPending writer; JanetChannelPending writer;
janet_chan_lock(channel);
if (channel->closed) { if (channel->closed) {
janet_chan_unlock(channel); janet_chan_unlock(channel);
*item = janet_wrap_nil(); *item = janet_wrap_nil();
@@ -931,6 +949,11 @@ static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice)
return 1; return 1;
} }
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice) {
janet_chan_lock(channel);
return janet_channel_pop_with_lock(channel, item, is_choice);
}
JanetChannel *janet_channel_unwrap(void *abstract) { JanetChannel *janet_channel_unwrap(void *abstract) {
return abstract; return abstract;
} }
@@ -973,13 +996,32 @@ JANET_CORE_FN(cfun_channel_pop,
janet_await(); janet_await();
} }
static void chan_unlock_args(const Janet *argv, int32_t n) {
for (int32_t i = 0; i < n; i++) {
int32_t len;
const Janet *data;
JanetChannel *chan;
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
chan = janet_getchannel(data, 0);
} else {
chan = janet_getchannel(argv, i);
}
janet_chan_unlock(chan);
}
}
JANET_CORE_FN(cfun_channel_choice, JANET_CORE_FN(cfun_channel_choice,
"(ev/select & clauses)", "(ev/select & clauses)",
"Block until the first of several channel operations occur. Returns a tuple of the form [:give chan], [:take chan x], or [:close chan], where " "Block until the first of several channel operations occur. Returns a "
"a :give tuple is the result of a write and :take tuple is the result of a read. Each clause must be either a channel (for " "tuple of the form [:give chan], [:take chan x], or [:close chan], "
"a channel take operation) or a tuple [channel x] for a channel give operation. Operations are tried in order, such that the first " "where a :give tuple is the result of a write and a :take tuple is the "
"clauses will take precedence over later clauses. Both and give and take operations can return a [:close chan] tuple, which indicates that " "result of a read. Each clause must be either a channel (for a channel "
"the specified channel was closed while waiting, or that the channel was already closed.") { "take operation) or a tuple [channel x] (for a channel give operation). "
"Operations are tried in order such that earlier clauses take "
"precedence over later clauses. Both give and take operations can "
"return a [:close chan] tuple, which indicates that the specified "
"channel was closed while waiting, or that the channel was already "
"closed.") {
janet_arity(argc, 1, -1); janet_arity(argc, 1, -1);
int32_t len; int32_t len;
const Janet *data; const Janet *data;
@@ -992,29 +1034,29 @@ JANET_CORE_FN(cfun_channel_choice,
janet_chan_lock(chan); janet_chan_lock(chan);
if (chan->closed) { if (chan->closed) {
janet_chan_unlock(chan); janet_chan_unlock(chan);
chan_unlock_args(argv, i);
return make_close_result(chan); return make_close_result(chan);
} }
if (janet_q_count(&chan->items) < chan->limit) { if (janet_q_count(&chan->items) < chan->limit) {
janet_chan_unlock(chan); janet_channel_push_with_lock(chan, data[1], 1);
janet_channel_push(chan, data[1], 1); chan_unlock_args(argv, i);
return make_write_result(chan); return make_write_result(chan);
} }
janet_chan_unlock(chan);
} else { } else {
/* Read */ /* Read */
JanetChannel *chan = janet_getchannel(argv, i); JanetChannel *chan = janet_getchannel(argv, i);
janet_chan_lock(chan); janet_chan_lock(chan);
if (chan->closed) { if (chan->closed) {
janet_chan_unlock(chan); janet_chan_unlock(chan);
chan_unlock_args(argv, i);
return make_close_result(chan); return make_close_result(chan);
} }
if (chan->items.head != chan->items.tail) { if (chan->items.head != chan->items.tail) {
Janet item; Janet item;
janet_chan_unlock(chan); janet_channel_pop_with_lock(chan, &item, 1);
janet_channel_pop(chan, &item, 1); chan_unlock_args(argv, i);
return make_read_result(chan, item); return make_read_result(chan, item);
} }
janet_chan_unlock(chan);
} }
} }
@@ -1023,12 +1065,12 @@ JANET_CORE_FN(cfun_channel_choice,
if (janet_indexed_view(argv[i], &data, &len) && len == 2) { if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
/* Write */ /* Write */
JanetChannel *chan = janet_getchannel(data, 0); JanetChannel *chan = janet_getchannel(data, 0);
janet_channel_push(chan, data[1], 1); janet_channel_push_with_lock(chan, data[1], 1);
} else { } else {
/* Read */ /* Read */
Janet item; Janet item;
JanetChannel *chan = janet_getchannel(argv, i); JanetChannel *chan = janet_getchannel(argv, i);
janet_channel_pop(chan, &item, 1); janet_channel_pop_with_lock(chan, &item, 1);
} }
} }
@@ -1249,16 +1291,7 @@ JanetFiber *janet_loop1(void) {
while (peek_timeout(&to) && to.when <= now) { while (peek_timeout(&to) && to.when <= now) {
pop_timeout(0); pop_timeout(0);
if (to.curr_fiber != NULL) { if (to.curr_fiber != NULL) {
/* This is a deadline (for a fiber, not a function call) */ if (janet_fiber_can_resume(to.curr_fiber)) {
JanetFiberStatus s = janet_fiber_status(to.curr_fiber);
int isFinished = (s == JANET_STATUS_DEAD ||
s == JANET_STATUS_ERROR ||
s == JANET_STATUS_USER0 ||
s == JANET_STATUS_USER1 ||
s == JANET_STATUS_USER2 ||
s == JANET_STATUS_USER3 ||
s == JANET_STATUS_USER4);
if (!isFinished) {
janet_cancel(to.fiber, janet_cstringv("deadline expired")); janet_cancel(to.fiber, janet_cstringv("deadline expired"));
} }
} else { } else {
@@ -1282,6 +1315,9 @@ JanetFiber *janet_loop1(void) {
if (task.expected_sched_id != task.fiber->sched_id) continue; if (task.expected_sched_id != task.fiber->sched_id) continue;
Janet res; Janet res;
JanetSignal sig = janet_continue_signal(task.fiber, task.value, &res, task.sig); JanetSignal sig = janet_continue_signal(task.fiber, task.value, &res, task.sig);
if (!janet_fiber_can_resume(task.fiber)) {
janet_table_remove(&janet_vm.active_tasks, janet_wrap_fiber(task.fiber));
}
void *sv = task.fiber->supervisor_channel; void *sv = task.fiber->supervisor_channel;
int is_suspended = sig == JANET_SIGNAL_EVENT || sig == JANET_SIGNAL_YIELD || sig == JANET_SIGNAL_INTERRUPT; int is_suspended = sig == JANET_SIGNAL_EVENT || sig == JANET_SIGNAL_YIELD || sig == JANET_SIGNAL_INTERRUPT;
if (is_suspended) { if (is_suspended) {
@@ -1313,15 +1349,8 @@ JanetFiber *janet_loop1(void) {
/* Drop timeouts that are no longer needed */ /* Drop timeouts that are no longer needed */
while ((has_timeout = peek_timeout(&to))) { while ((has_timeout = peek_timeout(&to))) {
if (to.curr_fiber != NULL) { if (to.curr_fiber != NULL) {
JanetFiberStatus s = janet_fiber_status(to.curr_fiber); if (!janet_fiber_can_resume(to.curr_fiber)) {
int is_finished = (s == JANET_STATUS_DEAD || janet_table_remove(&janet_vm.active_tasks, janet_wrap_fiber(to.curr_fiber));
s == JANET_STATUS_ERROR ||
s == JANET_STATUS_USER0 ||
s == JANET_STATUS_USER1 ||
s == JANET_STATUS_USER2 ||
s == JANET_STATUS_USER3 ||
s == JANET_STATUS_USER4);
if (is_finished) {
pop_timeout(0); pop_timeout(0);
continue; continue;
} }
@@ -1473,6 +1502,10 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
state = state->_next; state = state->_next;
} }
} }
/* Close the stream if requested and no more listeners are left */
if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->state) {
janet_stream_close(stream);
}
} }
} }
} }
@@ -1627,6 +1660,10 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
janet_unlisten(state, 0); janet_unlisten(state, 0);
state = next_state; state = next_state;
} }
/* Close the stream if requested and no more listeners are left */
if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->state) {
janet_stream_close(stream);
}
} }
} }
} }
@@ -1825,6 +1862,10 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
state = next_state; state = next_state;
} }
/* Close the stream if requested and no more listeners are left */
if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->state) {
janet_stream_close(stream);
}
} }
} }
} }
@@ -1928,6 +1969,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
JanetAsyncStatus status3 = JANET_ASYNC_STATUS_NOT_DONE; JanetAsyncStatus status3 = JANET_ASYNC_STATUS_NOT_DONE;
JanetAsyncStatus status4 = JANET_ASYNC_STATUS_NOT_DONE; JanetAsyncStatus status4 = JANET_ASYNC_STATUS_NOT_DONE;
state->event = pfd; state->event = pfd;
JanetStream *stream = state->stream;
if (mask & POLLOUT) if (mask & POLLOUT)
status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE); status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE);
if (mask & POLLIN) if (mask & POLLIN)
@@ -1941,6 +1983,10 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
status3 == JANET_ASYNC_STATUS_DONE || status3 == JANET_ASYNC_STATUS_DONE ||
status4 == JANET_ASYNC_STATUS_DONE) status4 == JANET_ASYNC_STATUS_DONE)
janet_unlisten(state, 0); janet_unlisten(state, 0);
/* Close the stream if requested and no more listeners are left */
if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->state) {
janet_stream_close(stream);
}
} }
} }
@@ -2212,9 +2258,9 @@ typedef struct {
JanetReadMode mode; JanetReadMode mode;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
OVERLAPPED overlapped; OVERLAPPED overlapped;
DWORD flags;
#ifdef JANET_NET #ifdef JANET_NET
WSABUF wbuf; WSABUF wbuf;
DWORD flags;
struct sockaddr from; struct sockaddr from;
int fromlen; int fromlen;
#endif #endif
@@ -2273,7 +2319,7 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
#ifdef JANET_NET #ifdef JANET_NET
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) { if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
state->wbuf.len = (ULONG) chunk_size; state->wbuf.len = (ULONG) chunk_size;
state->wbuf.buf = state->chunk_buf; state->wbuf.buf = (char *) state->chunk_buf;
status = WSARecvFrom((SOCKET) s->stream->handle, &state->wbuf, 1, status = WSARecvFrom((SOCKET) s->stream->handle, &state->wbuf, 1,
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped, NULL); NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped, NULL);
if (status && (WSA_IO_PENDING != WSAGetLastError())) { if (status && (WSA_IO_PENDING != WSAGetLastError())) {
@@ -2288,8 +2334,8 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
state->overlapped.Offset = (DWORD) state->bytes_read; state->overlapped.Offset = (DWORD) state->bytes_read;
status = ReadFile(s->stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped); status = ReadFile(s->stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped);
if (!status && (ERROR_IO_PENDING != WSAGetLastError())) { if (!status && (ERROR_IO_PENDING != GetLastError())) {
if (WSAGetLastError() == ERROR_BROKEN_PIPE) { if (GetLastError() == ERROR_BROKEN_PIPE) {
if (state->bytes_read) { if (state->bytes_read) {
janet_schedule(s->fiber, janet_wrap_buffer(state->buf)); janet_schedule(s->fiber, janet_wrap_buffer(state->buf));
} else { } else {
@@ -2427,7 +2473,8 @@ void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, in
typedef enum { typedef enum {
JANET_ASYNC_WRITEMODE_WRITE, JANET_ASYNC_WRITEMODE_WRITE,
JANET_ASYNC_WRITEMODE_SEND, JANET_ASYNC_WRITEMODE_SEND,
JANET_ASYNC_WRITEMODE_SENDTO JANET_ASYNC_WRITEMODE_SENDTO,
JANET_ASYNC_WRITEMODE_CONNECT
} JanetWriteMode; } JanetWriteMode;
typedef struct { typedef struct {
@@ -2441,9 +2488,9 @@ typedef struct {
void *dest_abst; void *dest_abst;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
OVERLAPPED overlapped; OVERLAPPED overlapped;
DWORD flags;
#ifdef JANET_NET #ifdef JANET_NET
WSABUF wbuf; WSABUF wbuf;
DWORD flags;
#endif #endif
#else #else
int flags; int flags;
@@ -2451,6 +2498,31 @@ typedef struct {
#endif #endif
} StateWrite; } StateWrite;
static JanetAsyncStatus handle_connect(JanetListenerState *s) {
#ifdef JANET_WINDOWS
int res = 0;
int size = sizeof(res);
int r = getsockopt((SOCKET)s->stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
#else
int res = 0;
socklen_t size = sizeof res;
int r = getsockopt(s->stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
#endif
if (r == 0) {
if (res == 0) {
janet_schedule(s->fiber, janet_wrap_abstract(s->stream));
} else {
s->stream->flags |= JANET_STREAM_TOCLOSE;
janet_cancel(s->fiber, janet_cstringv(strerror(res)));
}
} else {
s->stream->flags |= JANET_STREAM_TOCLOSE;
janet_cancel(s->fiber, janet_ev_lasterr());
}
return JANET_ASYNC_STATUS_DONE;
}
JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event) { JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event) {
StateWrite *state = (StateWrite *) s; StateWrite *state = (StateWrite *) s;
switch (event) { switch (event) {
@@ -2480,6 +2552,11 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
} }
break; break;
case JANET_ASYNC_EVENT_USER: { case JANET_ASYNC_EVENT_USER: {
#ifdef JANET_NET
if (state->mode == JANET_ASYNC_WRITEMODE_CONNECT) {
return handle_connect(s);
}
#endif
/* Begin write */ /* Begin write */
int32_t len; int32_t len;
const uint8_t *bytes; const uint8_t *bytes;
@@ -2528,7 +2605,7 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
state->overlapped.Offset = (DWORD) 0xFFFFFFFF; state->overlapped.Offset = (DWORD) 0xFFFFFFFF;
state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF; state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
status = WriteFile(s->stream->handle, bytes, len, NULL, &state->overlapped); status = WriteFile(s->stream->handle, bytes, len, NULL, &state->overlapped);
if (!status && (ERROR_IO_PENDING != WSAGetLastError())) { if (!status && (ERROR_IO_PENDING != GetLastError())) {
janet_cancel(s->fiber, janet_ev_lasterr()); janet_cancel(s->fiber, janet_ev_lasterr());
return JANET_ASYNC_STATUS_DONE; return JANET_ASYNC_STATUS_DONE;
} }
@@ -2543,6 +2620,11 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
janet_cancel(s->fiber, janet_cstringv("stream hup")); janet_cancel(s->fiber, janet_cstringv("stream hup"));
return JANET_ASYNC_STATUS_DONE; return JANET_ASYNC_STATUS_DONE;
case JANET_ASYNC_EVENT_WRITE: { case JANET_ASYNC_EVENT_WRITE: {
#ifdef JANET_NET
if (state->mode == JANET_ASYNC_WRITEMODE_CONNECT) {
return handle_connect(s);
}
#endif
int32_t start, len; int32_t start, len;
const uint8_t *bytes; const uint8_t *bytes;
start = state->start; start = state->start;
@@ -2645,6 +2727,10 @@ void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, i
void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) { void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) {
janet_ev_write_generic(stream, (void *) str, dest, JANET_ASYNC_WRITEMODE_SENDTO, 0, flags); janet_ev_write_generic(stream, (void *) str, dest, JANET_ASYNC_WRITEMODE_SENDTO, 0, flags);
} }
void janet_ev_connect(JanetStream *stream, int flags) {
janet_ev_write_generic(stream, NULL, NULL, JANET_ASYNC_WRITEMODE_CONNECT, 0, flags);
}
#endif #endif
/* For a pipe ID */ /* For a pipe ID */
@@ -2664,15 +2750,15 @@ int janet_make_pipe(JanetHandle handles[2], int mode) {
* so we lift from the windows source code and modify for our own version. * so we lift from the windows source code and modify for our own version.
*/ */
JanetHandle shandle, chandle; JanetHandle shandle, chandle;
UCHAR PipeNameBuffer[MAX_PATH]; CHAR PipeNameBuffer[MAX_PATH];
SECURITY_ATTRIBUTES saAttr; SECURITY_ATTRIBUTES saAttr;
memset(&saAttr, 0, sizeof(saAttr)); memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(saAttr); saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE; saAttr.bInheritHandle = TRUE;
sprintf(PipeNameBuffer, sprintf(PipeNameBuffer,
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x", "\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
GetCurrentProcessId(), (unsigned int) GetCurrentProcessId(),
InterlockedIncrement(&PipeSerialNumber)); (unsigned int) InterlockedIncrement(&PipeSerialNumber));
/* server handle goes to subprocess */ /* server handle goes to subprocess */
shandle = CreateNamedPipeA( shandle = CreateNamedPipeA(
@@ -2728,7 +2814,7 @@ error:
JANET_CORE_FN(cfun_ev_go, JANET_CORE_FN(cfun_ev_go,
"(ev/go fiber-or-fun &opt value supervisor)", "(ev/go fiber-or-fun &opt value supervisor)",
"Put a fiber on the event loop to be resumed later. If a function is used, it is wrapped" "Put a fiber on the event loop to be resumed later. If a function is used, it is wrapped "
"with `fiber/new` first. " "with `fiber/new` first. "
"Optionally pass a value to resume with, otherwise resumes with nil. Returns the fiber. " "Optionally pass a value to resume with, otherwise resumes with nil. Returns the fiber. "
"An optional `core/channel` can be provided as a supervisor. When various " "An optional `core/channel` can be provided as a supervisor. When various "
@@ -2775,6 +2861,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
uint32_t flags = args.tag; uint32_t flags = args.tag;
args.tag = 0; args.tag = 0;
janet_init(); janet_init();
janet_vm.sandbox_flags = (uint32_t) args.argi;
JanetTryState tstate; JanetTryState tstate;
JanetSignal signal = janet_try(&tstate); JanetSignal signal = janet_try(&tstate);
if (!signal) { if (!signal) {
@@ -2824,7 +2911,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
JanetFiber *fiber; JanetFiber *fiber;
if (!janet_checktype(fiberv, JANET_FIBER)) { if (!janet_checktype(fiberv, JANET_FIBER)) {
if (!janet_checktype(fiberv, JANET_FUNCTION)) { if (!janet_checktype(fiberv, JANET_FUNCTION)) {
janet_panicf("expected function|fiber, got %v", fiberv); janet_panicf("expected function or fiber, got %v", fiberv);
} }
JanetFunction *func = janet_unwrap_function(fiberv); JanetFunction *func = janet_unwrap_function(fiberv);
if (func->def->min_arity > 1) { if (func->def->min_arity > 1) {
@@ -2927,19 +3014,19 @@ JANET_CORE_FN(cfun_ev_thread,
JanetEVGenericMessage arguments; JanetEVGenericMessage arguments;
memset(&arguments, 0, sizeof(arguments)); memset(&arguments, 0, sizeof(arguments));
arguments.tag = (uint32_t) flags; arguments.tag = (uint32_t) flags;
arguments.argi = argc; arguments.argi = (uint32_t) janet_vm.sandbox_flags;
arguments.argp = buffer; arguments.argp = buffer;
arguments.fiber = NULL; arguments.fiber = NULL;
janet_ev_threaded_call(janet_go_thread_subr, arguments, janet_ev_default_threaded_callback); janet_ev_threaded_call(janet_go_thread_subr, arguments, janet_ev_default_threaded_callback);
return janet_wrap_nil(); return janet_wrap_nil();
} else { } else {
janet_ev_threaded_await(janet_go_thread_subr, (uint32_t) flags, argc, buffer); janet_ev_threaded_await(janet_go_thread_subr, (uint32_t) flags, (uint32_t) janet_vm.sandbox_flags, buffer);
} }
} }
JANET_CORE_FN(cfun_ev_give_supervisor, JANET_CORE_FN(cfun_ev_give_supervisor,
"(ev/give-supervisor tag & payload)", "(ev/give-supervisor tag & payload)",
"Send a message to the current supervior channel if there is one. The message will be a " "Send a message to the current supervisor channel if there is one. The message will be a "
"tuple of all of the arguments combined into a single message, where the first element is tag. " "tuple of all of the arguments combined into a single message, where the first element is tag. "
"By convention, tag should be a keyword indicating the type of message. Returns nil.") { "By convention, tag should be a keyword indicating the type of message. Returns nil.") {
janet_arity(argc, 1, -1); janet_arity(argc, 1, -1);
@@ -3170,6 +3257,20 @@ JANET_CORE_FN(janet_cfun_rwlock_write_release,
return argv[0]; return argv[0];
} }
JANET_CORE_FN(janet_cfun_ev_all_tasks,
"(ev/all-tasks)",
"Get an array of all active fibers that are being used by the scheduler.") {
janet_fixarity(argc, 0);
(void) argv;
JanetArray *array = janet_array(janet_vm.active_tasks.count);
for (int32_t i = 0; i < janet_vm.active_tasks.capacity; i++) {
if (!janet_checktype(janet_vm.active_tasks.data[i].key, JANET_NIL)) {
janet_array_push(array, janet_vm.active_tasks.data[i].key);
}
}
return janet_wrap_array(array);
}
void janet_lib_ev(JanetTable *env) { void janet_lib_ev(JanetTable *env) {
JanetRegExt ev_cfuns_ext[] = { JanetRegExt ev_cfuns_ext[] = {
JANET_CORE_REG("ev/give", cfun_channel_push), JANET_CORE_REG("ev/give", cfun_channel_push),
@@ -3200,6 +3301,7 @@ void janet_lib_ev(JanetTable *env) {
JANET_CORE_REG("ev/acquire-wlock", janet_cfun_rwlock_write_lock), JANET_CORE_REG("ev/acquire-wlock", janet_cfun_rwlock_write_lock),
JANET_CORE_REG("ev/release-rlock", janet_cfun_rwlock_read_release), JANET_CORE_REG("ev/release-rlock", janet_cfun_rwlock_read_release),
JANET_CORE_REG("ev/release-wlock", janet_cfun_rwlock_write_release), JANET_CORE_REG("ev/release-wlock", janet_cfun_rwlock_write_release),
JANET_CORE_REG("ev/all-tasks", janet_cfun_ev_all_tasks),
JANET_REG_END JANET_REG_END
}; };

View File

@@ -26,9 +26,10 @@
#define JANET_FEATURES_H_defined #define JANET_FEATURES_H_defined
#if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \ #if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \
|| defined(__bsdi__) || defined(__DragonFly__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__)
/* Use BSD source on any BSD systems, include OSX */ /* Use BSD source on any BSD systems, include OSX */
# define _BSD_SOURCE # define _BSD_SOURCE
# define _POSIX_C_SOURCE 200809L
#else #else
/* Use POSIX feature flags */ /* Use POSIX feature flags */
# ifndef _POSIX_C_SOURCE # ifndef _POSIX_C_SOURCE
@@ -36,6 +37,10 @@
# endif # endif
#endif #endif
#if defined(__APPLE__)
#define _DARWIN_C_SOURCE
#endif
/* Needed for sched.h for cpu count */ /* Needed for sched.h for cpu count */
#ifdef __linux__ #ifdef __linux__
#define _GNU_SOURCE #define _GNU_SOURCE
@@ -45,6 +50,11 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
/* needed for inet_pton and InitializeSRWLock */
#ifdef __MINGW32__
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#endif
/* Needed for realpath on linux, as well as pthread rwlocks. */ /* Needed for realpath on linux, as well as pthread rwlocks. */
#ifndef _XOPEN_SOURCE #ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
@@ -61,4 +71,9 @@
#define _NETBSD_SOURCE #define _NETBSD_SOURCE
#endif #endif
/* Needed for several things when building with -std=c99. */
#if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__))
#define __BSD_VISIBLE 1
#endif
#endif #endif

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 "gc.h"
#endif #endif
#ifdef JANET_FFI #ifdef JANET_FFI
@@ -309,6 +310,7 @@ static JanetFFIPrimType decode_ffi_prim(const uint8_t *name) {
if (!janet_cstrcmp(name, "void")) return JANET_FFI_TYPE_VOID; if (!janet_cstrcmp(name, "void")) return JANET_FFI_TYPE_VOID;
if (!janet_cstrcmp(name, "bool")) return JANET_FFI_TYPE_BOOL; if (!janet_cstrcmp(name, "bool")) return JANET_FFI_TYPE_BOOL;
if (!janet_cstrcmp(name, "ptr")) return JANET_FFI_TYPE_PTR; if (!janet_cstrcmp(name, "ptr")) return JANET_FFI_TYPE_PTR;
if (!janet_cstrcmp(name, "pointer")) return JANET_FFI_TYPE_PTR;
if (!janet_cstrcmp(name, "string")) return JANET_FFI_TYPE_STRING; if (!janet_cstrcmp(name, "string")) return JANET_FFI_TYPE_STRING;
if (!janet_cstrcmp(name, "float")) return JANET_FFI_TYPE_FLOAT; if (!janet_cstrcmp(name, "float")) return JANET_FFI_TYPE_FLOAT;
if (!janet_cstrcmp(name, "double")) return JANET_FFI_TYPE_DOUBLE; if (!janet_cstrcmp(name, "double")) return JANET_FFI_TYPE_DOUBLE;
@@ -838,7 +840,6 @@ JANET_CORE_FN(cfun_ffi_signature,
} }
/* Add reference items */ /* Add reference items */
size_t old_stack_count = stack_count;
stack_count += 2 * ref_stack_count; stack_count += 2 * ref_stack_count;
if (stack_count & 0x1) { if (stack_count & 0x1) {
stack_count++; stack_count++;
@@ -1179,7 +1180,13 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
/* hack to get proper stack placement and avoid clobbering from logic above - shift stack down, otherwise we have issues. /* hack to get proper stack placement and avoid clobbering from logic above - shift stack down, otherwise we have issues.
* Technically, this writes into 16 bytes of unallocated stack memory */ * Technically, this writes into 16 bytes of unallocated stack memory */
#ifdef JANET_MINGW
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
if (stack_size) memmove(stack - stack_shift, stack, stack_size); if (stack_size) memmove(stack - stack_shift, stack, stack_size);
#ifdef JANET_MINGW
#pragma GCC diagnostic pop
#endif
switch (signature->variant) { switch (signature->variant) {
default: default:
@@ -1296,6 +1303,7 @@ JANET_CORE_FN(cfun_ffi_jitfn,
"(ffi/jitfn bytes)", "(ffi/jitfn bytes)",
"Create an abstract type that can be used as the pointer argument to `ffi/call`. The content " "Create an abstract type that can be used as the pointer argument to `ffi/call`. The content "
"of `bytes` is architecture specific machine code that will be copied into executable memory.") { "of `bytes` is architecture specific machine code that will be copied into executable memory.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetByteView bytes = janet_getbytes(argv, 0); JanetByteView bytes = janet_getbytes(argv, 0);
@@ -1303,7 +1311,11 @@ JANET_CORE_FN(cfun_ffi_jitfn,
size_t alloc_size = ((size_t) bytes.len + FFI_PAGE_MASK) & ~FFI_PAGE_MASK; size_t alloc_size = ((size_t) bytes.len + FFI_PAGE_MASK) & ~FFI_PAGE_MASK;
#ifdef JANET_FFI_JIT #ifdef JANET_FFI_JIT
#ifdef JANET_EV
JanetFFIJittedFn *fn = janet_abstract_threaded(&janet_type_ffijit, sizeof(JanetFFIJittedFn)); JanetFFIJittedFn *fn = janet_abstract_threaded(&janet_type_ffijit, sizeof(JanetFFIJittedFn));
#else
JanetFFIJittedFn *fn = janet_abstract(&janet_type_ffijit, sizeof(JanetFFIJittedFn));
#endif
fn->function_pointer = NULL; fn->function_pointer = NULL;
fn->size = 0; fn->size = 0;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1344,6 +1356,7 @@ JANET_CORE_FN(cfun_ffi_call,
"(ffi/call pointer signature & args)", "(ffi/call pointer signature & args)",
"Call a raw pointer as a function pointer. The function signature specifies " "Call a raw pointer as a function pointer. The function signature specifies "
"how Janet values in `args` are converted to native machine types.") { "how Janet values in `args` are converted to native machine types.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_arity(argc, 2, -1); janet_arity(argc, 2, -1);
void *function_pointer = janet_ffi_get_callable_pointer(argv, 0); void *function_pointer = janet_ffi_get_callable_pointer(argv, 0);
JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type); JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type);
@@ -1364,18 +1377,25 @@ JANET_CORE_FN(cfun_ffi_call,
} }
JANET_CORE_FN(cfun_ffi_buffer_write, JANET_CORE_FN(cfun_ffi_buffer_write,
"(ffi/write ffi-type data &opt buffer)", "(ffi/write ffi-type data &opt buffer index)",
"Append a native tyep 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 modifed buffer or a new buffer if one is not supplied.") {
janet_arity(argc, 2, 3); janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_arity(argc, 2, 4);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
uint32_t el_size = (uint32_t) type_size(type); uint32_t el_size = (uint32_t) type_size(type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size); JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
int32_t index = janet_optnat(argv, argc, 3, 0);
int32_t old_count = buffer->count;
if (index > old_count) janet_panic("index out of bounds");
buffer->count = index;
janet_buffer_extra(buffer, el_size); janet_buffer_extra(buffer, el_size);
memset(buffer->data, 0, el_size); buffer->count = old_count;
janet_ffi_write_one(buffer->data, argv, 1, type, JANET_FFI_MAX_RECUR); memset(buffer->data + index, 0, el_size);
buffer->count += el_size; janet_ffi_write_one(buffer->data + index, argv, 1, type, JANET_FFI_MAX_RECUR);
index += el_size;
if (buffer->count < index) buffer->count = index;
return janet_wrap_buffer(buffer); return janet_wrap_buffer(buffer);
} }
@@ -1384,6 +1404,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read,
"Parse a native struct out of a buffer and convert it to normal Janet data structures. " "Parse a native struct out of a buffer and convert it to normal Janet data structures. "
"This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although " "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although "
"this is unsafe.") { "this is unsafe.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
JanetFFIType type = decode_ffi_type(argv[0]); JanetFFIType type = decode_ffi_type(argv[0]);
size_t offset = (size_t) janet_optnat(argv, argc, 2, 0); size_t offset = (size_t) janet_optnat(argv, argc, 2, 0);
@@ -1430,6 +1451,7 @@ JANET_CORE_FN(janet_core_raw_native,
" or run any code from it. This is different than `native`, which will " " or run any code from it. This is different than `native`, which will "
"run initialization code to get a module table. If `path` is nil, opens the current running binary. " "run initialization code to get a module table. If `path` is nil, opens the current running binary. "
"Returns a `core/native`.") { "Returns a `core/native`.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_arity(argc, 0, 1); janet_arity(argc, 0, 1);
const char *path = janet_optcstring(argv, argc, 0, NULL); const char *path = janet_optcstring(argv, argc, 0, NULL);
Clib lib = load_clib(path); Clib lib = load_clib(path);
@@ -1445,6 +1467,7 @@ JANET_CORE_FN(janet_core_native_lookup,
"(ffi/lookup native symbol-name)", "(ffi/lookup native symbol-name)",
"Lookup a symbol from a native object. All symbol lookups will return a raw pointer " "Lookup a symbol from a native object. All symbol lookups will return a raw pointer "
"if the symbol is found, else nil.") { "if the symbol is found, else nil.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
const char *sym = janet_getcstring(argv, 1); const char *sym = janet_getcstring(argv, 1);
@@ -1458,6 +1481,7 @@ JANET_CORE_FN(janet_core_native_close,
"(ffi/close native)", "(ffi/close native)",
"Free a native object. Dereferencing pointers to symbols in the object will have undefined " "Free a native object. Dereferencing pointers to symbols in the object will have undefined "
"behavior after freeing.") { "behavior after freeing.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
if (anative->closed) janet_panic("native object already closed"); if (anative->closed) janet_panic("native object already closed");
@@ -1469,23 +1493,42 @@ JANET_CORE_FN(janet_core_native_close,
JANET_CORE_FN(cfun_ffi_malloc, JANET_CORE_FN(cfun_ffi_malloc,
"(ffi/malloc size)", "(ffi/malloc size)",
"Allocates memory directly using the system memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") { "Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
size_t size = janet_getsize(argv, 0); size_t size = janet_getsize(argv, 0);
if (size == 0) return janet_wrap_nil(); if (size == 0) return janet_wrap_nil();
return janet_wrap_pointer(malloc(size)); return janet_wrap_pointer(janet_malloc(size));
} }
JANET_CORE_FN(cfun_ffi_free, JANET_CORE_FN(cfun_ffi_free,
"(ffi/free pointer)", "(ffi/free pointer)",
"Free memory allocated with `ffi/malloc`.") { "Free memory allocated with `ffi/malloc`. Returns nil.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil(); if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil();
void *pointer = janet_getpointer(argv, 0); void *pointer = janet_getpointer(argv, 0);
free(pointer); janet_free(pointer);
return janet_wrap_nil(); return janet_wrap_nil();
} }
JANET_CORE_FN(cfun_ffi_pointer_buffer,
"(ffi/pointer-buffer pointer capacity &opt count offset)",
"Create a buffer from a pointer. The underlying memory of the buffer will not be "
"reallocated or freed by the garbage collector, allowing unmanaged, mutable memory "
"to be manipulated with buffer functions. Attempts to resize or extend the buffer "
"beyond its initial capacity will raise an error. As with many FFI functions, this is memory "
"unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") {
janet_sandbox_assert(JANET_SANDBOX_FFI);
janet_arity(argc, 2, 4);
void *pointer = janet_getpointer(argv, 0);
int32_t capacity = janet_getnat(argv, 1);
int32_t count = janet_optnat(argv, argc, 2, 0);
int64_t offset = janet_optinteger64(argv, argc, 3, 0);
uint8_t *offset_pointer = ((uint8_t *) pointer) + offset;
return janet_wrap_buffer(janet_pointer_buffer_unsafe(offset_pointer, capacity, count));
}
void janet_lib_ffi(JanetTable *env) { void janet_lib_ffi(JanetTable *env) {
JanetRegExt ffi_cfuns[] = { JanetRegExt ffi_cfuns[] = {
JANET_CORE_REG("ffi/native", janet_core_raw_native), JANET_CORE_REG("ffi/native", janet_core_raw_native),
@@ -1502,6 +1545,7 @@ void janet_lib_ffi(JanetTable *env) {
JANET_CORE_REG("ffi/jitfn", cfun_ffi_jitfn), JANET_CORE_REG("ffi/jitfn", cfun_ffi_jitfn),
JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc), JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc),
JANET_CORE_REG("ffi/free", cfun_ffi_free), JANET_CORE_REG("ffi/free", cfun_ffi_free),
JANET_CORE_REG("ffi/pointer-buffer", cfun_ffi_pointer_buffer),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, ffi_cfuns); janet_core_cfuns_ext(env, NULL, ffi_cfuns);

View File

@@ -495,6 +495,8 @@ JANET_CORE_FN(cfun_fiber_new,
"* :t - block termination signals: error + user[0-4]\n" "* :t - block termination signals: error + user[0-4]\n"
"* :u - block user signals\n" "* :u - block user signals\n"
"* :y - block yield signals\n" "* :y - block yield signals\n"
"* :w - block await signals (user9)\n"
"* :r - block interrupt signals (user8)\n"
"* :0-9 - block a specific user signal\n\n" "* :0-9 - block a specific user signal\n\n"
"The sigmask argument also can take environment flags. If any mutually " "The sigmask argument also can take environment flags. If any mutually "
"exclusive flags are present, the last flag takes precedence.\n\n" "exclusive flags are present, the last flag takes precedence.\n\n"
@@ -518,7 +520,7 @@ JANET_CORE_FN(cfun_fiber_new,
} else { } else {
switch (view.bytes[i]) { switch (view.bytes[i]) {
default: default:
janet_panicf("invalid flag %c, expected a, t, d, e, u, y, i, or p", view.bytes[i]); janet_panicf("invalid flag %c, expected a, t, d, e, u, y, w, r, i, or p", view.bytes[i]);
break; break;
case 'a': case 'a':
fiber->flags |= fiber->flags |=
@@ -548,6 +550,12 @@ JANET_CORE_FN(cfun_fiber_new,
case 'y': case 'y':
fiber->flags |= JANET_FIBER_MASK_YIELD; fiber->flags |= JANET_FIBER_MASK_YIELD;
break; break;
case 'w':
fiber->flags |= JANET_FIBER_MASK_USER9;
break;
case 'r':
fiber->flags |= JANET_FIBER_MASK_USER8;
break;
case 'i': case 'i':
if (!janet_vm.fiber->env) { if (!janet_vm.fiber->env) {
janet_vm.fiber->env = janet_table(0); janet_vm.fiber->env = janet_table(0);
@@ -575,7 +583,9 @@ JANET_CORE_FN(cfun_fiber_status,
"* :error - the fiber has errored out\n" "* :error - the fiber has errored out\n"
"* :debug - the fiber is suspended in debug mode\n" "* :debug - the fiber is suspended in debug mode\n"
"* :pending - the fiber has been yielded\n" "* :pending - the fiber has been yielded\n"
"* :user(0-9) - the fiber is suspended by a user signal\n" "* :user(0-7) - the fiber is suspended by a user signal\n"
"* :interrupted - the fiber was interrupted\n"
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
"* :alive - the fiber is currently running and cannot be resumed\n" "* :alive - the fiber is currently running and cannot be resumed\n"
"* :new - the fiber has just been created and not yet run") { "* :new - the fiber has just been created and not yet run") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
@@ -625,11 +635,7 @@ JANET_CORE_FN(cfun_fiber_setmaxstack,
return argv[0]; return argv[0];
} }
JANET_CORE_FN(cfun_fiber_can_resume, int janet_fiber_can_resume(JanetFiber *fiber) {
"(fiber/can-resume? fiber)",
"Check if a fiber is finished and cannot be resumed.") {
janet_fixarity(argc, 1);
JanetFiber *fiber = janet_getfiber(argv, 0);
JanetFiberStatus s = janet_fiber_status(fiber); JanetFiberStatus s = janet_fiber_status(fiber);
int isFinished = s == JANET_STATUS_DEAD || int isFinished = s == JANET_STATUS_DEAD ||
s == JANET_STATUS_ERROR || s == JANET_STATUS_ERROR ||
@@ -638,7 +644,15 @@ JANET_CORE_FN(cfun_fiber_can_resume,
s == JANET_STATUS_USER2 || s == JANET_STATUS_USER2 ||
s == JANET_STATUS_USER3 || s == JANET_STATUS_USER3 ||
s == JANET_STATUS_USER4; s == JANET_STATUS_USER4;
return janet_wrap_boolean(!isFinished); return !isFinished;
}
JANET_CORE_FN(cfun_fiber_can_resume,
"(fiber/can-resume? fiber)",
"Check if a fiber is finished and cannot be resumed.") {
janet_fixarity(argc, 1);
JanetFiber *fiber = janet_getfiber(argv, 0);
return janet_wrap_boolean(janet_fiber_can_resume(fiber));
} }
JANET_CORE_FN(cfun_fiber_last_value, JANET_CORE_FN(cfun_fiber_last_value,

View File

@@ -209,6 +209,12 @@ static void janet_mark_funcdef(JanetFuncDef *def) {
janet_mark_string(def->source); janet_mark_string(def->source);
if (def->name) if (def->name)
janet_mark_string(def->name); janet_mark_string(def->name);
if (def->symbolmap) {
for (int i = 0; i < def->symbolmap_length; i++) {
janet_mark_string(def->symbolmap[i].symbol);
}
}
} }
static void janet_mark_function(JanetFunction *func) { static void janet_mark_function(JanetFunction *func) {
@@ -314,6 +320,7 @@ static void janet_deinit_block(JanetGCObject *mem) {
janet_free(def->bytecode); janet_free(def->bytecode);
janet_free(def->sourcemap); janet_free(def->sourcemap);
janet_free(def->closure_bitset); janet_free(def->closure_bitset);
janet_free(def->symbolmap);
} }
break; break;
} }

View File

@@ -138,7 +138,7 @@ int64_t janet_unwrap_s64(Janet x) {
break; break;
} }
} }
janet_panicf("bad s64 initializer: %t", x); janet_panicf("can not convert %t %q to 64 bit signed integer", x, x);
return 0; return 0;
} }
@@ -169,7 +169,7 @@ uint64_t janet_unwrap_u64(Janet x) {
break; break;
} }
} }
janet_panicf("bad u64 initializer: %t", x); janet_panicf("can not convert %t %q to a 64 bit unsigned integer", x, x);
return 0; return 0;
} }
@@ -502,6 +502,18 @@ static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
return janet_wrap_abstract(box); return janet_wrap_abstract(box);
} }
static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
janet_fixarity(argc, 2);
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
int64_t op2 = janet_unwrap_s64(argv[0]);
int64_t op1 = janet_unwrap_s64(argv[1]);
int64_t x = op1 % op2;
*box = (op1 > 0)
? ((op2 > 0) ? x : (0 == x ? x : x + op2))
: ((op2 > 0) ? (0 == x ? x : x + op2) : x);
return janet_wrap_abstract(box);
}
OPMETHOD(int64_t, s64, add, +) OPMETHOD(int64_t, s64, add, +)
OPMETHOD(int64_t, s64, sub, -) OPMETHOD(int64_t, s64, sub, -)
OPMETHODINVERT(int64_t, s64, subi, -) OPMETHODINVERT(int64_t, s64, subi, -)
@@ -509,6 +521,7 @@ OPMETHOD(int64_t, s64, mul, *)
DIVMETHOD_SIGNED(int64_t, s64, div, /) DIVMETHOD_SIGNED(int64_t, s64, div, /)
DIVMETHOD_SIGNED(int64_t, s64, rem, %) DIVMETHOD_SIGNED(int64_t, s64, rem, %)
DIVMETHODINVERT_SIGNED(int64_t, s64, divi, /) DIVMETHODINVERT_SIGNED(int64_t, s64, divi, /)
DIVMETHODINVERT_SIGNED(int64_t, s64, remi, %)
OPMETHOD(int64_t, s64, and, &) OPMETHOD(int64_t, s64, and, &)
OPMETHOD(int64_t, s64, or, |) OPMETHOD(int64_t, s64, or, |)
OPMETHOD(int64_t, s64, xor, ^) OPMETHOD(int64_t, s64, xor, ^)
@@ -521,6 +534,7 @@ OPMETHOD(uint64_t, u64, mul, *)
DIVMETHOD(uint64_t, u64, div, /) DIVMETHOD(uint64_t, u64, div, /)
DIVMETHOD(uint64_t, u64, mod, %) DIVMETHOD(uint64_t, u64, mod, %)
DIVMETHODINVERT(uint64_t, u64, divi, /) DIVMETHODINVERT(uint64_t, u64, divi, /)
DIVMETHODINVERT(uint64_t, u64, modi, %)
OPMETHOD(uint64_t, u64, and, &) OPMETHOD(uint64_t, u64, and, &)
OPMETHOD(uint64_t, u64, or, |) OPMETHOD(uint64_t, u64, or, |)
OPMETHOD(uint64_t, u64, xor, ^) OPMETHOD(uint64_t, u64, xor, ^)
@@ -542,9 +556,9 @@ static JanetMethod it_s64_methods[] = {
{"/", cfun_it_s64_div}, {"/", cfun_it_s64_div},
{"r/", cfun_it_s64_divi}, {"r/", cfun_it_s64_divi},
{"mod", cfun_it_s64_mod}, {"mod", cfun_it_s64_mod},
{"rmod", cfun_it_s64_mod}, {"rmod", cfun_it_s64_modi},
{"%", cfun_it_s64_rem}, {"%", cfun_it_s64_rem},
{"r%", cfun_it_s64_rem}, {"r%", cfun_it_s64_remi},
{"&", cfun_it_s64_and}, {"&", cfun_it_s64_and},
{"r&", cfun_it_s64_and}, {"r&", cfun_it_s64_and},
{"|", cfun_it_s64_or}, {"|", cfun_it_s64_or},
@@ -567,9 +581,9 @@ static JanetMethod it_u64_methods[] = {
{"/", cfun_it_u64_div}, {"/", cfun_it_u64_div},
{"r/", cfun_it_u64_divi}, {"r/", cfun_it_u64_divi},
{"mod", cfun_it_u64_mod}, {"mod", cfun_it_u64_mod},
{"rmod", cfun_it_u64_mod}, {"rmod", cfun_it_u64_modi},
{"%", cfun_it_u64_mod}, {"%", cfun_it_u64_mod},
{"r%", cfun_it_u64_mod}, {"r%", cfun_it_u64_modi},
{"&", cfun_it_u64_and}, {"&", cfun_it_u64_and},
{"r&", cfun_it_u64_and}, {"r&", cfun_it_u64_and},
{"|", cfun_it_u64_or}, {"|", cfun_it_u64_or},

View File

@@ -69,12 +69,15 @@ static int32_t checkflags(const uint8_t *str) {
break; break;
case 'w': case 'w':
flags |= JANET_FILE_WRITE; flags |= JANET_FILE_WRITE;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
case 'a': case 'a':
flags |= JANET_FILE_APPEND; flags |= JANET_FILE_APPEND;
janet_sandbox_assert(JANET_SANDBOX_FS);
break; break;
case 'r': case 'r':
flags |= JANET_FILE_READ; flags |= JANET_FILE_READ;
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
break; break;
} }
for (i = 1; i < len; i++) { for (i = 1; i < len; i++) {
@@ -84,6 +87,7 @@ static int32_t checkflags(const uint8_t *str) {
break; break;
case '+': case '+':
if (flags & JANET_FILE_UPDATE) return -1; if (flags & JANET_FILE_UPDATE) return -1;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
flags |= JANET_FILE_UPDATE; flags |= JANET_FILE_UPDATE;
break; break;
case 'b': case 'b':
@@ -116,6 +120,7 @@ JANET_CORE_FN(cfun_io_temp,
"(file/temp)", "(file/temp)",
"Open an anonymous temporary file that is removed on close. " "Open an anonymous temporary file that is removed on close. "
"Raises an error on failure.") { "Raises an error on failure.") {
janet_sandbox_assert(JANET_SANDBOX_FS_TEMP);
(void)argv; (void)argv;
janet_fixarity(argc, 0); janet_fixarity(argc, 0);
// XXX use mkostemp when we can to avoid CLOEXEC race. // XXX use mkostemp when we can to avoid CLOEXEC race.
@@ -148,6 +153,7 @@ JANET_CORE_FN(cfun_io_fopen,
flags = checkflags(fmode); flags = checkflags(fmode);
} else { } else {
fmode = (const uint8_t *)"r"; fmode = (const uint8_t *)"r";
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
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);
@@ -243,6 +249,13 @@ JANET_CORE_FN(cfun_io_fwrite,
return argv[0]; return argv[0];
} }
static void io_assert_writeable(JanetFile *iof) {
if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed");
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
janet_panic("file is not writeable");
}
/* Flush the bytes in the file */ /* Flush the bytes in the file */
JANET_CORE_FN(cfun_io_fflush, JANET_CORE_FN(cfun_io_fflush,
"(file/flush f)", "(file/flush f)",
@@ -250,10 +263,7 @@ JANET_CORE_FN(cfun_io_fflush,
"buffered for efficiency reasons. Returns the file handle.") { "buffered for efficiency reasons. Returns the file handle.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED) io_assert_writeable(iof);
janet_panic("file is closed");
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
janet_panic("file is not writeable");
if (fflush(iof->file)) if (fflush(iof->file))
janet_panic("could not flush file"); janet_panic("could not flush file");
return argv[0]; return argv[0];
@@ -269,6 +279,7 @@ int janet_file_close(JanetFile *file) {
if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) { if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
ret = fclose(file->file); ret = fclose(file->file);
file->flags |= JANET_FILE_CLOSED; file->flags |= JANET_FILE_CLOSED;
file->file = NULL; /* NULL derefence is easier to debug then other problems */
return ret; return ret;
} }
return 0; return 0;
@@ -337,11 +348,24 @@ JANET_CORE_FN(cfun_io_fseek,
return argv[0]; return argv[0];
} }
JANET_CORE_FN(cfun_io_ftell,
"(file/tell f)",
"Get the current value of the file position for file `f`.") {
janet_fixarity(argc, 1);
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed");
long pos = ftell(iof->file);
if (pos == -1) janet_panic("error getting position in file");
return janet_wrap_number((double)pos);
}
static JanetMethod io_file_methods[] = { static JanetMethod io_file_methods[] = {
{"close", cfun_io_fclose}, {"close", cfun_io_fclose},
{"flush", cfun_io_fflush}, {"flush", cfun_io_fflush},
{"read", cfun_io_fread}, {"read", cfun_io_fread},
{"seek", cfun_io_fseek}, {"seek", cfun_io_fseek},
{"tell", cfun_io_ftell},
{"write", cfun_io_fwrite}, {"write", cfun_io_fwrite},
{NULL, NULL} {NULL, NULL}
}; };
@@ -449,6 +473,7 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
if (janet_abstract_type(abstract) != &janet_file_type) if (janet_abstract_type(abstract) != &janet_file_type)
return janet_wrap_nil(); return janet_wrap_nil();
JanetFile *iofile = abstract; JanetFile *iofile = abstract;
io_assert_writeable(iofile);
f = iofile->file; f = iofile->file;
break; break;
} }
@@ -564,6 +589,10 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
if (janet_abstract_type(abstract) != &janet_file_type) if (janet_abstract_type(abstract) != &janet_file_type)
return janet_wrap_nil(); return janet_wrap_nil();
JanetFile *iofile = abstract; JanetFile *iofile = abstract;
if (iofile->flags & JANET_FILE_CLOSED) {
janet_panic("cannot print to closed file");
}
io_assert_writeable(iofile);
f = iofile->file; f = iofile->file;
break; break;
} }
@@ -688,6 +717,7 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
if (janet_abstract_type(abstract) != &janet_file_type) if (janet_abstract_type(abstract) != &janet_file_type)
break; break;
JanetFile *iofile = abstract; JanetFile *iofile = abstract;
io_assert_writeable(iofile);
f = iofile->file; f = iofile->file;
} }
fwrite(buffer.data, buffer.count, 1, f); fwrite(buffer.data, buffer.count, 1, f);
@@ -718,17 +748,17 @@ JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
return janet_getabstract(argv, n, &janet_file_type); return janet_getabstract(argv, n, &janet_file_type);
} }
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) { FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
JanetFile *iof = janet_getabstract(argv, n, &janet_file_type); JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
if (NULL != flags) *flags = iof->flags; if (NULL != flags) *flags = iof->flags;
return iof->file; return iof->file;
} }
JanetFile *janet_makejfile(FILE *f, int flags) { JanetFile *janet_makejfile(FILE *f, int32_t flags) {
return makef(f, flags); return makef(f, flags);
} }
Janet janet_makefile(FILE *f, int flags) { Janet janet_makefile(FILE *f, int32_t flags) {
return janet_wrap_abstract(makef(f, flags)); return janet_wrap_abstract(makef(f, flags));
} }
@@ -736,7 +766,7 @@ JanetAbstract janet_checkfile(Janet j) {
return janet_checkabstract(j, &janet_file_type); return janet_checkabstract(j, &janet_file_type);
} }
FILE *janet_unwrapfile(Janet j, int *flags) { FILE *janet_unwrapfile(Janet j, int32_t *flags) {
JanetFile *iof = janet_unwrap_abstract(j); JanetFile *iof = janet_unwrap_abstract(j);
if (NULL != flags) *flags = iof->flags; if (NULL != flags) *flags = iof->flags;
return iof->file; return iof->file;
@@ -766,6 +796,7 @@ void janet_lib_io(JanetTable *env) {
JANET_CORE_REG("file/write", cfun_io_fwrite), JANET_CORE_REG("file/write", cfun_io_fwrite),
JANET_CORE_REG("file/flush", cfun_io_fflush), JANET_CORE_REG("file/flush", cfun_io_fflush),
JANET_CORE_REG("file/seek", cfun_io_fseek), JANET_CORE_REG("file/seek", cfun_io_fseek),
JANET_CORE_REG("file/tell", cfun_io_ftell),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, io_cfuns); janet_core_cfuns_ext(env, NULL, io_cfuns);

View File

@@ -67,7 +67,8 @@ enum {
LB_UNSAFE_POINTER, /* 222 */ LB_UNSAFE_POINTER, /* 222 */
LB_STRUCT_PROTO, /* 223 */ LB_STRUCT_PROTO, /* 223 */
#ifdef JANET_EV #ifdef JANET_EV
LB_THREADED_ABSTRACT/* 224 */ LB_THREADED_ABSTRACT, /* 224 */
LB_POINTER_BUFFER, /* 224 */
#endif #endif
} LeadBytes; } LeadBytes;
@@ -153,6 +154,10 @@ static void pushbytes(MarshalState *st, const uint8_t *bytes, int32_t len) {
janet_buffer_push_bytes(st->buf, bytes, len); janet_buffer_push_bytes(st->buf, bytes, len);
} }
static void pushpointer(MarshalState *st, void *ptr) {
janet_buffer_push_bytes(st->buf, (const uint8_t *) &ptr, sizeof(ptr));
}
/* Marshal a size_t onto the buffer */ /* Marshal a size_t onto the buffer */
static void push64(MarshalState *st, uint64_t x) { static void push64(MarshalState *st, uint64_t x) {
if (x <= 0xF0) { if (x <= 0xF0) {
@@ -252,6 +257,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
pushint(st, def->environments_length); pushint(st, def->environments_length);
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS) if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
pushint(st, def->defs_length); pushint(st, def->defs_length);
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
pushint(st, def->symbolmap_length);
if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) if (def->flags & JANET_FUNCDEF_FLAG_HASNAME)
marshal_one(st, janet_wrap_string(def->name), flags); marshal_one(st, janet_wrap_string(def->name), flags);
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE) if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE)
@@ -261,6 +268,14 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
for (int32_t i = 0; i < def->constants_length; i++) for (int32_t i = 0; i < def->constants_length; i++)
marshal_one(st, def->constants[i], flags); marshal_one(st, def->constants[i], flags);
/* Marshal symbol map, if needed */
for (int32_t i = 0; i < def->symbolmap_length; i++) {
pushint(st, (int32_t) def->symbolmap[i].birth_pc);
pushint(st, (int32_t) def->symbolmap[i].death_pc);
pushint(st, (int32_t) def->symbolmap[i].slot_index);
marshal_one(st, janet_wrap_symbol(def->symbolmap[i].symbol), flags);
}
/* marshal the bytecode */ /* marshal the bytecode */
janet_marshal_u32s(st, def->bytecode, def->bytecode_length); janet_marshal_u32s(st, def->bytecode, def->bytecode_length);
@@ -270,7 +285,7 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
/* marshal the sub funcdefs if needed */ /* marshal the sub funcdefs if needed */
for (int32_t i = 0; i < def->defs_length; i++) for (int32_t i = 0; i < def->defs_length; i++)
marshal_one_def(st, def->defs[i], flags); marshal_one_def(st, def->defs[i], flags + 1);
/* marshal source maps if needed */ /* marshal source maps if needed */
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) { if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
@@ -501,6 +516,16 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
JanetBuffer *buffer = janet_unwrap_buffer(x); JanetBuffer *buffer = janet_unwrap_buffer(x);
/* Record reference */ /* Record reference */
MARK_SEEN(); MARK_SEEN();
#ifdef JANET_EV
if ((flags & JANET_MARSHAL_UNSAFE) &&
(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
pushbyte(st, LB_POINTER_BUFFER);
pushint(st, buffer->count);
pushint(st, buffer->capacity);
pushpointer(st, buffer->data);
return;
}
#endif
pushbyte(st, LB_BUFFER); pushbyte(st, LB_BUFFER);
pushint(st, buffer->count); pushint(st, buffer->count);
pushbytes(st, buffer->data, buffer->count); pushbytes(st, buffer->data, buffer->count);
@@ -596,8 +621,7 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
if (!(flags & JANET_MARSHAL_UNSAFE)) goto no_registry; if (!(flags & JANET_MARSHAL_UNSAFE)) goto no_registry;
MARK_SEEN(); MARK_SEEN();
pushbyte(st, LB_UNSAFE_POINTER); pushbyte(st, LB_UNSAFE_POINTER);
void *ptr = janet_unwrap_pointer(x); pushpointer(st, janet_unwrap_pointer(x));
pushbytes(st, (uint8_t *) &ptr, sizeof(void *));
return; return;
} }
no_registry: no_registry:
@@ -824,6 +848,8 @@ static const uint8_t *unmarshal_one_def(
def->constants = NULL; def->constants = NULL;
def->bytecode = NULL; def->bytecode = NULL;
def->sourcemap = NULL; def->sourcemap = NULL;
def->symbolmap = NULL;
def->symbolmap_length = 0;
janet_v_push(st->lookup_defs, def); janet_v_push(st->lookup_defs, def);
/* Set default lengths to zero */ /* Set default lengths to zero */
@@ -831,6 +857,7 @@ static const uint8_t *unmarshal_one_def(
int32_t constants_length = 0; int32_t constants_length = 0;
int32_t environments_length = 0; int32_t environments_length = 0;
int32_t defs_length = 0; int32_t defs_length = 0;
int32_t symbolmap_length = 0;
/* Read flags and other fixed values */ /* Read flags and other fixed values */
def->flags = readint(st, &data); def->flags = readint(st, &data);
@@ -846,6 +873,8 @@ static const uint8_t *unmarshal_one_def(
environments_length = readnat(st, &data); environments_length = readnat(st, &data);
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS) if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
defs_length = readnat(st, &data); defs_length = readnat(st, &data);
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
symbolmap_length = readnat(st, &data);
/* Check name and source (optional) */ /* Check name and source (optional) */
if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) { if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) {
@@ -874,6 +903,26 @@ static const uint8_t *unmarshal_one_def(
} }
def->constants_length = constants_length; def->constants_length = constants_length;
/* Unmarshal symbol map, if needed */
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP) {
size_t size = sizeof(JanetSymbolMap) * symbolmap_length;
def->symbolmap = janet_malloc(size);
if (def->symbolmap == NULL) {
JANET_OUT_OF_MEMORY;
}
for (int32_t i = 0; i < symbolmap_length; i++) {
def->symbolmap[i].birth_pc = (uint32_t) readint(st, &data);
def->symbolmap[i].death_pc = (uint32_t) readint(st, &data);
def->symbolmap[i].slot_index = (uint32_t) readint(st, &data);
Janet value;
data = unmarshal_one(st, data, &value, flags + 1);
if (!janet_checktype(value, JANET_SYMBOL))
janet_panic("expected symbol in symbol map");
def->symbolmap[i].symbol = janet_unwrap_symbol(value);
}
def->symbolmap_length = (uint32_t) symbolmap_length;
}
/* Unmarshal bytecode */ /* Unmarshal bytecode */
def->bytecode = janet_malloc(sizeof(uint32_t) * bytecode_length); def->bytecode = janet_malloc(sizeof(uint32_t) * bytecode_length);
if (!def->bytecode) { if (!def->bytecode) {
@@ -1380,6 +1429,29 @@ static const uint8_t *unmarshal_one(
janet_v_push(st->lookup, *out); janet_v_push(st->lookup, *out);
return data; return data;
} }
#ifdef JANET_EV
case LB_POINTER_BUFFER: {
data++;
int32_t count = readnat(st, &data);
int32_t capacity = readnat(st, &data);
MARSH_EOS(st, data + sizeof(void *));
union {
void *ptr;
uint8_t bytes[sizeof(void *)];
} u;
if (!(flags & JANET_MARSHAL_UNSAFE)) {
janet_panicf("unsafe flag not given, "
"will not unmarshal raw pointer at index %d",
(int)(data - st->start));
}
memcpy(u.bytes, data, sizeof(void *));
data += sizeof(void *);
JanetBuffer *buffer = janet_pointer_buffer_unsafe(u.ptr, capacity, count);
*out = janet_wrap_buffer(buffer);
janet_v_push(st->lookup, *out);
return data;
}
#endif
case LB_UNSAFE_CFUNCTION: { case LB_UNSAFE_CFUNCTION: {
MARSH_EOS(st, data + sizeof(JanetCFunction)); MARSH_EOS(st, data + sizeof(JanetCFunction));
data++; data++;

View File

@@ -150,8 +150,8 @@ JANET_CORE_FN(cfun_rng_uniform,
JANET_CORE_FN(cfun_rng_int, JANET_CORE_FN(cfun_rng_int,
"(math/rng-int rng &opt max)", "(math/rng-int rng &opt max)",
"Extract a random random integer in the range [0, max] from the RNG. If " "Extract a random integer in the range [0, max) for max > 0 from the RNG. "
"no max is given, the default is 2^31 - 1." "If max is 0, return 0. If no max is given, the default is 2^31 - 1."
) { ) {
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type); JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
@@ -254,43 +254,45 @@ JANET_CORE_FN(janet_srand,
return janet_wrap_nil(); return janet_wrap_nil();
} }
#define JANET_DEFINE_MATHOP(name, fop, doc)\ #define JANET_DEFINE_NAMED_MATHOP(janet_name, fop, doc)\
JANET_CORE_FN(janet_##name, "(math/" #name " x)", doc) {\ JANET_CORE_FN(janet_##fop, "(math/" janet_name " x)", doc) {\
janet_fixarity(argc, 1); \ janet_fixarity(argc, 1); \
double x = janet_getnumber(argv, 0); \ double x = janet_getnumber(argv, 0); \
return janet_wrap_number(fop(x)); \ return janet_wrap_number(fop(x)); \
} }
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosine of x.") #define JANET_DEFINE_MATHOP(fop, doc) JANET_DEFINE_NAMED_MATHOP(#fop, fop, doc)
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.") JANET_DEFINE_MATHOP(acos, "Returns the arccosine of x.")
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.") JANET_DEFINE_MATHOP(asin, "Returns the arcsin of x.")
JANET_DEFINE_MATHOP(cosh, cosh, "Returns the hyperbolic cosine of x.") JANET_DEFINE_MATHOP(atan, "Returns the arctangent of x.")
JANET_DEFINE_MATHOP(acosh, acosh, "Returns the hyperbolic arccosine of x.") JANET_DEFINE_MATHOP(cos, "Returns the cosine of x.")
JANET_DEFINE_MATHOP(sin, sin, "Returns the sine of x.") JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
JANET_DEFINE_MATHOP(sinh, sinh, "Returns the hyperbolic sine of x.") JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
JANET_DEFINE_MATHOP(asinh, asinh, "Returns the hypberbolic arcsine of x.") JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
JANET_DEFINE_MATHOP(tan, tan, "Returns the tangent of x.") JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
JANET_DEFINE_MATHOP(tanh, tanh, "Returns the hyperbolic tangent of x.") JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
JANET_DEFINE_MATHOP(atanh, atanh, "Returns the hyperbolic arctangent of x.") JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
JANET_DEFINE_MATHOP(exp, exp, "Returns e to the power of x.") JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
JANET_DEFINE_MATHOP(exp2, exp2, "Returns 2 to the power of x.") JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
JANET_DEFINE_MATHOP(expm1, expm1, "Returns e to the power of x minus 1.") JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
JANET_DEFINE_MATHOP(log, log, "Returns the natural logarithm of x.") JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
JANET_DEFINE_MATHOP(log10, log10, "Returns the log base 10 of x.") JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
JANET_DEFINE_MATHOP(log2, log2, "Returns the log base 2 of x.") JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
JANET_DEFINE_MATHOP(sqrt, sqrt, "Returns the square root of x.") JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
JANET_DEFINE_MATHOP(cbrt, cbrt, "Returns the cube root of x.") JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
JANET_DEFINE_MATHOP(ceil, ceil, "Returns the smallest integer value number that is not less than x.") JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
JANET_DEFINE_MATHOP(abs, fabs, "Return the absolute value of x.") JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
JANET_DEFINE_MATHOP(floor, floor, "Returns the largest integer value number that is not greater than x.") JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
JANET_DEFINE_MATHOP(trunc, trunc, "Returns the integer between x and 0 nearest to x.") JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
JANET_DEFINE_MATHOP(round, round, "Returns the integer nearest to x.") JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
JANET_DEFINE_MATHOP(gamma, tgamma, "Returns gamma(x).") JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
JANET_DEFINE_MATHOP(lgamma, lgamma, "Returns log-gamma(x).") JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
JANET_DEFINE_MATHOP(log1p, log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)") JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
JANET_DEFINE_MATHOP(erf, erf, "Returns the error function of x.") JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
JANET_DEFINE_MATHOP(erfc, erfc, "Returns the complementary error function of x.") JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\ #define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
JANET_CORE_FN(janet_##name, signature, doc) {\ JANET_CORE_FN(janet_##name, signature, doc) {\
@@ -303,7 +305,7 @@ JANET_CORE_FN(janet_##name, signature, doc) {\
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.") JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.") JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.") JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point vaue after x in the direction of y.") JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") { JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
@@ -315,7 +317,7 @@ static double janet_gcd(double x, double y) {
#ifdef NAN #ifdef NAN
return NAN; return NAN;
#else #else
return 0.0 \ 0.0; return 0.0 / 0.0;
#endif #endif
} }
if (isinf(x) || isinf(y)) return INFINITY; if (isinf(x) || isinf(y)) return INFINITY;
@@ -368,7 +370,7 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/floor", janet_floor), JANET_CORE_REG("math/floor", janet_floor),
JANET_CORE_REG("math/ceil", janet_ceil), JANET_CORE_REG("math/ceil", janet_ceil),
JANET_CORE_REG("math/pow", janet_pow), JANET_CORE_REG("math/pow", janet_pow),
JANET_CORE_REG("math/abs", janet_abs), JANET_CORE_REG("math/abs", janet_fabs),
JANET_CORE_REG("math/sinh", janet_sinh), JANET_CORE_REG("math/sinh", janet_sinh),
JANET_CORE_REG("math/cosh", janet_cosh), JANET_CORE_REG("math/cosh", janet_cosh),
JANET_CORE_REG("math/tanh", janet_tanh), JANET_CORE_REG("math/tanh", janet_tanh),
@@ -383,7 +385,7 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/hypot", janet_hypot), JANET_CORE_REG("math/hypot", janet_hypot),
JANET_CORE_REG("math/exp2", janet_exp2), JANET_CORE_REG("math/exp2", janet_exp2),
JANET_CORE_REG("math/log1p", janet_log1p), JANET_CORE_REG("math/log1p", janet_log1p),
JANET_CORE_REG("math/gamma", janet_gamma), JANET_CORE_REG("math/gamma", janet_tgamma),
JANET_CORE_REG("math/log-gamma", janet_lgamma), JANET_CORE_REG("math/log-gamma", janet_lgamma),
JANET_CORE_REG("math/erfc", janet_erfc), JANET_CORE_REG("math/erfc", janet_erfc),
JANET_CORE_REG("math/erf", janet_erf), JANET_CORE_REG("math/erf", janet_erf),

View File

@@ -34,9 +34,11 @@
#include <windows.h> #include <windows.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <mswsock.h> #include <mswsock.h>
#ifdef JANET_MSVC
#pragma comment (lib, "Ws2_32.lib") #pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib") #pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "Advapi32.lib") #pragma comment (lib, "Advapi32.lib")
#endif
#else #else
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
@@ -173,7 +175,6 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
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;
SOCKET lsock = (SOCKET) stream->handle;
JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
NetStateAccept *state = (NetStateAccept *)s; NetStateAccept *state = (NetStateAccept *)s;
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED)); memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
@@ -333,6 +334,7 @@ JANET_CORE_FN(cfun_net_sockaddr,
"given in the port argument. On Linux, abstract " "given in the port argument. On Linux, abstract "
"unix domain sockets are specified with a leading '@' character in port. If `multi` is truthy, will " "unix domain sockets are specified with a leading '@' character in port. If `multi` is truthy, will "
"return all address that match in an array instead of just the first.") { "return all address that match in an array instead of just the first.") {
janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT); /* connect OR listen */
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
int socktype = janet_get_sockettype(argv, argc, 2); int socktype = janet_get_sockettype(argv, argc, 2);
int is_unix = 0; int is_unix = 0;
@@ -378,6 +380,7 @@ JANET_CORE_FN(cfun_net_connect,
"to specify a connection type, either :stream or :datagram. The default is :stream. " "to specify a connection type, either :stream or :datagram. The default is :stream. "
"Bindhost is an optional string to select from what address to make the outgoing " "Bindhost is an optional string to select from what address to make the outgoing "
"connection, with the default being the same as using the OS's preferred address. ") { "connection, with the default being the same as using the OS's preferred address. ") {
janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT);
janet_arity(argc, 2, 5); janet_arity(argc, 2, 5);
/* Check arguments */ /* Check arguments */
@@ -457,7 +460,7 @@ JANET_CORE_FN(cfun_net_connect,
if (binding) { if (binding) {
struct addrinfo *rp = NULL; struct addrinfo *rp = NULL;
int did_bind = 0; int did_bind = 0;
for (rp = ai; rp != NULL; rp = rp->ai_next) { for (rp = binding; rp != NULL; rp = rp->ai_next) {
if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) { if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) {
did_bind = 1; did_bind = 1;
break; break;
@@ -474,14 +477,20 @@ JANET_CORE_FN(cfun_net_connect,
} }
} }
/* Wrap socket in abstract type JanetStream */
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
/* Set up the socket for non-blocking IO before connecting */
janet_net_socknoblock(sock);
/* Connect to socket */ /* Connect to socket */
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL); int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
Janet lasterr = janet_ev_lasterr(); int err = WSAGetLastError();
freeaddrinfo(ai); freeaddrinfo(ai);
#else #else
int status = connect(sock, addr, addrlen); int status = connect(sock, addr, addrlen);
Janet lasterr = janet_ev_lasterr(); int err = errno;
if (is_unix) { if (is_unix) {
janet_free(ai); janet_free(ai);
} else { } else {
@@ -489,17 +498,22 @@ JANET_CORE_FN(cfun_net_connect,
} }
#endif #endif
if (status == -1) { if (status != 0) {
JSOCKCLOSE(sock); #ifdef JANET_WINDOWS
janet_panicf("could not connect socket: %V", lasterr); if (err != WSAEWOULDBLOCK) {
#else
if (err != EINPROGRESS) {
#endif
JSOCKCLOSE(sock);
Janet lasterr = janet_ev_lasterr();
janet_panicf("could not connect socket: %V", lasterr);
}
} }
/* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */ /* Handle the connect() result in the event loop*/
janet_net_socknoblock(sock); janet_ev_connect(stream, MSG_NOSIGNAL);
/* Wrap socket in abstract type JanetStream */ janet_await();
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
return janet_wrap_abstract(stream);
} }
static const char *serverify_socket(JSock sfd) { static const char *serverify_socket(JSock sfd) {
@@ -572,6 +586,7 @@ JANET_CORE_FN(cfun_net_listen,
"The type parameter specifies the type of network connection, either " "The type parameter specifies the type of network connection, either "
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is " "a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
":stream. The host and port arguments are the same as in net/address.") { ":stream. The host and port arguments are the same as in net/address.") {
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
/* Get host, port, and handler*/ /* Get host, port, and handler*/
@@ -706,7 +721,7 @@ JANET_CORE_FN(cfun_net_getsockname,
if (getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)) { if (getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)) {
janet_panicf("Failed to get localname on %v: %V", argv[0], janet_ev_lasterr()); janet_panicf("Failed to get localname on %v: %V", argv[0], janet_ev_lasterr());
} }
janet_assert(slen <= sizeof(ss), "socket address truncated"); janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
return janet_so_getname(&ss); return janet_so_getname(&ss);
} }
@@ -722,13 +737,13 @@ JANET_CORE_FN(cfun_net_getpeername,
if (getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen)) { if (getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen)) {
janet_panicf("Failed to get peername on %v: %V", argv[0], janet_ev_lasterr()); janet_panicf("Failed to get peername on %v: %V", argv[0], janet_ev_lasterr());
} }
janet_assert(slen <= sizeof(ss), "socket address truncated"); janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
return janet_so_getname(&ss); return janet_so_getname(&ss);
} }
JANET_CORE_FN(cfun_net_address_unpack, JANET_CORE_FN(cfun_net_address_unpack,
"(net/address-unpack address)", "(net/address-unpack address)",
"Given an address returned by net/adress, return a host, port pair. Unix domain sockets " "Given an address returned by net/address, return a host, port pair. Unix domain sockets "
"will have only the path in the returned tuple.") { "will have only the path in the returned tuple.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
struct sockaddr *sa = janet_getabstract(argv, 0, &janet_address_type); struct sockaddr *sa = janet_getabstract(argv, 0, &janet_address_type);
@@ -798,7 +813,7 @@ JANET_CORE_FN(cfun_stream_chunk,
} }
JANET_CORE_FN(cfun_stream_recv_from, JANET_CORE_FN(cfun_stream_recv_from,
"(net/recv-from stream nbytes buf &opt timoeut)", "(net/recv-from stream nbytes buf &opt timeout)",
"Receives data from a server stream and puts it into a buffer. Returns the socket-address the " "Receives data from a server stream and puts it into a buffer. Returns the socket-address the "
"packet came from. Takes an optional timeout in seconds, after which will return nil.") { "packet came from. Takes an optional timeout in seconds, after which will return nil.") {
janet_arity(argc, 3, 4); janet_arity(argc, 3, 4);
@@ -868,6 +883,98 @@ JANET_CORE_FN(cfun_stream_flush,
return argv[0]; return argv[0];
} }
struct sockopt_type {
const char *name;
int level;
int optname;
enum JanetType type;
};
/* List of supported socket options; The type JANET_POINTER is used
* for options that require special handling depending on the type. */
static const struct sockopt_type sockopt_type_list[] = {
{ "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
{ "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
{ "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
{ "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
{ "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
{ "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
{ NULL, 0, 0, JANET_POINTER }
};
JANET_CORE_FN(cfun_net_setsockopt,
"(net/setsockopt stream option value)",
"set socket options.\n"
"\n"
"supported options and associated value types:\n"
"- :so-broadcast boolean\n"
"- :so-reuseaddr boolean\n"
"- :so-keepalive boolean\n"
"- :ip-multicast-ttl number\n"
"- :ip-add-membership string\n"
"- :ip-drop-membership string\n"
"- :ipv6-join-group string\n"
"- :ipv6-leave-group string\n") {
janet_arity(argc, 3, 3);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_SOCKET);
JanetKeyword optstr = janet_getkeyword(argv, 1);
const struct sockopt_type *st = sockopt_type_list;
while (st->name) {
if (janet_cstrcmp(optstr, st->name) == 0) {
break;
}
st++;
}
if (st->name == NULL) {
janet_panicf("unknown socket option %q", argv[1]);
}
union {
int v_int;
struct ip_mreq v_mreq;
struct ipv6_mreq v_mreq6;
} val;
void *optval = (void *)&val;
socklen_t optlen = 0;
if (st->type == JANET_BOOLEAN) {
val.v_int = janet_getboolean(argv, 2);
optlen = sizeof(val.v_int);
} else if (st->type == JANET_NUMBER) {
val.v_int = janet_getinteger(argv, 2);
optlen = sizeof(val.v_int);
} else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq, 0, sizeof val.v_mreq);
val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
optlen = sizeof(val.v_mreq);
} else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
val.v_mreq6.ipv6mr_interface = 0;
inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
optlen = sizeof(val.v_mreq6);
} else {
janet_panicf("invalid socket option type");
}
janet_assert(optlen != 0, "invalid socket option value");
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
if (r == -1) {
janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno));
}
return janet_wrap_nil();
}
static const JanetMethod net_stream_methods[] = { static const JanetMethod net_stream_methods[] = {
{"chunk", cfun_stream_chunk}, {"chunk", cfun_stream_chunk},
{"close", janet_cfun_stream_close}, {"close", janet_cfun_stream_close},
@@ -882,6 +989,7 @@ static const JanetMethod net_stream_methods[] = {
{"evchunk", janet_cfun_stream_chunk}, {"evchunk", janet_cfun_stream_chunk},
{"evwrite", janet_cfun_stream_write}, {"evwrite", janet_cfun_stream_write},
{"shutdown", cfun_net_shutdown}, {"shutdown", cfun_net_shutdown},
{"setsockopt", cfun_net_setsockopt},
{NULL, NULL} {NULL, NULL}
}; };
@@ -906,6 +1014,7 @@ void janet_lib_net(JanetTable *env) {
JANET_CORE_REG("net/peername", cfun_net_getpeername), JANET_CORE_REG("net/peername", cfun_net_getpeername),
JANET_CORE_REG("net/localname", cfun_net_getsockname), JANET_CORE_REG("net/localname", cfun_net_getsockname),
JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack), JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack),
JANET_CORE_REG("net/setsockopt", cfun_net_setsockopt),
JANET_REG_END JANET_REG_END
}; };
janet_core_cfuns_ext(env, NULL, net_cfuns); janet_core_cfuns_ext(env, NULL, net_cfuns);

View File

@@ -118,18 +118,26 @@ JANET_CORE_FN(os_which,
"(os/which)", "(os/which)",
"Check the current operating system. Returns one of:\n\n" "Check the current operating system. Returns one of:\n\n"
"* :windows\n\n" "* :windows\n\n"
"* :mingw\n\n"
"* :cygwin\n\n"
"* :macos\n\n" "* :macos\n\n"
"* :web - Web assembly (emscripten)\n\n" "* :web - Web assembly (emscripten)\n\n"
"* :linux\n\n" "* :linux\n\n"
"* :freebsd\n\n" "* :freebsd\n\n"
"* :openbsd\n\n" "* :openbsd\n\n"
"* :netbsd\n\n" "* :netbsd\n\n"
"* :dragonfly\n\n"
"* :bsd\n\n"
"* :posix - A POSIX compatible system (default)\n\n" "* :posix - A POSIX compatible system (default)\n\n"
"May also return a custom keyword specified at build time.") { "May also return a custom keyword specified at build time.") {
janet_fixarity(argc, 0); janet_fixarity(argc, 0);
(void) argv; (void) argv;
#if defined(JANET_OS_NAME) #if defined(JANET_OS_NAME)
return janet_ckeywordv(janet_stringify(JANET_OS_NAME)); return janet_ckeywordv(janet_stringify(JANET_OS_NAME));
#elif defined(JANET_MINGW)
return janet_ckeywordv("mingw");
#elif defined(JANET_CYGWIN)
return janet_ckeywordv("cygwin");
#elif defined(JANET_WINDOWS) #elif defined(JANET_WINDOWS)
return janet_ckeywordv("windows"); return janet_ckeywordv("windows");
#elif defined(JANET_APPLE) #elif defined(JANET_APPLE)
@@ -144,6 +152,8 @@ JANET_CORE_FN(os_which,
return janet_ckeywordv("netbsd"); return janet_ckeywordv("netbsd");
#elif defined(__OpenBSD__) #elif defined(__OpenBSD__)
return janet_ckeywordv("openbsd"); return janet_ckeywordv("openbsd");
#elif defined(__DragonFly__)
return janet_ckeywordv("dragonfly");
#elif defined(JANET_BSD) #elif defined(JANET_BSD)
return janet_ckeywordv("bsd"); return janet_ckeywordv("bsd");
#else #else
@@ -159,6 +169,8 @@ JANET_CORE_FN(os_arch,
"* :x64\n\n" "* :x64\n\n"
"* :arm\n\n" "* :arm\n\n"
"* :aarch64\n\n" "* :aarch64\n\n"
"* :riscv32\n\n"
"* :riscv64\n\n"
"* :sparc\n\n" "* :sparc\n\n"
"* :wasm\n\n" "* :wasm\n\n"
"* :unknown\n") { "* :unknown\n") {
@@ -177,6 +189,10 @@ JANET_CORE_FN(os_arch,
return janet_ckeywordv("aarch64"); return janet_ckeywordv("aarch64");
#elif defined(_M_ARM) || defined(__arm__) #elif defined(_M_ARM) || defined(__arm__)
return janet_ckeywordv("arm"); return janet_ckeywordv("arm");
#elif (defined(__riscv) && (__riscv_xlen == 64))
return janet_ckeywordv("riscv64");
#elif (defined(__riscv) && (__riscv_xlen == 32))
return janet_ckeywordv("riscv32");
#elif (defined(__sparc__)) #elif (defined(__sparc__))
return janet_ckeywordv("sparc"); return janet_ckeywordv("sparc");
#elif (defined(__ppc__)) #elif (defined(__ppc__))
@@ -188,6 +204,27 @@ JANET_CORE_FN(os_arch,
#endif #endif
} }
/* Detect the compiler used to build the interpreter */
JANET_CORE_FN(os_compiler,
"(os/compiler)",
"Get the compiler used to compile the interpreter. Returns one of:\n\n"
"* :gcc\n\n"
"* :clang\n\n"
"* :msvc\n\n"
"* :unknown\n\n") {
janet_fixarity(argc, 0);
(void) argv;
#if defined(_MSC_VER)
return janet_ckeywordv("msvc");
#elif defined(__clang__)
return janet_ckeywordv("clang");
#elif defined(__GNUC__)
return janet_ckeywordv("gcc");
#else
return janet_ckeywordv("unknown");
#endif
}
#undef janet_stringify1 #undef janet_stringify1
#undef janet_stringify #undef janet_stringify
@@ -445,7 +482,9 @@ typedef struct {
static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) { static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) {
JanetProc *proc = (JanetProc *) args.argp; JanetProc *proc = (JanetProc *) args.argp;
WaitForSingleObject(proc->pHandle, INFINITE); WaitForSingleObject(proc->pHandle, INFINITE);
GetExitCodeProcess(proc->pHandle, &args.tag); DWORD exitcode = 0;
GetExitCodeProcess(proc->pHandle, &exitcode);
args.tag = (int32_t) exitcode;
return args; return args;
} }
@@ -585,12 +624,99 @@ JANET_CORE_FN(os_proc_wait,
#endif #endif
} }
struct keyword_signal {
const char *keyword;
int signal;
};
#ifndef JANET_WINDOWS
static const struct keyword_signal signal_keywords[] = {
#ifdef SIGKILL
{"kill", SIGKILL},
#endif
{"int", SIGINT},
{"abrt", SIGABRT},
{"fpe", SIGFPE},
{"ill", SIGILL},
{"segv", SIGSEGV},
#ifdef SIGTERM
{"term", SIGTERM},
#endif
#ifdef SIGARLM
{"alrm", SIGALRM},
#endif
#ifdef SIGHUP
{"hup", SIGHUP},
#endif
#ifdef SIGPIPE
{"pipe", SIGPIPE},
#endif
#ifdef SIGQUIT
{"quit", SIGQUIT},
#endif
#ifdef SIGUSR1
{"usr1", SIGUSR1},
#endif
#ifdef SIGUSR2
{"usr2", SIGUSR2},
#endif
#ifdef SIGCHLD
{"chld", SIGCHLD},
#endif
#ifdef SIGCONT
{"cont", SIGCONT},
#endif
#ifdef SIGSTOP
{"stop", SIGSTOP},
#endif
#ifdef SIGTSTP
{"tstp", SIGTSTP},
#endif
#ifdef SIGTTIN
{"ttin", SIGTTIN},
#endif
#ifdef SIGTTOU
{"ttou", SIGTTOU},
#endif
#ifdef SIGBUS
{"bus", SIGBUS},
#endif
#ifdef SIGPOLL
{"poll", SIGPOLL},
#endif
#ifdef SIGPROF
{"prof", SIGPROF},
#endif
#ifdef SIGSYS
{"sys", SIGSYS},
#endif
#ifdef SIGTRAP
{"trap", SIGTRAP},
#endif
#ifdef SIGURG
{"urg", SIGURG},
#endif
#ifdef SIGVTALRM
{"vtlarm", SIGVTALRM},
#endif
#ifdef SIGXCPU
{"xcpu", SIGXCPU},
#endif
#ifdef SIGXFSZ
{"xfsz", SIGXFSZ},
#endif
{NULL, 0},
};
#endif
JANET_CORE_FN(os_proc_kill, JANET_CORE_FN(os_proc_kill,
"(os/proc-kill proc &opt wait)", "(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 `wait` is truthy, will wait for the process to finish and "
"returns the exit code. Otherwise, returns `proc`.") { "returns the exit code. Otherwise, returns `proc`. If signal is specified send it instead."
janet_arity(argc, 1, 2); "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);
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) {
janet_panicf("cannot kill process that has already finished"); janet_panicf("cannot kill process that has already finished");
@@ -604,7 +730,22 @@ JANET_CORE_FN(os_proc_kill,
CloseHandle(proc->pHandle); CloseHandle(proc->pHandle);
CloseHandle(proc->tHandle); CloseHandle(proc->tHandle);
#else #else
int status = kill(proc->pid, SIGKILL); int signal = -1;
if (argc == 3) {
JanetKeyword signal_kw = janet_getkeyword(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);
if (status) { if (status) {
janet_panic(strerror(errno)); janet_panic(strerror(errno));
} }
@@ -843,6 +984,7 @@ 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) { static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
/* Get flags */ /* Get flags */
@@ -902,9 +1044,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
janet_panic("failed to create pipes"); janet_panic("failed to create pipes");
} }
/* Result */
int status = 0;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
HANDLE pHandle, tHandle; HANDLE pHandle, tHandle;
@@ -983,6 +1122,9 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
#else #else
/* Result */
int status = 0;
const char **child_argv = janet_smalloc(sizeof(char *) * ((size_t) exargs.len + 1)); const char **child_argv = janet_smalloc(sizeof(char *) * ((size_t) exargs.len + 1));
for (int32_t i = 0; i < exargs.len; i++) for (int32_t i = 0; i < exargs.len; i++)
child_argv[i] = janet_getcstring(exargs.items, i); child_argv[i] = janet_getcstring(exargs.items, i);
@@ -1003,21 +1145,21 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
if (pipe_in != JANET_HANDLE_NONE) { if (pipe_in != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_in, 0); posix_spawn_file_actions_adddup2(&actions, pipe_in, 0);
posix_spawn_file_actions_addclose(&actions, pipe_in); posix_spawn_file_actions_addclose(&actions, pipe_in);
} else if (new_in != JANET_HANDLE_NONE) { } else if (new_in != JANET_HANDLE_NONE && new_in != 0) {
posix_spawn_file_actions_adddup2(&actions, new_in, 0); posix_spawn_file_actions_adddup2(&actions, new_in, 0);
posix_spawn_file_actions_addclose(&actions, new_in); posix_spawn_file_actions_addclose(&actions, new_in);
} }
if (pipe_out != JANET_HANDLE_NONE) { if (pipe_out != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_out, 1); posix_spawn_file_actions_adddup2(&actions, pipe_out, 1);
posix_spawn_file_actions_addclose(&actions, pipe_out); posix_spawn_file_actions_addclose(&actions, pipe_out);
} else if (new_out != JANET_HANDLE_NONE) { } else if (new_out != JANET_HANDLE_NONE && new_out != 1) {
posix_spawn_file_actions_adddup2(&actions, new_out, 1); posix_spawn_file_actions_adddup2(&actions, new_out, 1);
posix_spawn_file_actions_addclose(&actions, new_out); posix_spawn_file_actions_addclose(&actions, new_out);
} }
if (pipe_err != JANET_HANDLE_NONE) { if (pipe_err != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_err, 2); posix_spawn_file_actions_adddup2(&actions, pipe_err, 2);
posix_spawn_file_actions_addclose(&actions, pipe_err); posix_spawn_file_actions_addclose(&actions, pipe_err);
} else if (new_err != JANET_HANDLE_NONE) { } else if (new_err != JANET_HANDLE_NONE && new_err != 2) {
posix_spawn_file_actions_adddup2(&actions, new_err, 2); posix_spawn_file_actions_adddup2(&actions, new_err, 2);
posix_spawn_file_actions_addclose(&actions, new_err); posix_spawn_file_actions_addclose(&actions, new_err);
} }
@@ -1045,7 +1187,8 @@ 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) {
janet_panicf("%p: %s", argv[0], strerror(errno)); /* correct for macos bug where errno is not set */
janet_panicf("%p: %s", argv[0], strerror(errno ? errno : ENOENT));
} }
#endif #endif
@@ -1136,6 +1279,7 @@ static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) {
JANET_CORE_FN(os_shell, JANET_CORE_FN(os_shell,
"(os/shell str)", "(os/shell str)",
"Pass a command string str directly to the system shell.") { "Pass a command string str directly to the system shell.") {
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
janet_arity(argc, 0, 1); janet_arity(argc, 0, 1);
const char *cmd = argc const char *cmd = argc
? janet_getcstring(argv, 0) ? janet_getcstring(argv, 0)
@@ -1155,6 +1299,7 @@ JANET_CORE_FN(os_shell,
JANET_CORE_FN(os_environ, JANET_CORE_FN(os_environ,
"(os/environ)", "(os/environ)",
"Get a copy of the OS environment table.") { "Get a copy of the OS environment table.") {
janet_sandbox_assert(JANET_SANDBOX_ENV);
(void) argv; (void) argv;
janet_fixarity(argc, 0); janet_fixarity(argc, 0);
int32_t nenv = 0; int32_t nenv = 0;
@@ -1186,6 +1331,7 @@ JANET_CORE_FN(os_environ,
JANET_CORE_FN(os_getenv, JANET_CORE_FN(os_getenv,
"(os/getenv variable &opt dflt)", "(os/getenv variable &opt dflt)",
"Get the string value of an environment variable.") { "Get the string value of an environment variable.") {
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); const char *res = getenv(cstr);
@@ -1209,6 +1355,7 @@ JANET_CORE_FN(os_setenv,
#define SETENV(K,V) setenv(K, V, 1) #define SETENV(K,V) setenv(K, V, 1)
#define UNSETENV(K) unsetenv(K) #define UNSETENV(K) unsetenv(K)
#endif #endif
janet_sandbox_assert(JANET_SANDBOX_ENV);
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
const char *ks = janet_getcstring(argv, 0); const char *ks = janet_getcstring(argv, 0);
const char *vs = janet_optcstring(argv, argc, 1, NULL); const char *vs = janet_optcstring(argv, argc, 1, NULL);
@@ -1233,13 +1380,32 @@ JANET_CORE_FN(os_time,
} }
JANET_CORE_FN(os_clock, JANET_CORE_FN(os_clock,
"(os/clock)", "(os/clock &opt source)",
"Return the number of whole + fractional seconds since some fixed point in time. The clock " "Return the number of whole + fractional seconds of the requested clock source.\n\n"
"is guaranteed to be non-decreasing in real time.") { "The `source` argument selects the clock source to use, when not specified the default "
janet_fixarity(argc, 0); "is `:realtime`:\n"
(void) argv; "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous "
" jumps in the system time\n"
"- :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"
"- :cputime: Return the CPU time consumed by this process (i.e. all threads in the process)\n") {
janet_sandbox_assert(JANET_SANDBOX_HRTIME);
janet_arity(argc, 0, 1);
enum JanetTimeSource source = JANET_TIME_REALTIME;
if (argc == 1) {
JanetKeyword sourcestr = janet_getkeyword(argv, 0);
if (janet_cstrcmp(sourcestr, "realtime") == 0) {
source = JANET_TIME_REALTIME;
} else if (janet_cstrcmp(sourcestr, "monotonic") == 0) {
source = JANET_TIME_MONOTONIC;
} else if (janet_cstrcmp(sourcestr, "cputime") == 0) {
source = JANET_TIME_CPUTIME;
} else {
janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]);
}
}
struct timespec tv; struct timespec tv;
if (janet_gettime(&tv)) 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); double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
return janet_wrap_number(dtime); return janet_wrap_number(dtime);
} }
@@ -1305,6 +1471,40 @@ JANET_CORE_FN(os_cryptorand,
return janet_wrap_buffer(buffer); return janet_wrap_buffer(buffer);
} }
/* Helper function to get given or current time as local or UTC struct tm.
* - arg n+0: optional time_t to be converted, uses current time if not given
* - arg n+1: optional truthy to indicate the convnersion uses local time */
static struct tm *time_to_tm(const Janet *argv, int32_t argc, int32_t n, struct tm *t_infos) {
time_t t;
if (argc > n && !janet_checktype(argv[n], JANET_NIL)) {
int64_t integer = janet_getinteger64(argv, n);
t = (time_t) integer;
} else {
time(&t);
}
struct tm *t_info = NULL;
if (argc > n + 1 && janet_truthy(argv[n + 1])) {
/* local time */
#ifdef JANET_WINDOWS
_tzset();
localtime_s(t_infos, &t);
t_info = t_infos;
#else
tzset();
t_info = localtime_r(&t, t_infos);
#endif
} else {
/* utc time */
#ifdef JANET_WINDOWS
gmtime_s(t_infos, &t);
t_info = t_infos;
#else
t_info = gmtime_r(&t, t_infos);
#endif
}
return t_info;
}
JANET_CORE_FN(os_date, JANET_CORE_FN(os_date,
"(os/date &opt time local)", "(os/date &opt time local)",
"Returns the given time as a date struct, or the current time if `time` is not given. " "Returns the given time as a date struct, or the current time if `time` is not given. "
@@ -1322,34 +1522,8 @@ JANET_CORE_FN(os_date,
"* :dst - if Day Light Savings is in effect") { "* :dst - if Day Light Savings is in effect") {
janet_arity(argc, 0, 2); janet_arity(argc, 0, 2);
(void) argv; (void) argv;
time_t t;
struct tm t_infos; struct tm t_infos;
struct tm *t_info = NULL; struct tm *t_info = time_to_tm(argv, argc, 0, &t_infos);
if (argc && !janet_checktype(argv[0], JANET_NIL)) {
int64_t integer = janet_getinteger64(argv, 0);
t = (time_t) integer;
} else {
time(&t);
}
if (argc >= 2 && janet_truthy(argv[1])) {
/* local time */
#ifdef JANET_WINDOWS
_tzset();
localtime_s(&t_infos, &t);
t_info = &t_infos;
#else
tzset();
t_info = localtime_r(&t, &t_infos);
#endif
} else {
/* utc time */
#ifdef JANET_WINDOWS
gmtime_s(&t_infos, &t);
t_info = &t_infos;
#else
t_info = gmtime_r(&t, &t_infos);
#endif
}
JanetKV *st = janet_struct_begin(9); JanetKV *st = janet_struct_begin(9);
janet_struct_put(st, janet_ckeywordv("seconds"), janet_wrap_number(t_info->tm_sec)); janet_struct_put(st, janet_ckeywordv("seconds"), janet_wrap_number(t_info->tm_sec));
janet_struct_put(st, janet_ckeywordv("minutes"), janet_wrap_number(t_info->tm_min)); janet_struct_put(st, janet_ckeywordv("minutes"), janet_wrap_number(t_info->tm_min));
@@ -1363,6 +1537,34 @@ JANET_CORE_FN(os_date,
return janet_wrap_struct(janet_struct_end(st)); return janet_wrap_struct(janet_struct_end(st));
} }
#define SIZETIMEFMT 250
JANET_CORE_FN(os_strftime,
"(os/strftime fmt &opt time local)",
"Format the given time as a string, or the current time if `time` is not given. "
"The time is formatted according to the same rules as the ISO C89 function strftime(). "
"The time is formatted in UTC unless `local` is truthy, in which case the date is formatted for "
"the local timezone.") {
janet_arity(argc, 1, 3);
const char *fmt = janet_getcstring(argv, 0);
/* ANSI X3.159-1989, section 4.12.3.5 "The strftime function" */
static const char *valid = "aAbBcdHIjmMpSUwWxXyYZ%";
const char *p = fmt;
while (*p) {
if (*p++ == '%') {
if (!strchr(valid, *p)) {
janet_panicf("invalid conversion specifier '%%%c'", *p);
}
p++;
}
}
struct tm t_infos;
struct tm *t_info = time_to_tm(argv, argc, 1, &t_infos);
char buf[SIZETIMEFMT];
(void)strftime(buf, SIZETIMEFMT, fmt, t_info);
return janet_cstringv(buf);
}
static int entry_getdst(Janet env_entry) { static int entry_getdst(Janet env_entry) {
Janet v; Janet v;
if (janet_checktype(env_entry, JANET_TABLE)) { if (janet_checktype(env_entry, JANET_TABLE)) {
@@ -1477,6 +1679,7 @@ JANET_CORE_FN(os_link,
"Iff symlink is truthy, creates a symlink. " "Iff symlink is truthy, creates a symlink. "
"Iff symlink is falsey or not provided, " "Iff symlink is falsey or not provided, "
"creates a hard link. Does not work on Windows.") { "creates a hard link. Does not work on Windows.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
(void) argc; (void) argc;
@@ -1495,6 +1698,7 @@ JANET_CORE_FN(os_link,
JANET_CORE_FN(os_symlink, JANET_CORE_FN(os_symlink,
"(os/symlink oldpath newpath)", "(os/symlink oldpath newpath)",
"Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`.") { "Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
(void) argc; (void) argc;
@@ -1517,6 +1721,7 @@ JANET_CORE_FN(os_mkdir,
"Create a new directory. The path will be relative to the current directory if relative, otherwise " "Create a new directory. The path will be relative to the current directory if relative, otherwise "
"it will be an absolute path. Returns true if the directory was created, false if the directory already exists, and " "it will be an absolute path. Returns true if the directory was created, false if the directory already exists, and "
"errors otherwise.") { "errors otherwise.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1532,6 +1737,7 @@ JANET_CORE_FN(os_mkdir,
JANET_CORE_FN(os_rmdir, JANET_CORE_FN(os_rmdir,
"(os/rmdir path)", "(os/rmdir path)",
"Delete a directory. The directory must be empty to succeed.") { "Delete a directory. The directory must be empty to succeed.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1546,6 +1752,7 @@ JANET_CORE_FN(os_rmdir,
JANET_CORE_FN(os_cd, JANET_CORE_FN(os_cd,
"(os/cd path)", "(os/cd path)",
"Change current directory to path. Returns nil on success, errors on failure.") { "Change current directory to path. Returns nil on success, errors on failure.") {
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1561,6 +1768,7 @@ JANET_CORE_FN(os_touch,
"(os/touch path &opt actime modtime)", "(os/touch path &opt actime modtime)",
"Update the access time and modification times for a file. By default, sets " "Update the access time and modification times for a file. By default, sets "
"times to the current time.") { "times to the current time.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
struct utimbuf timebuf, *bufp; struct utimbuf timebuf, *bufp;
@@ -1769,9 +1977,11 @@ static Janet os_stat_changed(jstat_t *st) {
} }
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
static Janet os_stat_blocks(jstat_t *st) { static Janet os_stat_blocks(jstat_t *st) {
(void) st;
return janet_wrap_number(0); return janet_wrap_number(0);
} }
static Janet os_stat_blocksize(jstat_t *st) { static Janet os_stat_blocksize(jstat_t *st) {
(void) st;
return janet_wrap_number(0); return janet_wrap_number(0);
} }
#else #else
@@ -1808,6 +2018,7 @@ static const struct OsStatGetter os_stat_getters[] = {
}; };
static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) { static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) {
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
JanetTable *tab = NULL; JanetTable *tab = NULL;
@@ -1859,7 +2070,7 @@ static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) {
JANET_CORE_FN(os_stat, JANET_CORE_FN(os_stat,
"(os/stat path &opt tab|key)", "(os/stat path &opt tab|key)",
"Gets information about a file or directory. Returns a table if the second argument is a keyword, returns " "Gets information about a file or directory. Returns a table if the second argument is a keyword, returns "
" only that information from stat. If the file or directory does not exist, returns nil. The keys are:\n\n" "only that information from stat. If the file or directory does not exist, returns nil. The keys are:\n\n"
"* :dev - the device that the file is on\n\n" "* :dev - the device that the file is on\n\n"
"* :mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other\n\n" "* :mode - the type of file, one of :file, :directory, :block, :character, :fifo, :socket, :link, or :other\n\n"
"* :int-permissions - A Unix permission integer like 8r744\n\n" "* :int-permissions - A Unix permission integer like 8r744\n\n"
@@ -1889,6 +2100,7 @@ JANET_CORE_FN(os_chmod,
"`os/perm-string`, or an integer as returned by `os/perm-int`. " "`os/perm-string`, or an integer as returned by `os/perm-int`. "
"When `mode` is an integer, it is interpreted as a Unix permission value, best specified in octal, like " "When `mode` is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
"8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") { "8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
const char *path = janet_getcstring(argv, 0); const char *path = janet_getcstring(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1904,6 +2116,7 @@ JANET_CORE_FN(os_chmod,
JANET_CORE_FN(os_umask, JANET_CORE_FN(os_umask,
"(os/umask mask)", "(os/umask mask)",
"Set a new umask, returns the old umask.") { "Set a new umask, returns the old umask.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
int mask = (int) os_getmode(argv, 0); int mask = (int) os_getmode(argv, 0);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@@ -1919,6 +2132,7 @@ JANET_CORE_FN(os_dir,
"(os/dir dir &opt array)", "(os/dir dir &opt array)",
"Iterate over files and subdirectories in a directory. Returns an array of paths parts, " "Iterate over files and subdirectories in a directory. Returns an array of paths parts, "
"with only the file name or directory name and no prefix.") { "with only the file name or directory name and no prefix.") {
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
const char *dir = janet_getcstring(argv, 0); const char *dir = janet_getcstring(argv, 0);
JanetArray *paths = (argc == 2) ? janet_getarray(argv, 1) : janet_array(0); JanetArray *paths = (argc == 2) ? janet_getarray(argv, 1) : janet_array(0);
@@ -1956,6 +2170,7 @@ JANET_CORE_FN(os_dir,
JANET_CORE_FN(os_rename, JANET_CORE_FN(os_rename,
"(os/rename oldname newname)", "(os/rename oldname newname)",
"Rename a file on disk to a new path. Returns nil.") { "Rename a file on disk to a new path. Returns nil.") {
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
janet_fixarity(argc, 2); janet_fixarity(argc, 2);
const char *src = janet_getcstring(argv, 0); const char *src = janet_getcstring(argv, 0);
const char *dest = janet_getcstring(argv, 1); const char *dest = janet_getcstring(argv, 1);
@@ -1969,7 +2184,8 @@ JANET_CORE_FN(os_rename,
JANET_CORE_FN(os_realpath, JANET_CORE_FN(os_realpath,
"(os/realpath path)", "(os/realpath path)",
"Get the absolute path for a given path, following ../, ./, and symlinks. " "Get the absolute path for a given path, following ../, ./, and symlinks. "
"Returns an absolute path as a string. Will raise an error on Windows.") { "Returns an absolute path as a string.") {
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
const char *src = janet_getcstring(argv, 0); const char *src = janet_getcstring(argv, 0);
#ifdef JANET_NO_REALPATH #ifdef JANET_NO_REALPATH
@@ -2047,6 +2263,7 @@ JANET_CORE_FN(os_open,
uint32_t stream_flags = 0; uint32_t stream_flags = 0;
JanetHandle fd; JanetHandle fd;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
(void) mode;
DWORD desiredAccess = 0; DWORD desiredAccess = 0;
DWORD shareMode = 0; DWORD shareMode = 0;
DWORD creationDisp = 0; DWORD creationDisp = 0;
@@ -2063,19 +2280,23 @@ JANET_CORE_FN(os_open,
case 'r': case 'r':
desiredAccess |= GENERIC_READ; desiredAccess |= GENERIC_READ;
stream_flags |= JANET_STREAM_READABLE; stream_flags |= JANET_STREAM_READABLE;
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
break; break;
case 'w': case 'w':
desiredAccess |= GENERIC_WRITE; desiredAccess |= GENERIC_WRITE;
stream_flags |= JANET_STREAM_WRITABLE; stream_flags |= JANET_STREAM_WRITABLE;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
case 'c': case 'c':
creatUnix |= OCREAT; creatUnix |= OCREAT;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
case 'e': case 'e':
creatUnix |= OEXCL; creatUnix |= OEXCL;
break; break;
case 't': case 't':
creatUnix |= OTRUNC; creatUnix |= OTRUNC;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
/* Windows only flags */ /* Windows only flags */
case 'D': case 'D':
@@ -2145,19 +2366,23 @@ JANET_CORE_FN(os_open,
case 'r': case 'r':
read_flag = 1; read_flag = 1;
stream_flags |= JANET_STREAM_READABLE; stream_flags |= JANET_STREAM_READABLE;
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
break; break;
case 'w': case 'w':
write_flag = 1; write_flag = 1;
stream_flags |= JANET_STREAM_WRITABLE; stream_flags |= JANET_STREAM_WRITABLE;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
case 'c': case 'c':
open_flags |= O_CREAT; open_flags |= O_CREAT;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
case 'e': case 'e':
open_flags |= O_EXCL; open_flags |= O_EXCL;
break; break;
case 't': case 't':
open_flags |= O_TRUNC; open_flags |= O_TRUNC;
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
break; break;
/* posix only */ /* posix only */
case 'x': case 'x':
@@ -2230,49 +2455,68 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/exit", os_exit), JANET_CORE_REG("os/exit", os_exit),
JANET_CORE_REG("os/which", os_which), JANET_CORE_REG("os/which", os_which),
JANET_CORE_REG("os/arch", os_arch), JANET_CORE_REG("os/arch", os_arch),
JANET_CORE_REG("os/compiler", os_compiler),
#ifndef JANET_REDUCED_OS #ifndef JANET_REDUCED_OS
/* misc (un-sandboxed) */
JANET_CORE_REG("os/cpu-count", os_cpu_count),
JANET_CORE_REG("os/cwd", os_cwd),
JANET_CORE_REG("os/cryptorand", os_cryptorand),
JANET_CORE_REG("os/perm-string", os_permission_string),
JANET_CORE_REG("os/perm-int", os_permission_int),
JANET_CORE_REG("os/mktime", os_mktime),
JANET_CORE_REG("os/time", os_time), /* not high resolution */
JANET_CORE_REG("os/date", os_date), /* not high resolution */
JANET_CORE_REG("os/strftime", os_strftime),
JANET_CORE_REG("os/sleep", os_sleep),
/* env functions */
JANET_CORE_REG("os/environ", os_environ), JANET_CORE_REG("os/environ", os_environ),
JANET_CORE_REG("os/getenv", os_getenv), JANET_CORE_REG("os/getenv", os_getenv),
JANET_CORE_REG("os/setenv", os_setenv),
/* fs read */
JANET_CORE_REG("os/dir", os_dir), JANET_CORE_REG("os/dir", os_dir),
JANET_CORE_REG("os/stat", os_stat), JANET_CORE_REG("os/stat", os_stat),
JANET_CORE_REG("os/lstat", os_lstat), JANET_CORE_REG("os/lstat", os_lstat),
JANET_CORE_REG("os/chmod", os_chmod), JANET_CORE_REG("os/chmod", os_chmod),
JANET_CORE_REG("os/touch", os_touch), JANET_CORE_REG("os/touch", os_touch),
JANET_CORE_REG("os/realpath", os_realpath),
JANET_CORE_REG("os/cd", os_cd), JANET_CORE_REG("os/cd", os_cd),
JANET_CORE_REG("os/cpu-count", os_cpu_count),
#ifndef JANET_NO_UMASK #ifndef JANET_NO_UMASK
JANET_CORE_REG("os/umask", os_umask), JANET_CORE_REG("os/umask", os_umask),
#endif #endif
#ifndef JANET_NO_SYMLINKS
JANET_CORE_REG("os/readlink", os_readlink),
#endif
/* fs write */
JANET_CORE_REG("os/mkdir", os_mkdir), JANET_CORE_REG("os/mkdir", os_mkdir),
JANET_CORE_REG("os/rmdir", os_rmdir), JANET_CORE_REG("os/rmdir", os_rmdir),
JANET_CORE_REG("os/rm", os_remove), JANET_CORE_REG("os/rm", os_remove),
JANET_CORE_REG("os/link", os_link), JANET_CORE_REG("os/link", os_link),
JANET_CORE_REG("os/rename", os_rename),
#ifndef JANET_NO_SYMLINKS #ifndef JANET_NO_SYMLINKS
JANET_CORE_REG("os/symlink", os_symlink), JANET_CORE_REG("os/symlink", os_symlink),
JANET_CORE_REG("os/readlink", os_readlink),
#endif #endif
/* processes */
#ifndef JANET_NO_PROCESSES #ifndef JANET_NO_PROCESSES
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),
/* 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) */
JANET_CORE_REG("os/proc-wait", os_proc_wait), JANET_CORE_REG("os/proc-wait", os_proc_wait),
JANET_CORE_REG("os/proc-kill", os_proc_kill), JANET_CORE_REG("os/proc-kill", os_proc_kill),
JANET_CORE_REG("os/proc-close", os_proc_close), JANET_CORE_REG("os/proc-close", os_proc_close),
#endif #endif
JANET_CORE_REG("os/setenv", os_setenv),
JANET_CORE_REG("os/time", os_time), /* high resolution timers */
JANET_CORE_REG("os/mktime", os_mktime),
JANET_CORE_REG("os/clock", os_clock), JANET_CORE_REG("os/clock", os_clock),
JANET_CORE_REG("os/sleep", os_sleep),
JANET_CORE_REG("os/cwd", os_cwd),
JANET_CORE_REG("os/cryptorand", os_cryptorand),
JANET_CORE_REG("os/date", os_date),
JANET_CORE_REG("os/rename", os_rename),
JANET_CORE_REG("os/realpath", os_realpath),
JANET_CORE_REG("os/perm-string", os_permission_string),
JANET_CORE_REG("os/perm-int", os_permission_int),
#ifdef JANET_EV #ifdef JANET_EV
JANET_CORE_REG("os/open", os_open), 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),
#endif #endif
#endif #endif

View File

@@ -1194,7 +1194,8 @@ static Janet parser_state_delimiters(const JanetParser *_p) {
} }
} }
} }
str = janet_string(p->buf + oldcount, (int32_t)(p->bufcount - oldcount)); /* avoid ptr arithmetic on NULL */
str = janet_string(oldcount ? p->buf + oldcount : p->buf, (int32_t)(p->bufcount - oldcount));
p->bufcount = oldcount; p->bufcount = oldcount;
return janet_wrap_string(str); return janet_wrap_string(str);
} }
@@ -1205,10 +1206,11 @@ static Janet parser_state_frames(const JanetParser *p) {
states->count = count; states->count = count;
uint8_t *buf = p->buf; uint8_t *buf = p->buf;
/* Iterate arg stack backwards */ /* Iterate arg stack backwards */
Janet *args = p->args + p->argcount; Janet *args = p->argcount ? p->args + p->argcount : p->args; /* avoid ptr arithmetic on NULL */
for (int32_t i = count - 1; i >= 0; --i) { for (int32_t i = count - 1; i >= 0; --i) {
JanetParseState *s = p->states + i; JanetParseState *s = p->states + i;
if (s->flags & PFLAG_CONTAINER) { /* avoid ptr arithmetic on args if NULL */
if ((s->flags & PFLAG_CONTAINER) && s->argn) {
args -= s->argn; args -= s->argn;
} }
states->data[i] = janet_wrap_parse_state(s, args, buf, (uint32_t) p->bufcount); states->data[i] = janet_wrap_parse_state(s, args, buf, (uint32_t) p->bufcount);

View File

@@ -1034,7 +1034,7 @@ static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
emit_3(r, RULE_CAPTURE_NUM, rule, base, tag); emit_3(r, RULE_CAPTURE_NUM, rule, base, tag);
return; return;
error: error:
peg_panicf(b, "expected integer between 2 and 36, got %v", argv[2]); peg_panicf(b, "expected integer between 2 and 36, got %v", argv[1]);
} }
static void spec_reference(Builder *b, int32_t argc, const Janet *argv) { static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
@@ -1100,7 +1100,7 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
Janet fun = argv[1]; Janet fun = argv[1];
if (!janet_checktype(fun, JANET_FUNCTION) && if (!janet_checktype(fun, JANET_FUNCTION) &&
!janet_checktype(fun, JANET_CFUNCTION)) { !janet_checktype(fun, JANET_CFUNCTION)) {
peg_panicf(b, "expected function|cfunction, got %v", fun); peg_panicf(b, "expected function or cfunction, got %v", fun);
} }
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0; uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
uint32_t cindex = emit_constant(b, fun); uint32_t cindex = emit_constant(b, fun);
@@ -1637,7 +1637,7 @@ typedef struct {
JanetPeg *peg; JanetPeg *peg;
PegState s; PegState s;
JanetByteView bytes; JanetByteView bytes;
JanetByteView repl; Janet subst;
int32_t start; int32_t start;
} PegCall; } PegCall;
@@ -1653,7 +1653,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
ret.peg = compile_peg(argv[0]); ret.peg = compile_peg(argv[0]);
} }
if (get_replace) { if (get_replace) {
ret.repl = janet_getbytes(argv, 1); ret.subst = argv[1];
ret.bytes = janet_getbytes(argv, 2); ret.bytes = janet_getbytes(argv, 2);
} else { } else {
ret.bytes = janet_getbytes(argv, 1); ret.bytes = janet_getbytes(argv, 1);
@@ -1738,7 +1738,8 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
trail = i; trail = i;
} }
int32_t nexti = (int32_t)(result - c.bytes.bytes); int32_t nexti = (int32_t)(result - c.bytes.bytes);
janet_buffer_push_bytes(ret, c.repl.bytes, c.repl.len); JanetByteView subst = janet_text_substitution(&c.subst, c.bytes.bytes + i, nexti - i, c.s.captures);
janet_buffer_push_bytes(ret, subst.bytes, subst.len);
trail = nexti; trail = nexti;
if (nexti == i) nexti++; if (nexti == i) nexti++;
i = nexti; i = nexti;
@@ -1754,14 +1755,20 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
} }
JANET_CORE_FN(cfun_peg_replace_all, JANET_CORE_FN(cfun_peg_replace_all,
"(peg/replace-all peg repl text &opt start & args)", "(peg/replace-all peg subst text &opt start & args)",
"Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") { "Replace all matches of `peg` in `text` with `subst`, returning a new buffer. "
"The peg does not need to make captures to do replacement. "
"If `subst` is a function, it will be called with the "
"matching text followed by any captures.") {
return cfun_peg_replace_generic(argc, argv, 0); return cfun_peg_replace_generic(argc, argv, 0);
} }
JANET_CORE_FN(cfun_peg_replace, JANET_CORE_FN(cfun_peg_replace,
"(peg/replace peg repl text &opt start & args)", "(peg/replace peg repl text &opt start & args)",
"Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. " "Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
"The peg does not need to make captures to do replacement. "
"If `subst` is a function, it will be called with the "
"matching text followed by any captures. "
"If no matches are found, returns the input string in a new buffer.") { "If no matches are found, returns the input string in a new buffer.") {
return cfun_peg_replace_generic(argc, argv, 1); return cfun_peg_replace_generic(argc, argv, 1);
} }

View File

@@ -109,7 +109,7 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
pbuf.p = pointer; pbuf.p = pointer;
*c++ = '<'; *c++ = '<';
/* Maximum of 32 bytes for abstract type name */ /* Maximum of 32 bytes for abstract type name */
for (i = 0; title[i] && i < 32; ++i) for (i = 0; i < 32 && title[i]; ++i)
*c++ = ((uint8_t *)title) [i]; *c++ = ((uint8_t *)title) [i];
*c++ = ' '; *c++ = ' ';
*c++ = '0'; *c++ = '0';
@@ -637,7 +637,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
} }
} }
janet_sorted_keys(kvs, cap, S->keysort_buffer + ks_start); janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
S->keysort_start += len; S->keysort_start += len;
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) { if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
len = JANET_PRETTY_DICT_LIMIT; len = JANET_PRETTY_DICT_LIMIT;
@@ -736,7 +736,7 @@ static void pushtypes(JanetBuffer *buffer, int types) {
if (first) { if (first) {
first = 0; first = 0;
} else { } else {
janet_buffer_push_u8(buffer, '|'); janet_buffer_push_cstring(buffer, (types == 1) ? " or " : ", ");
} }
janet_buffer_push_cstring(buffer, janet_type_names[i]); janet_buffer_push_cstring(buffer, janet_type_names[i]);
} }
@@ -809,7 +809,8 @@ static const char *scanformat(
*(form++) = '%'; *(form++) = '%';
const char *p2 = strfrmt; const char *p2 = strfrmt;
while (p2 <= p) { while (p2 <= p) {
if (strchr(FMT_REPLACE_INTTYPES, *p2) != NULL) { char *loc = strchr(FMT_REPLACE_INTTYPES, *p2);
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); strcpy(form, mapping);
@@ -845,7 +846,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
} }
case 'd': case 'd':
case 'i': { case 'i': {
int64_t n = va_arg(args, long); int64_t n = va_arg(args, int);
nb = snprintf(item, MAX_ITEM, form, n); nb = snprintf(item, MAX_ITEM, form, n);
break; break;
} }
@@ -853,7 +854,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 long); uint64_t n = va_arg(args, unsigned int);
nb = snprintf(item, MAX_ITEM, form, n); nb = snprintf(item, MAX_ITEM, form, n);
break; break;
} }

View File

@@ -27,6 +27,8 @@
#include "util.h" #include "util.h"
#endif #endif
/* The JanetRegisterAllocator is really just a bitset. */
void janetc_regalloc_init(JanetcRegisterAllocator *ra) { void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
ra->chunks = NULL; ra->chunks = NULL;
ra->count = 0; ra->count = 0;
@@ -139,6 +141,14 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
ra->chunks[chunk] &= ~ithbit(bit); ra->chunks[chunk] &= ~ithbit(bit);
} }
/* Check if a register is set. */
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) {
int32_t chunk = reg >> 5;
int32_t bit = reg & 0x1F;
while (chunk >= ra->count) pushchunk(ra);
return !!(ra->chunks[chunk] & ithbit(bit));
}
/* Get a register that will fit in 8 bits (< 256). Do not call this /* Get a register that will fit in 8 bits (< 256). Do not call this
* twice with the same value of nth without calling janetc_regalloc_free * twice with the same value of nth without calling janetc_regalloc_free
* on the returned register before. */ * on the returned register before. */

View File

@@ -56,5 +56,6 @@ int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth
void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth); void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth);
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src); void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src);
void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg); void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg);
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg);
#endif #endif

View File

@@ -39,6 +39,10 @@ static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv)
static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) { static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) {
JanetSlot ret; JanetSlot ret;
if (!(opts.flags & JANET_FOPTS_ACCEPT_SPLICE)) {
janetc_cerror(opts.compiler, "splice can only be used in function parameters and data constructors, it has no effect here");
return janetc_cslot(janet_wrap_nil());
}
if (argn != 1) { if (argn != 1) {
janetc_cerror(opts.compiler, "expected 1 argument to splice"); janetc_cerror(opts.compiler, "expected 1 argument to splice");
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
@@ -75,7 +79,9 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
const uint8_t *head = janet_unwrap_symbol(tup[0]); const uint8_t *head = janet_unwrap_symbol(tup[0]);
if (!janet_cstrcmp(head, "unquote")) { if (!janet_cstrcmp(head, "unquote")) {
if (level == 0) { if (level == 0) {
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]); JanetFopts subopts = janetc_fopts_default(opts.compiler);
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
return janetc_value(subopts, tup[1]);
} else { } else {
level--; level--;
} }
@@ -203,8 +209,9 @@ static int destructure(JanetCompiler *c,
janetc_emit(c, JOP_JUMP); janetc_emit(c, JOP_JUMP);
int32_t label_loop_exit = janet_v_count(c->buffer); int32_t label_loop_exit = janet_v_count(c->buffer);
c->buffer[label_loop_cond_jump] |= (label_loop_exit - label_loop_cond_jump) << 16; /* avoid shifting negative numbers */
c->buffer[label_loop_loop] |= (label_loop_start - label_loop_loop) << 8; c->buffer[label_loop_cond_jump] |= (uint32_t)(label_loop_exit - label_loop_cond_jump) << 16;
c->buffer[label_loop_loop] |= (uint32_t)(label_loop_start - label_loop_loop) << 8;
janetc_freeslot(c, argi); janetc_freeslot(c, argi);
janetc_freeslot(c, arg); janetc_freeslot(c, arg);
@@ -257,7 +264,7 @@ static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) { static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
if (argn != 2) { if (argn != 2) {
janetc_cerror(opts.compiler, "expected 2 arguments"); janetc_cerror(opts.compiler, "expected 2 arguments to set");
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
} }
JanetFopts subopts = janetc_fopts_default(opts.compiler); JanetFopts subopts = janetc_fopts_default(opts.compiler);
@@ -328,11 +335,11 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
return tab; return tab;
} }
static JanetSlot dohead(JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) { static JanetSlot dohead(const char *kind, JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) {
JanetFopts subopts = janetc_fopts_default(c); JanetFopts subopts = janetc_fopts_default(c);
JanetSlot ret; JanetSlot ret;
if (argn < 2) { if (argn < 2) {
janetc_cerror(c, "expected at least 2 arguments"); janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
} }
*head = argv[0]; *head = argv[0];
@@ -347,7 +354,17 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) && int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
ret.index > 0 && ret.index > 0 &&
ret.envindex >= 0; ret.envindex >= 0;
if (!isUnnamedRegister) { /* optimization for `(def x my-def)` - don't emit a movn/movf instruction, we can just alias my-def */
/* TODO - implement optimization for `(def x my-var)` correctly as well w/ de-aliasing */
int canAlias = !(flags & JANET_SLOT_MUTABLE) &&
!(ret.flags & JANET_SLOT_MUTABLE) &&
(ret.flags & JANET_SLOT_NAMED) &&
(ret.index >= 0) &&
(ret.envindex == -1);
if (canAlias) {
ret.flags &= ~JANET_SLOT_MUTABLE;
isUnnamedRegister = 1; /* don't free slot after use - is an alias for another slot */
} else if (!isUnnamedRegister) {
/* Slot is not able to be named */ /* Slot is not able to be named */
JanetSlot localslot = janetc_farslot(c); JanetSlot localslot = janetc_farslot(c);
janetc_copy(c, localslot, ret); janetc_copy(c, localslot, ret);
@@ -397,7 +414,7 @@ static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
JanetCompiler *c = opts.compiler; JanetCompiler *c = opts.compiler;
Janet head; Janet head;
JanetTable *attr_table = handleattr(c, argn, argv); JanetTable *attr_table = handleattr(c, argn, argv);
JanetSlot ret = dohead(c, opts, &head, argn, argv); JanetSlot ret = dohead("var", c, opts, &head, argn, argv);
if (c->result.status == JANET_COMPILE_ERROR) if (c->result.status == JANET_COMPILE_ERROR)
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
destructure(c, argv[0], ret, varleaf, attr_table); destructure(c, argv[0], ret, varleaf, attr_table);
@@ -447,7 +464,7 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
Janet head; Janet head;
opts.flags &= ~JANET_FOPTS_HINT; opts.flags &= ~JANET_FOPTS_HINT;
JanetTable *attr_table = handleattr(c, argn, argv); JanetTable *attr_table = handleattr(c, argn, argv);
JanetSlot ret = dohead(c, opts, &head, argn, argv); JanetSlot ret = dohead("def", c, opts, &head, argn, argv);
if (c->result.status == JANET_COMPILE_ERROR) if (c->result.status == JANET_COMPILE_ERROR)
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
destructure(c, argv[0], ret, defleaf, attr_table); destructure(c, argv[0], ret, defleaf, attr_table);
@@ -487,6 +504,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
/* Get options */ /* Get options */
condopts = janetc_fopts_default(c); condopts = janetc_fopts_default(c);
bodyopts = opts; bodyopts = opts;
bodyopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
/* Set target for compilation */ /* Set target for compilation */
target = (drop || tail) target = (drop || tail)
@@ -563,6 +581,7 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) {
subopts.flags = JANET_FOPTS_DROP; subopts.flags = JANET_FOPTS_DROP;
} else { } else {
subopts = opts; subopts = opts;
subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
} }
ret = janetc_value(subopts, argv[i]); ret = janetc_value(subopts, argv[i]);
if (i != argn - 1) { if (i != argn - 1) {
@@ -586,6 +605,7 @@ static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv
subopts.flags = JANET_FOPTS_DROP; subopts.flags = JANET_FOPTS_DROP;
} else { } else {
subopts = opts; subopts = opts;
subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
} }
ret = janetc_value(subopts, argv[i]); ret = janetc_value(subopts, argv[i]);
if (i != argn - 1) { if (i != argn - 1) {
@@ -697,8 +717,8 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
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;
if (argn < 2) { if (argn < 1) {
janetc_cerror(c, "expected at least 2 arguments"); janetc_cerror(c, "expected at least 1 argument to while");
return janetc_cslot(janet_wrap_nil()); return janetc_cslot(janet_wrap_nil());
} }
@@ -958,12 +978,26 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
max_arity = (vararg || allow_extra) ? INT32_MAX : arity; max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
if (!seenopt) min_arity = arity; if (!seenopt) min_arity = arity;
/* Check for self ref */ /* Check for self ref (also avoid if arguments shadow own name) */
if (selfref) { if (selfref) {
JanetSlot slot = janetc_farslot(c); /* Check if the parameters shadow the function name. If so, don't
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION; * emit JOP_LOAD_SELF and add a binding since that most users
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1); * seem to expect that function parameters take precedence over the
janetc_nameslot(c, janet_unwrap_symbol(head), slot); * function name */
const uint8_t *sym = janet_unwrap_symbol(head);
int32_t len = janet_v_count(c->scope->syms);
int found = 0;
for (int32_t i = 0; i < len; i++) {
if (c->scope->syms[i].sym == sym) {
found = 1;
}
}
if (!found) {
JanetSlot slot = janetc_farslot(c);
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
janetc_nameslot(c, sym, slot);
}
} }
/* Compile function body */ /* Compile function body */

View File

@@ -23,6 +23,7 @@
#ifndef JANET_STATE_H_defined #ifndef JANET_STATE_H_defined
#define JANET_STATE_H_defined #define JANET_STATE_H_defined
#include <janet.h>
#include <stdint.h> #include <stdint.h>
#ifdef JANET_EV #ifdef JANET_EV
@@ -60,7 +61,7 @@ typedef struct {
int is_error; int is_error;
} JanetTimeout; } JanetTimeout;
/* Registry table for C functions - containts metadata that can /* Registry table for C functions - contains metadata that can
* be looked up by cfunction pointer. All strings here are pointing to * be looked up by cfunction pointer. All strings here are pointing to
* static memory not managed by Janet. */ * static memory not managed by Janet. */
typedef struct { typedef struct {
@@ -91,7 +92,7 @@ struct JanetVM {
int auto_suspend; int auto_suspend;
/* The current running fiber on the current thread. /* The current running fiber on the current thread.
* Set and unset by janet_run. */ * Set and unset by functions in vm.c */
JanetFiber *fiber; JanetFiber *fiber;
JanetFiber *root_fiber; JanetFiber *root_fiber;
@@ -107,7 +108,7 @@ struct JanetVM {
size_t registry_count; size_t registry_count;
int registry_dirty; int registry_dirty;
/* Registry for abstract abstract types that can be marshalled. /* Registry for abstract types that can be marshalled.
* We need this to look up the constructors when unmarshalling. */ * We need this to look up the constructors when unmarshalling. */
JanetTable *abstract_registry; JanetTable *abstract_registry;
@@ -135,6 +136,9 @@ struct JanetVM {
size_t scratch_cap; size_t scratch_cap;
size_t scratch_len; size_t scratch_len;
/* Sandbox flags */
uint32_t sandbox_flags;
/* Random number generator */ /* Random number generator */
JanetRNG rng; JanetRNG rng;
@@ -155,6 +159,7 @@ struct JanetVM {
size_t listener_cap; size_t listener_cap;
size_t extra_listeners; 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 */
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
void **iocp; void **iocp;
#elif defined(JANET_EV_EPOLL) #elif defined(JANET_EV_EPOLL)

View File

@@ -364,14 +364,13 @@ JANET_CORE_FN(cfun_string_findall,
struct replace_state { struct replace_state {
struct kmp_state kmp; struct kmp_state kmp;
const uint8_t *subst; Janet subst;
int32_t substlen;
}; };
static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) { static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
janet_arity(argc, 3, 4); janet_arity(argc, 3, 4);
JanetByteView pat = janet_getbytes(argv, 0); JanetByteView pat = janet_getbytes(argv, 0);
JanetByteView subst = janet_getbytes(argv, 1); Janet subst = argv[1];
JanetByteView text = janet_getbytes(argv, 2); JanetByteView text = janet_getbytes(argv, 2);
int32_t start = 0; int32_t start = 0;
if (argc == 4) { if (argc == 4) {
@@ -380,13 +379,14 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
} }
kmp_init(&s->kmp, text.bytes, text.len, pat.bytes, pat.len); kmp_init(&s->kmp, text.bytes, text.len, pat.bytes, pat.len);
s->kmp.i = start; s->kmp.i = start;
s->subst = subst.bytes; s->subst = subst;
s->substlen = subst.len;
} }
JANET_CORE_FN(cfun_string_replace, JANET_CORE_FN(cfun_string_replace,
"(string/replace patt subst str)", "(string/replace patt subst str)",
"Replace the first occurrence of `patt` with `subst` in the string `str`. " "Replace the first occurrence of `patt` with `subst` in the string `str`. "
"If `subst` is a function, it will be called with `patt` only if a match is found, "
"and should return the actual replacement text to use. "
"Will return the new string if `patt` is found, otherwise returns `str`.") { "Will return the new string if `patt` is found, otherwise returns `str`.") {
int32_t result; int32_t result;
struct replace_state s; struct replace_state s;
@@ -397,10 +397,11 @@ JANET_CORE_FN(cfun_string_replace,
kmp_deinit(&s.kmp); kmp_deinit(&s.kmp);
return janet_stringv(s.kmp.text, s.kmp.textlen); return janet_stringv(s.kmp.text, s.kmp.textlen);
} }
buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + s.substlen); JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + subst.len);
safe_memcpy(buf, s.kmp.text, result); safe_memcpy(buf, s.kmp.text, result);
safe_memcpy(buf + result, s.subst, s.substlen); safe_memcpy(buf + result, subst.bytes, subst.len);
safe_memcpy(buf + result + s.substlen, safe_memcpy(buf + result + subst.len,
s.kmp.text + result + s.kmp.patlen, s.kmp.text + result + s.kmp.patlen,
s.kmp.textlen - result - s.kmp.patlen); s.kmp.textlen - result - s.kmp.patlen);
kmp_deinit(&s.kmp); kmp_deinit(&s.kmp);
@@ -411,6 +412,8 @@ JANET_CORE_FN(cfun_string_replaceall,
"(string/replace-all patt subst str)", "(string/replace-all patt subst str)",
"Replace all instances of `patt` with `subst` in the string `str`. Overlapping " "Replace all instances of `patt` with `subst` in the string `str`. Overlapping "
"matches will not be counted, only the first match in such a span will be replaced. " "matches will not be counted, only the first match in such a span will be replaced. "
"If `subst` is a function, it will be called with `patt` once for each match, "
"and should return the actual replacement text to use. "
"Will return the new string if `patt` is found, otherwise returns `str`.") { "Will return the new string if `patt` is found, otherwise returns `str`.") {
int32_t result; int32_t result;
struct replace_state s; struct replace_state s;
@@ -419,8 +422,9 @@ JANET_CORE_FN(cfun_string_replaceall,
replacesetup(argc, argv, &s); replacesetup(argc, argv, &s);
janet_buffer_init(&b, s.kmp.textlen); janet_buffer_init(&b, s.kmp.textlen);
while ((result = kmp_next(&s.kmp)) >= 0) { while ((result = kmp_next(&s.kmp)) >= 0) {
JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
janet_buffer_push_bytes(&b, s.kmp.text + lastindex, result - lastindex); janet_buffer_push_bytes(&b, s.kmp.text + lastindex, result - lastindex);
janet_buffer_push_bytes(&b, s.subst, s.substlen); janet_buffer_push_bytes(&b, subst.bytes, subst.len);
lastindex = result + s.kmp.patlen; lastindex = result + s.kmp.patlen;
kmp_seti(&s.kmp, lastindex); kmp_seti(&s.kmp, lastindex);
} }
@@ -531,7 +535,30 @@ JANET_CORE_FN(cfun_string_join,
JANET_CORE_FN(cfun_string_format, JANET_CORE_FN(cfun_string_format,
"(string/format format & values)", "(string/format format & values)",
"Similar to C's `snprintf`, but specialized for operating with Janet values. Returns " "Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
"a new string.") { "a new string.\n\n"
"The following conversion specifiers are supported, where the upper case specifiers generate "
"upper case output:\n"
"- `c`: ASCII character.\n"
"- `d`, `i`: integer, formatted as a decimal number.\n"
"- `x`, `X`: integer, formatted as a hexadecimal number.\n"
"- `o`: integer, formatted as an octal number.\n"
"- `f`, `F`: floating point number, formatted as a decimal number.\n"
"- `e`, `E`: floating point number, formatted in scientific notation.\n"
"- `g`, `G`: floating point number, formatted in its shortest form.\n"
"- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
"- `s`: formatted as a string, precision indicates padding and maximum length.\n"
"- `t`: emit the type of the given value.\n"
"- `v`: format with (describe x)"
"- `V`: format with (string x)"
"- `j`: format to jdn (Janet data notation).\n"
"\n"
"The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
"variants generate colored output. These speficiers can take a precision "
"argument to specify the maximum nesting depth to print.\n"
"- `p`, `P`: pretty format, truncating if necessary\n"
"- `m`, `M`: pretty format without truncating.\n"
"- `q`, `Q`: pretty format on one line, truncating if necessary.\n"
"- `n`, `N`: pretty format on one line without truncation.\n") {
janet_arity(argc, 1, -1); janet_arity(argc, 1, -1);
JanetBuffer *buffer = janet_buffer(0); JanetBuffer *buffer = janet_buffer(0);
const char *strfrmt = (const char *) janet_getstring(argv, 0); const char *strfrmt = (const char *) janet_getstring(argv, 0);

View File

@@ -39,9 +39,11 @@
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#ifdef JANET_DYNAMIC_MODULES #ifdef JANET_DYNAMIC_MODULES
#include <psapi.h> #include <psapi.h>
#ifdef JANET_MSVC
#pragma comment (lib, "Psapi.lib") #pragma comment (lib, "Psapi.lib")
#endif #endif
#endif #endif
#endif
#ifdef JANET_APPLE #ifdef JANET_APPLE
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
@@ -90,8 +92,8 @@ const char *const janet_signal_names[14] = {
"user5", "user5",
"user6", "user6",
"user7", "user7",
"user8", "interrupt",
"user9" "await"
}; };
const char *const janet_status_names[16] = { const char *const janet_status_names[16] = {
@@ -107,8 +109,8 @@ const char *const janet_status_names[16] = {
"user5", "user5",
"user6", "user6",
"user7", "user7",
"user8", "interrupted",
"user9", "suspended",
"new", "new",
"alive" "alive"
}; };
@@ -116,6 +118,7 @@ const char *const janet_status_names[16] = {
#ifndef JANET_PRF #ifndef JANET_PRF
int32_t janet_string_calchash(const uint8_t *str, int32_t len) { int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
if (NULL == str) return 5381;
const uint8_t *end = str + len; const uint8_t *end = str + len;
uint32_t hash = 5381; uint32_t hash = 5381;
while (str < end) while (str < end)
@@ -660,6 +663,59 @@ JanetBinding janet_binding_from_entry(Janet entry) {
return binding; return binding;
} }
/* If the value at the given address can be coerced to a byte view,
return that byte view. If it can't, replace the value at the address
with the result of janet_to_string, and return a byte view over that
string. */
static JanetByteView memoize_byte_view(Janet *value) {
JanetByteView result;
if (!janet_bytes_view(*value, &result.bytes, &result.len)) {
JanetString str = janet_to_string(*value);
*value = janet_wrap_string(str);
result.bytes = str;
result.len = janet_string_length(str);
}
return result;
}
static JanetByteView to_byte_view(Janet value) {
JanetByteView result;
if (!janet_bytes_view(value, &result.bytes, &result.len)) {
JanetString str = janet_to_string(value);
result.bytes = str;
result.len = janet_string_length(str);
}
return result;
}
JanetByteView janet_text_substitution(
Janet *subst,
const uint8_t *bytes,
uint32_t len,
JanetArray *extra_argv) {
int32_t extra_argc = extra_argv == NULL ? 0 : extra_argv->count;
JanetType type = janet_type(*subst);
switch (type) {
case JANET_FUNCTION:
case JANET_CFUNCTION: {
int32_t argc = 1 + extra_argc;
Janet *argv = janet_tuple_begin(argc);
argv[0] = janet_stringv(bytes, len);
for (int32_t i = 0; i < extra_argc; i++) {
argv[i + 1] = extra_argv->data[i];
}
janet_tuple_end(argv);
if (type == JANET_FUNCTION) {
return to_byte_view(janet_call(janet_unwrap_function(*subst), argc, argv));
} else {
return to_byte_view(janet_unwrap_cfunction(*subst)(argc, argv));
}
}
default:
return memoize_byte_view(subst);
}
}
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) { JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
Janet entry = janet_table_get(env, janet_wrap_symbol(sym)); Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
return janet_binding_from_entry(entry); return janet_binding_from_entry(entry);
@@ -819,34 +875,73 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe
/* Clock shims for various platforms */ /* Clock shims for various platforms */
#ifdef JANET_GETTIME #ifdef JANET_GETTIME
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
int janet_gettime(struct timespec *spec) { #include <profileapi.h>
FILETIME ftime; int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
GetSystemTimeAsFileTime(&ftime); if (source == JANET_TIME_REALTIME) {
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); FILETIME ftime;
/* Windows epoch is January 1, 1601 apparently */ GetSystemTimeAsFileTime(&ftime);
wintime -= 116444736000000000LL; int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
spec->tv_sec = wintime / 10000000LL; /* Windows epoch is January 1, 1601 apparently */
/* Resolution is 100 nanoseconds. */ wintime -= 116444736000000000LL;
spec->tv_nsec = wintime % 10000000LL * 100; spec->tv_sec = wintime / 10000000LL;
/* Resolution is 100 nanoseconds. */
spec->tv_nsec = wintime % 10000000LL * 100;
} else if (source == JANET_TIME_MONOTONIC) {
LARGE_INTEGER count;
LARGE_INTEGER perf_freq;
QueryPerformanceCounter(&count);
QueryPerformanceFrequency(&perf_freq);
spec->tv_sec = count.QuadPart / perf_freq.QuadPart;
spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart);
} else if (source == JANET_TIME_CPUTIME) {
FILETIME creationTime, exitTime, kernelTime, userTime;
GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime);
int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime;
spec->tv_sec = tmp / 10000000LL;
spec->tv_nsec = tmp % 10000000LL * 100;
}
return 0; return 0;
} }
/* clock_gettime() wasn't available on Mac until 10.12. */ /* clock_gettime() wasn't available on Mac until 10.12. */
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12) #elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
#include <mach/clock.h> #include <mach/clock.h>
#include <mach/mach.h> #include <mach/mach.h>
int janet_gettime(struct timespec *spec) { int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
clock_serv_t cclock; if (source == JANET_TIME_REALTIME) {
mach_timespec_t mts; clock_serv_t cclock;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); mach_timespec_t mts;
clock_get_time(cclock, &mts); host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
mach_port_deallocate(mach_task_self(), cclock); clock_get_time(cclock, &mts);
spec->tv_sec = mts.tv_sec; mach_port_deallocate(mach_task_self(), cclock);
spec->tv_nsec = mts.tv_nsec; spec->tv_sec = mts.tv_sec;
spec->tv_nsec = mts.tv_nsec;
} else if (source == JANET_TIME_MONOTONIC) {
clock_serv_t cclock;
int nsecs;
mach_msg_type_number_t count;
host_get_clock_service(mach_host_self(), clock, &cclock);
clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
mach_port_deallocate(mach_task_self(), cclock);
clock_getres(CLOCK_MONOTONIC, spec);
}
if (source == JANET_TIME_CPUTIME) {
clock_t tmp = clock();
spec->tv_sec = tmp;
spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
}
return 0; return 0;
} }
#else #else
int janet_gettime(struct timespec *spec) { int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
return clock_gettime(CLOCK_REALTIME, spec); clockid_t cid = CLOCK_REALTIME;
if (source == JANET_TIME_REALTIME) {
cid = CLOCK_REALTIME;
} else if (source == JANET_TIME_MONOTONIC) {
cid = CLOCK_MONOTONIC;
} else if (source == JANET_TIME_CPUTIME) {
cid = CLOCK_PROCESS_CPUTIME_ID;
}
return clock_gettime(cid, spec);
} }
#endif #endif
#endif #endif
@@ -863,13 +958,13 @@ int janet_cryptorand(uint8_t *out, size_t n) {
unsigned int v; unsigned int v;
if (rand_s(&v)) if (rand_s(&v))
return -1; return -1;
for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < n); j++) { for (int32_t j = 0; (j < (int32_t) sizeof(unsigned int)) && (i + j < n); j++) {
out[i + j] = v & 0xff; out[i + j] = v & 0xff;
v = v >> 8; v = v >> 8;
} }
} }
return 0; return 0;
#elif defined(JANET_LINUX) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) ) #elif defined(JANET_LINUX) || defined(JANET_CYGWIN) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) )
/* 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.
@@ -920,6 +1015,13 @@ char *get_processed_name(const char *name) {
return ret; return ret;
} }
#if defined(JANET_NO_DYNAMIC_MODULES)
const char *error_clib(void) {
return "dynamic modules not supported";
}
#else
#if defined(JANET_WINDOWS) #if defined(JANET_WINDOWS)
static char error_clib_buf[256]; static char error_clib_buf[256];
@@ -967,6 +1069,7 @@ void *symbol_clib(HINSTANCE clib, const char *sym) {
} }
} }
#endif
#endif #endif
/* Alloc function macro fills */ /* Alloc function macro fills */

View File

@@ -93,6 +93,11 @@ void janet_buffer_format(
Janet *argv); Janet *argv);
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter); Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
JanetBinding janet_binding_from_entry(Janet entry); JanetBinding janet_binding_from_entry(Janet entry);
JanetByteView janet_text_substitution(
Janet *subst,
const uint8_t *bytes,
uint32_t len,
JanetArray *extra_args);
/* Registry functions */ /* Registry functions */
void janet_registry_put( void janet_registry_put(
@@ -121,7 +126,12 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
/* Clock gettime */ /* Clock gettime */
#ifdef JANET_GETTIME #ifdef JANET_GETTIME
int janet_gettime(struct timespec *spec); enum JanetTimeSource {
JANET_TIME_REALTIME,
JANET_TIME_MONOTONIC,
JANET_TIME_CPUTIME
};
int janet_gettime(struct timespec *spec, enum JanetTimeSource source);
#endif #endif
/* strdup */ /* strdup */
@@ -135,7 +145,7 @@ int janet_gettime(struct timespec *spec);
typedef int Clib; typedef int Clib;
#define load_clib(name) ((void) name, 0) #define load_clib(name) ((void) name, 0)
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL) #define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
#define error_clib() "dynamic libraries not supported" const char *error_clib(void);
#define free_clib(c) ((void) (c), 0) #define free_clib(c) ((void) (c), 0)
#elif defined(JANET_WINDOWS) #elif defined(JANET_WINDOWS)
#include <windows.h> #include <windows.h>
@@ -150,7 +160,7 @@ typedef void *Clib;
#define load_clib(name) dlopen((name), RTLD_NOW) #define load_clib(name) dlopen((name), RTLD_NOW)
#define free_clib(lib) dlclose((lib)) #define free_clib(lib) dlclose((lib))
#define symbol_clib(lib, sym) dlsym((lib), (sym)) #define symbol_clib(lib, sym) dlsym((lib), (sym))
#define error_clib() dlerror() #define error_clib dlerror
#endif #endif
char *get_processed_name(const char *name); char *get_processed_name(const char *name);

View File

@@ -272,6 +272,7 @@ int janet_equals(Janet x, Janet y) {
const Janet *t1 = janet_unwrap_tuple(x); const Janet *t1 = janet_unwrap_tuple(x);
const Janet *t2 = janet_unwrap_tuple(y); const Janet *t2 = janet_unwrap_tuple(y);
if (t1 == t2) break; if (t1 == t2) break;
if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(t1) ^ janet_tuple_flag(t2))) return 0;
if (janet_tuple_hash(t1) != janet_tuple_hash(t2)) return 0; if (janet_tuple_hash(t1) != janet_tuple_hash(t2)) return 0;
if (janet_tuple_length(t1) != janet_tuple_length(t2)) return 0; if (janet_tuple_length(t1) != janet_tuple_length(t2)) return 0;
push_traversal_node(janet_tuple_head(t1), janet_tuple_head(t2), 0); push_traversal_node(janet_tuple_head(t1), janet_tuple_head(t2), 0);
@@ -321,6 +322,7 @@ int32_t janet_hash(Janet x) {
break; break;
case JANET_TUPLE: case JANET_TUPLE:
hash = janet_tuple_hash(janet_unwrap_tuple(x)); hash = janet_tuple_hash(janet_unwrap_tuple(x));
hash += (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
break; break;
case JANET_STRUCT: case JANET_STRUCT:
hash = janet_struct_hash(janet_unwrap_struct(x)); hash = janet_struct_hash(janet_unwrap_struct(x));
@@ -412,6 +414,9 @@ int janet_compare(Janet x, Janet y) {
case JANET_TUPLE: { case JANET_TUPLE: {
const Janet *lhs = janet_unwrap_tuple(x); const Janet *lhs = janet_unwrap_tuple(x);
const Janet *rhs = janet_unwrap_tuple(y); const Janet *rhs = janet_unwrap_tuple(y);
if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(lhs) ^ janet_tuple_flag(rhs))) {
return (janet_tuple_flag(lhs) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : -1;
}
push_traversal_node(janet_tuple_head(lhs), janet_tuple_head(rhs), 1); push_traversal_node(janet_tuple_head(lhs), janet_tuple_head(rhs), 1);
break; break;
} }
@@ -434,20 +439,21 @@ int janet_compare(Janet x, Janet y) {
return status - 2; return status - 2;
} }
static int32_t getter_checkint(Janet key, int32_t max) { static int32_t getter_checkint(JanetType type, Janet key, int32_t max) {
if (!janet_checkint(key)) goto bad; if (!janet_checkint(key)) goto bad;
int32_t ret = janet_unwrap_integer(key); int32_t ret = janet_unwrap_integer(key);
if (ret < 0) goto bad; if (ret < 0) goto bad;
if (ret >= max) goto bad; if (ret >= max) goto bad;
return ret; return ret;
bad: bad:
janet_panicf("expected integer key in range [0, %d), got %v", max, key); janet_panicf("expected integer key for %s in range [0, %d), got %v", janet_type_names[type], max, key);
} }
/* Gets a value and returns. Can panic. */ /* Gets a value and returns. Can panic. */
Janet janet_in(Janet ds, Janet key) { Janet janet_in(Janet ds, Janet key) {
Janet value; Janet value;
switch (janet_type(ds)) { JanetType type = janet_type(ds);
switch (type) {
default: default:
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds); janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds);
break; break;
@@ -459,19 +465,19 @@ Janet janet_in(Janet ds, Janet key) {
break; break;
case JANET_ARRAY: { case JANET_ARRAY: {
JanetArray *array = janet_unwrap_array(ds); JanetArray *array = janet_unwrap_array(ds);
int32_t index = getter_checkint(key, array->count); int32_t index = getter_checkint(type, key, array->count);
value = array->data[index]; value = array->data[index];
break; break;
} }
case JANET_TUPLE: { case JANET_TUPLE: {
const Janet *tuple = janet_unwrap_tuple(ds); const Janet *tuple = janet_unwrap_tuple(ds);
int32_t len = janet_tuple_length(tuple); int32_t len = janet_tuple_length(tuple);
value = tuple[getter_checkint(key, len)]; value = tuple[getter_checkint(type, key, len)];
break; break;
} }
case JANET_BUFFER: { case JANET_BUFFER: {
JanetBuffer *buffer = janet_unwrap_buffer(ds); JanetBuffer *buffer = janet_unwrap_buffer(ds);
int32_t index = getter_checkint(key, buffer->count); int32_t index = getter_checkint(type, key, buffer->count);
value = janet_wrap_integer(buffer->data[index]); value = janet_wrap_integer(buffer->data[index]);
break; break;
} }
@@ -479,7 +485,7 @@ Janet janet_in(Janet ds, Janet key) {
case JANET_SYMBOL: case JANET_SYMBOL:
case JANET_KEYWORD: { case JANET_KEYWORD: {
const uint8_t *str = janet_unwrap_string(ds); const uint8_t *str = janet_unwrap_string(ds);
int32_t index = getter_checkint(key, janet_string_length(str)); int32_t index = getter_checkint(type, key, janet_string_length(str));
value = janet_wrap_integer(str[index]); value = janet_wrap_integer(str[index]);
break; break;
} }
@@ -747,13 +753,14 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
} }
void janet_put(Janet ds, Janet key, Janet value) { void janet_put(Janet ds, Janet key, Janet value) {
switch (janet_type(ds)) { JanetType type = janet_type(ds);
switch (type) {
default: default:
janet_panicf("expected %T, got %v", janet_panicf("expected %T, got %v",
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds); JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
case JANET_ARRAY: { case JANET_ARRAY: {
JanetArray *array = janet_unwrap_array(ds); JanetArray *array = janet_unwrap_array(ds);
int32_t index = getter_checkint(key, INT32_MAX - 1); int32_t index = getter_checkint(type, key, INT32_MAX - 1);
if (index >= array->count) { if (index >= array->count) {
janet_array_setcount(array, index + 1); janet_array_setcount(array, index + 1);
} }
@@ -762,7 +769,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
} }
case JANET_BUFFER: { case JANET_BUFFER: {
JanetBuffer *buffer = janet_unwrap_buffer(ds); JanetBuffer *buffer = janet_unwrap_buffer(ds);
int32_t index = getter_checkint(key, INT32_MAX - 1); int32_t index = getter_checkint(type, key, INT32_MAX - 1);
if (!janet_checkint(value)) if (!janet_checkint(value))
janet_panicf("can only put integers in buffers, got %v", value); janet_panicf("can only put integers in buffers, got %v", value);
if (index >= buffer->count) { if (index >= buffer->count) {

View File

@@ -918,7 +918,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
int32_t i; int32_t i;
for (i = 0; i < elen; ++i) { for (i = 0; i < elen; ++i) {
int32_t inherit = fd->environments[i]; int32_t inherit = fd->environments[i];
if (inherit == -1) { if (inherit == -1 || inherit >= func->def->environments_length) {
JanetStackFrame *frame = janet_stack_frame(stack); JanetStackFrame *frame = janet_stack_frame(stack);
if (!frame->env) { if (!frame->env) {
/* Lazy capture of current stack frame */ /* Lazy capture of current stack frame */
@@ -1423,6 +1423,7 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) { if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
*out = in; *out = in;
janet_fiber_set_status(fiber, sig); janet_fiber_set_status(fiber, sig);
fiber->last_value = child->last_value;
return sig; return sig;
} }
/* Check if we need any special handling for certain opcodes */ /* Check if we need any special handling for certain opcodes */
@@ -1559,6 +1560,9 @@ int janet_init(void) {
janet_vm.scratch_len = 0; janet_vm.scratch_len = 0;
janet_vm.scratch_cap = 0; janet_vm.scratch_cap = 0;
/* Sandbox flags */
janet_vm.sandbox_flags = 0;
/* Initialize registry */ /* Initialize registry */
janet_vm.registry = NULL; janet_vm.registry = NULL;
janet_vm.registry_cap = 0; janet_vm.registry_cap = 0;
@@ -1600,6 +1604,18 @@ int janet_init(void) {
return 0; return 0;
} }
/* Disable some features at runtime with no way to re-enable them */
void janet_sandbox(uint32_t flags) {
janet_sandbox_assert(JANET_SANDBOX_SANDBOX);
janet_vm.sandbox_flags |= flags;
}
void janet_sandbox_assert(uint32_t forbidden_flags) {
if (forbidden_flags & janet_vm.sandbox_flags) {
janet_panic("operation forbidden by sandbox");
}
}
/* Clear all memory associated with the VM */ /* Clear all memory associated with the VM */
void janet_deinit(void) { void janet_deinit(void) {
janet_clear_memory(); janet_clear_memory();

View File

@@ -67,6 +67,11 @@ extern "C" {
#define JANET_LINUX 1 #define JANET_LINUX 1
#endif #endif
/* Check for Cygwin */
#if defined(__CYGWIN__)
#define JANET_CYGWIN 1
#endif
/* Check Unix */ /* Check Unix */
#if defined(_AIX) \ #if defined(_AIX) \
|| defined(__APPLE__) /* Darwin */ \ || defined(__APPLE__) /* Darwin */ \
@@ -87,6 +92,16 @@ extern "C" {
#define JANET_WINDOWS 1 #define JANET_WINDOWS 1
#endif #endif
/* Check if compiling with MSVC - else assume a GCC-like compiler by default */
#ifdef _MSC_VER
#define JANET_MSVC
#endif
/* Check Mingw 32-bit and 64-bit */
#ifdef __MINGW32__
#define JANET_MINGW
#endif
/* Check 64-bit vs 32-bit */ /* Check 64-bit vs 32-bit */
#if ((defined(__x86_64__) || defined(_M_X64)) \ #if ((defined(__x86_64__) || defined(_M_X64)) \
&& (defined(JANET_POSIX) || defined(JANET_WINDOWS))) \ && (defined(JANET_POSIX) || defined(JANET_WINDOWS))) \
@@ -96,7 +111,8 @@ extern "C" {
|| (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \ || (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \
|| 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 */
#define JANET_64 1 #define JANET_64 1
#else #else
#define JANET_32 1 #define JANET_32 1
@@ -166,7 +182,7 @@ extern "C" {
/* Enable or disable the FFI library. Currently, FFI only enabled on /* Enable or disable the FFI library. Currently, FFI only enabled on
* x86-64 operating systems. */ * x86-64 operating systems. */
#ifndef JANET_NO_FFI #ifndef JANET_NO_FFI
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64)) #if !defined(__EMSCRIPTEN__)
#define JANET_FFI #define JANET_FFI
#endif #endif
#endif #endif
@@ -264,10 +280,11 @@ extern "C" {
#ifndef JANET_NO_NANBOX #ifndef JANET_NO_NANBOX
#ifdef JANET_32 #ifdef JANET_32
#define JANET_NANBOX_32 #define JANET_NANBOX_32
#elif defined(__x86_64__) || defined(_WIN64) #elif defined(__x86_64__) || defined(_WIN64) || defined(__riscv)
/* We will only enable nanboxing by default on 64 bit systems /* We will only enable nanboxing by default on 64 bit systems
* on x86. This is mainly because the approach is tied to the * for x64 and risc-v. This is mainly because the approach is tied to the
* implicit 47 bit address space. */ * implicit 47 bit address space. Many arches allow/require this, but not all,
* and it requires cooperation from the OS. ARM should also work in many configurations. */
#define JANET_NANBOX_64 #define JANET_NANBOX_64
#endif #endif
#endif #endif
@@ -340,7 +357,7 @@ typedef struct JanetOSRWLock JanetOSRWLock;
/* What to do when out of memory */ /* What to do when out of memory */
#ifndef JANET_OUT_OF_MEMORY #ifndef JANET_OUT_OF_MEMORY
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0) #define JANET_OUT_OF_MEMORY do { fprintf(stderr, "%s:%d - janet out of memory\n", __FILE__, __LINE__); exit(1); } while (0)
#endif #endif
#ifdef JANET_BSD #ifdef JANET_BSD
@@ -432,6 +449,7 @@ typedef struct JanetReg JanetReg;
typedef struct JanetRegExt JanetRegExt; typedef struct JanetRegExt JanetRegExt;
typedef struct JanetMethod JanetMethod; typedef struct JanetMethod JanetMethod;
typedef struct JanetSourceMapping JanetSourceMapping; typedef struct JanetSourceMapping JanetSourceMapping;
typedef struct JanetSymbolMap JanetSymbolMap;
typedef struct JanetView JanetView; typedef struct JanetView JanetView;
typedef struct JanetByteView JanetByteView; typedef struct JanetByteView JanetByteView;
typedef struct JanetDictView JanetDictView; typedef struct JanetDictView JanetDictView;
@@ -550,6 +568,7 @@ typedef void *JanetAbstract;
#define JANET_STREAM_WRITABLE 0x400 #define JANET_STREAM_WRITABLE 0x400
#define JANET_STREAM_ACCEPTABLE 0x800 #define JANET_STREAM_ACCEPTABLE 0x800
#define JANET_STREAM_UDPSERVER 0x1000 #define JANET_STREAM_UDPSERVER 0x1000
#define JANET_STREAM_TOCLOSE 0x10000
typedef enum { typedef enum {
JANET_ASYNC_EVENT_INIT, JANET_ASYNC_EVENT_INIT,
@@ -987,6 +1006,7 @@ struct JanetAbstractHead {
/* Some function definition flags */ /* Some function definition flags */
#define JANET_FUNCDEF_FLAG_VARARG 0x10000 #define JANET_FUNCDEF_FLAG_VARARG 0x10000
#define JANET_FUNCDEF_FLAG_NEEDSENV 0x20000 #define JANET_FUNCDEF_FLAG_NEEDSENV 0x20000
#define JANET_FUNCDEF_FLAG_HASSYMBOLMAP 0x40000
#define JANET_FUNCDEF_FLAG_HASNAME 0x80000 #define JANET_FUNCDEF_FLAG_HASNAME 0x80000
#define JANET_FUNCDEF_FLAG_HASSOURCE 0x100000 #define JANET_FUNCDEF_FLAG_HASSOURCE 0x100000
#define JANET_FUNCDEF_FLAG_HASDEFS 0x200000 #define JANET_FUNCDEF_FLAG_HASDEFS 0x200000
@@ -1002,6 +1022,14 @@ struct JanetSourceMapping {
int32_t column; int32_t column;
}; };
/* Symbol to slot mapping & lifetime structure. */
struct JanetSymbolMap {
uint32_t birth_pc;
uint32_t death_pc;
uint32_t slot_index;
const uint8_t *symbol;
};
/* A function definition. Contains information needed to instantiate closures. */ /* A function definition. Contains information needed to instantiate closures. */
struct JanetFuncDef { struct JanetFuncDef {
JanetGCObject gc; JanetGCObject gc;
@@ -1015,6 +1043,7 @@ struct JanetFuncDef {
JanetSourceMapping *sourcemap; JanetSourceMapping *sourcemap;
JanetString source; JanetString source;
JanetString name; JanetString name;
JanetSymbolMap *symbolmap;
int32_t flags; int32_t flags;
int32_t slotcount; /* The amount of stack space required for the function */ int32_t slotcount; /* The amount of stack space required for the function */
@@ -1025,6 +1054,7 @@ struct JanetFuncDef {
int32_t bytecode_length; int32_t bytecode_length;
int32_t environments_length; int32_t environments_length;
int32_t defs_length; int32_t defs_length;
int32_t symbolmap_length;
}; };
/* A function environment */ /* A function environment */
@@ -1450,6 +1480,7 @@ JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); 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_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_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 */
@@ -1556,8 +1587,10 @@ JANET_API Janet janet_array_pop(JanetArray *array);
JANET_API Janet janet_array_peek(JanetArray *array); JANET_API Janet janet_array_peek(JanetArray *array);
/* Buffer functions */ /* Buffer functions */
#define JANET_BUFFER_FLAG_NO_REALLOC 0x10000
JANET_API JanetBuffer *janet_buffer(int32_t capacity); JANET_API JanetBuffer *janet_buffer(int32_t capacity);
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity); JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
JANET_API JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count);
JANET_API void janet_buffer_deinit(JanetBuffer *buffer); JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth); JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth);
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count); JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count);
@@ -1656,6 +1689,7 @@ JANET_API void janet_table_clear(JanetTable *table);
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv); JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv); JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber); JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber);
JANET_API int janet_fiber_can_resume(JanetFiber *fiber);
JANET_API JanetFiber *janet_current_fiber(void); JANET_API JanetFiber *janet_current_fiber(void);
JANET_API JanetFiber *janet_root_fiber(void); JANET_API JanetFiber *janet_root_fiber(void);
@@ -1770,6 +1804,24 @@ JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv);
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err); JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix); JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix);
/* Sandboxing API */
#define JANET_SANDBOX_SANDBOX 1
#define JANET_SANDBOX_SUBPROCESS 2
#define JANET_SANDBOX_NET_CONNECT 4
#define JANET_SANDBOX_NET_LISTEN 8
#define JANET_SANDBOX_FFI 16
#define JANET_SANDBOX_FS_WRITE 32
#define JANET_SANDBOX_FS_READ 64
#define JANET_SANDBOX_HRTIME 128
#define JANET_SANDBOX_ENV 256
#define JANET_SANDBOX_DYNAMIC_MODULES 512
#define JANET_SANDBOX_FS_TEMP 1024
#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_ALL (UINT32_MAX)
JANET_API void janet_sandbox(uint32_t flags);
JANET_API void janet_sandbox_assert(uint32_t forbidden_flags);
/* Scratch Memory API */ /* Scratch Memory API */
typedef void (*JanetScratchFinalizer)(void *); typedef void (*JanetScratchFinalizer)(void *);
@@ -1828,7 +1880,7 @@ JANET_API Janet janet_resolve_core(const char *name);
/* sourcemaps only */ /* sourcemaps only */
#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_} #define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_}
#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \ #define JANET_FN_S(CNAME, USAGE, DOCSTRING) \
static int32_t CNAME##_sourceline_ = __LINE__; \ static const int32_t CNAME##_sourceline_ = __LINE__; \
Janet CNAME (int32_t argc, Janet *argv) Janet CNAME (int32_t argc, Janet *argv)
#define JANET_DEF_S(ENV, JNAME, VAL, DOC) \ #define JANET_DEF_S(ENV, JNAME, VAL, DOC) \
janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__) janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__)
@@ -1844,7 +1896,7 @@ JANET_API Janet janet_resolve_core(const char *name);
/* sourcemaps and docstrings */ /* sourcemaps and docstrings */
#define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_} #define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_}
#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \ #define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \
static int32_t CNAME##_sourceline_ = __LINE__; \ static const int32_t CNAME##_sourceline_ = __LINE__; \
static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \ static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
Janet CNAME (int32_t argc, Janet *argv) Janet CNAME (int32_t argc, Janet *argv)
#define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \ #define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \
@@ -1918,6 +1970,7 @@ JANET_API JanetTable *janet_gettable(const Janet *argv, int32_t n);
JANET_API JanetStruct janet_getstruct(const Janet *argv, int32_t n); JANET_API JanetStruct janet_getstruct(const Janet *argv, int32_t n);
JANET_API JanetString janet_getstring(const Janet *argv, int32_t n); JANET_API JanetString janet_getstring(const Janet *argv, int32_t n);
JANET_API const char *janet_getcstring(const Janet *argv, int32_t n); JANET_API const char *janet_getcstring(const Janet *argv, int32_t n);
JANET_API const char *janet_getcbytes(const Janet *argv, int32_t n);
JANET_API JanetSymbol janet_getsymbol(const Janet *argv, int32_t n); JANET_API JanetSymbol janet_getsymbol(const Janet *argv, int32_t n);
JANET_API JanetKeyword janet_getkeyword(const Janet *argv, int32_t n); JANET_API JanetKeyword janet_getkeyword(const Janet *argv, int32_t n);
JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n); JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n);
@@ -1947,6 +2000,7 @@ JANET_API JanetTuple janet_opttuple(const Janet *argv, int32_t argc, int32_t n,
JANET_API JanetStruct janet_optstruct(const Janet *argv, int32_t argc, int32_t n, JanetStruct dflt); JANET_API JanetStruct janet_optstruct(const Janet *argv, int32_t argc, int32_t n, JanetStruct dflt);
JANET_API JanetString janet_optstring(const Janet *argv, int32_t argc, int32_t n, JanetString dflt); JANET_API JanetString janet_optstring(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const char *dflt); JANET_API const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
JANET_API const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
JANET_API JanetSymbol janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, JanetString dflt); JANET_API JanetSymbol janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API JanetKeyword janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, JanetString dflt); JANET_API JanetKeyword janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API JanetFiber *janet_optfiber(const Janet *argv, int32_t argc, int32_t n, JanetFiber *dflt); JANET_API JanetFiber *janet_optfiber(const Janet *argv, int32_t argc, int32_t n, JanetFiber *dflt);

View File

@@ -33,6 +33,9 @@
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif #endif
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
#endif
#endif #endif
void janet_line_init(); void janet_line_init();
@@ -144,8 +147,11 @@ static void setup_console_output(void) {
DWORD dwMode = 0; DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode); GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
dwMode |= ENABLE_PROCESSED_OUTPUT;
SetConsoleMode(hOut, dwMode); SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001); if (IsValidCodePage(65001)) {
SetConsoleOutputCP(65001);
}
} }
/* Ansi terminal raw mode */ /* Ansi terminal raw mode */
@@ -296,6 +302,7 @@ static char *sdup(const char *s) {
return memcpy(mem, s, len); return memcpy(mem, s, len);
} }
#ifndef _WIN32
static int curpos(void) { static int curpos(void) {
char buf[32]; char buf[32];
int cols, rows; int cols, rows;
@@ -311,6 +318,7 @@ static int curpos(void) {
if (sscanf(buf + 2, "%d;%d", &rows, &cols) != 2) return -1; if (sscanf(buf + 2, "%d;%d", &rows, &cols) != 2) return -1;
return cols; return cols;
} }
#endif
static int getcols(void) { static int getcols(void) {
#ifdef _WIN32 #ifdef _WIN32
@@ -950,6 +958,7 @@ static int line() {
break; break;
#ifndef _WIN32 #ifndef _WIN32
case 26: /* ctrl-z */ case 26: /* ctrl-z */
clearlines();
norawmode(); norawmode();
kill(getpid(), SIGSTOP); kill(getpid(), SIGSTOP);
rawmode(); rawmode();

View File

@@ -24,6 +24,11 @@
(def errsym (keyword (gensym))) (def errsym (keyword (gensym)))
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg)) ~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
(defn check-compile-error
[form]
(def result (compile form))
(assert (table? result) (string/format "expected compilation error for %j, but compiled without error" form)))
(defmacro assert-no-error (defmacro assert-no-error
[msg & forms] [msg & forms]
(def errsym (keyword (gensym))) (def errsym (keyword (gensym)))

View File

@@ -228,7 +228,7 @@
(assert (= 14 (sum (map inc @[1 2 3 4]))) "sum map") (assert (= 14 (sum (map inc @[1 2 3 4]))) "sum map")
(def myfun (juxt + - * /)) (def myfun (juxt + - * /))
(assert (= '[2 -2 2 0.5] (myfun 2)) "juxt") (assert (= [2 -2 2 0.5] (myfun 2)) "juxt")
# Case statements # Case statements
(assert (assert
@@ -252,6 +252,9 @@
(def xs (apply tuple (seq [x :down [8 -2] :when (even? x)] (tuple (/ x 2) x)))) (def xs (apply tuple (seq [x :down [8 -2] :when (even? x)] (tuple (/ x 2) x))))
(assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2") (assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2")
(def xs (catseq [x :range [0 3]] [x x]))
(assert (deep= xs @[0 0 1 1 2 2]) "catseq")
# :range-to and :down-to # :range-to and :down-to
(assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x)) "loop :range-to") (assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x)) "loop :range-to")
(assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) "loop :down-to") (assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) "loop :down-to")
@@ -320,11 +323,27 @@
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300]) @[111 222 333])) (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300]) @[111 222 333]))
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]) @[1111 2222 3333])) (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]) @[1111 2222 3333]))
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] [10000 20000 30000]) @[11111 22222 33333])) (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] [10000 20000 30000]) @[11111 22222 33333]))
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] [10000 20000 30000] [100000 200000 300000]) @[111111 222222 333333]))
# Mapping uses the shortest sequence # Mapping uses the shortest sequence
(assert (deep= (map + [1 2 3 4] [10 20 30]) @[11 22 33])) (assert (deep= (map + [1 2 3 4] [10 20 30]) @[11 22 33]))
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200]) @[111 222])) (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200]) @[111 222]))
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000]) @[1111])) (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000]) @[1111]))
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000] []) @[]))
# Variadic arguments to map-like functions
(assert (deep= (mapcat tuple [1 2 3 4] [5 6 7 8]) @[1 5 2 6 3 7 4 8]))
(assert (deep= (keep |(if (> $1 0) (/ $0 $1)) [1 2 3 4 5] [1 2 1 0 1]) @[1 1 3 5]))
(assert (= (count = [1 3 2 4 3 5 4 2 1] [1 2 3 4 5 4 3 2 1]) 4))
(assert (= (some not= (range 5) (range 5)) nil))
(assert (= (some = [1 2 3 4 5] [5 4 3 2 1]) true))
(assert (= (all = (range 5) (range 5)) true))
(assert (= (all not= [1 2 3 4 5] [5 4 3 2 1]) false))
(assert (= false (deep-not= [1] [1])) "issue #1149")
# Sort function # Sort function
(assert (deep= (assert (deep=
@@ -342,6 +361,8 @@
(assert (= (and 0 1 nil) nil) "and 0 1 nil") (assert (= (and 0 1 nil) nil) "and 0 1 nil")
(assert (= (and 1) 1) "and 1") (assert (= (and 1) 1) "and 1")
(assert (= (and) true) "and with no arguments") (assert (= (and) true) "and with no arguments")
(assert (= (and 1 true) true) "and with trailing true")
(assert (= (and 1 true 2) 2) "and with internal true")
(assert (= (or true true) true) "or true true") (assert (= (or true true) true) "or true true")
(assert (= (or true false) true) "or true false") (assert (= (or true false) true) "or true false")
@@ -353,4 +374,11 @@
(assert (= (or 1) 1) "or 1") (assert (= (or 1) 1) "or 1")
(assert (= (or) nil) "or with no arguments") (assert (= (or) nil) "or with no arguments")
(def yielder
(coro
(defer (yield :end)
(repeat 5 (yield :item)))))
(def items (seq [x :in yielder] x))
(assert (deep= @[:item :item :item :item :item :end] items) "yield within nested fibers")
(end-suite) (end-suite)

View File

@@ -41,10 +41,10 @@
# Looping idea # Looping idea
(def xs (def xs
(seq [x :in '[-1 0 1] y :in '[-1 0 1] :when (not= x y 0)] (tuple x y))) (seq [x :in [-1 0 1] y :in [-1 0 1] :when (not= x y 0)] (tuple x y)))
(def txs (apply tuple xs)) (def txs (apply tuple xs))
(assert (= txs '[[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested seq") (assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested seq")
# Generators # Generators
(def gen (generate [x :range [0 100] :when (pos? (% x 4))] x)) (def gen (generate [x :range [0 100] :when (pos? (% x 4))] x))
@@ -72,6 +72,10 @@
(assert (= (string/replace "X" "." "XXX...XXX...XXX") ".XX...XXX...XXX") "string/replace 1") (assert (= (string/replace "X" "." "XXX...XXX...XXX") ".XX...XXX...XXX") "string/replace 1")
(assert (= (string/replace-all "X" "." "XXX...XXX...XXX") "...............") "string/replace-all 1") (assert (= (string/replace-all "X" "." "XXX...XXX...XXX") "...............") "string/replace-all 1")
(assert (= (string/replace-all "XX" "." "XXX...XXX...XXX") ".X....X....X") "string/replace-all 2") (assert (= (string/replace-all "XX" "." "XXX...XXX...XXX") ".X....X....X") "string/replace-all 2")
(assert (= (string/replace "xx" string/ascii-upper "xxyxyxyxxxy") "XXyxyxyxxxy") "string/replace function")
(assert (= (string/replace-all "xx" string/ascii-upper "xxyxyxyxxxy") "XXyxyxyXXxy") "string/replace-all function")
(assert (= (string/replace "x" 12 "xyx") "12yx") "string/replace stringable")
(assert (= (string/replace-all "x" 12 "xyx") "12y12") "string/replace-all stringable")
(assert (= (string/ascii-lower "ABCabc&^%!@:;.") "abcabc&^%!@:;.") "string/ascii-lower") (assert (= (string/ascii-lower "ABCabc&^%!@:;.") "abcabc&^%!@:;.") "string/ascii-lower")
(assert (= (string/ascii-upper "ABCabc&^%!@:;.") "ABCABC&^%!@:;.") "string/ascii-lower") (assert (= (string/ascii-upper "ABCabc&^%!@:;.") "ABCABC&^%!@:;.") "string/ascii-lower")
(assert (= (string/reverse "") "") "string/reverse 1") (assert (= (string/reverse "") "") "string/reverse 1")

View File

@@ -349,7 +349,7 @@
(def janet-longstring (def janet-longstring
~{:delim (some "`") ~{:delim (some "`")
:open (capture :delim :n) :open (capture :delim :n)
:close (cmt (* (not (> -1 "`")) (-> :n) (<- :delim)) ,=) :close (cmt (* (not (> -1 "`")) (-> :n) (<- (backmatch :n))) ,=)
:main (* :open (any (if-not :close 1)) :close -1)}) :main (* :open (any (if-not :close 1)) :close -1)})
(check-match janet-longstring "`john" false) (check-match janet-longstring "`john" false)
@@ -359,6 +359,7 @@
(check-match janet-longstring "`` ``" true) (check-match janet-longstring "`` ``" true)
(check-match janet-longstring "``` `` ```" true) (check-match janet-longstring "``` `` ```" true)
(check-match janet-longstring "`` ```" false) (check-match janet-longstring "`` ```" false)
(check-match janet-longstring "`a``b`" false)
# Line and column capture # Line and column capture

View File

@@ -83,8 +83,13 @@
(assert (deep= (drop 10 []) []) "drop 2") (assert (deep= (drop 10 []) []) "drop 2")
(assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3") (assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3")
(assert (deep= (drop 10 [1 2 3]) []) "drop 4") (assert (deep= (drop 10 [1 2 3]) []) "drop 4")
(assert (deep= (drop -2 [:a :b :c]) [:a :b :c]) "drop 5") (assert (deep= (drop -1 [1 2 3]) [1 2]) "drop 5")
(assert-error :invalid-type (drop 3 {}) "drop 6") (assert (deep= (drop -10 [1 2 3]) []) "drop 6")
(assert (deep= (drop 1 "abc") "bc") "drop 7")
(assert (deep= (drop 10 "abc") "") "drop 8")
(assert (deep= (drop -1 "abc") "ab") "drop 9")
(assert (deep= (drop -10 "abc") "") "drop 10")
(assert-error :invalid-type (drop 3 {}) "drop 11")
# drop-until # drop-until
@@ -98,4 +103,18 @@
# Quasiquote bracketed tuples # Quasiquote bracketed tuples
(assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) "quasiquote bracket tuples") (assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) "quasiquote bracket tuples")
# No useless splices
(check-compile-error '((splice [1 2 3]) 0))
(check-compile-error '(if ;[1 2] 5))
(check-compile-error '(while ;[1 2 3] (print :hi)))
(check-compile-error '(def x ;[1 2 3]))
(check-compile-error '(fn [x] ;[x 1 2 3]))
# No splice propagation
(check-compile-error '(+ 1 (do ;[2 3 4]) 5))
(check-compile-error '(+ 1 (upscope ;[2 3 4]) 5))
# compiler inlines when condition is constant, ensure that optimization doesn't break
(check-compile-error '(+ 1 (if true ;[3 4])))
(check-compile-error '(+ 1 (if false nil ;[3 4])))
(end-suite) (end-suite)

View File

@@ -261,4 +261,12 @@
(modcheck -10 3) (modcheck -10 3)
(modcheck -10 -3) (modcheck -10 -3)
# Check for issue #1130
(var d (int/s64 7))
(mod 0 d)
(var d (int/s64 7))
(def result (seq [n :in (range -21 0)] (mod n d)))
(assert (deep= result (map int/s64 @[0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6])) "issue #1130")
(end-suite) (end-suite)

View File

@@ -60,7 +60,7 @@
:buffer (/ '(* "@" :bytes) (constant :string)) :buffer (/ '(* "@" :bytes) (constant :string))
:long-bytes {:delim (some "`") :long-bytes {:delim (some "`")
:open (capture :delim :n) :open (capture :delim :n)
:close (cmt (* (not (> -1 "`")) (-> :n) ':delim) ,=) :close (cmt (* (not (> -1 "`")) (-> :n) '(backmatch :n)) ,=)
:main (drop (* :open (any (if-not :close 1)) :close))} :main (drop (* :open (any (if-not :close 1)) :close))}
:long-string (/ ':long-bytes (constant :string)) :long-string (/ ':long-bytes (constant :string))
:long-buffer (/ '(* "@" :long-bytes) (constant :string)) :long-buffer (/ '(* "@" :long-bytes) (constant :string))
@@ -239,6 +239,12 @@
(assert (= (os/mktime (os/date now true) true) now) "local os/mktime") (assert (= (os/mktime (os/date now true) true) now) "local os/mktime")
(assert (= (os/mktime {:year 1970}) 0) "os/mktime default values") (assert (= (os/mktime {:year 1970}) 0) "os/mktime default values")
# OS strftime test
(assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 0) "1970-01-01 00:00:00") "strftime UTC epoch")
(assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 1388608200) "2014-01-01 20:30:00") "strftime january 2014")
(assert (= (try (os/strftime "%%%d%t") ([err] err)) "invalid conversion specifier '%t'") "invalid conversion specifier")
# Appending buffer to self # Appending buffer to self
(with-dyns [:out @""] (with-dyns [:out @""]
@@ -294,9 +300,12 @@
(assert-error "comptime issue" (eval '(comptime (error "oops")))) (assert-error "comptime issue" (eval '(comptime (error "oops"))))
(with [f (file/temp)] (with [f (file/temp)]
(assert (= 0 (file/tell f)) "start of file")
(file/write f "foo\n") (file/write f "foo\n")
(assert (= 4 (file/tell f)) "after written string")
(file/flush f) (file/flush f)
(file/seek f :set 0) (file/seek f :set 0)
(assert (= 0 (file/tell f)) "start of file again")
(assert (= (string (file/read f :all)) "foo\n") "temp files work")) (assert (= (string (file/read f :all)) "foo\n") "temp files work"))
(var counter 0) (var counter 0)
@@ -324,4 +333,29 @@
(assert (pos? (length (gensym))) "gensym not empty, regression #753") (assert (pos? (length (gensym))) "gensym not empty, regression #753")
# os/clock. These tests might prove fragile under CI because they
# rely on measured time. We'll see.
(defmacro measure-time [clocks & body]
(def [t1 t2] [(gensym) (gensym)])
~(do
(def ,t1 (map |(os/clock $) ,clocks))
,;body
(def ,t2 (map |(os/clock $) ,clocks))
(zipcoll ,clocks (map |(- ;$) (map tuple ,t2 ,t1))))
)
# Spin for 0.1 seconds
(def dt (measure-time [:realtime :monotonic :cputime]
(def t1 (os/clock :monotonic))
(while (< (- (os/clock :monotonic) t1) 0.1) true)))
(assert (> (dt :monotonic) 0.10))
(assert (> (dt :cputime) 0.05))
# Sleep for 0.1 seconds
(def dt (measure-time [:realtime :monotonic :cputime] (os/sleep 0.1)))
(assert (> (dt :monotonic) 0.10))
(assert (< (dt :cputime) 0.05))
(end-suite) (end-suite)

View File

@@ -330,7 +330,6 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02
(assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all") (assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all")
# Peg replace and replace-all # Peg replace and replace-all
(var ti 0)
(defn check-replacer (defn check-replacer
[x y z] [x y z]
(assert (= (string/replace x y z) (string (peg/replace x y z))) "replacer test replace") (assert (= (string/replace x y z) (string (peg/replace x y z))) "replacer test replace")
@@ -339,6 +338,32 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02
(check-replacer "abc" "Z" "") (check-replacer "abc" "Z" "")
(check-replacer "aba" "ZZZZZZ" "ababababababa") (check-replacer "aba" "ZZZZZZ" "ababababababa")
(check-replacer "aba" "" "ababababababa") (check-replacer "aba" "" "ababababababa")
(check-replacer "aba" string/ascii-upper "ababababababa")
(check-replacer "aba" 123 "ababababababa")
(assert (= (string (peg/replace-all ~(set "ab") string/ascii-upper "abcaa"))
"ABcAA")
"peg/replace-all cfunction")
(assert (= (string (peg/replace-all ~(set "ab") |$ "abcaa"))
"abcaa")
"peg/replace-all function")
(defn peg-test [name f peg subst text expected]
(assert (= (string (f peg subst text)) expected) name))
(peg-test "peg/replace has access to captures"
peg/replace
~(sequence "." (capture (set "ab")))
(fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char)))
".a.b.c"
".a -> A, .b.c")
(peg-test "peg/replace-all has access to captures"
peg/replace-all
~(sequence "." (capture (set "ab")))
(fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char)))
".a.b.c"
".a -> A, .b -> B, .c")
# Peg bug # Peg bug
(assert (deep= @[] (peg/match '(any 1) @"")) "peg empty pattern 1") (assert (deep= @[] (peg/match '(any 1) @"")) "peg empty pattern 1")

View File

@@ -52,6 +52,11 @@
(def retval (os/proc-wait p)) (def retval (os/proc-wait p))
(assert (not= retval 24) "Process was *not* terminated by parent")) (assert (not= retval 24) "Process was *not* terminated by parent"))
(let [p (os/spawn [janet "-e" `(do (ev/sleep 30) (os/exit 24)`] :p)]
(os/proc-kill p false :term)
(def retval (os/proc-wait p))
(assert (not= retval 24) "Process was *not* terminated by parent"))
# Parallel subprocesses # Parallel subprocesses
(defn calc-1 (defn calc-1
@@ -97,6 +102,20 @@
(os/execute [janet "-e" `(repeat 20 (print :hello))`] :p {:out f}) (os/execute [janet "-e" `(repeat 20 (print :hello))`] :p {:out f})
(file/flush f))) (file/flush f)))
# each-line iterator
(assert-no-error "file/lines iterator"
(def outstream (os/open "unique.txt" :wct))
(def buf1 "123\n456\n")
(defer (:close outstream)
(:write outstream buf1))
(var buf2 "")
(with [f (file/open "unique.txt" :r)]
(each line (file/lines f)
(set buf2 (string buf2 line))))
(assert (= buf1 buf2) "file/lines iterator")
(os/rm "unique.txt"))
# Issue #593 # Issue #593
(assert-no-error "file writing 3" (assert-no-error "file writing 3"
(def outfile (file/open "unique.txt" :w)) (def outfile (file/open "unique.txt" :w))

View File

@@ -44,6 +44,43 @@
(assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4])))) "macex1 qq bracket tuple") (assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4])))) "macex1 qq bracket tuple")
(assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah]) "macex1 qq array") (assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah]) "macex1 qq array")
# Sourcemaps in threading macros
(defn check-threading [macro expansion]
(def expanded (macex1 (tuple macro 0 '(x) '(y))))
(assert (= expanded expansion) (string macro " expansion value"))
(def smap-x (tuple/sourcemap (get expanded 1)))
(def smap-y (tuple/sourcemap expanded))
(def line first)
(defn column [t] (t 1))
(assert (not= smap-x [-1 -1]) (string macro " x sourcemap existence"))
(assert (not= smap-y [-1 -1]) (string macro " y sourcemap existence"))
(assert (or (< (line smap-x) (line smap-y))
(and (= (line smap-x) (line smap-y))
(< (column smap-x) (column smap-y))))
(string macro " relation between x and y sourcemap")))
(check-threading '-> '(y (x 0)))
(check-threading '->> '(y (x 0)))
# keep-syntax
(let [brak '[1 2 3]
par '(1 2 3)]
(tuple/setmap brak 2 1)
(assert (deep= (keep-syntax brak @[1 2 3]) @[1 2 3]) "keep-syntax brackets ignore array")
(assert (= (keep-syntax! brak @[1 2 3]) '[1 2 3]) "keep-syntax! brackets replace array")
(assert (= (keep-syntax! par (map inc @[1 2 3])) '(2 3 4)) "keep-syntax! parens coerce array")
(assert (not= (keep-syntax! brak @[1 2 3]) '(1 2 3)) "keep-syntax! brackets not parens")
(assert (not= (keep-syntax! par @[1 2 3]) '[1 2 3]) "keep-syntax! parens not brackets")
(assert (= (tuple/sourcemap brak)
(tuple/sourcemap (keep-syntax! brak @[1 2 3]))) "keep-syntax! brackets source map")
(keep-syntax par brak)
(assert (not= (tuple/sourcemap brak) (tuple/sourcemap par)) "keep-syntax no mutate")
(assert (= (keep-syntax 1 brak) brak) "keep-syntax brackets ignore type"))
# Cancel test # Cancel test
(def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti)) (def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti))
(assert (= 1 (resume f)) "cancel resume 1") (assert (= 1 (resume f)) "cancel resume 1")
@@ -144,7 +181,7 @@
(assert (< 1000 1e23) "greater than immediate 2") (assert (< 1000 1e23) "greater than immediate 2")
# os/execute with environment variables # os/execute with environment variables
(assert (= 0 (os/execute [(dyn :executable) "-e" "(+ 1 2 3)"] :pe {"HELLO" "WORLD"})) "os/execute with env") (assert (= 0 (os/execute [(dyn :executable) "-e" "(+ 1 2 3)"] :pe (merge (os/environ) {"HELLO" "WORLD"}))) "os/execute with env")
# Regression #638 # Regression #638
(compwhen (compwhen
@@ -216,4 +253,13 @@
# Check missing struct proto bug. # Check missing struct proto bug.
(assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) "missing struct proto") (assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) "missing struct proto")
# Test thaw and freeze
(def table-to-freeze @{:c 22 :b [1 2 3 4] :d @"test" :e "test2"})
(def table-to-freeze-with-inline-proto @{:a @[1 2 3] :b @[1 2 3 4] :c 22 :d @"test" :e @"test2"})
(def struct-to-thaw (struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2"))
(table/setproto table-to-freeze @{:a @[1 2 3]})
(assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"} (freeze table-to-freeze)))
(assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze)))
(assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw)))
(end-suite) (end-suite)

View File

@@ -102,5 +102,7 @@
(assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function") (assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function")
(assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct")) (assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct"))
(end-suite) (def f (asm (disasm (fn [x] (fn [y] (+ x y))))))
(assert (= ((f 10) 37) 47) "asm environment tables")
(end-suite)

View File

@@ -28,7 +28,8 @@
(assert (= (thunk) 1) "delay 3") (assert (= (thunk) 1) "delay 3")
(assert (= counter 1) "delay 4") (assert (= counter 1) "delay 4")
(def has-ffi (dyn 'ffi/native)) # We should get ARM support...
(def has-ffi (and (dyn 'ffi/native) (= (os/arch) :x64)))
# FFI check # FFI check
(compwhen has-ffi (compwhen has-ffi

50
test/suite0015.janet Normal file
View File

@@ -0,0 +1,50 @@
# test *debug* flags
(import ./helper :prefix "" :exit true)
(start-suite 15)
(assert (deep= (in (disasm (defn a [] (def x 10) x)) :symbolmap)
@[[0 2 0 'a] [0 2 1 'x]])
"symbolslots when *debug* is true")
(defn a [arg]
(def x 10)
(do
(def y 20)
(def z 30)
(+ x y z)))
(def symbolslots (in (disasm a) :symbolslots))
(def f (asm (disasm a)))
(assert (deep= (in (disasm f) :symbolslots)
symbolslots)
"symbolslots survive disasm/asm")
(comment
(setdyn *debug* true)
(setdyn :pretty-format "%.40M")
(def f (fn [x] (fn [y] (+ x y))))
(assert (deep= (map last (in (disasm (f 10)) :symbolmap))
@['x 'y])
"symbolslots upvalues"))
(assert (deep= (in (disasm (defn a [arg]
(def x 10)
(do
(def y 20)
(def z 30)
(+ x y z)))) :symbolmap)
@[[0 6 0 'arg]
[0 6 1 'a]
[0 6 2 'x]
[1 6 3 'y]
[2 6 4 'z]])
"arg & inner symbolslots")
# buffer/push-at
(assert (deep= @"abc456" (buffer/push-at @"abc123" 3 "456")) "buffer/push-at 1")
(assert (deep= @"abc456789" (buffer/push-at @"abc123" 3 "456789")) "buffer/push-at 2")
(assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4")) "buffer/push-at 3")
(assert (= 10 (do (var x 10) (def y x) (++ x) y)) "no invalid aliasing")
(end-suite)