1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-19 00:35:11 +00:00

Compare commits

...

181 Commits

Author SHA1 Message Date
Calvin Rose
ead0ed5d41 Merge branch 'master' into compile-opt 2025-09-21 20:02:31 -05:00
Calvin Rose
73b1cf547e Update CHANGELOG 2025-09-21 10:26:49 -05:00
Calvin Rose
ed2ae562c6 Merge pull request #1647 from sogaiu/tweak-flycheck-related-docstrings 2025-09-21 00:53:51 -05:00
sogaiu
dd59d84b51 Tweak some flycheck-related docstrings 2025-09-21 14:09:35 +09:00
Calvin Rose
06873fbf0b Update CHANGELOG.md 2025-09-20 14:30:10 -05:00
Calvin Rose
1ff26d702a Refactor flycheck to allow customization. Address #1638
Bindings can define their own flycheckers in a simple fashion.
2025-09-20 10:32:16 -05:00
Calvin Rose
4da568254a Manually declare chroot on macos. 2025-09-19 21:17:02 -05:00
Calvin Rose
357f1f94ca Add os/posix-chroot
Gated in a similar manner to os/posix-fork.
2025-09-19 16:32:45 -05:00
Calvin Rose
015e49c806 Merge pull request #1645 from pyrmont/bugfix.avoid-apc-use
Use `SetEvent` rather than `QueueUserAPC` on Windows
2025-09-19 16:31:13 -05:00
Michael Camilleri
6b06ab5f9c Remove unused function on Windows 2025-09-17 15:51:53 +09:00
Michael Camilleri
fe6c6e15a6 Add workaround to timer resolution issue on Windows 2025-09-17 15:48:37 +09:00
Michael Camilleri
b4eb52ca45 Start worker thread in suspended state on Windows 2025-09-16 11:57:40 +09:00
Michael Camilleri
aca5428846 Use SetEvent rather than QueueUserAPC on Windows 2025-09-16 10:24:24 +09:00
Calvin Rose
3dab9737e2 Fix #1643, but add features.h 2025-09-15 15:33:18 -05:00
Calvin Rose
e601e8faab Merge pull request #1640 from sogaiu/add-some-windows-commentary
Improve windows-related docs and notes
2025-09-15 15:29:38 -05:00
sogaiu
07cf63622f Improve windows-related docs and notes 2025-09-11 14:14:07 +09:00
Calvin Rose
8e7b1e9ce0 Don't try for backwards compat with compiled modules - Address #1639
This guarantee is difficult to maintain and shouldn't be needed.
2025-09-06 10:35:10 -05:00
Calvin Rose
355c514f0e Minor version bump for linking. 2025-09-02 07:08:51 -05:00
Calvin Rose
976329abc1 Update CHANGELOG.md 2025-09-01 15:59:39 -05:00
Calvin Rose
ab3e843433 Add test case for string/format %s of buffer 2025-09-01 14:24:51 -05:00
Calvin Rose
148e108864 Remove strnlen and correctly address #1600 2025-09-01 14:04:30 -05:00
Calvin Rose
c90c737345 Revert reordering 2. 2025-09-01 13:46:09 -05:00
Calvin Rose
13b9976382 Revert reordering of janet_deinit 2025-09-01 13:44:55 -05:00
Calvin Rose
095a81286a Add per-thread finalizer calls in missing places. 2025-09-01 12:38:11 -05:00
Calvin Rose
82416e4e4e Address #1629 - janet_deinit called before threaded channel message sent
to thread.

If we take a reference to another thread inside channel code, make sure
that we increase the refcount to avoid a use after free.
2025-09-01 12:30:29 -05:00
Calvin Rose
ae51434a05 Fix #1604 - Add JANET_DO_ERROR_* defines for failure flags from janet_dobytes. 2025-09-01 09:43:27 -05:00
Calvin Rose
bb6ac423a7 Merge pull request #1637 from jsks/spelling-fixes
Small spelling fixes
2025-08-31 09:32:02 -05:00
Joshua Krusell
c5ba3c0513 Small spelling fixes 2025-08-31 12:14:51 +02:00
Calvin Rose
e9c6678614 Update janet for 1.39.1 2025-08-30 08:11:18 -05:00
Calvin Rose
800457c1bf Update meson.build version. 2025-08-30 08:09:43 -05:00
Calvin Rose
2a85781616 Merge pull request #1632 from jsks/jsks-channel
Export channel utilities
2025-08-30 08:08:55 -05:00
Calvin Rose
7c15e7f7dc Merge pull request #1633 from aeiouaeiouaeiouaeiouaeiouaeiou/janet-legacy-macos1
os.c: use JANET_SPAWN_NO_CHDIR macros for macOS <10.15
2025-08-30 08:08:43 -05:00
aeiouaeiouaeiouaeiouaeiouaeiou
896c28b0c8 os.c: use JANET_SPAWN_NO_CHDIR macros for macOS <10.15
Signed-off-by: aeiouaeiouaeiouaeiouaeiouaeiou <aeioudev@outlook.com>
2025-08-29 13:43:28 +03:00
Joshua Krusell
e7bb0dd58e Export channel utilities 2025-08-29 12:19:53 +02:00
Calvin Rose
4e02f27eb9 Prepare for 1.39.0 release 2025-08-24 17:09:39 -05:00
Calvin Rose
fd234461d7 Merge pull request #1628 from snltd/illumos-support
illumos support
2025-08-24 16:18:09 -05:00
Calvin Rose
eabb215391 Use janet_gettime instead of clock_gettime in ev.c
We made the wrapper, let's use it. Also switch poll implementation to a
monotonic clock instead of realtime to be more inline with epoll and
kqueue.
2025-08-21 19:10:08 -05:00
snltd
deede6bae0 illumos support 2025-08-18 18:39:11 +00:00
Calvin Rose
697fdcff6d Merge pull request #1627 from nlordell/fix/system-monotonic-clock
Read System Clock for Monotonic Time
2025-08-18 07:53:26 -05:00
Nicholas Rodrigues Lordello
ad8a5cb6c7 Read System Clock for Monotonic Time
This PR changes the `janet_gettime` implementation for OSX <10.12 to
read the system clock for `(os/clock :monotonic)`. As far as I was able
to find online this is _a_ monotonic clock, although it produces
different values from `clock_gettime(CLOCK_MONOTONIC, ...)` on the same
system. I can speculate that this is related to `SYSTEM_CLOCK` monotonic
time being implemented with `mach_absolute_time` which is documented to
_not advance during sleep_, and I suspect that
`clock_gettime(CLOCK_MONOTONIC, ...)` does.

**Resources**:
- `clock_get_time` implementation for the `SYSTEM_CLOCK`:
  <e3723e1f17/osfmk/kern/clock_oldops.c (L284-L296)>
  <2ff845c2e0/osfmk/arm/rtclock.c (L248-L260)>
- `mach_absolute_time` and `mach_continuous_time` definitions:
  <e3723e1f17/osfmk/mach/mach_time.h (L55-L68)>
- Stack overflow post for implementing `clock_gettime` on OS X before 10.12:
  <https://stackoverflow.com/questions/11680461/monotonic-clock-on-osx>
2025-08-18 14:41:09 +02:00
Calvin Rose
99abada2c2 Merge pull request #1626 from nlordell/fix/clocks-per-sec
Use `CLOCKS_PER_SEC` instead of `CLOCKS_PER_SECOND`
2025-08-18 07:35:20 -05:00
Nicholas Rodrigues Lordello
0624936711 Use CLOCKS_PER_SEC
The POSIX standard defines that `clock(3)` returns a `clock_t` as a
number of clock ticks in `CLOCKS_PER_SEC` and not `CLOCKS_PER_SECOND`,
[source](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html).
2025-08-18 09:15:30 +02:00
Calvin Rose
f764788b36 Merge pull request #1624 from nlordell/fix/libjanet-ldflags
Fix `LDFLAGS` Usage in Makefile
2025-08-17 20:58:40 -05:00
Calvin Rose
4701bc6543 Add test patch for #1625 2025-08-17 20:55:52 -05:00
Nicholas Rodrigues Lordello
156fb0c999 Fix LDFLAGS Usage in Makefile
This PR fixes what appears to be a typo `LDFLAGS` written with an
additional `_` in the Makefile for setting the default linker flags for
`libjanet`.
2025-08-18 01:01:38 +02:00
Calvin Rose
bf34340737 Merge pull request #1623 from tttuuu888/work-issue-1622
Add `net/socket` for unbound socket creation for #1622
2025-08-16 06:48:43 -05:00
Seungki Kim
20535e8626 Add net/socket for unbound socket creation #1622 2025-08-14 00:40:22 +09:00
Calvin Rose
1ead670e33 Merge pull request #1621 from tttuuu888/work-issue-1620
Fix: Correctly flag UDP streams in net/connect #1620
2025-08-12 16:04:52 -05:00
Seungki Kim
3ad86108f2 Fix: Correctly flag UDP streams in net/connect #1620 2025-08-11 20:56:04 +09:00
Calvin Rose
0aee7765cf Windows quirk fix 2025-08-02 20:04:55 -05:00
Calvin Rose
4894a4673a Fix abstract unix sockets for issue #1618 - address #1618 2025-08-02 19:00:47 -05:00
Calvin Rose
f00d3199c3 Fix #1609 Remove sigaction if JANET_NO_EV defined. 2025-08-02 18:19:39 -05:00
Calvin Rose
e34a8545e6 Merge pull request #1615 from ifreund/net-server-datagram
net/server: improve error for truthy handler and type :datagram
2025-08-02 18:06:24 -05:00
Isaac Freund
f974c0667b net/server: improve error for truthy handler and type :datagram
Since it is invalid to call accept on a datagram socket, net/server
always errors if handler is truthy and type is :datagram.

Add an assert to give a better error message in this case and clarify
the documentation.

References: https://github.com/janet-lang/janet/issues/1614
2025-07-18 09:57:17 +02:00
Calvin Rose
ddc122958b Merge pull request #1607 from sarna/master
Clarify :fresh usage in import
2025-07-13 18:35:56 -05:00
Calvin Rose
2e363bf29c Remove extra call to filewatcher - address #1608 2025-07-13 18:34:12 -05:00
Calvin Rose
312f9faae8 Address #1609 - compile with JANET_NO_PROCESSES 2025-07-13 18:24:23 -05:00
sarna
8c9cd63cb1 Add tests for import arg validation 2025-07-13 16:32:50 +02:00
sarna
2af3f21d69 Validate optional args to import 2025-07-13 16:31:12 +02:00
sarna
c4e3fa03fa Clarify :fresh usage in import 2025-07-12 14:38:22 +02:00
Calvin Rose
91b7bcad3d Merge pull request #1606 from pyrmont/bugfix.bundle-install
Support complex dependency coordinates in `bundle/install`
2025-07-06 10:32:54 -05:00
Michael Camilleri
8d2a9c1148 Allow :dependencies value in info.jdn to contain dictionaries for complex dependency coordinates 2025-07-06 05:45:41 +09:00
Michael Camilleri
f1d47bd05a Use :dependencies argument in bundle/install for dependency checking 2025-07-02 23:38:36 +09:00
Calvin Rose
58b1491592 Merge pull request #1605 from iacore/patch-2
Patch try to accept (try body ([] catch-body))
2025-06-29 10:01:32 -05:00
Calvin Rose
21a6ed3bd3 Revert order change from f4ecb5a
janet_interpreter_interrupt should always be called before janet_interpreter_interrupt_handled, and the original code ensured that.
2025-06-26 19:20:07 -05:00
iacore
e815c91e85 Patch try to accept (try body ([] catch-body)) 2025-06-24 06:18:34 +00:00
Calvin Rose
42bc504188 Merge branch 'master' into compile-opt 2025-06-21 16:49:24 -05:00
Calvin Rose
d96e584869 Remove windows-2019 from github CI 2025-06-19 17:29:48 -05:00
Calvin Rose
f4ecb5a90f Reorder post event / interrupt sequence in deadline.
The interrupt message should come _after_ the post event is made.
2025-06-18 22:13:14 -05:00
Calvin Rose
f181948aa9 Merge pull request #1601 from edsrzf/limit-buffer-read
Use strnlen when checking for null byte
2025-06-14 12:25:18 -05:00
Evan Shaw
bbe6b90331 Use strnlen when checking for null byte 2025-06-14 22:29:30 +12:00
Calvin Rose
27f01e2664 Merge pull request #1597 from sogaiu/remove-more-underline-bits
Remove some underline bits from doc-format
2025-05-21 21:39:16 -05:00
sogaiu
877967966a Remove some underline bits from doc-format 2025-05-21 18:38:09 +09:00
Calvin Rose
56c5a0ca09 Address #1591 - remove _ behavior of docstring format 2025-05-20 19:17:32 -05:00
Calvin Rose
f3ad13c2d4 Always cancel thread on windows. 2025-05-18 14:02:32 -05:00
Calvin Rose
8ac4eec370 Change ifdef structure. 2025-05-18 13:20:19 -05:00
Calvin Rose
92e91259c3 Don't call pthread cancel on normal exits.
Calling pthread_cancel on threads that can exit normally is not needed.
Instead, we immediately call pthread_join if a thread can exit normally.
2025-05-18 09:52:11 -05:00
Calvin Rose
e355cb07e0 Reorder declarations. 2025-05-18 09:27:01 -05:00
Calvin Rose
5bbfcdacd5 Work on #1596 - No detached threads, make sure to call pthread_join
Call pthread_join on all worker threads for timeouts. Previously, we
were leaking some threads, as well as creating a timeout and leaving
has_worker unset on certain timeouts.
2025-05-18 08:36:53 -05:00
Calvin Rose
790a4f2636 Make tests pass with clang sanitizers.
Fix some issue with clang sanitizers, name -fsanitize=thread and
-fsanitize=undefined. The threading issue arose from the implementation
of ev/deadlock when allowing for interpreter intrerrupts, as this is
implemented by racing a timeout thread with a worker thread.

The undefined behavior issue arose in some very old code in corelib.c that will
actually work as expected for most compilers, but was both undefined and
unecessary as we have a correct implemenation in util.c.
2025-05-17 21:28:41 -05:00
Calvin Rose
84bb84b0b7 OpenBSD 7.7 -> 7.6 rollback 2025-05-16 18:58:07 -05:00
Calvin Rose
29f2b5c345 Update openbsd package for srht 2025-05-16 18:57:29 -05:00
Calvin Rose
4643c8fa35 Squashed commit of the following:
commit c5b3da1ffe
Author: Calvin Rose <calsrose@gmail.com>
Date:   Fri May 16 18:35:33 2025 -0500

    Inter
2025-05-16 18:49:45 -05:00
Calvin Rose
c3317905a1 Move sysir C lowering to separate file 2025-05-07 07:25:05 -05:00
Calvin Rose
0066a5a304 Start removing NASM dependence.
Start setting up a test suite for sysir and work towards emitting jitted
x86 machine code.
2025-05-04 20:20:11 -05:00
Calvin Rose
3dd6e744de More work on e_mov - work on loading immediates. 2025-04-15 20:40:05 -05:00
Calvin Rose
862b4e9688 Add sysir test suite stub 2025-04-13 07:47:52 -05:00
Calvin Rose
c9305a0a42 Format. 2025-04-11 22:44:36 -05:00
Calvin Rose
3cbdf26aa2 Merge branch 'master' into compile-opt 2025-04-11 22:44:28 -05:00
Calvin Rose
11e6a5a315 More work on compile-opt. 2025-03-31 21:48:46 -05:00
Calvin Rose
871f8ebf4e More work on moving to machine code emission. 2025-03-30 13:38:33 -05:00
Calvin Rose
c677c72a73 Add tool that lets us more easily compare compilation paths.
Compare:
IR -> C -> x64
vs.
IR -> x64
2025-03-29 20:37:51 -05:00
Calvin Rose
af73e214b2 Begin work on emitting machine code directly.
Far from done, we are currently splicing raw bytes in NASM output.
2025-03-29 16:52:11 -05:00
Calvin Rose
a6e0a8228c Merge branch 'master' into compile-opt 2025-03-28 16:24:27 -05:00
Calvin Rose
9a1cd6fdd9 Add janet_sysir_scalarize
Makes it easier to add simpler backends without needing to completely
handle vectorization.
2025-02-24 19:12:17 -06:00
Calvin Rose
768c9b23e1 Update drawing 2. 2025-02-19 08:21:20 -06:00
Calvin Rose
059253fdee Merge branch 'master' into compile-opt 2025-02-17 18:08:17 -06:00
Calvin Rose
4396f01297 More work on drawing example and frontend changes. 2024-12-01 08:43:54 -06:00
Calvin Rose
046d299d77 More work on x86 backend. 2024-11-26 11:18:46 -06:00
Calvin Rose
9fa9286fca Add more drawing examples. 2024-11-25 09:42:00 -06:00
Calvin Rose
c13ef02ea2 Add drawing2.janet so we can keep the working drawing example. 2024-11-25 08:51:40 -06:00
Calvin Rose
52cedbc4b4 More work on drawing example. 2024-11-25 07:33:31 -06:00
Calvin Rose
d345e551f1 Correct pointer arith type checking. 2024-11-24 20:06:16 -06:00
Calvin Rose
0fb1773c19 Merge branch 'master' into compile-opt 2024-11-24 19:15:47 -06:00
Calvin Rose
a6ea38a23b More working on pointer arithmetic. 2024-11-24 18:44:26 -06:00
Calvin Rose
bc79489068 Begin working on drawing example. 2024-11-24 15:53:20 -06:00
Calvin Rose
b096babcbf Merge branch 'master' into compile-opt 2024-11-23 10:29:14 -06:00
Calvin Rose
bed80bf1d3 Merge branch 'master' into compile-opt 2024-10-12 07:58:43 -05:00
Calvin Rose
80ed6538d0 Add constant checking in sysir middle end. 2024-10-05 12:05:04 -05:00
Calvin Rose
6577a18cef Better printing for complex constants.
Also added stub for checking if constants are valid in IR, but it is
not currently used.
2024-09-30 08:14:01 -05:00
Calvin Rose
731592a80e Merge branch 'master' into compile-opt 2024-09-29 17:27:49 -05:00
Calvin Rose
ea332ff81e More work on making the temporary frontend a little nicer.
We need to create abstractions around more of the backend
to properly test and experiment with things, even if the frontend
is not final.
2024-09-29 15:55:10 -05:00
Calvin Rose
f36d544deb MSVC and strange errors. 2024-09-29 12:30:51 -05:00
Calvin Rose
e96dd512f3 Work on some local type inference.
Right to left type inference in expressions for binary operators.
2024-09-29 11:37:04 -05:00
Calvin Rose
a588f1f242 More small tweaks to compile-opt. 2024-09-29 07:13:27 -05:00
Calvin Rose
ae15eadfaf Merge branch 'master' into compile-opt 2024-09-28 16:00:30 -05:00
Calvin Rose
3618b72f4d Merge branch 'master' into compile-opt 2024-09-08 12:28:51 -05:00
Calvin Rose
3510e235ee More work on compile-opt 2024-06-21 17:16:56 -05:00
Calvin Rose
b6fb7ae69c x64 allow dynamically switching between windows and sysv target. 2024-06-17 23:02:05 -05:00
Calvin Rose
e5765b26d4 Working examples on windows.
Add some support for windows x64 ABI.
2024-06-17 07:07:20 -07:00
Calvin Rose
cdb3baaca3 Work on windows. 2024-06-16 13:37:25 -07:00
Calvin Rose
c413bc2b4e Don't assign variables positions on the stack that clobber import info.
(return address, previous basepoint, etc.)
2024-06-16 10:06:22 -05:00
Calvin Rose
dfdf734fc7 Merge branch 'master' into compile-opt 2024-06-16 09:31:11 -05:00
Calvin Rose
314e684097 More work on x64 backend. 2024-06-14 16:57:32 -05:00
Calvin Rose
232a8faa35 More work compile-opt. 2024-06-13 07:27:48 -05:00
Calvin Rose
c31d8b52ff Add typed constants and lots more. 2024-06-12 13:57:33 -05:00
Calvin Rose
f0395763b7 More work on x86 target.
Also remove all (limited) type inference from the sysir. Type
inference is better done in frontend, limited inference in backend
just covers compilers issues.

Simple hello world with nasm working.
2024-06-10 20:16:04 -05:00
Calvin Rose
5b3c5a5969 Lots of work on calling conventions and x86 backend.
We need the ability to represent multiple calling conventions in IR.
All backends need to support a :default CC, but can also support more
for interop with system libraries, code from other compilers, syscalls, etc.

Also allow void returns.
2024-06-10 08:47:27 -05:00
Calvin Rose
af10c1d4b5 More work on x64 backend, especially branching.
Needs changes to IR to allow encoding immediates in all
instructions where possible. This makes the IR denser, means
we don't need `constant` and `callk`, and allows certain optimizations
like comparing to zero, using `inc` and `dec`, etc which are
specializations of more general instructions with constants.
2024-06-08 13:20:34 -05:00
Calvin Rose
3995fa86e2 More work on function calls. 2024-06-07 20:20:16 -05:00
Calvin Rose
9d7a279999 Merge branch 'master' into compile-opt 2024-06-07 19:28:17 -05:00
Calvin Rose
3e273ce03a More work on sysir. 2024-06-07 10:09:53 -05:00
Calvin Rose
25b7c74089 More work on register allocation and spilling.
Setup frontend.janet to show the basics of what is going on. Currently
emitting "fake" instructions just to hash out the idea.

One apparent issue is how we handle register spilling during variable
argument IR instructions (function calls). Arguments should come
_before_ the function call not after.
2024-06-05 17:50:11 -05:00
Calvin Rose
9e47cd94bd Delete extra code. 2024-06-04 21:10:35 -05:00
Calvin Rose
7ea118f248 Begin work on simple x64 backend.
Introduce register allocation, spilling, etc. First implementation
will likely emit textual assembly and use a very bad register allocation
algorithm.
2024-06-03 08:35:08 -05:00
Calvin Rose
480c5b5e9d Change how labels are recorded.
Disallow jumping to arbitrary instructions - instead, only allow jumps
to label ids. This will make various transformations and validations
easier since adding or remove instructions does not break jumps.
2024-06-02 09:43:33 -05:00
Calvin Rose
8a394f2506 Merge branch 'master' into compile-opt 2024-06-01 13:03:36 -05:00
Calvin Rose
2c208f5d01 Merge branch 'master' into compile-opt 2024-05-15 07:49:46 -05:00
Calvin Rose
08e6051af8 More work on sysir compiler - basic function calls (without prototypes). 2024-05-15 07:24:15 -05:00
Calvin Rose
19212e6f5c Remove net.c changes. 2024-05-12 18:09:22 -05:00
Calvin Rose
8875adf69e Merge branch 'master' into compile-opt 2024-05-12 16:22:06 -05:00
Calvin Rose
745567a2e0 More work on frontend. 2024-05-09 22:22:38 -05:00
Calvin Rose
ef2dfcd7c3 More work on a proof of concept frontend.
Basic frontend being prototyped in examples/sysir/frontend.janet. Still
a lot of work needs to be done here, and some of this code will
eventually move to C most likely, but this is a good way to better
exercise our backend.

Type inference - at the very least _forward_ inference, is the most
needed change here. While one could do this in the compiler
frontend, doing so in sysir/asm is not so much of an issue. "Inference"
here means inserting "bind" instructions when there is only a single
type that will work correctly.
2024-05-05 14:45:00 -05:00
Calvin Rose
f582fe1f69 Update compiler opt 2024-05-04 16:14:59 -05:00
Calvin Rose
3cc3312b7b Merge branch 'master' into compile-opt 2024-05-04 16:14:35 -05:00
Calvin Rose
f2d25a0da2 Add test case. 2024-05-04 16:14:26 -05:00
Calvin Rose
dfd05ddf7e Merge branch 'master' into compile-opt 2023-11-02 10:58:51 -05:00
Calvin Rose
31be7bad8e Merge branch 'master' into compile-opt 2023-11-01 17:36:14 -05:00
Calvin Rose
3a782d27b1 Allow for multiple functions in a sysir "context".
Allows for in memory linking.
2023-10-22 16:05:38 -05:00
Calvin Rose
f08874e65e Merge branch 'master' into compile-opt 2023-10-22 09:03:52 -05:00
Calvin Rose
6a78b6d1c6 Merge remote-tracking branch 'origin/compile-opt' into compile-opt 2023-10-10 20:28:09 -05:00
Calvin Rose
97963d1396 Update printing for operating on pointers. 2023-09-05 17:01:31 -05:00
Calvin Rose
efbc46c69e Add support for using operators on arrays (and pointers to arrays).
Allows more expressive yet type checked representation of array
algorithms.
2023-09-03 12:32:28 -05:00
Calvin Rose
9b9f67c371 Merge branch 'master' into compile-opt 2023-09-03 10:18:54 -05:00
Calvin Rose
61791e4a4c Update docstring. 2023-09-03 10:18:37 -05:00
Calvin Rose
c3a4fb6735 Merge branch 'master' into compile-opt 2023-08-20 18:55:15 -05:00
Calvin Rose
e5893d0692 Fix reference counting for threaded abstract types.
Was very borked. The sweep phase should drop references to unused
abstracts but wasn't, resulting in each collection decrementing the
count by one until 0 was hit, even if other threads maintained a
reference.
2023-08-20 14:50:46 -05:00
Calvin Rose
5f5e5cf693 Merge branch 'master' into compile-opt 2023-08-20 13:08:56 -05:00
Calvin Rose
46bda4e6fa Stub out type inference pass. 2023-08-16 14:09:25 -05:00
Calvin Rose
fdbf4f2666 Merge branch 'master' into compile-opt 2023-08-13 12:36:19 -05:00
Calvin Rose
b939671b79 Add check for redefining types. 2023-08-13 11:09:20 -05:00
Calvin Rose
4b8e7a416f Have separate instructions for pointer arith 2023-08-12 17:36:06 -05:00
Calvin Rose
1e1e7a5cfd Update garbage collection for sysir abstract type. 2023-08-12 13:47:23 -05:00
Calvin Rose
91e459e4a5 Format sysir. 2023-08-12 13:43:51 -05:00
Calvin Rose
b6adc257f4 Merge branch 'master' into compile-opt 2023-08-12 13:43:28 -05:00
Calvin Rose
a2bd98390e More work on the sysir. 2023-08-12 13:42:52 -05:00
Calvin Rose
d9912f38f8 Add union types and change name of type constructor instructions. 2023-08-12 10:29:24 -05:00
Calvin Rose
8007806c8e Add better support for arrays and struct fields in IR.
Also add option for named registers.
2023-08-08 18:56:02 -05:00
Calvin Rose
de2440d458 Lots todo 2023-08-07 10:54:41 -05:00
Calvin Rose
43ab06467f Merge branch 'master' into compile-opt 2023-08-07 09:40:27 -05:00
Calvin Rose
3fe4cfd14c Add labels back to sysir 2023-08-07 09:39:35 -05:00
Calvin Rose
75be5fd4c6 Update sysir to have better field support. 2023-08-06 20:00:49 -05:00
Calvin Rose
7c7136fd70 Merge branch 'master' into compile-opt 2023-08-06 17:09:13 -05:00
Calvin Rose
cfa32d58a7 More work on sysir, add initial work for recursive types. 2023-08-06 15:50:21 -05:00
Calvin Rose
7cc176f0c0 Add source mapping to emitted C. 2023-07-16 16:08:28 -05:00
Calvin Rose
4d7baef89e Merge branch 'master' into compile-opt 2023-07-04 13:52:54 -05:00
Calvin Rose
29af4a932d Fix NAN typo. 2023-05-12 19:08:00 -05:00
Calvin Rose
ef94a0f0b4 Rename sysdialect to sysir 2023-05-12 18:11:14 -05:00
Calvin Rose
517dc208ca Merge branch 'master' into compile-opt 2023-05-11 20:59:11 -05:00
Calvin Rose
fd7579dd07 More work on the sys-ir. 2023-04-08 10:51:46 -05:00
Calvin Rose
6b74400f2a Create system IR that can compile to C.
Work ongoing, still needs better pointer support, as well
as composite types.
2023-04-03 09:30:23 -05:00
62 changed files with 5612 additions and 262 deletions

View File

@@ -1,4 +1,4 @@
image: openbsd/7.4
image: openbsd/7.6
sources:
- https://git.sr.ht/~bakpakin/janet
packages:

View File

@@ -25,7 +25,7 @@ jobs:
name: Build and test on Windows
strategy:
matrix:
os: [ windows-latest, windows-2019 ]
os: [ windows-latest, windows-2022 ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout the repository
@@ -46,7 +46,7 @@ jobs:
name: Build and test on Windows Minimal build
strategy:
matrix:
os: [ windows-2019 ]
os: [ windows-2022 ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout the repository

1
.gitignore vendored
View File

@@ -37,6 +37,7 @@ temp.janet
temp.c
temp*janet
temp*.c
temp.*
scratch.janet
scratch.c

View File

@@ -2,6 +2,22 @@
All notable changes to this project will be documented in this file.
## Unreleased - ???
- Add `os/posix-chroot`
- Fix `ev/deadline` with interrupt race condition bug on Windows.
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.
## 1.39.1 - 2025-08-30
- Add support for chdir in os/spawn on older macOS versions
- Expose channels properly in C API
## 1.39.0 - 2025-08-24
- Various bug fixes
- Add `net/socket`
- Add support for illumos OS
- Raise helpful errors for incorrect arguments to `import`.
- Allow configuring `JANET_THREAD_LOCAL` during builds to allow multi-threading on unknown compilers.
- Make `ffi/write` append to a buffer instead of insert at 0 by default.
- Add `os/getpid` to get the current process id.

View File

@@ -47,14 +47,15 @@ SPORK_TAG?=master
HAS_SHARED?=1
DEBUGGER=gdb
SONAME_SETTER=-Wl,-soname,
STRIPFLAGS=-x -S
# For cross compilation
HOSTCC?=$(CC)
HOSTAR?=$(AR)
# Symbols are (optionally) removed later, keep -g as default!
CFLAGS?=-O2 -g
CFLAGS?=-O0 -g
LDFLAGS?=-rdynamic
LIBJANET_LDFLAGS?=$(LD_FLAGS)
LIBJANET_LDFLAGS?=$(LDFLAGS)
RUN:=$(RUN)
@@ -80,6 +81,12 @@ ifeq ($(UNAME), Darwin)
LDCONFIG:=true
else ifeq ($(UNAME), Linux)
CLIBS:=$(CLIBS) -lrt -ldl
else ifeq ($(UNAME), SunOS)
BUILD_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
BOOT_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
CLIBS:=-lsocket -lm
STRIPFLAGS=-x
LDCONFIG:=false
endif
# For other unix likes, add flags here!
@@ -131,7 +138,8 @@ JANET_LOCAL_HEADERS=src/core/features.h \
src/core/regalloc.h \
src/core/compile.h \
src/core/emit.h \
src/core/symcache.h
src/core/symcache.h \
src/core/sysir.h
JANET_CORE_SOURCES=src/core/abstract.c \
src/core/array.c \
@@ -166,6 +174,9 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/strtod.c \
src/core/struct.c \
src/core/symcache.c \
src/core/sysir.c \
src/core/sysir_c.c \
src/core/sysir_x86.c \
src/core/table.c \
src/core/tuple.c \
src/core/util.c \
@@ -213,9 +224,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
########################
ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.38.dylib
SONAME=libjanet.1.40.dylib
else
SONAME=libjanet.so.1.38
SONAME=libjanet.so.1.40
endif
ifeq ($(MINGW_COMPILER), clang)
@@ -289,7 +300,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
README.md build/c/janet.c build/c/shell.c
mkdir -p build/$(JANET_DIST_DIR)/bin
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet'
strip $(STRIPFLAGS) 'build/$(JANET_DIST_DIR)/bin/janet'
mkdir -p build/$(JANET_DIST_DIR)/include
cp build/janet.h build/$(JANET_DIST_DIR)/include/
mkdir -p build/$(JANET_DIST_DIR)/lib/
@@ -336,7 +347,7 @@ build/janet.pc: $(JANET_TARGET)
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
mkdir -p '$(DESTDIR)$(BINDIR)'
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
strip -x -S '$(DESTDIR)$(BINDIR)/janet'
strip $(STRIPFLAGS) '$(DESTDIR)$(BINDIR)/janet'
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'

View File

@@ -213,6 +213,10 @@ gmake install-jpm-git
NetBSD build instructions are the same as the FreeBSD build instructions.
Alternatively, install the package directly with `pkgin install janet`.
### illumos
Building on illumos is exactly the same as building on FreeBSD.
### 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#).

View File

@@ -20,11 +20,11 @@
@setlocal
@rem Example use asan
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi /DEBUG
@set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib /DEBUG
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
@set JANET_LINK=link /nologo
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
@rem set JANET_LINK=link /nologo
@set JANET_LINK_STATIC=lib /nologo
@@ -49,6 +49,7 @@ for %%f in (src\boot\*.c) do (
)
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
@if errorlevel 1 goto :BUILDFAIL
@rem note that there is no default sysroot being baked in
build\janet_boot . > build\c\janet.c
@if errorlevel 1 goto :BUILDFAIL

View File

@@ -0,0 +1,6 @@
# Linux only - uses abstract unix domain sockets
(ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn))))
(ev/sleep 1)
(def s (net/connect :unix "@abc123" :stream))
(:write s "hello")
(:close s)

View File

@@ -1,4 +1,4 @@
@{
:name "sample-dep1"
:dependencies ["sample-dep2"]
:dependencies [{:name "sample-dep2"}]
}

View File

@@ -0,0 +1,71 @@
###
### Create a .bmp file on linux.
###
# Quick run and view on Linux:
# build/janet examples/sysir/drawing.janet > temp.c && cc temp.c && ./a.out > temp.bmp && feh temp.bmp
(use ./frontend)
(defn-external write:void [fd:int mem:pointer size:uint])
(defn-external exit:void [x:int])
# assume 128x128 32 bit color image
# Size : 128 * 128 * 4 + align(14 + 40, 4) = 65592
# dib offset : align(14 + 40, 4) = 56
(defsys write_32:void [x:uint]
(write 1 (address x) 4)
(return))
(defsys write_16:void [x:uint]
(write 1 (address x) 2)
(return))
(defsys write_header:void [w:uint h:uint]
(write 1 "BM" 2)
(def size:uint (+ 56 (* w h 4)))
(write_32 size)
(write_32 0)
(write_32 56) # pixel array offset
# Begin DIB
(write_32 40) # dib size
(write_32 w)
(write_32 h)
(write_16 1) # color panes - must be 1
(write_16 32) # bits per pixel
(write_32 0) # compression method - no compression
(write_32 0) # image size - not needed when no compression, 0 should be fine
(write_32 4096) # pixels per meter - horizontal resolution
(write_32 4096) # pixels per meter - vertical resolution
(write_32 0) # number of colors in palette - no palette so 0
(write_32 0) # number of "important colors" ignored in practice
(write_16 0) # add "gap 1" to align pixel array to multiple of 4 bytes
(return))
(defsys draw:void [w:uint h:uint]
(def red:uint 0xFFFF0000)
(def blue:uint 0xFF0000FF)
(def size:uint (* w h 4))
(var y:uint 0)
(while (< y h)
(var x:uint 0)
(while (< x w)
(write_32 (if (> y 64) blue red))
(set x (+ 1 x)))
(set y (+ y 1)))
(return))
(defsys main:int []
(def w:uint 512)
(def h:uint 512)
(write_header w h)
(draw w h)
(return 0))
####
#(dump)
(print "#include <unistd.h>")
(dumpc)
#(dumpx64)

View File

@@ -0,0 +1,86 @@
###
### Create a .bmp file on linux.
###
# Quick run and view on Linux:
# build/janet examples/sysir/drawing2.janet > temp.c && cc temp.c && ./a.out > temp.bmp && feh temp.bmp
(use ./frontend)
(setdyn :verbose true)
# Pointer types
(defpointer p32 uint)
(defpointer p16 u16)
(defpointer cursor p32)
# External
(defn-external write:void [fd:int mem:pointer size:uint])
(defn-external exit:void [x:int])
(defn-external malloc:p32 [size:uint])
(defsys w32:void [c:cursor x:uint]
(def p:p32 (load c))
(store p x)
(store c (the p32 (pointer-add p 1)))
(return))
(defsys w16:void [c:cursor x:uint]
# Casting needs revisiting
(def p:p16 (cast (the p32 (load c))))
(store p (the u16 (cast x)))
(store c (the p32 (cast (the p16 (pointer-add p 1)))))
(return))
(defsys makebmp:p32 [w:uint h:uint]
(def size:uint (+ 56 (* w h 4)))
(def mem:p32 (malloc size))
(def c:cursor (cast (malloc 4)))
#(def cursor_data:p32 mem)
#(def c:cursor (address cursor_data))
(store c mem)
(w16 c 0x4D42) # ascii "BM"
(w32 c size)
(w32 c 0)
(w32 c 56)
(w32 c 40)
(w32 c w)
(w32 c h)
(w16 c 1)
(w16 c 32)
(w32 c 0)
(w32 c 0)
(w32 c 4096)
(w32 c 4096)
(w32 c 0)
(w32 c 0)
(w16 c 0) # padding
# Draw
(def red:uint 0xFFFF0000)
(def blue:uint 0xFF0000FF)
(def green:uint 0xFF00FF00)
(var y:uint 0)
(while (< y h)
(var x:uint 0)
(while (< x w)
(def d2:uint (+ (* x x) (* y y)))
(if (> d2 100000)
(if (> d2 200000) (w32 c green) (w32 c blue))
(w32 c red))
(set x (+ 1 x)))
(set y (+ y 1)))
(write 1 mem size)
(return mem))
(defsys main:int []
(def w:uint 512)
(def h:uint 512)
(makebmp w h)
(return 0))
####
(dumpx64)
#(print "#include <unistd.h>")
#(dumpc)

View File

@@ -0,0 +1,567 @@
# Make a language frontend for the sysir.
# Dialect:
# TODO -
# * arrays (declaration, loads, stores)
(defdyn *ret-type* "Current function return type")
(def slot-to-name @[])
(def name-to-slot @{})
(def type-to-name @[])
(def name-to-type @{})
(def slot-types @{})
(def functions @{})
(def type-fields @{})
(def syscalls @{})
(defn get-slot
[&opt new-name]
(def next-slot (length slot-to-name))
(array/push slot-to-name new-name)
(if new-name (put name-to-slot new-name next-slot))
next-slot)
(defn named-slot
[name]
(assert (get name-to-slot name)))
(defn make-type
[&opt new-name]
(def next-type (length type-to-name))
(array/push type-to-name new-name)
(if new-name (put name-to-type new-name next-type))
next-type)
(defn named-type
[name]
(def t (get name-to-type name))
(assert t)
t)
(defn binding-type
[name]
(def slot (assert (get name-to-slot name)))
(assert (get slot-types slot)))
(defn slot-type
[slot]
(assert (get slot-types slot)))
(defn assign-type
[name typ]
(def slot (get name-to-slot name))
(put slot-types slot typ))
(defn assign-slot-type
[slot typ]
(put slot-types slot typ))
(defn setup-default-types
[ctx]
(def into @[])
(defn add-prim-type
[name native-name]
(array/push into ~(type-prim ,name ,native-name))
(make-type name))
(add-prim-type 'float 'f32)
(add-prim-type 'double 'f64)
(add-prim-type 'int 's32)
(add-prim-type 'uint 'u32)
(add-prim-type 'long 's64)
(add-prim-type 'ulong 'u64)
(add-prim-type 'boolean 'boolean)
(add-prim-type 's16 's16)
(add-prim-type 'u16 'u16)
(add-prim-type 'byte 'u8)
(add-prim-type 'void 'void)
(array/push into ~(type-pointer pointer void))
(make-type 'pointer)
(sysir/asm ctx into)
ctx)
(defn type-extract
"Given a symbol:type combination, extract the proper name and the type separately"
[combined-name &opt default-type]
(def parts (string/split ":" combined-name 0 2))
(def [name tp] parts)
[(symbol name) (symbol (or tp default-type))])
(var do-binop nil)
(var do-comp nil)
###
### Inside functions
###
(defn visit1
"Take in a form and compile code and put it into `into`. Return result slot."
[code into &opt no-return type-hint]
(def subresult
(cond
# Compile a constant
(string? code) ~(pointer ,code)
(boolean? code) ~(boolean ,code)
(number? code) ~(,(or type-hint 'double) ,code) # TODO - should default to double
# Needed?
(= :core/u64 (type code)) ~(ulong ,code)
(= :core/s64 (type code)) ~(long ,code)
# Binding
(symbol? code)
(named-slot code)
# Array literals
(and (tuple? code) (= :brackets (tuple/type code)))
(do
(assert type-hint (string/format "unknown type for array literal %v" code))
~(,type-hint ,code))
# Compile forms
(and (tuple? code) (= :parens (tuple/type code)))
(do
(assert (> (length code) 0))
(def [op & args] code)
(case op
# Arithmetic
'+ (do-binop 'add args into type-hint)
'- (do-binop 'subtract args into type-hint)
'* (do-binop 'multiply args into type-hint)
'/ (do-binop 'divide args into type-hint)
'<< (do-binop 'shl args into type-hint)
'>> (do-binop 'shr args into type-hint)
# Comparison
'= (do-comp 'eq args into)
'not= (do-comp 'neq args into)
'< (do-comp 'lt args into)
'<= (do-comp 'lte args into)
'> (do-comp 'gt args into)
'>= (do-comp 'gte args into)
# Pointers
'pointer-add
(do
(assert (= 2 (length args)))
(def [base offset] args)
(def base-slot (visit1 base into false type-hint))
(def offset-slot (visit1 offset into false 'int))
(def slot (get-slot))
(when type-hint (array/push into ~(bind ,slot ,type-hint)))
(array/push into ~(pointer-add ,slot ,base-slot ,offset-slot))
slot)
'pointer-sub
(do
(assert (= 2 (length args)))
(def [base offset] args)
(def base-slot (visit1 base into false type-hint))
(def offset-slot (visit1 offset into false 'int))
(def slot (get-slot))
(when type-hint (array/push into ~(bind ,slot ,type-hint)))
(array/push into ~(pointer-subtract ,slot ,base-slot ,offset-slot))
slot)
# Type hinting
'the
(do
(assert (= 2 (length args)))
(def [xtype x] args)
(def result (visit1 x into false xtype))
(if (tuple? result) # constant
(let [[t y] result]
(assertf (= t xtype) "type mismatch, %p doesn't match %p" t xtype)
[xtype y])
(do
(array/push into ~(bind ,result ,xtype))
result)))
# Casting
'cast
(do
(assert (= 1 (length args)))
(assert type-hint) # should we add an explicit cast type?
(def [x] args)
(def slot (get-slot))
(def result (visit1 x into false))
(array/push into ~(bind ,slot ,type-hint))
(array/push into ~(cast ,slot ,result))
slot)
# Named bindings
'def
(do
(assert (= 2 (length args)))
(def [full-name value] args)
(assert (symbol? full-name))
(def [name tp] (type-extract full-name 'int))
(def result (visit1 value into false tp))
(def slot (get-slot name))
(assign-type name tp)
(array/push into ~(bind ,slot ,tp))
(array/push into ~(move ,slot ,result))
slot)
# Named variables
'var
(do
(assert (= 2 (length args)))
(def [full-name value] args)
(assert (symbol? full-name))
(def [name tp] (type-extract full-name 'int))
(def result (visit1 value into false tp))
(def slot (get-slot name))
(assign-type name tp)
(array/push into ~(bind ,slot ,tp))
(array/push into ~(move ,slot ,result))
slot)
# Address of (& operator in C)
'address
(do
(assert (= 1 (length args)))
(def [thing] args)
(def [name tp] (type-extract thing 'int))
(def result (visit1 thing into false tp))
(def slot (get-slot))
#
(array/push into ~(bind ,slot ,type-hint))
(array/push into ~(address ,slot ,result))
slot)
'load
(do
(assert (= 1 (length args)))
(assert type-hint)
(def [thing] args)
# (def [name tp] (type-extract thing 'pointer))
(def result (visit1 thing into false))
(def slot (get-slot))
(def ptype type-hint)
(array/push into ~(bind ,slot ,ptype))
(array/push into ~(load ,slot ,result))
slot)
'store
(do
(assert (= 2 (length args)))
(def [dest value] args)
# (def [name tp] (type-extract dest 'pointer))
(def dest-r (visit1 dest into false))
(def value-r (visit1 value into false))
(array/push into ~(store ,dest-r ,value-r))
value-r)
# Assignment
'set
(do
(assert (= 2 (length args)))
(def [to x] args)
(def type-hint (binding-type to))
(def result (visit1 x into false type-hint))
(def toslot (named-slot to))
(array/push into ~(move ,toslot ,result))
toslot)
# Return
'return
(do
(assert (>= 1 (length args)))
(if (empty? args)
(array/push into '(return))
(do
(def [x] args)
(array/push into ~(return ,(visit1 x into false (dyn *ret-type*))))))
nil)
# Sequence of operations
'do
(do
(each form (slice args 0 -2) (visit1 form into true))
(visit1 (last args) into false type-hint))
# While loop
'while
(do
(def lab-test (keyword (gensym)))
(def lab-exit (keyword (gensym)))
(assert (< 1 (length args)))
(def [cnd & body] args)
(array/push into lab-test)
(def condition-slot (visit1 cnd into false 'boolean))
(array/push into ~(branch-not ,condition-slot ,lab-exit))
(each code body
(visit1 code into true))
(array/push into ~(jump ,lab-test))
(array/push into lab-exit)
nil)
# Branch
'if
(do
(def lab (keyword (gensym)))
(def lab-end (keyword (gensym)))
(assert (< 2 (length args) 4))
(def [cnd tru fal] args)
(def condition-slot (visit1 cnd into false 'boolean))
(def ret (if type-hint (get-slot)))
(when type-hint (array/push into ~(bind ,ret ,type-hint)))
(array/push into ~(branch ,condition-slot ,lab))
# false path
(if type-hint
(array/push into ~(move ,ret ,(visit1 fal into false type-hint)))
(visit1 fal into true))
(array/push into ~(jump ,lab-end))
(array/push into lab)
# true path
(if type-hint
(array/push into ~(move ,ret ,(visit1 tru into false type-hint)))
(visit1 tru into true))
(array/push into lab-end)
ret)
# Insert IR
'ir
(do
(assert no-return)
(array/push into ;args)
nil)
# Assume function call or syscall
(do
(def slots @[])
(def signature (get functions op))
(def is-syscall (get syscalls op))
(assert signature (string "unknown function " op))
(def ret (if no-return nil (get-slot)))
(when ret
(array/push into ~(bind ,ret ,(first signature)))
(assign-type ret (first signature)))
(each [arg-type arg] (map tuple (drop 1 signature) args)
(array/push slots (visit1 arg into false arg-type)))
(if is-syscall
(array/push into ~(syscall :default ,ret (int ,is-syscall) ,;slots))
(array/push into ~(call :default ,ret [pointer ,op] ,;slots)))
ret)))
(errorf "cannot compile %q" code)))
# Check type-hint matches return type
(if type-hint
(when-let [t (first subresult)] # TODO - Disallow empty types
(assert (= type-hint t) (string/format "%j, expected type %v, got %v" code type-hint t))))
subresult)
(varfn do-binop
"Emit an operation such as (+ x y).
Extended to support any number of arguments such as (+ x y z ...)"
[opcode args into type-hint]
(var typ type-hint)
(var final nil)
(def slots @[])
(each arg args
(def right (visit1 arg into false typ))
(when (number? right) (array/push slots right))
# If we don't have a type hint, infer types from bottom up
(when (nil? typ)
(when-let [new-typ (get slot-types right)]
(set typ new-typ)))
(set final
(if final
(let [result (get-slot)]
(array/push slots result)
(array/push into ~(,opcode ,result ,final ,right))
result)
right)))
(assert typ (string "unable to infer type for %j" [opcode ;args]))
(each slot (distinct slots)
(array/push into ~(bind ,slot ,typ)))
(assert final))
(varfn do-comp
"Emit a comparison form such as (= x y z ...)"
[opcode args into]
(def result (get-slot))
(def needs-temp (> 2 (length args)))
(def temp-result (if needs-temp (get-slot) nil))
(array/push into ~(bind ,result boolean))
(when needs-temp
(array/push into ~(bind ,temp-result boolean)))
(var left nil)
(var first-compare true)
(var typ nil)
(each arg args
(def right (visit1 arg into false typ))
# If we don't have a type hint, infer types from bottom up
(when (nil? typ)
(when-let [new-typ (get slot-types right)]
(set typ new-typ)))
(when left
(if first-compare
(array/push into ~(,opcode ,result ,left ,right))
(do
(array/push into ~(,opcode ,temp-result ,left ,right))
(array/push into ~(and ,result ,temp-result ,result))))
(set first-compare false))
(set left right))
result)
###
### Top level
###
(defn top
"Visit and emit code for a top level form."
[ctx form]
(assert (tuple? form))
(def [head & rest] form)
(case head
# Declare a struct
'defstruct
(do
(def into @[])
(def [name & fields] rest)
(assert (even? (length fields)) "expected an even number of fields for struct definition")
(def field-types @[])
(each [field-name typ] (partition 2 fields)
# TODO - don't ignore field names
(array/push field-types typ))
(array/push into ~(type-struct ,name ,;field-types))
# (eprintf "%.99M" into)
(sysir/asm ctx into))
# Declare a union
'defunion
(do
(def into @[])
(def [name & fields] rest)
(assert (even? (length fields)) "expected an even number of fields for struct definition")
(def field-types @[])
(each [field-name typ] (partition 2 fields)
# TODO - don't ignore field names
(array/push field-types typ))
(array/push into ~(type-union ,name ,;field-types))
# (eprintf "%.99M" into)
(sysir/asm ctx into))
# Declare a pointer type
'defpointer
(do
(def into @[])
(def [name element] rest)
(def field-types @[])
(array/push into ~(type-pointer ,name ,element))
# (eprintf "%.99M" into)
(sysir/asm ctx into))
# Declare an array type
'defarray
(do
(def into @[])
(def [name element cnt] rest)
(assert (and (pos? cnt) (int? cnt)) "expected positive integer for array count")
(array/push into ~(type-array ,name ,element ,cnt))
# (eprintf "%.99M" into)
(sysir/asm ctx into))
# External function
'defn-external
(do
(def [name args] rest)
(assert (tuple? args))
(def [fn-name fn-tp] (type-extract name 'void))
(def pcount (length args)) #TODO - more complicated signatures
(def signature @[fn-tp])
(each arg args
(def [name tp] (type-extract arg 'int))
(array/push signature tp))
(put functions fn-name (freeze signature)))
# External syscall
'defn-syscall
(do
(def [name sysnum args] rest)
(assert (tuple? args))
(def [fn-name fn-tp] (type-extract name 'void))
(def pcount (length args)) #TODO - more complicated signatures
(def signature @[fn-tp])
(each arg args
(def [name tp] (type-extract arg 'int))
(array/push signature tp))
(put syscalls fn-name sysnum)
(put functions fn-name (freeze signature)))
# Top level function definition
'defn
(do
# TODO doc strings
(table/clear name-to-slot)
(table/clear slot-types)
(array/clear slot-to-name)
(def [name args & body] rest)
(assert (tuple? args))
(def [fn-name fn-tp] (type-extract name 'void))
(def pcount (length args)) #TODO - more complicated signatures
(def ir-asm
@[~(link-name ,(string fn-name))
~(parameter-count ,pcount)])
(def signature @[fn-tp])
(each arg args
(def [name tp] (type-extract arg 'int))
(def slot (get-slot name))
(assign-type name tp)
(array/push signature tp)
(array/push ir-asm ~(bind ,slot ,tp)))
(with-dyns [*ret-type* fn-tp]
(each part body
(visit1 part ir-asm true)))
(put functions fn-name (freeze signature))
(when (dyn :verbose) (eprintf "%.99M" ir-asm))
(sysir/asm ctx ir-asm))
(errorf "unknown form %p" form)))
###
### Setup
###
(def ctx (sysir/context))
(setup-default-types ctx)
(defn compile1
[x]
(top ctx x))
(defn dump
[]
(eprintf "%.99M\n" (sysir/to-ir ctx)))
(defn dumpx64
[]
(print (sysir/to-x64 ctx)))
(defn dumpx64-windows
[]
(print (sysir/to-x64 ctx @"" :windows)))
(defn dumpc
[]
(print (sysir/to-c ctx)))
###
### Top Level aliases
###
(defmacro defstruct [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defstruct ,;args))])
(defmacro defunion [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defunion ,;args))])
(defmacro defarray [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defarray ,;args))])
(defmacro defpointer [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defpointer ,;args))])
(defmacro defn-external [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn-external ,;args))])
(defmacro defn-syscall [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn-syscall ,;args))])
(defmacro defsys [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn ,;args))])

View File

@@ -0,0 +1,16 @@
(use ./frontend)
(defn-external printf:int [fmt:pointer])
(defn-external exit:void [x:int])
(defsys _start:void []
(printf "hello, world!\n")
(exit (the int 0))
(return))
(defn main [& args]
(def [_ what] args)
(eprint "MODE: " what)
(case what
"c" (dumpc)
"x64" (dumpx64)))

5
examples/sysir/run_drawing.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
valgrind build/janet examples/sysir/drawing.janet > temp.c
cc temp.c
./a.out > temp.bmp
feh temp.bmp

5
examples/sysir/run_drawing2.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
valgrind build/janet examples/sysir/drawing2.janet > temp.nasm
nasm -felf64 temp.nasm -l temp.lst -o temp.o
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
valgrind ./temp.bin

View File

@@ -0,0 +1,4 @@
janet.exe examples/sysir/windows_samples.janet > temp.nasm
nasm -fwin64 temp.nasm -l temp.lst -o temp.o
link /entry:Start /subsystem:windows kernel32.lib user32.lib temp.o /out:temp.exe
temp.exe

5
examples/sysir/run_samples.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
valgrind build/janet examples/sysir/samples.janet > temp.nasm
nasm -felf64 temp.nasm -l temp.lst -o temp.o
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
valgrind ./temp.bin

View File

@@ -0,0 +1,72 @@
(use ./frontend)
(defstruct vec3
a float
b float
c float)
(defunion myunion
a float
b double
c long)
(defarray myvec float 4)
(defarray mymat myvec 4)
(defn-external printf:int [fmt:pointer x:int]) # TODO varargs
(defn-external exit:void [x:int])
(defsys square:int
[num:int]
(return (* 1 num num)))
(defsys simple:int [x:int]
(def xyz:int (+ 1 2 3))
(return (* x 2 x)))
(defsys myprog:int []
(def xyz:int (+ 1 2 3))
(def abc:int (* 4 5 6))
(def x:boolean (= xyz 5))
(var i:int 0)
(while (< i 10)
(set i (+ 1 i))
(printf "i = %d\n" i))
(printf "hello, world!\n%d\n" (if x abc xyz))
(return (simple (* abc xyz))))
(defsys doloop [x:int y:int]
(var i:int x)
(while (< i y)
(set i (+ 1 i))
(printf "i = %d\n" i))
(myprog)
(return x))
(defsys _start:void []
#(syscall 1 1 "Hello, world!\n" 14)
(doloop 10 20)
(exit (the int 0))
(return))
(defsys test_inttypes:ulong []
(def x:ulong 123:u)
(return (+ x x)))
(defsys test_arrays:myvec [a:myvec b:myvec]
(return (+ a b)))
'(defsys make_array:myvec []
(def vec:myvec [0 0 0 0])
(return vec))
'(defsys make_mat:mymat []
(def mat:mymat [[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]])
(return mat))
####
#(dump)
#(dumpc)
(dumpx64)

View File

@@ -0,0 +1,10 @@
(def ir-asm
'((link-name "redefine_type_fail")
(type-prim Real f32)
(type-prim 1 s32)
(bind bob Real)
(return bob)))
(def ctx (sysir/context))
(sysir/asm ctx ir-asm)
(print (sysir/to-c ctx))

View File

@@ -0,0 +1,14 @@
(use ./frontend)
(def winmain
'(defn Start:void []
(MessageBoxExA (the pointer 0) "Hello, world!" "Test" 0 (the s16 0))
(ExitProcess (the int 0))
(return)))
####
(compile1 winmain)
#(dump)
#(dumpc)
(dumpx64-windows)

24
examples/sysir/x64.janet Normal file
View File

@@ -0,0 +1,24 @@
(use ./frontend)
(defn-external printf:int [fmt:pointer x:int])
(defn-external exit:void [x:int])
(defsys doloop [x:int y:int]
(var i:int x)
(printf "initial i = %d\n" i)
(while (< i y)
(set i (+ 1 i))
(printf "i = %d\n" i))
(return x))
(defsys _start:void []
(doloop 10 20)
(exit (the int 0))
(return))
(defn main [& args]
(def [_ what] args)
(eprint "MODE: " what)
(case what
"c" (dumpc)
"x64" (dumpx64)))

View File

@@ -214,7 +214,7 @@ Don't execute a script, only compile it to check for errors. Useful for linting
.BR \-m\ syspath
Set the dynamic binding :syspath to the string syspath so that Janet will load system modules
from a directory different than the default. The default is set when Janet is built, and defaults to
/usr/local/lib/janet on Linux/Posix, and C:/Janet/Library on Windows. This option supersedes JANET_PATH.
/usr/local/lib/janet on Linux/Posix. On Windows, there is no default value. This option supersedes JANET_PATH.
.TP
.BR \-c\ source\ output
@@ -255,8 +255,7 @@ and then arguments to the script.
.RS
The location to look for Janet libraries. This is the only environment variable Janet needs to
find native and source code modules. If no JANET_PATH is set, Janet will look in
the default location set at compile time. This should be a list of as well as a colon
separate list of such directories.
the default location set at compile time. This should be a colon-separated list of directory names on Linux/Posix, and a semicolon-separated list on Windows. Note that a typical setup (i.e. not NixOS / Guix) will only use a single directory.
.RE
.B JANET_PROFILE

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.38.0')
version : '1.40.0')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -126,7 +126,8 @@ core_headers = [
'src/core/regalloc.h',
'src/core/compile.h',
'src/core/emit.h',
'src/core/symcache.h'
'src/core/symcache.h',
'src/core/sysir.h',
]
core_src = [
@@ -163,6 +164,9 @@ core_src = [
'src/core/strtod.c',
'src/core/struct.c',
'src/core/symcache.c',
'src/core/sysir.c',
'src/core/sysir_c.c',
'src/core/sysir_x86.c',
'src/core/table.c',
'src/core/tuple.c',
'src/core/util.c',
@@ -281,6 +285,7 @@ test_files = [
'test/suite-corelib.janet',
'test/suite-debug.janet',
'test/suite-ev.janet',
'test/suite-ev2.janet',
'test/suite-ffi.janet',
'test/suite-filewatch.janet',
'test/suite-inttypes.janet',
@@ -296,6 +301,7 @@ test_files = [
'test/suite-strtod.janet',
'test/suite-struct.janet',
'test/suite-symcache.janet',
'test/suite-sysir.janet',
'test/suite-table.janet',
'test/suite-tuple.janet',
'test/suite-unknown.janet',

View File

@@ -7,7 +7,7 @@
###
###
(def defn :macro
(def defn :macro :flycheck
```
(defn name & more)
@@ -43,7 +43,7 @@
# Build return value
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
(defn defmacro :macro
(defn defmacro :macro :flycheck
"Define a macro."
[name & more]
(setdyn name @{}) # override old macro definitions in the case of a recursive macro
@@ -57,12 +57,12 @@
[f & args]
(f ;args))
(defmacro defmacro-
(defmacro defmacro- :flycheck
"Define a private macro that will not be exported."
[name & more]
(apply defn name :macro :private more))
(defmacro defn-
(defmacro defn- :flycheck
"Define a private function that will not be exported."
[name & more]
(apply defn name :private more))
@@ -144,7 +144,7 @@
(defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns)))
(defmacro %= "Shorthand for (set x (% x n))." [x & ns] ~(set ,x (,% ,x ,;ns)))
(defmacro assert
(defmacro assert :flycheck # should top level assert flycheck?
"Throw an error if x is not truthy. Will not evaluate `err` if x is truthy."
[x &opt err]
(def v (gensym))
@@ -154,11 +154,11 @@
,v
(,error ,(if err err (string/format "assert failure in %j" x))))))
(defmacro defdyn
(defmacro defdyn :flycheck
``Define an alias for a keyword that is used as a dynamic binding. The
alias is a normal, lexically scoped binding that can be used instead of
a keyword to prevent typos. `defdyn` does not set dynamic bindings or otherwise
replace `dyn` and `setdyn`. The alias _must_ start and end with the `*` character, usually
replace `dyn` and `setdyn`. The alias *must* start and end with the `*` character, usually
called "earmuffs".``
[alias & more]
(assert (symbol? alias) "alias must be a symbol")
@@ -171,6 +171,9 @@
(defdyn *macro-form*
"Inside a macro, is bound to the source form that invoked the macro")
(defdyn *flychecking*
"Check if the current form is being evaluated inside `flycheck`. Will be `true` while flychecking.")
(defdyn *lint-error*
"The current lint error level. The error level is the lint level at which compilation will exit with an error and not continue.")
@@ -290,22 +293,6 @@
(array/concat accum body)
(tuple/slice accum 0))
(defmacro try
``Try something and catch errors. `body` is any expression,
and `catch` should be a form, the first element of which is a tuple. This tuple
should contain a binding for errors and an optional binding for
the fiber wrapping the body. Returns the result of `body` if no error,
or the result of `catch` if an error.``
[body catch]
(let [[[err fib]] catch
f (gensym)
r (gensym)]
~(let [,f (,fiber/new (fn :try [] ,body) :ie)
,r (,resume ,f)]
(if (,= (,fiber/status ,f) :error)
(do (def ,err ,r) ,(if fib ~(def ,fib ,f)) ,;(tuple/slice catch 1))
,r))))
(defmacro protect
`Evaluate expressions, while capturing any errors. Evaluates to a tuple
of two elements. The first element is true if successful, false if an
@@ -352,6 +339,23 @@
(tuple 'if $fi $fi ret))))))
ret)
(defmacro try
``Try something and catch errors. `body` is any expression,
and `catch` should be a form, the first element of which is a tuple. This tuple
should contain a binding for errors and an optional binding for
the fiber wrapping the body. Returns the result of `body` if no error,
or the result of `catch` if an error.``
[body catch]
(assert (and (not (empty? catch)) (indexed? (catch 0))) "the first element of `catch` must be a tuple or array")
(let [[err fib] (catch 0)
r (or err (gensym))
f (or fib (gensym))]
~(let [,f (,fiber/new (fn :try [] ,body) :ie)
,r (,resume ,f)]
(if (,= (,fiber/status ,f) :error)
(do ,;(tuple/slice catch 1))
,r))))
(defmacro with-syms
"Evaluates `body` with each symbol in `syms` bound to a generated, unique symbol."
[syms & body]
@@ -2288,8 +2292,8 @@
(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`
child values 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)
@@ -2353,7 +2357,7 @@
(set macexvar macex)
(defmacro varfn
(defmacro varfn :flycheck
``Create a function that can be rebound. `varfn` has the same signature
as `defn`, but defines functions in the environment as vars. If a var `name`
already exists in the environment, it is rebound to the new function. Returns
@@ -3180,12 +3184,17 @@
use the name of the module as a prefix. One can also use "`:export true`"
to re-export the imported symbols. If "`:exit true`" is given as an argument,
any errors encountered at the top level in the module will cause `(os/exit 1)`
to be called. Dynamic bindings will NOT be imported. Use :fresh to bypass the
module cache. Use `:only [foo bar baz]` to only import select bindings into the
current environment.``
to be called. Dynamic bindings will NOT be imported. Use :fresh with a truthy
value to bypass the module cache. Use `:only [foo bar baz]` to only import
select bindings into the current environment.``
[path & args]
(assertf (even? (length args)) "args should have even length: %n" args)
(def ps (partition 2 args))
(def argm (mapcat (fn [[k v]] [k (case k :as (string v) :only ~(quote ,v) v)]) ps))
(def argm
(mapcat (fn [[k v]]
(assertf (keyword? k) "expected keyword, got %s: %n" (type k) k)
[k (case k :as (string v) :only ~(quote ,v) v)])
ps))
(tuple import* (string path) ;argm))
(defmacro use
@@ -3248,12 +3257,10 @@
# Terminal codes for emission/tokenization
(def delimiters
(if has-color
{:underline ["\e[4m" "\e[24m"]
:code ["\e[97m" "\e[39m"]
{:code ["\e[97m" "\e[39m"]
:italics ["\e[4m" "\e[24m"]
:bold ["\e[1m" "\e[22m"]}
{:underline ["_" "_"]
:code ["`" "`"]
{:code ["`" "`"]
:italics ["*" "*"]
:bold ["**" "**"]}))
(def modes @{})
@@ -3384,7 +3391,6 @@
(= b (chr `\`)) (do
(++ token-length)
(buffer/push token (get line (++ i))))
(= b (chr "_")) (delim :underline)
(= b (chr "*"))
(if (= (chr "*") (get line (+ i 1)))
(do (++ i)
@@ -3916,8 +3922,14 @@
(compwhen (dyn 'net/listen)
(defn net/server
"Start a server asynchronously with `net/listen` and `net/accept-loop`. Returns the new server stream."
``
Starts a server with `net/listen`. Runs `net/accept-loop` asynchronously if
`handler` is set and `type` is `:stream` (the default). It is invalid to set
`handler` if `type` is `:datagram`. Returns the new server stream.
``
[host port &opt handler type no-reuse]
(assert (not (and (= type :datagram) handler))
"handler not supported for :datagram servers")
(def s (net/listen host port type no-reuse))
(if handler
(ev/go (fn [] (net/accept-loop s handler))))
@@ -3936,7 +3948,7 @@
[& forms]
(def state (gensym))
(def loaded (gensym))
~((fn []
~((fn :delay []
(var ,state nil)
(var ,loaded nil)
(fn []
@@ -3968,7 +3980,7 @@
:lazy lazy
:map-symbols map-symbols}))
(defmacro ffi/defbind-alias
(defmacro ffi/defbind-alias :flycheck
"Generate bindings for native functions in a convenient manner.
Similar to defbind but allows for the janet function name to be
different than the FFI function."
@@ -3979,6 +3991,8 @@
(def formal-args (map 0 arg-pairs))
(def type-args (map 1 arg-pairs))
(def computed-type-args (eval ~[,;type-args]))
(if (dyn *flychecking*)
(break ~(defn ,alias ,;meta [,;formal-args] nil)))
(def {:native lib
:lazy lazy
:native-lazy llib
@@ -3994,7 +4008,7 @@
~(defn ,alias ,;meta [,;formal-args]
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))
(defmacro ffi/defbind
(defmacro ffi/defbind :flycheck
"Generate bindings for native functions in a convenient manner."
[name ret-type & body]
~(ffi/defbind-alias ,name ,name ,ret-type ,;body)))
@@ -4005,6 +4019,51 @@
###
###
(def- flycheck-specials @{})
(defn- flycheck-evaluator
``
An evaluator function that is passed to `run-context` that lints
(flychecks) code for `flycheck`. This means code will be parsed,
compiled, and have macros expanded, but the code will not be
evaluated.
``
[thunk source env where]
(when (and (tuple? source) (= (tuple/type source) :parens))
(def head (source 0))
(def entry (get env head {}))
(def fc (get flycheck-specials head (get entry :flycheck)))
(cond
# Sometimes safe form
(function? fc)
(fc thunk source env where)
# Always safe form
fc
(thunk))))
(defn flycheck
```
Check a file for errors without running the file. Found errors
will be printed to stderr in the usual format. Top level functions
and macros that have the metadata `:flycheck` will also be evaluated
during flychecking. For full control, the `:flycheck` metadata can
also be a function that takes 4 arguments - `thunk`, `source`, `env`,
and `where`, the same as the `:evaluator` argument to `run-context`.
Other arguments to `flycheck` are the same as `dofile`. Returns nil.
```
[path &keys kwargs]
(def mc @{})
(def new-env (make-env (get kwargs :env)))
(put new-env *flychecking* true)
(put new-env *module-cache* @{})
(put new-env *module-loading* @{})
(put new-env *module-make-env* (fn :make-flycheck-env [&] (make-env new-env)))
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs) :env new-env)
([e f]
(debug/stacktrace f e "")))
nil)
(defn- no-side-effects
`Check if form may have side effects. If returns true, then the src
must not have side effects, such as calling a C function.`
@@ -4020,59 +4079,29 @@
(all no-side-effects (values src)))
true))
(defn- is-safe-def [x] (no-side-effects (last x)))
(defn- is-safe-def [thunk source env where]
(if (no-side-effects (last source))
(thunk)))
(def- safe-forms {'defn true 'varfn true 'defn- true 'defmacro true 'defmacro- true
'def is-safe-def 'var is-safe-def 'def- is-safe-def 'var- is-safe-def
'defglobal is-safe-def 'varglobal is-safe-def 'defdyn true})
(def- importers {'import true 'import* true 'dofile true 'require true})
(defn- use-2 [evaluator args]
(each a args (import* (string a) :prefix "" :evaluator evaluator)))
(defn- flycheck-evaluator
``An evaluator function that is passed to `run-context` that lints (flychecks) code.
This means code will parsed and compiled, macros executed, but the code will not be run.
Used by `flycheck`.``
(defn- flycheck-importer
[thunk source env where]
(when (tuple? source)
(def head (source 0))
(def safe-check
(or
(safe-forms head)
(if (symbol? head)
(if (string/has-prefix? "define-" head) is-safe-def))))
(cond
# Sometimes safe form
(function? safe-check)
(if (safe-check source) (thunk))
# Always safe form
safe-check
(thunk)
# Use
(= 'use head)
(use-2 flycheck-evaluator (tuple/slice source 1))
# Import-like form
(importers head)
(let [[l c] (tuple/sourcemap source)
newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)]
((compile newtup env where))))))
(let [[l c] (tuple/sourcemap source)
newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)]
((compile newtup env where))))
(defn flycheck
``Check a file for errors without running the file. Found errors will be printed to stderr
in the usual format. Macros will still be executed, however, so
arbitrary execution is possible. Other arguments are the same as `dofile`. `path` can also be
a file value such as stdin. Returns nil.``
[path &keys kwargs]
(def old-modcache (table/clone module/cache))
(table/clear module/cache)
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs))
([e f]
(debug/stacktrace f e "")))
(table/clear module/cache)
(merge-into module/cache old-modcache)
nil)
(defn- flycheck-use
[thunk source env where]
(each a (drop 1 source) (import* (string a) :prefix "" :evaluator flycheck-evaluator)))
# Add metadata to defs and import macros for flychecking
(each sym ['def 'var]
(put flycheck-specials sym is-safe-def))
(each sym ['def- 'var- 'defglobal 'varglobal]
(put (dyn sym) :flycheck is-safe-def))
(each sym ['import 'import* 'dofile 'require]
(put (dyn sym) :flycheck flycheck-importer))
(each sym ['use]
(put (dyn sym) :flycheck flycheck-use))
###
###
@@ -4296,20 +4325,14 @@
"Install a bundle from the local filesystem. The name of the bundle will be inferred from the bundle, or passed as a parameter :name in `config`."
[path &keys config]
(def path (bundle-rpath path))
(def clean (get config :clean))
(def check (get config :check))
(def s (sep))
# Check meta file for dependencies and default name
(def infofile-pre-1 (string path s "bundle" s "info.jdn"))
(def infofile-pre (if (fexists infofile-pre-1) infofile-pre-1 (string path s "info.jdn"))) # allow for alias
(var default-bundle-name nil)
(when (os/stat infofile-pre :mode)
(def info (-> infofile-pre slurp parse))
(def deps (get info :dependencies @[]))
(set default-bundle-name (get info :name))
(def missing (seq [d :in deps :when (not (bundle/installed? d))] (string d)))
(when (next missing) (errorf "missing dependencies %s" (string/join missing ", "))))
(def bundle-name (get config :name default-bundle-name))
# Detect bundle name
(def infofile-src1 (string path s "bundle" s "info.jdn"))
(def infofile-src2 (string path s "info.jdn"))
(def infofile-src (cond (fexists infofile-src1) infofile-src1
(fexists infofile-src2) infofile-src2))
(def info (-?> infofile-src slurp parse))
(def bundle-name (get config :name (get info :name)))
(assertf bundle-name "unable to infer bundle name for %v, use :name argument" path)
(assertf (not (string/check-set "\\/" bundle-name))
"bundle name %v cannot contain path separators" bundle-name)
@@ -4319,28 +4342,32 @@
# Setup installed paths
(prime-bundle-paths)
(os/mkdir (bundle-dir bundle-name))
# Aliases for common bundle/ files
(def bundle.janet (string path s "bundle.janet"))
(when (fexists bundle.janet) (copyfile bundle.janet (bundle-file bundle-name "init.janet")))
(when (fexists infofile-pre) (copyfile infofile-pre (bundle-file bundle-name "info.jdn")))
# Copy infofile
(def infofile-dest (bundle-file bundle-name "info.jdn"))
(when infofile-src (copyfile infofile-src infofile-dest))
# Copy aliased initfile
(def initfile-alias (string path s "bundle.janet"))
(def initfile-dest (bundle-file bundle-name "init.janet"))
(when (fexists initfile-alias) (copyfile initfile-alias initfile-dest))
# Copy some files into the new location unconditionally
(def implicit-sources (string path s "bundle"))
(when (= :directory (os/stat implicit-sources :mode))
(copyrf implicit-sources (bundle-dir bundle-name)))
(def man @{:name bundle-name :local-source path :files @[]})
(merge-into man config)
(def infofile (bundle-file bundle-name "info.jdn"))
(put man :auto-remove (get config :auto-remove))
(sync-manifest man)
(edefer (do (print "installation error, uninstalling") (bundle/uninstall bundle-name))
(when (os/stat infofile :mode)
(def info (-> infofile slurp parse))
(def deps (get info :dependencies @[]))
(when (os/stat infofile-dest :mode)
(def info (-> infofile-dest slurp parse))
(def deps (seq [d :in (get info :dependencies @[])]
(string (if (dictionary? d) (get d :name) d))))
(def missing (filter (complement bundle/installed?) deps))
(when (next missing)
(error (string "missing dependencies " (string/join missing ", "))))
(put man :dependencies deps)
(put man :info info))
(def clean (get config :clean))
(def check (get config :check))
(def module (get-bundle-module bundle-name))
(def all-hooks (seq [[k v] :pairs module :when (symbol? k) :unless (get v :private)] (keyword k)))
(put man :hooks all-hooks)
@@ -4631,7 +4658,7 @@
--reinstall (-B) name : Reinstall a bundle by bundle name
--uninstall (-u) name : Uninstall a bundle by bundle name
--update-all (-U) : Reinstall all installed bundles
--prune (-P) : Uninstalled all bundles that are orphaned
--prune (-P) : Uninstall all bundles that are orphaned
--list (-L) : List all installed bundles
-- : Stop handling options
```)
@@ -4838,7 +4865,8 @@
"src/core/regalloc.h"
"src/core/compile.h"
"src/core/emit.h"
"src/core/symcache.h"])
"src/core/symcache.h"
"src/core/sysir.h"])
(def core-sources
["src/core/abstract.c"
@@ -4874,6 +4902,9 @@
"src/core/strtod.c"
"src/core/struct.c"
"src/core/symcache.c"
"src/core/sysir.c"
"src/core/sysir_c.c"
"src/core/sysir_x86.c"
"src/core/table.c"
"src/core/tuple.c"
"src/core/util.c"

View File

@@ -4,10 +4,10 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 38
#define JANET_VERSION_MINOR 40
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.38.0"
#define JANET_VERSION "1.40.0"
/* #define JANET_BUILD "local" */

View File

@@ -164,7 +164,7 @@ void janet_os_mutex_lock(JanetOSMutex *mutex) {
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex);
if (ret) janet_panic("cannot release lock");
if (ret) janet_panicf("cannot release lock: %s", strerror(ret));
}
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {

View File

@@ -591,7 +591,7 @@ JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
#ifdef _MSC_VER
return _InterlockedOrNoFence(x, 0);
return _InterlockedOr(x, 0);
#elif defined(JANET_USE_STDATOMIC)
return atomic_load_explicit(x, memory_order_relaxed);
#else

View File

@@ -1127,4 +1127,5 @@ void janet_lib_compile(JanetTable *env) {
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, cfuns);
janet_lib_sysir(env);
}

View File

@@ -268,6 +268,9 @@ JanetSlot janetc_cslot(Janet x);
/* Search for a symbol */
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
/* Load the system dialect IR */
void janet_lib_sysir(JanetTable *env);
/* Bytecode optimization */
void janet_bytecode_movopt(JanetFuncDef *def);
void janet_bytecode_remove_noops(JanetFuncDef *def);

View File

@@ -66,7 +66,7 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
JanetBuildConfig modconf = getter();
JanetBuildConfig host = janet_config_current();
if (host.major != modconf.major ||
host.minor < modconf.minor ||
host.minor != modconf.minor ||
host.bits != modconf.bits) {
char errbuf[128];
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
@@ -653,22 +653,15 @@ JANET_CORE_FN(janet_core_check_int,
"(int? x)",
"Check if x can be exactly represented as a 32 bit signed two's complement integer.") {
janet_fixarity(argc, 1);
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
double num = janet_unwrap_number(argv[0]);
return janet_wrap_boolean(num == (double)((int32_t)num));
ret_false:
return janet_wrap_false();
return janet_wrap_boolean(janet_checkint(argv[0]));
}
JANET_CORE_FN(janet_core_check_nat,
"(nat? x)",
"Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.") {
janet_fixarity(argc, 1);
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
double num = janet_unwrap_number(argv[0]);
return janet_wrap_boolean(num >= 0 && (num == (double)((int32_t)num)));
ret_false:
return janet_wrap_false();
if (!janet_checkint(argv[0])) return janet_wrap_false();
return janet_wrap_boolean(janet_unwrap_integer(argv[0]) >= 0);
}
JANET_CORE_FN(janet_core_is_bytes,
@@ -753,6 +746,7 @@ typedef struct SandboxOption {
static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL},
{"chroot", JANET_SANDBOX_CHROOT},
{"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI},
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
@@ -778,6 +772,7 @@ JANET_CORE_FN(janet_core_sandbox,
"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"
"* :chroot - disallow calling `os/posix-chroot`\n"
"* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"

View File

@@ -117,6 +117,9 @@ typedef struct {
double sec;
JanetVM *vm;
JanetFiber *fiber;
#ifdef JANET_WINDOWS
HANDLE cancel_event;
#endif
} JanetThreadedTimeout;
#define JANET_MAX_Q_CAPACITY 0x7FFFFFF
@@ -604,8 +607,43 @@ void janet_ev_init_common(void) {
#endif
}
#if JANET_ANDROID
static void janet_timeout_stop(int sig_num) {
if (sig_num == SIGUSR1) {
pthread_exit(0);
}
}
#endif
static void handle_timeout_worker(JanetTimeout to, int cancel) {
if (!to.has_worker) return;
#ifdef JANET_WINDOWS
if (cancel && to.worker_event) {
SetEvent(to.worker_event);
}
WaitForSingleObject(to.worker, INFINITE);
CloseHandle(to.worker);
if (to.worker_event) {
CloseHandle(to.worker_event);
}
#else
#ifdef JANET_ANDROID
if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill");
#else
if (cancel) janet_assert(!pthread_cancel(to.worker), "pthread_cancel");
#endif
void *res = NULL;
janet_assert(!pthread_join(to.worker, &res), "pthread_join");
#endif
}
/* Common deinit code */
void janet_ev_deinit_common(void) {
JanetTimeout to;
while (peek_timeout(&to)) {
handle_timeout_worker(to, 1);
pop_timeout(0);
}
janet_q_deinit(&janet_vm.spawn);
janet_free(janet_vm.tq);
janet_table_deinit(&janet_vm.threaded_abstracts);
@@ -648,19 +686,6 @@ void janet_addtimeout_nil(double sec) {
add_timeout(to);
}
#ifdef JANET_WINDOWS
static VOID CALLBACK janet_timeout_stop(ULONG_PTR ptr) {
UNREFERENCED_PARAMETER(ptr);
ExitThread(0);
}
#elif JANET_ANDROID
static void janet_timeout_stop(int sig_num) {
if (sig_num == SIGUSR1) {
pthread_exit(0);
}
}
#endif
static void janet_timeout_cb(JanetEVGenericMessage msg) {
(void) msg;
janet_interpreter_interrupt_handled(&janet_vm);
@@ -670,8 +695,16 @@ static void janet_timeout_cb(JanetEVGenericMessage msg) {
static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
janet_free(ptr);
SleepEx((DWORD)(tto.sec * 1000), TRUE);
if (janet_fiber_can_resume(tto.fiber)) {
JanetTimestamp wait_begin = ts_now();
DWORD duration = (DWORD)round(tto.sec * 1000);
DWORD res = WAIT_TIMEOUT;
JanetTimestamp wait_end = ts_now();
for (size_t i = 1; res == WAIT_TIMEOUT && (wait_end - wait_begin) < duration; i++) {
res = WaitForSingleObject(tto.cancel_event, (duration + i));
wait_end = ts_now();
}
/* only send interrupt message if result is WAIT_TIMEOUT */
if (res == WAIT_TIMEOUT) {
janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
@@ -696,11 +729,9 @@ static void *janet_timeout_body(void *ptr) {
? (long)((tto.sec - ((uint32_t)tto.sec)) * 1000000000)
: 0;
nanosleep(&ts, &ts);
if (janet_fiber_can_resume(tto.fiber)) {
janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
}
janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
return NULL;
}
#endif
@@ -820,6 +851,34 @@ static int janet_chanat_gc(void *p, size_t s) {
return 0;
}
static void janet_chanat_remove_vmref(JanetQueue *fq) {
JanetChannelPending *pending = fq->data;
if (fq->head <= fq->tail) {
for (int32_t i = fq->head; i < fq->tail; i++) {
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
}
} else {
for (int32_t i = fq->head; i < fq->capacity; i++) {
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
}
for (int32_t i = 0; i < fq->tail; i++) {
if (pending[i].thread == &janet_vm) pending[i].thread = NULL;
}
}
}
static int janet_chanat_gcperthread(void *p, size_t s) {
(void) s;
JanetChannel *chan = p;
janet_chan_lock(chan);
/* Make sure that the internals of the threaded channel no longer reference _this_ thread. Replace
* those references with NULL. */
janet_chanat_remove_vmref(&chan->read_pending);
janet_chanat_remove_vmref(&chan->write_pending);
janet_chan_unlock(chan);
return 0;
}
static void janet_chanat_mark_fq(JanetQueue *fq) {
JanetChannelPending *pending = fq->data;
if (fq->head <= fq->tail) {
@@ -902,8 +961,9 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ);
if (is_read) {
JanetChannelPending reader;
if (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
while (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
JanetVM *vm = reader.thread;
if (!vm) continue;
JanetEVGenericMessage msg;
msg.tag = reader.mode;
msg.fiber = reader.fiber;
@@ -911,11 +971,13 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
msg.argp = channel;
msg.argj = x;
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
break;
}
} else {
JanetChannelPending writer;
if (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
while (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
JanetVM *vm = writer.thread;
if (!vm) continue;
JanetEVGenericMessage msg;
msg.tag = writer.mode;
msg.fiber = writer.fiber;
@@ -923,6 +985,7 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
msg.argp = channel;
msg.argj = janet_wrap_nil();
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
break;
}
}
}
@@ -986,7 +1049,9 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
msg.argi = (int32_t) reader.sched_id;
msg.argp = channel;
msg.argj = x;
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
if (vm) {
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
}
} else {
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
janet_schedule(reader.fiber, make_read_result(channel, x));
@@ -1041,7 +1106,9 @@ static int janet_channel_pop_with_lock(JanetChannel *channel, Janet *item, int i
msg.argi = (int32_t) writer.sched_id;
msg.argp = channel;
msg.argj = janet_wrap_nil();
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
if (vm) {
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
}
} else {
if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
janet_schedule(writer.fiber, make_write_result(channel));
@@ -1305,7 +1372,9 @@ JANET_CORE_FN(cfun_channel_close,
msg.tag = JANET_CP_MODE_CLOSE;
msg.argi = (int32_t) writer.sched_id;
msg.argj = janet_wrap_nil();
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
if (vm) {
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
}
} else {
if (janet_fiber_can_resume(writer.fiber)) {
if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
@@ -1326,7 +1395,9 @@ JANET_CORE_FN(cfun_channel_close,
msg.tag = JANET_CP_MODE_CLOSE;
msg.argi = (int32_t) reader.sched_id;
msg.argj = janet_wrap_nil();
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
if (vm) {
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
}
} else {
if (janet_fiber_can_resume(reader.fiber)) {
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
@@ -1419,7 +1490,10 @@ const JanetAbstractType janet_channel_type = {
NULL, /* compare */
NULL, /* hash */
janet_chanat_next,
JANET_ATEND_NEXT
NULL, /* call */
NULL, /* length */
NULL, /* bytes */
janet_chanat_gcperthread
};
/* Main event loop */
@@ -1452,6 +1526,7 @@ JanetFiber *janet_loop1(void) {
}
}
}
handle_timeout_worker(to, 0);
}
/* Run scheduled fibers unless interrupts need to be handled. */
@@ -1499,27 +1574,14 @@ JanetFiber *janet_loop1(void) {
while ((has_timeout = peek_timeout(&to))) {
if (to.curr_fiber != NULL) {
if (!janet_fiber_can_resume(to.curr_fiber)) {
if (to.has_worker) {
#ifdef JANET_WINDOWS
QueueUserAPC(janet_timeout_stop, to.worker, 0);
WaitForSingleObject(to.worker, INFINITE);
CloseHandle(to.worker);
#else
#ifdef JANET_ANDROID
pthread_kill(to.worker, SIGUSR1);
#else
pthread_cancel(to.worker);
#endif
void *res;
pthread_join(to.worker, &res);
#endif
}
janet_table_remove(&janet_vm.active_tasks, janet_wrap_fiber(to.curr_fiber));
pop_timeout(0);
janet_table_remove(&janet_vm.active_tasks, janet_wrap_fiber(to.curr_fiber));
handle_timeout_worker(to, 1);
continue;
}
} else if (to.fiber->sched_id != to.sched_id) {
pop_timeout(0);
handle_timeout_worker(to, 1);
continue;
}
break;
@@ -1684,7 +1746,7 @@ void janet_stream_level_triggered(JanetStream *stream) {
static JanetTimestamp ts_now(void) {
struct timespec now;
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
uint64_t res = 1000 * now.tv_sec;
res += now.tv_nsec / 1000000;
return res;
@@ -1842,7 +1904,7 @@ JanetTimestamp to_interval(const JanetTimestamp ts) {
static JanetTimestamp ts_now(void) {
struct timespec now;
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
uint64_t res = 1000 * now.tv_sec;
res += now.tv_nsec / 1000000;
return res;
@@ -1996,7 +2058,7 @@ void janet_ev_deinit(void) {
static JanetTimestamp ts_now(void) {
struct timespec now;
janet_assert(-1 != clock_gettime(CLOCK_REALTIME, &now), "failed to get time");
janet_assert(-1 != janet_gettime(&now, JANET_TIME_MONOTONIC), "failed to get time");
uint64_t res = 1000 * now.tv_sec;
res += now.tv_nsec / 1000000;
return res;
@@ -2168,7 +2230,7 @@ void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGenericMessage ms
event.cb = cb;
int fd = vm->selfpipe[1];
/* handle a bit of back pressure before giving up. */
int tries = 4;
int tries = 20;
while (tries > 0) {
int status;
do {
@@ -3174,6 +3236,7 @@ JANET_NO_RETURN void janet_sleep_await(double sec) {
to.is_error = 0;
to.sched_id = to.fiber->sched_id;
to.curr_fiber = NULL;
to.has_worker = 0;
add_timeout(to);
janet_await();
}
@@ -3219,7 +3282,13 @@ JANET_CORE_FN(cfun_ev_deadline,
tto->vm = &janet_vm;
tto->fiber = tocheck;
#ifdef JANET_WINDOWS
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, 0, NULL);
HANDLE cancel_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == cancel_event) {
janet_free(tto);
janet_panic("failed to create cancel event");
}
tto->cancel_event = cancel_event;
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, CREATE_SUSPENDED, NULL);
if (NULL == worker) {
janet_free(tto);
janet_panic("failed to create thread");
@@ -3231,10 +3300,13 @@ JANET_CORE_FN(cfun_ev_deadline,
janet_free(tto);
janet_panicf("%s", janet_strerror(err));
}
janet_assert(!pthread_detach(worker), "pthread_detach");
#endif
to.has_worker = 1;
to.worker = worker;
#ifdef JANET_WINDOWS
to.worker_event = cancel_event;
ResumeThread(worker);
#endif
} else {
to.has_worker = 0;
}
@@ -3529,8 +3601,6 @@ void janet_lib_ev(JanetTable *env) {
janet_register_abstract_type(&janet_channel_type);
janet_register_abstract_type(&janet_mutex_type);
janet_register_abstract_type(&janet_rwlock_type);
janet_lib_filewatch(env);
}
#endif

View File

@@ -633,7 +633,7 @@ JANET_CORE_FN(cfun_filewatch_add,
"* `:modified`\n\n"
"* `:renamed-old`\n\n"
"* `:renamed-new`\n\n"
"On Linux, events will a `:type` corresponding to the possible flags, excluding `:all`.\n"
"On Linux, events will have a `:type` corresponding to the possible flags, excluding `:all`.\n"
"") {
janet_arity(argc, 2, -1);
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);

View File

@@ -132,7 +132,7 @@ static void janet_mark_many(const Janet *values, int32_t n) {
}
}
/* Mark a bunch of key values items in memory */
/* Mark only the keys from a sequence of key-value pairs */
static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n;
while (kvs < end) {
@@ -141,7 +141,7 @@ static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
}
}
/* Mark a bunch of key values items in memory */
/* Mark only the values from a sequence of key-value pairs */
static void janet_mark_values(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n;
while (kvs < end) {
@@ -150,7 +150,7 @@ static void janet_mark_values(const JanetKV *kvs, int32_t n) {
}
}
/* Mark a bunch of key values items in memory */
/* Mark key-value pairs */
static void janet_mark_kvs(const JanetKV *kvs, int32_t n) {
const JanetKV *end = kvs + n;
while (kvs < end) {
@@ -346,6 +346,9 @@ static void janet_deinit_block(JanetGCObject *mem) {
break;
case JANET_MEMORY_ABSTRACT: {
JanetAbstractHead *head = (JanetAbstractHead *)mem;
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
}
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
@@ -497,9 +500,12 @@ void janet_sweep() {
/* If not visited... */
if (!janet_truthy(items[i].value)) {
void *abst = janet_unwrap_abstract(items[i].key);
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
}
if (0 == janet_abstract_decref(abst)) {
/* Run finalizer */
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
@@ -672,8 +678,11 @@ void janet_clear_memory(void) {
for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
void *abst = janet_unwrap_abstract(items[i].key);
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
}
if (0 == janet_abstract_decref(abst)) {
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}

View File

@@ -341,7 +341,7 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
/* Needs argc >= offset + 2 */
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1,
* otherwise 0. Also, ignores is_bind when is a unix socket. */
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix, socklen_t *sizeout) {
/* Unix socket support - not yet supported on windows. */
#ifndef JANET_WINDOWS
if (janet_keyeq(argv[offset], "unix")) {
@@ -352,15 +352,14 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
}
saddr->sun_family = AF_UNIX;
size_t path_size = sizeof(saddr->sun_path);
snprintf(saddr->sun_path, path_size, "%s", path);
*sizeout = sizeof(struct sockaddr_un);
#ifdef JANET_LINUX
if (path[0] == '@') {
saddr->sun_path[0] = '\0';
snprintf(saddr->sun_path + 1, path_size - 1, "%s", path + 1);
} else
#endif
{
snprintf(saddr->sun_path, path_size, "%s", path);
*sizeout = offsetof(struct sockaddr_un, sun_path) + janet_string_length(path);
}
#endif
*is_unix = 1;
return (struct addrinfo *) saddr;
}
@@ -385,6 +384,11 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
janet_panicf("could not get address info: %s", gai_strerror(status));
}
*is_unix = 0;
#ifdef JANET_WINDOWS
*sizeout = 0;
#else
*sizeout = sizeof(struct sockaddr_un);
#endif
return ai;
}
@@ -405,12 +409,13 @@ JANET_CORE_FN(cfun_net_sockaddr,
int socktype = janet_get_sockettype(argv, argc, 2);
int is_unix = 0;
int make_arr = (argc >= 3 && janet_truthy(argv[3]));
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
socklen_t addrsize = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrsize);
#ifndef JANET_WINDOWS
/* no unix domain socket support on windows yet */
if (is_unix) {
void *abst = janet_abstract(&janet_address_type, sizeof(struct sockaddr_un));
memcpy(abst, ai, sizeof(struct sockaddr_un));
void *abst = janet_abstract(&janet_address_type, addrsize);
memcpy(abst, ai, addrsize);
Janet ret = janet_wrap_abstract(abst);
return make_arr ? janet_wrap_array(janet_array_n(&ret, 1)) : ret;
}
@@ -461,7 +466,8 @@ JANET_CORE_FN(cfun_net_connect,
}
/* Where we're connecting to */
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
socklen_t addrlen = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrlen);
/* Check if we're binding address */
struct addrinfo *binding = NULL;
@@ -486,7 +492,6 @@ JANET_CORE_FN(cfun_net_connect,
/* Create socket */
JSock sock = JSOCKDEFAULT;
void *addr = NULL;
socklen_t addrlen = 0;
#ifndef JANET_WINDOWS
if (is_unix) {
sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
@@ -496,7 +501,6 @@ JANET_CORE_FN(cfun_net_connect,
janet_panicf("could not create socket: %V", v);
}
addr = (void *) ai;
addrlen = sizeof(struct sockaddr_un);
} else
#endif
{
@@ -543,7 +547,9 @@ JANET_CORE_FN(cfun_net_connect,
}
/* Wrap socket in abstract type JanetStream */
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
uint32_t udp_flag = 0;
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
/* Set up the socket for non-blocking IO before connecting */
janet_net_socknoblock(sock);
@@ -581,6 +587,56 @@ JANET_CORE_FN(cfun_net_connect,
net_sched_connect(stream);
}
JANET_CORE_FN(cfun_net_socket,
"(net/socket &opt type)",
"Creates a new unbound socket. Type is an optional keyword, "
"either a :stream (usually tcp), or :datagram (usually udp). The default is :stream.") {
janet_arity(argc, 0, 1);
int socktype = janet_get_sockettype(argv, argc, 0);
/* Create socket */
JSock sfd = JSOCKDEFAULT;
struct addrinfo *ai = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = socktype;
hints.ai_flags = 0;
int status = getaddrinfo(NULL, "0", &hints, &ai);
if (status) {
janet_panicf("could not get address info: %s", gai_strerror(status));
}
struct addrinfo *rp = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) {
#ifdef JANET_WINDOWS
sfd = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
#else
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
#endif
if (JSOCKVALID(sfd)) {
break;
}
}
freeaddrinfo(ai);
if (!JSOCKVALID(sfd)) {
Janet v = janet_ev_lasterr();
janet_panicf("could not create socket: %V", v);
}
/* Wrap socket in abstract type JanetStream */
uint32_t udp_flag = 0;
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
JanetStream *stream = make_stream(sfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
/* Set up the socket for non-blocking IO */
janet_net_socknoblock(sfd);
return janet_wrap_abstract(stream);
}
static const char *serverify_socket(JSock sfd, int reuse_addr, int reuse_port) {
/* Set various socket options */
int enable = 1;
@@ -664,7 +720,8 @@ JANET_CORE_FN(cfun_net_listen,
/* Get host, port, and handler*/
int socktype = janet_get_sockettype(argv, argc, 2);
int is_unix = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
socklen_t addrlen = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix, &addrlen);
int reuse = !(argc >= 4 && janet_truthy(argv[3]));
JSock sfd = JSOCKDEFAULT;
@@ -676,7 +733,7 @@ JANET_CORE_FN(cfun_net_listen,
janet_panicf("could not create socket: %V", janet_ev_lasterr());
}
const char *err = serverify_socket(sfd, reuse, 0);
if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
if (NULL != err || bind(sfd, (struct sockaddr *)ai, addrlen)) {
JSOCKCLOSE(sfd);
janet_free(ai);
if (err) {
@@ -964,6 +1021,7 @@ struct sockopt_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[] = {
{ "tcp-nodelay", IPPROTO_TCP, TCP_NODELAY, JANET_BOOLEAN },
{ "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
{ "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
{ "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
@@ -985,6 +1043,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
"- :so-broadcast boolean\n"
"- :so-reuseaddr boolean\n"
"- :so-keepalive boolean\n"
"- :tcp-nodelay boolean\n"
"- :ip-multicast-ttl number\n"
"- :ip-add-membership string\n"
"- :ip-drop-membership string\n"
@@ -1078,6 +1137,7 @@ void janet_lib_net(JanetTable *env) {
JanetRegExt net_cfuns[] = {
JANET_CORE_REG("net/address", cfun_net_sockaddr),
JANET_CORE_REG("net/listen", cfun_net_listen),
JANET_CORE_REG("net/socket", cfun_net_socket),
JANET_CORE_REG("net/accept", cfun_stream_accept),
JANET_CORE_REG("net/accept-loop", cfun_stream_accept_loop),
JANET_CORE_REG("net/read", cfun_stream_read),

View File

@@ -66,6 +66,8 @@
#ifdef JANET_APPLE
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#include <AvailabilityMacros.h>
int chroot(const char *dirname);
#else
extern char **environ;
#endif
@@ -81,8 +83,14 @@ extern char **environ;
#ifndef JANET_SPAWN_NO_CHDIR
#ifdef __GLIBC__
#define JANET_SPAWN_CHDIR
#elif defined(JANET_APPLE) /* Some older versions may not work here. */
#elif defined(JANET_APPLE)
/* The posix_spawn_file_actions_addchdir_np function
* has only been implemented since macOS 10.15 */
#if defined(MAC_OS_X_VERSION_10_15) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15)
#define JANET_SPAWN_CHDIR
#else
#define JANET_SPAWN_NO_CHDIR
#endif
#elif defined(__FreeBSD__) /* Not all BSDs work, for example openBSD doesn't seem to support this */
#define JANET_SPAWN_CHDIR
#endif
@@ -173,6 +181,8 @@ JANET_CORE_FN(os_which,
return janet_ckeywordv("dragonfly");
#elif defined(JANET_BSD)
return janet_ckeywordv("bsd");
#elif defined(JANET_ILLUMOS)
return janet_ckeywordv("illumos");
#else
return janet_ckeywordv("posix");
#endif
@@ -312,6 +322,13 @@ JANET_CORE_FN(os_cpu_count,
return dflt;
}
return janet_wrap_integer(result);
#elif defined(JANET_ILLUMOS)
(void) dflt;
long result = sysconf(_SC_NPROCESSORS_CONF);
if (result < 0) {
return dflt;
}
return janet_wrap_integer(result);
#else
return dflt;
#endif
@@ -1525,6 +1542,27 @@ JANET_CORE_FN(os_posix_fork,
#endif
}
JANET_CORE_FN(os_posix_chroot,
"(os/posix-chroot dirname)",
"Call `chroot` to change the root directory to `dirname`. "
"Not supported on all systems (POSIX only).") {
janet_sandbox_assert(JANET_SANDBOX_CHROOT);
janet_fixarity(argc, 1);
#ifdef JANET_WINDOWS
janet_panic("not supported on Windows");
#else
const char *root = janet_getcstring(argv, 0);
int result;
do {
result = chroot(root);
} while (result == -1 && errno == EINTR);
if (result == -1) {
janet_panic(janet_strerror(errno));
}
return janet_wrap_nil();
#endif
}
#ifdef JANET_EV
/* Runs in a separate thread */
static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) {
@@ -2833,6 +2871,7 @@ void janet_lib_os(JanetTable *env) {
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/posix-chroot", os_posix_chroot),
#ifndef JANET_NO_UMASK
JANET_CORE_REG("os/umask", os_umask),
#endif
@@ -2863,6 +2902,9 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/proc-kill", os_proc_kill),
JANET_CORE_REG("os/proc-close", os_proc_close),
JANET_CORE_REG("os/getpid", os_proc_getpid),
#ifdef JANET_EV
JANET_CORE_REG("os/sigaction", os_sigaction),
#endif
#endif
/* high resolution timers */
@@ -2871,7 +2913,6 @@ void janet_lib_os(JanetTable *env) {
#ifdef JANET_EV
JANET_CORE_REG("os/open", os_open), /* fs read and write */
JANET_CORE_REG("os/pipe", os_pipe),
JANET_CORE_REG("os/sigaction", os_sigaction),
#endif
#endif
JANET_REG_END

View File

@@ -1060,19 +1060,11 @@ void janet_buffer_format(
break;
}
case 's': {
JanetByteView bytes = janet_getbytes(argv, arg);
const uint8_t *s = bytes.bytes;
int32_t l = bytes.len;
const char *s = janet_getcbytes(argv, arg);
if (form[2] == '\0')
janet_buffer_push_bytes(b, s, l);
janet_buffer_push_cstring(b, s);
else {
if (l != (int32_t) strlen((const char *) s))
janet_panic("string contains zeros");
if (!strchr(form, '.') && l >= 100) {
janet_panic("no precision and string is too long to be formatted");
} else {
nb = snprintf(item, MAX_ITEM, form, s);
}
nb = snprintf(item, MAX_ITEM, form, s);
}
break;
}

View File

@@ -26,7 +26,8 @@
#include "state.h"
#endif
/* Run a string */
/* Run a string of code. The return value is a set of error flags, JANET_DO_ERROR_RUNTIME, JANET_DO_ERROR_COMPILE, and JANET_DOR_ERROR_PARSE if
* any errors were encountered in those phases. More information is printed to stderr. */
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
JanetParser *parser;
int errflags = 0, done = 0;
@@ -55,7 +56,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
janet_stacktrace_ext(fiber, ret, "");
errflags |= 0x01;
errflags |= JANET_DO_ERROR_RUNTIME;
done = 1;
}
} else {
@@ -75,7 +76,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
line, col, (const char *)cres.error);
}
errflags |= 0x02;
errflags |= JANET_DO_ERROR_COMPILE;
done = 1;
}
}
@@ -89,7 +90,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
break;
case JANET_PARSE_ERROR: {
const char *e = janet_parser_error(parser);
errflags |= 0x04;
errflags |= JANET_DO_ERROR_PARSE;
ret = janet_cstringv(e);
int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column;

View File

@@ -23,8 +23,11 @@
#ifndef JANET_STATE_H_defined
#define JANET_STATE_H_defined
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include <stdint.h>
#endif
#ifdef JANET_EV
#ifdef JANET_WINDOWS
@@ -65,6 +68,7 @@ typedef struct {
int has_worker;
#ifdef JANET_WINDOWS
HANDLE worker;
HANDLE worker_event;
#else
pthread_t worker;
#endif

1915
src/core/sysir.c Normal file

File diff suppressed because it is too large Load Diff

337
src/core/sysir.h Normal file
View File

@@ -0,0 +1,337 @@
/*
* Copyright (c) 2024 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/****
* The System Dialect Intermediate Representation (sysir) is a compiler intermediate representation
* that for "System Janet" a dialect for "System Programming". Sysir can then be retargeted to C or direct to machine
* code for JIT or AOT compilation.
*/
/* TODO
* [x] encode constants directly in 3 address codes - makes codegen easier
* [x] typed constants
* [x] named registers and types
* [x] better type errors (perhaps mostly for compiler debugging - full type system goes on top)
* [-] x86/x64 machine code target - in progress
* [ ] handle floating point types
* [ ] handle array types
* [ ] emit machine code directly
* [ ] target specific extensions - custom instructions and custom primitives
* [ ] better casting semantics
* [x] separate pointer arithmetic from generalized arithmetic (easier to instrument code for safety)?
* [x] fixed-size array types
* [x] recursive pointer types
* [ ] global and thread local state
* [x] union types?
* [x] incremental compilation - save type definitions for later
* [ ] Extension to C target for interfacing with Janet
* [x] pointer math, pointer types
* [x] composite types - support for load, store, move, and function args.
* [x] Have some mechanism for field access (dest = src.offset)
* [x] Related, move type creation as opcodes like in SPIRV - have separate virtual "type slots" and value slots for this.
* [x] support for stack allocation of arrays
* [ ] more math intrinsics
* [x] source mapping (using built in Janet source mapping metadata on tuples)
* [x] unit type or void type
* [ ] (typed) function pointer types and remove calling untyped pointers
* [x] APL array semantics for binary operands (maybe?)
* [ ] a few built-in array combinators (maybe?)
* [ ] multiple error messages in one pass
* [ ] better verification of constants
* [x] don't allow redefining types
* [ ] generate elf/mach-o/pe directly
* [ ] elf
* [ ] mach-o
* [ ] pe
* [ ] generate dwarf info
*/
#ifndef JANET_SYSIR_H
#define JANET_SYSIR_H
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include "state.h"
#endif
typedef enum {
JANET_PRIM_U8,
JANET_PRIM_S8,
JANET_PRIM_U16,
JANET_PRIM_S16,
JANET_PRIM_U32,
JANET_PRIM_S32,
JANET_PRIM_U64,
JANET_PRIM_S64,
JANET_PRIM_F32,
JANET_PRIM_F64,
JANET_PRIM_POINTER,
JANET_PRIM_BOOLEAN,
JANET_PRIM_STRUCT,
JANET_PRIM_UNION,
JANET_PRIM_ARRAY,
JANET_PRIM_VOID,
JANET_PRIM_UNKNOWN
} JanetPrim;
typedef struct {
const char *name;
JanetPrim prim;
} JanetPrimName;
typedef enum {
JANET_SYSOP_LINK_NAME,
JANET_SYSOP_PARAMETER_COUNT,
JANET_SYSOP_CALLING_CONVENTION,
JANET_SYSOP_MOVE,
JANET_SYSOP_CAST,
JANET_SYSOP_ADD,
JANET_SYSOP_SUBTRACT,
JANET_SYSOP_MULTIPLY,
JANET_SYSOP_DIVIDE,
JANET_SYSOP_BAND,
JANET_SYSOP_BOR,
JANET_SYSOP_BXOR,
JANET_SYSOP_BNOT,
JANET_SYSOP_SHL,
JANET_SYSOP_SHR,
JANET_SYSOP_LOAD,
JANET_SYSOP_STORE,
JANET_SYSOP_GT,
JANET_SYSOP_LT,
JANET_SYSOP_EQ,
JANET_SYSOP_NEQ,
JANET_SYSOP_GTE,
JANET_SYSOP_LTE,
JANET_SYSOP_CALL,
JANET_SYSOP_SYSCALL,
JANET_SYSOP_RETURN,
JANET_SYSOP_JUMP,
JANET_SYSOP_BRANCH,
JANET_SYSOP_BRANCH_NOT,
JANET_SYSOP_ADDRESS,
JANET_SYSOP_TYPE_PRIMITIVE,
JANET_SYSOP_TYPE_STRUCT,
JANET_SYSOP_TYPE_BIND,
JANET_SYSOP_ARG,
JANET_SYSOP_FIELD_GETP,
JANET_SYSOP_ARRAY_GETP,
JANET_SYSOP_ARRAY_PGETP,
JANET_SYSOP_TYPE_POINTER,
JANET_SYSOP_TYPE_ARRAY,
JANET_SYSOP_TYPE_UNION,
JANET_SYSOP_POINTER_ADD,
JANET_SYSOP_POINTER_SUBTRACT,
JANET_SYSOP_LABEL
} JanetSysOp;
typedef struct {
JanetPrim prim;
union {
struct {
uint32_t field_count;
uint32_t field_start;
} st;
struct {
uint32_t type;
} pointer;
struct {
uint32_t type;
uint64_t fixed_count;
} array;
};
} JanetSysTypeInfo;
typedef struct {
uint32_t type;
} JanetSysTypeField;
#define JANET_SYS_CALLFLAG_HAS_DEST 1
#define JANET_SYS_CALLFLAG_VARARGS 2
/* Allow read arguments to be constants to allow
* encoding immediates. This makes codegen easier. */
#define JANET_SYS_MAX_OPERAND 0x7FFFFFFFU
#define JANET_SYS_CONSTANT_PREFIX 0x80000000U
typedef enum {
JANET_SYS_CC_DEFAULT, /* Reasonable default - maps to a specific cc based on target */
JANET_SYS_CC_SYSCALL, /* Reasonable default for platform syscalls - maps to a specific cc based on target */
JANET_SYS_CC_X86_CDECL,
JANET_SYS_CC_X86_STDCALL,
JANET_SYS_CC_X86_FASTCALL,
JANET_SYS_CC_X64_SYSV,
JANET_SYS_CC_X64_SYSV_SYSCALL,
JANET_SYS_CC_X64_WINDOWS,
} JanetSysCallingConvention;
typedef enum {
JANET_SYS_TARGET_X64_WINDOWS, /* 64 bit, modern windows */
JANET_SYS_TARGET_X64_LINUX, /* x64 linux with recent kernel */
} JanetSysTarget;
typedef struct {
JanetSysOp opcode;
union {
struct {
uint32_t dest;
uint32_t lhs;
uint32_t rhs;
} three;
struct {
uint32_t dest;
uint32_t callee;
uint32_t arg_count;
uint8_t flags;
JanetSysCallingConvention calling_convention;
} call;
struct {
uint32_t dest;
uint32_t src;
} two;
struct {
uint32_t src;
} one;
struct {
uint32_t to;
} jump;
struct {
uint32_t cond;
uint32_t to;
} branch;
struct {
uint32_t dest_type;
uint32_t prim;
} type_prim;
struct {
uint32_t dest_type;
uint32_t arg_count;
} type_types;
struct {
uint32_t dest;
uint32_t type;
} type_bind;
struct {
uint32_t args[3];
} arg;
struct {
uint32_t r;
uint32_t st;
uint32_t field;
} field;
struct {
uint32_t dest_type;
uint32_t type;
// Include address space?
} pointer;
struct {
uint32_t dest_type;
uint32_t type;
uint64_t fixed_count;
} array;
struct {
uint32_t id;
} label;
struct {
uint32_t value;
uint32_t has_value;
} ret;
};
int32_t line;
int32_t column;
} JanetSysInstruction;
/* Shared data between multiple
* IR Function bodies. Used to link
* multiple functions together in a
* single executable or shared object with
* multiple entry points. Contains shared
* type declarations, as well as a table of linked
* functions. */
typedef struct {
uint32_t old_type_def_count;
uint32_t type_def_count;
uint32_t field_def_count;
JanetSysTypeInfo *type_defs;
JanetString *type_names;
JanetSysTypeField *field_defs;
JanetTable *irs;
JanetArray *ir_ordered;
JanetTable *type_name_lookup;
} JanetSysIRLinkage;
/* Keep source code information as well as
* typing information along with constants */
typedef struct {
uint32_t type;
Janet value;
// TODO - source and line
} JanetSysConstant;
/* IR representation for a single function.
* Allow for incremental compilation and linking. */
typedef struct {
JanetSysIRLinkage *linkage;
JanetString link_name;
uint32_t instruction_count;
uint32_t register_count;
uint32_t constant_count;
uint32_t return_type;
uint32_t has_return_type;
uint32_t parameter_count;
uint32_t label_count;
uint32_t *types;
JanetSysInstruction *instructions;
JanetString *register_names;
JanetSysConstant *constants;
JanetTable *register_name_lookup;
JanetTable *labels;
JanetSysCallingConvention calling_convention;
Janet error_ctx; /* Temporary for holding error messages */
} JanetSysIR;
/* Delay alignment info for the most part to the lowering phase */
typedef struct {
uint32_t size;
uint32_t alignment;
} JanetSysTypeLayout;
/* Keep track of names for each instruction */
extern const char *janet_sysop_names[];
extern const char *prim_to_prim_name[];
/* Utilities */
uint32_t janet_sys_optype(JanetSysIR *ir, uint32_t op);
/* Get list of uint32_t instruction arguments from a call or other variable length instruction.
Needs to be free with janet_sfree (or you can leak it and the garbage collector will eventually clean
* it up). */
uint32_t *janet_sys_callargs(JanetSysInstruction *instr, uint32_t *count);
/* Lowering */
void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into);
void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer);
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetSysTarget target, JanetBuffer *buffer);
#endif

376
src/core/sysir_c.c Normal file
View File

@@ -0,0 +1,376 @@
/*
* Copyright (c) 2024 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include "sysir.h"
#include "vector.h"
#include "util.h"
#endif
/* Lowering to C */
static const char *c_prim_names[] = {
"uint8_t",
"int8_t",
"uint16_t",
"int16_t",
"uint32_t",
"int32_t",
"uint64_t",
"int64_t",
"float",
"double",
"void *",
"bool",
"!!!struct",
"!!!union",
"!!!array",
"void",
"!!!unknown"
};
/* Print a C constant */
static void print_const_c(JanetSysIR *ir, JanetBuffer *buf, Janet c, uint32_t tid) {
/* JanetSysTypeInfo *tinfo = &ir->linkage->type_defs[tid]; */
if (janet_checktype(c, JANET_TUPLE)) {
const Janet *elements = janet_unwrap_tuple(c);
janet_formatb(buf, "((_t%d){", tid);
for (int32_t i = 0; i < janet_tuple_length(elements); i++) {
if (i > 0) janet_formatb(buf, ", ");
/* TODO - limit recursion? */
uint32_t sub_type = ir->linkage->type_defs[tid].array.type;
print_const_c(ir, buf, elements[i], sub_type);
}
janet_formatb(buf, "})");
} else if (janet_checktype(c, JANET_ABSTRACT)) {
/* Allow printing int types */
janet_formatb(buf, "%V", c);
} else {
janet_formatb(buf, "%v", c);
}
}
static void c_op_or_const(JanetSysIR *ir, JanetBuffer *buf, uint32_t reg) {
if (reg < JANET_SYS_MAX_OPERAND) {
janet_formatb(buf, "_r%u", reg);
} else {
uint32_t constant_id = reg - JANET_SYS_CONSTANT_PREFIX;
uint32_t tid = ir->constants[constant_id].type;
Janet c = ir->constants[constant_id].value;
print_const_c(ir, buf, c, tid);
}
}
static void c_emit_binop(JanetSysIR *ir, JanetBuffer *buffer, JanetBuffer *tempbuf, JanetSysInstruction instruction, const char *op, int pointer_sugar) {
uint32_t operand_type = ir->types[instruction.three.dest];
tempbuf->count = 0;
uint32_t index_index = 0;
int is_pointer = 0;
JanetSysIRLinkage *linkage = ir->linkage;
/* Top-level pointer semantics */
if (pointer_sugar && janet_sys_optype(ir, instruction.three.dest) == JANET_PRIM_POINTER) {
operand_type = linkage->type_defs[operand_type].pointer.type;
is_pointer = 1;
}
/* Add nested for loops for any dimensionality of array */
while (linkage->type_defs[operand_type].prim == JANET_PRIM_ARRAY) {
janet_formatb(buffer, " for (size_t _j%u = 0; _j%u < %u; _j%u++) ",
index_index, index_index,
linkage->type_defs[operand_type].array.fixed_count,
index_index);
if (is_pointer) {
janet_formatb(tempbuf, "->els[_j%u]", index_index);
is_pointer = 0;
} else {
janet_formatb(tempbuf, ".els[_j%u]", index_index);
}
operand_type = linkage->type_defs[operand_type].array.type;
index_index++;
}
if (is_pointer) {
janet_formatb(buffer, " *_r%u = *_r%u %s *_r%u;\n",
instruction.three.dest,
instruction.three.lhs,
op,
instruction.three.rhs);
janet_formatb(buffer, " *_r%u = *", instruction.three.dest);
c_op_or_const(ir, buffer, instruction.three.lhs);
janet_formatb(buffer, " %s ", op);
c_op_or_const(ir, buffer, instruction.three.rhs);
janet_formatb(buffer, ";\n");
} else {
Janet index_part = janet_wrap_buffer(tempbuf);
janet_formatb(buffer, " _r%u%V = ", instruction.three.dest, index_part);
c_op_or_const(ir, buffer, instruction.three.lhs);
janet_formatb(buffer, "%V %s ", index_part, op);
c_op_or_const(ir, buffer, instruction.three.rhs);
janet_formatb(buffer, "%V;\n", index_part);
}
}
void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
JanetBuffer *tempbuf = janet_buffer(0);
#define EMITBINOP(OP) c_emit_binop(ir, buffer, tempbuf, instruction, OP, 1)
#define EMITBINOP_NOSUGAR(OP) c_emit_binop(ir, buffer, tempbuf, instruction, OP, 0)
/* Prelude */
janet_formatb(buffer, "#include <stddef.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <sys/syscall.h>\n#define _t0 void\n\n");
/* Emit type defs */
for (uint32_t j = 0; j < (uint32_t) linkage->ir_ordered->count; j++) {
JanetSysIR *ir = janet_unwrap_abstract(linkage->ir_ordered->data[j]);
for (uint32_t i = 0; i < ir->instruction_count; i++) {
JanetSysInstruction instruction = ir->instructions[i];
switch (instruction.opcode) {
default:
continue;
case JANET_SYSOP_TYPE_PRIMITIVE:
case JANET_SYSOP_TYPE_STRUCT:
case JANET_SYSOP_TYPE_UNION:
case JANET_SYSOP_TYPE_POINTER:
case JANET_SYSOP_TYPE_ARRAY:
break;
}
if (instruction.line > 0) {
janet_formatb(buffer, "#line %d\n", instruction.line);
}
switch (instruction.opcode) {
default:
break;
case JANET_SYSOP_TYPE_PRIMITIVE:
janet_formatb(buffer, "typedef %s _t%u;\n", c_prim_names[instruction.type_prim.prim], instruction.type_prim.dest_type);
break;
case JANET_SYSOP_TYPE_STRUCT:
case JANET_SYSOP_TYPE_UNION:
janet_formatb(buffer, (instruction.opcode == JANET_SYSOP_TYPE_STRUCT) ? "typedef struct {\n" : "typedef union {\n");
for (uint32_t j = 0; j < instruction.type_types.arg_count; j++) {
uint32_t offset = j / 3 + 1;
uint32_t index = j % 3;
JanetSysInstruction arg_instruction = ir->instructions[i + offset];
janet_formatb(buffer, " _t%u _f%u;\n", arg_instruction.arg.args[index], j);
}
janet_formatb(buffer, "} _t%u;\n", instruction.type_types.dest_type);
break;
case JANET_SYSOP_TYPE_POINTER:
janet_formatb(buffer, "typedef _t%u *_t%u;\n", instruction.pointer.type, instruction.pointer.dest_type);
break;
case JANET_SYSOP_TYPE_ARRAY:
janet_formatb(buffer, "typedef struct { _t%u els[%u]; } _t%u;\n", instruction.array.type, instruction.array.fixed_count, instruction.array.dest_type);
break;
}
}
}
/* Emit function header */
for (uint32_t j = 0; j < (uint32_t) linkage->ir_ordered->count; j++) {
JanetSysIR *ir = janet_unwrap_abstract(linkage->ir_ordered->data[j]);
if (ir->link_name == NULL) {
continue;
}
janet_formatb(buffer, "\n\n_t%u %s(", ir->return_type, (ir->link_name != NULL) ? ir->link_name : janet_cstring("_thunk"));
for (uint32_t i = 0; i < ir->parameter_count; i++) {
if (i) janet_buffer_push_cstring(buffer, ", ");
janet_formatb(buffer, "_t%u _r%u", ir->types[i], i);
}
janet_buffer_push_cstring(buffer, ")\n{\n");
for (uint32_t i = ir->parameter_count; i < ir->register_count; i++) {
janet_formatb(buffer, " _t%u _r%u;\n", ir->types[i], i);
}
janet_buffer_push_cstring(buffer, "\n");
/* Emit body */
for (uint32_t i = 0; i < ir->instruction_count; i++) {
JanetSysInstruction instruction = ir->instructions[i];
if (instruction.line > 0) {
janet_formatb(buffer, "#line %d\n", instruction.line);
}
switch (instruction.opcode) {
case JANET_SYSOP_TYPE_PRIMITIVE:
case JANET_SYSOP_TYPE_BIND:
case JANET_SYSOP_TYPE_STRUCT:
case JANET_SYSOP_TYPE_UNION:
case JANET_SYSOP_TYPE_POINTER:
case JANET_SYSOP_TYPE_ARRAY:
case JANET_SYSOP_ARG:
case JANET_SYSOP_LINK_NAME:
case JANET_SYSOP_PARAMETER_COUNT:
case JANET_SYSOP_CALLING_CONVENTION:
break;
case JANET_SYSOP_LABEL: {
janet_formatb(buffer, "\n_label_%u:\n", instruction.label.id);
break;
}
case JANET_SYSOP_ADDRESS:
janet_formatb(buffer, " _r%u = (void *) &", instruction.two.dest);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ";\n");
break;
case JANET_SYSOP_JUMP:
janet_formatb(buffer, " goto _label_%u;\n", instruction.jump.to);
break;
case JANET_SYSOP_BRANCH:
case JANET_SYSOP_BRANCH_NOT:
janet_formatb(buffer, instruction.opcode == JANET_SYSOP_BRANCH ? " if (" : " if (!");
c_op_or_const(ir, buffer, instruction.branch.cond);
janet_formatb(buffer, ") goto _label_%u;\n", instruction.branch.to);
break;
case JANET_SYSOP_RETURN:
if (instruction.ret.has_value) {
janet_buffer_push_cstring(buffer, " return ");
c_op_or_const(ir, buffer, instruction.ret.value);
janet_buffer_push_cstring(buffer, ";\n");
} else {
janet_buffer_push_cstring(buffer, " return;\n");
}
break;
case JANET_SYSOP_ADD:
EMITBINOP("+");
break;
case JANET_SYSOP_POINTER_ADD:
EMITBINOP_NOSUGAR("+");
break;
case JANET_SYSOP_SUBTRACT:
EMITBINOP("-");
break;
case JANET_SYSOP_POINTER_SUBTRACT:
EMITBINOP_NOSUGAR("-");
break;
case JANET_SYSOP_MULTIPLY:
EMITBINOP("*");
break;
case JANET_SYSOP_DIVIDE:
EMITBINOP("/");
break;
case JANET_SYSOP_GT:
EMITBINOP(">");
break;
case JANET_SYSOP_GTE:
EMITBINOP(">");
break;
case JANET_SYSOP_LT:
EMITBINOP("<");
break;
case JANET_SYSOP_LTE:
EMITBINOP("<=");
break;
case JANET_SYSOP_EQ:
EMITBINOP("==");
break;
case JANET_SYSOP_NEQ:
EMITBINOP("!=");
break;
case JANET_SYSOP_BAND:
EMITBINOP("&");
break;
case JANET_SYSOP_BOR:
EMITBINOP("|");
break;
case JANET_SYSOP_BXOR:
EMITBINOP("^");
break;
case JANET_SYSOP_SHL:
EMITBINOP("<<");
break;
case JANET_SYSOP_SHR:
EMITBINOP(">>");
break;
case JANET_SYSOP_SYSCALL:
case JANET_SYSOP_CALL: {
if (instruction.call.flags & JANET_SYS_CALLFLAG_HAS_DEST) {
janet_formatb(buffer, " _r%u = ", instruction.call.dest);
} else {
janet_formatb(buffer, " ");
}
if (instruction.opcode == JANET_SYSOP_SYSCALL) {
janet_formatb(buffer, "syscall(");
c_op_or_const(ir, buffer, instruction.call.callee);
} else {
c_op_or_const(ir, buffer, instruction.call.callee);
janet_formatb(buffer, "(");
}
uint32_t count;
uint32_t *args = janet_sys_callargs(ir->instructions + i, &count);
for (uint32_t j = 0; j < count; j++) {
if (j || instruction.opcode == JANET_SYSOP_SYSCALL) janet_formatb(buffer, ", ");
c_op_or_const(ir, buffer, args[j]);
}
janet_formatb(buffer, ");\n");
break;
}
case JANET_SYSOP_CAST: {
uint32_t to = ir->types[instruction.two.dest];
janet_formatb(buffer, " _r%u = (_t%u) ", instruction.two.dest, to);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ";\n");
break;
}
case JANET_SYSOP_MOVE:
janet_formatb(buffer, " _r%u = ", instruction.two.dest);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ";\n");
break;
case JANET_SYSOP_BNOT:
janet_formatb(buffer, " _r%u = ~", instruction.two.dest);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ";\n");
break;
case JANET_SYSOP_LOAD:
janet_formatb(buffer, " _r%u = *(", instruction.two.dest);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ");\n");
break;
case JANET_SYSOP_STORE:
janet_formatb(buffer, " *(_r%u) = ", instruction.two.dest);
c_op_or_const(ir, buffer, instruction.two.src);
janet_formatb(buffer, ";\n");
break;
case JANET_SYSOP_FIELD_GETP:
janet_formatb(buffer, " _r%u = &(_r%u._f%u);\n", instruction.field.r, instruction.field.st, instruction.field.field);
janet_formatb(buffer, " _r%u = &(", instruction.field.r);
janet_formatb(buffer, "._f%u);\n", instruction.field.field);
break;
case JANET_SYSOP_ARRAY_GETP:
janet_formatb(buffer, " _r%u = &(_r%u.els[", instruction.three.dest, instruction.three.lhs);
c_op_or_const(ir, buffer, instruction.three.rhs);
janet_buffer_push_cstring(buffer, "]);\n");
break;
case JANET_SYSOP_ARRAY_PGETP:
janet_formatb(buffer, " _r%u = &(_r%u->els[", instruction.three.dest, instruction.three.lhs);
c_op_or_const(ir, buffer, instruction.three.rhs);
janet_buffer_push_cstring(buffer, "]);\n");
break;
}
}
janet_buffer_push_cstring(buffer, "}\n");
#undef EMITBINOP
#undef EMITBINOP_NOSUGAR
}
}

1104
src/core/sysir_x86.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -931,27 +931,24 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
#include <mach/clock.h>
#include <mach/mach.h>
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
if (source == JANET_TIME_REALTIME) {
if (source == JANET_TIME_CPUTIME) {
clock_t tmp = clock();
spec->tv_sec = tmp / CLOCKS_PER_SEC;
spec->tv_nsec = ((tmp - (spec->tv_sec * CLOCKS_PER_SEC)) * 1000000000) / CLOCKS_PER_SEC;
} else {
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_id_t cid = CALENDAR_CLOCK;
if (source == JANET_TIME_REALTIME) {
cid = CALENDAR_CLOCK;
} else if (source == JANET_TIME_MONOTONIC) {
cid = SYSTEM_CLOCK;
}
host_get_clock_service(mach_host_self(), cid, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
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;
}

View File

@@ -77,6 +77,11 @@ extern "C" {
#define JANET_CYGWIN 1
#endif
/* Check for Illumos */
#if defined(__illumos__)
#define JANET_ILLUMOS 1
#endif
/* Check Unix */
#if defined(_AIX) \
|| defined(__APPLE__) /* Darwin */ \
@@ -162,7 +167,7 @@ extern "C" {
#endif
/* Check sun */
#ifdef __sun
#if defined(__sun) && !defined(JANET_ILLUMOS)
#define JANET_NO_UTC_MKTIME
#endif
@@ -1183,6 +1188,7 @@ struct JanetAbstractType {
Janet(*call)(void *p, int32_t argc, Janet *argv);
size_t (*length)(void *p, size_t len);
JanetByteView(*bytes)(void *p, size_t len);
int (*gcperthread)(void *data, size_t len);
};
/* Some macros to let us add extra types to JanetAbstract types without
@@ -1202,7 +1208,8 @@ struct JanetAbstractType {
#define JANET_ATEND_NEXT NULL,JANET_ATEND_CALL
#define JANET_ATEND_CALL NULL,JANET_ATEND_LENGTH
#define JANET_ATEND_LENGTH NULL,JANET_ATEND_BYTES
#define JANET_ATEND_BYTES
#define JANET_ATEND_BYTES NULL,JANET_ATEND_GCPERTHREAD
#define JANET_ATEND_GCPERTHREAD
struct JanetReg {
const char *name;
@@ -1460,10 +1467,10 @@ JANET_API int32_t janet_abstract_incref(void *abst);
JANET_API int32_t janet_abstract_decref(void *abst);
/* Expose channel utilities */
JanetChannel *janet_channel_make(uint32_t limit);
JanetChannel *janet_channel_make_threaded(uint32_t limit);
JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
JANET_API JanetChannel *janet_channel_make(uint32_t limit);
JANET_API JanetChannel *janet_channel_make_threaded(uint32_t limit);
JANET_API JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
JANET_API JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
JANET_API int janet_channel_give(JanetChannel *channel, Janet x);
JANET_API int janet_channel_take(JanetChannel *channel, Janet *out);
@@ -1611,6 +1618,9 @@ JANET_API JanetTable *janet_core_env(JanetTable *replacements);
JANET_API JanetTable *janet_core_lookup_table(JanetTable *replacements);
/* Execute strings */
#define JANET_DO_ERROR_RUNTIME 0x01
#define JANET_DO_ERROR_COMPILE 0x02
#define JANET_DO_ERROR_PARSE 0x04
JANET_API int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out);
JANET_API int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Janet *out);
@@ -1889,6 +1899,7 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
#define JANET_SANDBOX_FFI_USE 2048
#define JANET_SANDBOX_FFI_JIT 4096
#define JANET_SANDBOX_SIGNAL 8192
#define JANET_SANDBOX_CHROOT 16384
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)

View File

@@ -27,7 +27,7 @@
(if x
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
(do
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (string e) x) (eflush)))
x)
(defn skip-asserts

View File

@@ -865,6 +865,13 @@
(assert (deep= ~(,import* "a" :as "b" :fresh maybe)
(macex '(import a :as b :fresh maybe))) "import macro 2")
# 2af3f21d
(assert-error "import macro 2" (macex '(import a :fresh)))
(assert-error "import macro 3" (macex '(import a :as b :fresh)))
(assert-error "import macro 4" (macex '(import b "notakeyword" value)))
(assert (deep= ~(,import* "a" :fresh nil)
(macex '(import a :fresh nil))) "import macro 5")
# #477 walk preserving bracket type
# 0a1d902f4
(assert (= :brackets (tuple/type (postwalk identity '[])))
@@ -983,6 +990,17 @@
(assert (= () '() (macex '())) "macex ()")
(assert (= '[] (macex '[])) "macex []")
# Knuth man or boy test
(var a nil)
(defn man-or-boy [x] (a x |1 |-1 |-1 |1 |0))
(varfn a [k x1 x2 x3 x4 x5]
(var k k)
(defn b [] (-- k) (a k b x1 x2 x3 x4))
(if (<= k 0)
(+ (x4) (x5))
(b)))
(assert (= -2 (man-or-boy 2)))
(assert (= -67 (man-or-boy 10)))
(assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn")
(assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2))))

View File

@@ -106,6 +106,8 @@
(calc-2 "(+ 9 10 11 12)"))
@[10 26 42]) "parallel subprocesses 2")
# (print "file piping")
# File piping
# a1cc5ca04
(assert-no-error "file writing 1"
@@ -225,6 +227,8 @@
(++ iterations)
(ev/write stream " ")))
# (print "local name / peer name testing")
# Test localname and peername
# 077bf5eba
(repeat 10
@@ -407,6 +411,8 @@
(while (def msg (ev/read connection 100))
(broadcast name (string msg)))))))
# (print "chat app testing")
# Now launch the chat server
(def chat-server (net/listen test-host test-port))
(ev/spawn
@@ -500,6 +506,8 @@
(let [s (net/listen :unix uds-path :stream)]
(:close s))))))
# (print "accept loop testing")
# net/accept-loop level triggering
(gccollect)
(def maxconn 50)
@@ -522,6 +530,8 @@
(assert (= maxconn connect-count))
(:close s)
# (print "running deadline tests...")
# Cancel os/proc-wait with ev/deadline
(let [p (os/spawn [;run janet "-e" "(os/sleep 4)"] :p)]
(var terminated-normally false)
@@ -546,9 +556,35 @@
(ev/sleep 0.15)
(assert (not terminated-normally) "early termination failure 3"))
(let [f (coro (forever :foo))]
(ev/deadline 0.01 nil f true)
(assert-error "deadline expired" (resume f)))
# Deadline with interrupt
(defmacro with-deadline2
``
Create a fiber to execute `body`, schedule the event loop to cancel
the task (root fiber) associated with `body`'s fiber, and start
`body`'s fiber by resuming it.
The event loop will try to cancel the root fiber if `body`'s fiber
has not completed after at least `sec` seconds.
`sec` is a number that can have a fractional part.
``
[sec & body]
(with-syms [f]
~(let [,f (coro ,;body)]
(,ev/deadline ,sec nil ,f true)
(,resume ,f))))
(for i 0 10
# (print "deadline 1 iteration " i)
(assert (= :done (with-deadline2 10
(ev/sleep 0.01)
:done)) "deadline with interrupt exits normally"))
(for i 0 10
# (print "deadline 2 iteration " i)
(let [f (coro (forever :foo))]
(ev/deadline 0.01 nil f true)
(assert-error "deadline expired" (resume f))))
# Use :err :stdout
(def- subproc-code '(do (eprint "hi") (eflush) (print "there") (flush)))

58
test/suite-ev2.janet Normal file
View File

@@ -0,0 +1,58 @@
# Copyright (c) 2025 Calvin Rose & contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
(import ./helper :prefix "" :exit true)
(start-suite)
# Issue #1629
(def thread-channel (ev/thread-chan 100))
(def super (ev/thread-chan 10))
(defn worker []
(while true
(def item (ev/take thread-channel))
(when (= item :deadline)
(ev/deadline 0.1 nil (fiber/current) true))))
(ev/thread worker nil :n super)
(ev/give thread-channel :item)
(ev/sleep 0.05)
(ev/give thread-channel :item)
(ev/sleep 0.05)
(ev/give thread-channel :deadline)
(ev/sleep 0.05)
(ev/give thread-channel :item)
(ev/sleep 0.05)
(ev/give thread-channel :item)
(ev/sleep 0.15)
(assert (deep= '(:error "deadline expired" nil) (ev/take super)) "deadline expirataion")
# Another variant
(def thread-channel (ev/thread-chan 100))
(def super (ev/thread-chan 10))
(defn worker []
(while true
(def item (ev/take thread-channel))
(when (= item :deadline)
(ev/deadline 0.1))))
(ev/thread worker nil :n super)
(ev/give thread-channel :deadline)
(ev/sleep 0.2)
(assert (deep= '(:error "deadline expired" nil) (ev/take super)) "deadline expirataion")
(end-suite)

View File

@@ -136,5 +136,8 @@
"keyword slice")
(assert (= 'symbol (symbol/slice "some_symbol_slice" 5 11)) "symbol slice")
# Check string formatting, #1600
(assert (= "" (string/format "%.99s" @"")) "string/format %s buffer")
(end-suite)

50
test/suite-sysir.janet Normal file
View File

@@ -0,0 +1,50 @@
# Copyright (c) 2025 Calvin Rose
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
(import ./helper :prefix "" :exit true)
(start-suite)
(use ../examples/sysir/frontend)
(assert true) # smoke test
(def janet (dyn *executable*))
(def run (filter next (string/split " " (os/getenv "SUBRUN" ""))))
(defn do-expect-directory
"Iterate a directory, evaluating all scripts in the directory. Assert that the captured output of the script
is as expected according to a matching .expect file."
[dir]
(each path (sorted (os/dir dir))
(when (string/has-suffix? ".janet" path)
(def fullpath (string dir "/" path))
(def proc (os/spawn [;run janet fullpath] :p {:out :pipe :err :out}))
(def buff @"")
(var ret-code nil)
(ev/gather
(while (ev/read (proc :out) 4096 buff))
(set ret-code (os/proc-wait proc)))
(def expect-file (string dir "/" path ".expect"))
(def expected-out (slurp expect-file))
(assert (= (string/trim expected-out) (string/trim buff))
(string "\nfile: " fullpath "\nexpected:\n======\n" expected-out "\n======\ngot:\n======\n" buff "\n======\n")))))
(do-expect-directory "test/sysir")
(end-suite)

28
test/sysir/arrays1.janet Normal file
View File

@@ -0,0 +1,28 @@
(def types-asm
'((type-prim Double f64)
(type-array BigVec Double 100)))
(def add-asm
'((link-name "add_vector")
(parameter-count 2)
# Declarations
(bind a BigVec)
(bind b BigVec)
(bind c BigVec)
(add c a b)
(return c)))
(def sub-asm
'((link-name "sub_vector")
(parameter-count 2)
(bind a BigVec)
(bind b BigVec)
(bind c BigVec)
(subtract c a b)
(return c)))
(def ctx (sysir/context))
(sysir/asm ctx types-asm)
(sysir/asm ctx add-asm)
(sysir/asm ctx sub-asm)
(printf "%.99j" (sysir/to-ir ctx))

View File

@@ -0,0 +1 @@
@[@[(type-prim Double f64) (type-array BigVec Double 100)] @[(parameter-count 0)] @[(link-name "add_vector") (parameter-count 2) (type-bind 0 BigVec) (type-bind 1 BigVec) (type-bind 2 BigVec) (add 2 0 1) (return 2)] @[(link-name "sub_vector") (parameter-count 2) (type-bind 0 BigVec) (type-bind 1 BigVec) (type-bind 2 BigVec) (subtract 2 0 1) (return 2)]]

22
test/sysir/arrays2.janet Normal file
View File

@@ -0,0 +1,22 @@
(def ir-asm
'((link-name "add_vectorp")
(parameter-count 2)
# Types
(type-prim Double f64)
(type-array BigVec Double 100)
(type-pointer BigVecP BigVec)
# Declarations
(bind 0 BigVecP)
(bind 1 BigVecP)
(bind 2 BigVecP)
(add 2 0 1)
(return 2)))
(def ctx (sysir/context))
(sysir/asm ctx ir-asm)
(printf "%j" (sysir/to-ir ctx))
(sysir/scalarize ctx)
(printf "%j" (sysir/to-ir ctx))
(print (sysir/to-c ctx))

View File

@@ -0,0 +1,66 @@
@[@[(type-prim Double f64) (type-array BigVec Double 100) (type-pointer BigVecP BigVec)] @[(link-name "add_vectorp") (parameter-count 2) (type-bind 0 BigVecP) (type-bind 1 BigVecP) (type-bind 2 BigVecP) (add 2 0 1) (return 2)]]
@[@[(type-prim Double f64) (type-array BigVec Double 100) (type-pointer BigVecP BigVec)] @[(link-name "add_vectorp") (parameter-count 2) (type-bind 0 BigVecP) (type-bind 1 BigVecP) (type-bind 2 BigVecP) (type-bind 3 U32Index) (type-bind 5 PointerTo) (type-bind 6 PointerTo) (type-bind 7 PointerTo) (type-bind 4 Boolean) (load 3 [U32Index 0]) (label 7) (gte 4 3 [U32Index 0]) (branch 4 21) (apgetp 5 0 3) (apgetp 6 1 3) (apgetp 7 2 3) (add 7 5 6) (add 3 3 [U32Index 1]) (jump 7) (label 21) (return 2)]]
#include <stddef.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/syscall.h>
#define _t0 void
#line 6
typedef double _t1;
#line 7
typedef struct { _t1 els[100]; } _t2;
#line 8
typedef _t2 *_t3;
_t3 add_vectorp(_t3 _r0, _t3 _r1)
{
_t3 _r2;
_t4 _r3;
_t5 _r4;
_t6 _r5;
_t6 _r6;
_t6 _r7;
#line 6
#line 7
#line 8
#line 11
#line 12
#line 13
#line 14
#line 14
#line 14
#line 14
#line 14
#line 14
_r3 = *(0);
#line 14
_label_0:
#line 14
_r4 = _r3 > 0;
#line 14
if (_r4) goto _label_1;
#line 14
_r5 = &(_r0->els[_r3]);
#line 14
_r6 = &(_r1->els[_r3]);
#line 14
_r7 = &(_r2->els[_r3]);
#line 14
_r7 = _r5 + _r6;
#line 14
_r3 = _r3 + 1;
#line 14
goto _label_0;
#line 14
_label_1:
#line 15
return _r2;
}

35
test/sysir/basic1.janet Normal file
View File

@@ -0,0 +1,35 @@
(def ir-asm
'((link-name "test_function")
# Types
(type-prim Int s32)
(type-prim Double f64)
(type-struct MyPair 0 1)
(type-pointer PInt Int)
(type-array DoubleArray 1 1024)
# Declarations
(bind 0 Int)
(bind 1 Int)
(bind 2 Int)
(bind 3 Double)
(bind bob Double)
(bind 5 Double)
(bind 6 MyPair)
# Code
(move 0 (Int 10))
(move 0 (Int 21))
:location
(add 2 1 0)
(move 3 (Double 1.77))
(call :default 3 (PInt sin) 3)
(cast bob 2)
(call :default bob (PInt test_function))
(add 5 bob 3)
(jump :location)
(return 5)))
(def ctx (sysir/context))
(sysir/asm ctx ir-asm)
(print (sysir/to-c ctx))

View File

@@ -0,0 +1,71 @@
#include <stddef.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/syscall.h>
#define _t0 void
#line 5
typedef int32_t _t1;
#line 6
typedef double _t2;
#line 7
typedef struct {
_t0 _f0;
_t1 _f1;
} _t3;
#line 8
typedef _t1 *_t4;
#line 9
typedef struct { _t1 els[1024]; } _t5;
_t2 test_function()
{
_t1 _r0;
_t1 _r1;
_t1 _r2;
_t2 _r3;
_t2 _r4;
_t2 _r5;
_t3 _r6;
#line 5
#line 6
#line 7
#line 7
#line 8
#line 9
#line 12
#line 13
#line 14
#line 15
#line 16
#line 17
#line 18
#line 21
_r0 = 10;
#line 22
_r0 = 21;
_label_0:
#line 24
_r2 = _r1 + _r0;
#line 25
_r3 = 1.77;
#line 26
_r3 = sin(_r3);
#line 26
#line 27
_r4 = (_t2) _r2;
#line 28
_r4 = test_function();
#line 29
_r5 = _r4 + _r3;
#line 30
goto _label_0;
#line 31
return _r5;
}

62
test/sysir/basic2.janet Normal file
View File

@@ -0,0 +1,62 @@
### typedef struct {float x; float y; float z;} Vec3;
###
### Vec3 addv(Vec3 a, Vec3 b) {
### Vec3 ret;
### ret.x = a.x + b.x;
### ret.y = a.y + b.y;
### ret.z = a.z + b.z;
### return ret;
### }
# Use fgetp for code gen
(def ir-asm
'((link-name "addv")
(parameter-count 2)
# Types
(type-prim Real f32)
(type-struct Vec3 Real Real Real)
(type-pointer PReal Real)
# Declarations
(bind position Vec3)
(bind velocity Vec3)
(bind next-position Vec3)
(bind dest Real)
(bind lhs Real)
(bind rhs Real)
(bind pdest PReal)
(bind plhs PReal)
(bind prhs PReal)
# Code
(fgetp pdest next-position 0)
(fgetp plhs position 0)
(fgetp prhs velocity 0)
(load lhs plhs)
(load rhs prhs)
(add dest lhs rhs)
(store pdest dest)
(fgetp pdest next-position 1)
(fgetp plhs position 1)
(fgetp prhs velocity 1)
(load lhs plhs)
(load rhs prhs)
(add dest lhs rhs)
(store pdest dest)
(fgetp pdest next-position 2)
(fgetp plhs position 2)
(fgetp prhs velocity 2)
(load lhs plhs)
(load rhs prhs)
(add dest lhs rhs)
(store pdest dest)
(return next-position)))
(def ctx (sysir/context))
(sysir/asm ctx ir-asm)
(print (sysir/to-c ctx))

View File

@@ -0,0 +1 @@
nope

1
test/sysir/smoke.janet Normal file
View File

@@ -0,0 +1 @@
(print "hello")

View File

@@ -0,0 +1 @@
hello

View File

@@ -37,6 +37,12 @@
Version="$(var.Version)"
Manufacturer="$(var.Manufacturer)"
UpgradeCode="$(var.UpgradeCode)">
<!--
perUser means destination will be under user's %AppData% directory,
not Program Files or similar.
see: https://learn.microsoft.com/en-us/windows/win32/msi/installation-context
-->
<Package Compressed="yes"
InstallScope="perUser"
Manufacturer="$(var.Manufacturer)"

21
tools/x64.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
case "$2" in
c)
rm temp.bin temp.o temp.nasm
build/janet "$@" > temp.c
gcc -nostdlib temp.c -c temp.o
;;
x64)
rm temp.bin temp.o temp.nasm
build/janet "$@" > temp.nasm
nasm -felf64 temp.nasm -l temp.lst -o temp.o
;;
*)
echo "Unknown mode $2"
exit
;;
esac
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
./temp.bin