mirror of
https://github.com/janet-lang/janet
synced 2025-11-18 08:15:13 +00:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e0c692d4e | ||
|
|
732fe0ad03 | ||
|
|
0c8622c803 | ||
|
|
94f2494f8d | ||
|
|
0f9ecc2da5 | ||
|
|
83f5da3b8f | ||
|
|
9b9f2a1713 | ||
|
|
8df4d47ede | ||
|
|
1c372fbf32 | ||
|
|
8ace580498 | ||
|
|
8241d9cbb4 | ||
|
|
6bd02bb5b6 | ||
|
|
2a3308005d | ||
|
|
0c34033b72 | ||
|
|
f1ec0cc48b | ||
|
|
98265f0637 | ||
|
|
1018cb9cca | ||
|
|
2204209133 | ||
|
|
95abff31d9 | ||
|
|
a776466423 | ||
|
|
511c1f4b0a | ||
|
|
c29195596e | ||
|
|
56027227fb | ||
|
|
c057e14b20 | ||
|
|
db7f741dad | ||
|
|
c901edbfb9 | ||
|
|
8fd1672963 | ||
|
|
9b99fc44b9 | ||
|
|
f393531335 | ||
|
|
6b8e5249ca | ||
|
|
6a96b615f0 | ||
|
|
8ec465d308 | ||
|
|
07bfd34c2f | ||
|
|
5f7878c00f | ||
|
|
aaf8ac2217 | ||
|
|
73b1cf547e | ||
|
|
ed2ae562c6 | ||
|
|
dd59d84b51 | ||
|
|
06873fbf0b | ||
|
|
1ff26d702a | ||
|
|
4da568254a | ||
|
|
357f1f94ca | ||
|
|
015e49c806 | ||
|
|
6b06ab5f9c | ||
|
|
fe6c6e15a6 | ||
|
|
b4eb52ca45 | ||
|
|
aca5428846 | ||
|
|
3dab9737e2 | ||
|
|
e601e8faab | ||
|
|
07cf63622f | ||
|
|
8e7b1e9ce0 | ||
|
|
355c514f0e | ||
|
|
976329abc1 | ||
|
|
ab3e843433 | ||
|
|
148e108864 | ||
|
|
c90c737345 | ||
|
|
13b9976382 | ||
|
|
095a81286a | ||
|
|
82416e4e4e | ||
|
|
ae51434a05 | ||
|
|
bb6ac423a7 | ||
|
|
c5ba3c0513 | ||
|
|
e9c6678614 | ||
|
|
800457c1bf | ||
|
|
2a85781616 | ||
|
|
7c15e7f7dc | ||
|
|
896c28b0c8 | ||
|
|
e7bb0dd58e | ||
|
|
4e02f27eb9 | ||
|
|
fd234461d7 | ||
|
|
eabb215391 | ||
|
|
deede6bae0 | ||
|
|
697fdcff6d | ||
|
|
ad8a5cb6c7 | ||
|
|
99abada2c2 | ||
|
|
0624936711 | ||
|
|
f764788b36 | ||
|
|
4701bc6543 | ||
|
|
156fb0c999 | ||
|
|
bf34340737 | ||
|
|
20535e8626 | ||
|
|
1ead670e33 | ||
|
|
3ad86108f2 | ||
|
|
0aee7765cf | ||
|
|
4894a4673a | ||
|
|
f00d3199c3 | ||
|
|
e34a8545e6 | ||
|
|
f974c0667b | ||
|
|
ddc122958b | ||
|
|
2e363bf29c | ||
|
|
312f9faae8 | ||
|
|
8c9cd63cb1 | ||
|
|
2af3f21d69 | ||
|
|
c4e3fa03fa | ||
|
|
91b7bcad3d | ||
|
|
8d2a9c1148 | ||
|
|
f1d47bd05a | ||
|
|
58b1491592 | ||
|
|
21a6ed3bd3 | ||
|
|
e815c91e85 | ||
|
|
d96e584869 | ||
|
|
f4ecb5a90f | ||
|
|
f181948aa9 | ||
|
|
bbe6b90331 | ||
|
|
27f01e2664 | ||
|
|
877967966a | ||
|
|
56c5a0ca09 | ||
|
|
f3ad13c2d4 | ||
|
|
8ac4eec370 | ||
|
|
92e91259c3 | ||
|
|
e355cb07e0 | ||
|
|
5bbfcdacd5 | ||
|
|
790a4f2636 | ||
|
|
84bb84b0b7 | ||
|
|
29f2b5c345 | ||
|
|
4643c8fa35 | ||
|
|
a8e2c8e5b8 | ||
|
|
3d3e880f52 | ||
|
|
bef8ba5e06 | ||
|
|
523639362c | ||
|
|
4b6d5e5671 | ||
|
|
a695454dae | ||
|
|
f2eaa5dee8 | ||
|
|
b27c830d90 | ||
|
|
92a852f2df | ||
|
|
647e218bed | ||
|
|
5ebe945ffd | ||
|
|
6254fffad0 | ||
|
|
5705b2f6c7 | ||
|
|
90a33bc88a | ||
|
|
1ba077c87d | ||
|
|
34629ae314 | ||
|
|
3edc4f35b2 | ||
|
|
06d01c099f | ||
|
|
d493eaf485 | ||
|
|
332f123abe | ||
|
|
38e841fc5c | ||
|
|
e8187fdee5 | ||
|
|
2fedb67cb3 | ||
|
|
bdab93c999 | ||
|
|
a9ff8b388f | ||
|
|
b12dfd784e | ||
|
|
e2cc8f2965 | ||
|
|
17524d2ed3 | ||
|
|
d2ee4aa074 | ||
|
|
363e32d455 | ||
|
|
31920e574d | ||
|
|
cf714ed591 | ||
|
|
b458404b41 | ||
|
|
707463a645 | ||
|
|
eac37ab869 | ||
|
|
a24e5b1eaa | ||
|
|
09ac85b1b9 | ||
|
|
87c1eab7d4 | ||
|
|
5a29a28c11 | ||
|
|
2ed186664f |
@@ -1,4 +1,4 @@
|
||||
image: openbsd/7.4
|
||||
image: openbsd/7.7
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
@@ -10,7 +10,6 @@ tasks:
|
||||
gmake
|
||||
gmake test
|
||||
doas gmake install
|
||||
gmake test-install
|
||||
doas gmake uninstall
|
||||
- meson_min: |
|
||||
cd janet
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -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
|
||||
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,6 +1,29 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 1.40.0 - 2025-11-15
|
||||
- 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.
|
||||
- Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes.
|
||||
Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
|
||||
|
||||
## 1.38.0 - 2025-03-18
|
||||
- Add `bundle/replace`
|
||||
- Add CLI flags for the `bundle/` module to install and manage bundles.
|
||||
|
||||
@@ -61,7 +61,7 @@ ensure a consistent code style for C.
|
||||
|
||||
## Janet style
|
||||
|
||||
All janet code in the project should be formatted similar to the code in core.janet.
|
||||
All janet code in the project should be formatted similar to the code in src/boot/boot.janet.
|
||||
The auto formatting from janet.vim will work well.
|
||||
|
||||
## Typo Fixing and One-Line changes
|
||||
|
||||
46
Makefile
46
Makefile
@@ -47,6 +47,7 @@ SPORK_TAG?=master
|
||||
HAS_SHARED?=1
|
||||
DEBUGGER=gdb
|
||||
SONAME_SETTER=-Wl,-soname,
|
||||
STRIPFLAGS=-x -S
|
||||
|
||||
# For cross compilation
|
||||
HOSTCC?=$(CC)
|
||||
@@ -54,7 +55,7 @@ HOSTAR?=$(AR)
|
||||
# Symbols are (optionally) removed later, keep -g as default!
|
||||
CFLAGS?=-O2 -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!
|
||||
@@ -213,9 +220,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)
|
||||
@@ -254,6 +261,7 @@ $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
# Testing assumes HOSTCC=CC
|
||||
|
||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
|
||||
EXAMPLE_SCRIPTS=$(wildcard examples/*.janet)
|
||||
|
||||
repl: $(JANET_TARGET)
|
||||
$(RUN) ./$(JANET_TARGET)
|
||||
@@ -261,21 +269,26 @@ repl: $(JANET_TARGET)
|
||||
debug: $(JANET_TARGET)
|
||||
$(DEBUGGER) ./$(JANET_TARGET)
|
||||
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
|
||||
VALGRIND_COMMAND=$(RUN) valgrind --leak-check=full --quiet
|
||||
CALLGRIND_COMMAND=$(RUN) valgrind --tool=callgrind
|
||||
|
||||
valgrind: $(JANET_TARGET)
|
||||
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
test: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
valtest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
|
||||
for f in examples/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
callgrind: $(JANET_TARGET)
|
||||
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
|
||||
$(CALLGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
calltest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
|
||||
for f in test/suite*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
########################
|
||||
##### Distribution #####
|
||||
@@ -289,7 +302,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 +349,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'
|
||||
@@ -406,9 +419,6 @@ clean:
|
||||
-rm -rf build vgcore.* callgrind.*
|
||||
-rm -rf test/install/build test/install/modpath
|
||||
|
||||
test-install:
|
||||
echo "JPM has been removed from default install."
|
||||
|
||||
help:
|
||||
@echo
|
||||
@echo 'Janet: A Dynamic Language & Bytecode VM'
|
||||
@@ -420,7 +430,8 @@ help:
|
||||
@echo ' make test Test a built Janet'
|
||||
@echo ' make valgrind Assess Janet with Valgrind'
|
||||
@echo ' make callgrind Assess Janet with Valgrind, using Callgrind'
|
||||
@echo ' make valtest Run the test suite with Valgrind to check for memory leaks'
|
||||
@echo ' make valtest Run the test suite and examples with Valgrind to check for memory leaks'
|
||||
@echo ' make calltest Run the test suite and examples with Callgrind'
|
||||
@echo ' make dist Create a distribution tarball'
|
||||
@echo ' make docs Generate documentation'
|
||||
@echo ' make debug Run janet with GDB or LLDB'
|
||||
@@ -430,6 +441,9 @@ help:
|
||||
@echo " make format Format Janet's own source files"
|
||||
@echo ' make grammar Generate a TextMate language grammar'
|
||||
@echo
|
||||
@echo ' make install-jpm-git Install jpm into the current filesystem'
|
||||
@echo ' make install-spork-git Install spork into the current filesystem'
|
||||
@echo
|
||||
|
||||
.PHONY: clean install repl debug valgrind test \
|
||||
valtest dist uninstall docs grammar format help compile-commands
|
||||
.PHONY: clean install install-jpm-git install-spork-git repl debug valgrind test \
|
||||
valtest callgrind callgrind-test dist uninstall docs grammar format help compile-commands
|
||||
|
||||
19
README.md
19
README.md
@@ -165,6 +165,21 @@ make install-jpm-git
|
||||
|
||||
Find out more about the available make targets by running `make help`.
|
||||
|
||||
### Alpine Linux
|
||||
|
||||
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good combination. Janet can also
|
||||
be built inside a docker container or similar in this manner.
|
||||
|
||||
```sh
|
||||
docker run -it --rm alpine /bin/ash
|
||||
$ apk add make gcc musl-dev git
|
||||
$ git clone https://github.com/janet-lang/janet.git
|
||||
$ cd janet
|
||||
$ make -j10
|
||||
$ make test
|
||||
$ make install
|
||||
```
|
||||
|
||||
### 32-bit Haiku
|
||||
|
||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
|
||||
@@ -198,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#).
|
||||
|
||||
@@ -41,34 +41,35 @@ if not exist build\boot mkdir build\boot
|
||||
@rem Build the bootstrap interpreter
|
||||
for %%f in (src\core\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@rem note that there is no default sysroot being baked in
|
||||
build\janet_boot . > build\c\janet.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.lib)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
echo === Run 'build_win test' to run tests. ==
|
||||
@@ -102,7 +103,7 @@ exit /b 0
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if not errorlevel 0 goto TESTFAIL
|
||||
@if errorlevel 1 goto TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
|
||||
6
examples/abstract-unix-socket.janet
Normal file
6
examples/abstract-unix-socket.janet
Normal 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)
|
||||
3
examples/sample-bad-bundle1/info.jdn
Normal file
3
examples/sample-bad-bundle1/info.jdn
Normal file
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
:name "sample-bad-bundle1"
|
||||
}
|
||||
3
examples/sample-bad-bundle2/info.jdn
Normal file
3
examples/sample-bad-bundle2/info.jdn
Normal file
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
:name "sample-bad-bundle2"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@{
|
||||
:name "sample-dep1"
|
||||
:dependencies ["sample-dep2"]
|
||||
:dependencies [{:name "sample-dep2"}]
|
||||
}
|
||||
|
||||
5
janet.1
5
janet.1
@@ -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
|
||||
|
||||
@@ -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')
|
||||
@@ -105,6 +105,9 @@ endif
|
||||
if get_option('arch_name') != ''
|
||||
conf.set('JANET_ARCH_NAME', get_option('arch_name'))
|
||||
endif
|
||||
if get_option('thread_local_prefix') != ''
|
||||
conf.set('JANET_THREAD_LOCAL', get_option('thread_local_prefix'))
|
||||
endif
|
||||
jconf = configure_file(output : 'janetconf.h',
|
||||
configuration : conf)
|
||||
|
||||
@@ -278,6 +281,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',
|
||||
|
||||
@@ -30,6 +30,7 @@ option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
|
||||
|
||||
option('arch_name', type : 'string', value: '')
|
||||
option('thread_local_prefix', type : 'string', value: '')
|
||||
option('os_name', type : 'string', value: '')
|
||||
option('shared', type : 'boolean', value: true)
|
||||
option('cryptorand', type : 'boolean', value: true)
|
||||
|
||||
@@ -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,26 @@
|
||||
(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 (gensym)
|
||||
f (gensym)]
|
||||
~(let [,f (,fiber/new (fn :try [] ,body) :ie)
|
||||
,r (,resume ,f)]
|
||||
(if (,= (,fiber/status ,f) :error)
|
||||
(do
|
||||
,(if err ~(def ,err ,r))
|
||||
,(if fib ~(def ,fib ,f))
|
||||
,;(tuple/slice catch 1))
|
||||
,r))))
|
||||
|
||||
(defmacro with-syms
|
||||
"Evaluates `body` with each symbol in `syms` bound to a generated, unique symbol."
|
||||
[syms & body]
|
||||
@@ -1084,16 +1091,29 @@
|
||||
(map-aggregator ,maptype ,res (,f x ;call-buffer)))))))
|
||||
|
||||
(defn map
|
||||
`Map a function over every value in a data structure and
|
||||
return an array of the results.`
|
||||
```
|
||||
Map a function `f` over every value in a data structure `ind`
|
||||
and return an array of results, but only if no `inds` are
|
||||
provided. Multiple data structures can be handled if each
|
||||
`inds` is a data structure and `f` is a function of arity
|
||||
one more than the number of `inds`. The resulting array has
|
||||
a length that is the shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[f ind & inds]
|
||||
(def res @[])
|
||||
(map-template :map res f ind inds)
|
||||
res)
|
||||
|
||||
(defn mapcat
|
||||
``Map a function over every element in an array or tuple and
|
||||
use `array/concat` to concatenate the results.``
|
||||
```
|
||||
Map a function `f` over every value in a data structure `ind`
|
||||
and use `array/concat` to concatenate the results, but only if
|
||||
no `inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `f` is a function of
|
||||
arity one more than the number of `inds`. Note that `f` is only
|
||||
applied to values at indeces up to the largest index of the
|
||||
shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[f ind & inds]
|
||||
(def res @[])
|
||||
(map-template :mapcat res f ind inds)
|
||||
@@ -1110,18 +1130,30 @@
|
||||
res)
|
||||
|
||||
(defn count
|
||||
``Count the number of items in `ind` for which `(pred item)`
|
||||
is true.``
|
||||
```
|
||||
Count the number of values in a data structure `ind` for which
|
||||
applying `pred` yields a truthy value, but only if no `inds` are
|
||||
provided. Multiple data structures can be handled if each `inds`
|
||||
is a data structure and `pred` is a function of arity one more
|
||||
than the number of `inds`. Note that `pred` is only applied to
|
||||
values at indeces up to the largest index of the shortest of
|
||||
`ind` and each of `inds`.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res 0)
|
||||
(map-template :count res pred ind inds)
|
||||
res)
|
||||
|
||||
(defn keep
|
||||
``Given a predicate `pred`, return a new array containing the truthy results
|
||||
of applying `pred` to each element in the indexed collection `ind`. This is
|
||||
different from `filter` which returns an array of the original elements where
|
||||
the predicate is truthy.``
|
||||
```
|
||||
Given a predicate `pred`, return a new array containing the
|
||||
truthy results of applying `pred` to each value in the data
|
||||
structure `ind`, but only if no `inds` are provided. Multiple
|
||||
data structures can be handled if each `inds` is a data
|
||||
structure and `pred` is a function of arity one more than the
|
||||
number of `inds`. The resulting array has a length that is no
|
||||
longer than the shortest of `ind` and each of `inds`.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(def res @[])
|
||||
(map-template :keep res pred ind inds)
|
||||
@@ -1774,8 +1806,8 @@
|
||||
(flatten-into @[] xs))
|
||||
|
||||
(defn kvs
|
||||
``Takes a table or struct and returns and array of key value pairs
|
||||
like `@[k v k v ...]`. Returns a new array.``
|
||||
``Takes a table or struct and returns a new array of key value pairs
|
||||
like `@[k v k v ...]`.``
|
||||
[dict]
|
||||
(def ret @[])
|
||||
(loop [k :keys dict] (array/push ret k (in dict k)))
|
||||
@@ -1908,7 +1940,7 @@
|
||||
that will match any value without creating a binding.
|
||||
|
||||
While a symbol pattern will ordinarily match any value, the pattern `(@ <sym>)`,
|
||||
where <sym> is any symbol, will attempt to match `x` against a value
|
||||
where `<sym>` is any symbol, will attempt to match `x` against a value
|
||||
already bound to `<sym>`, rather than matching and rebinding it.
|
||||
|
||||
Any other value pattern will only match if it is equal to `x`.
|
||||
@@ -2203,17 +2235,32 @@
|
||||
ret)
|
||||
|
||||
(defn all
|
||||
``Returns true if `(pred item)` is truthy for every item in `ind`.
|
||||
Otherwise, returns the first falsey result encountered.
|
||||
Returns true if `ind` is empty.``
|
||||
```
|
||||
Returns true if applying `pred` to every value in a data
|
||||
structure `ind` results in only truthy values, but only if no
|
||||
`inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `pred` is a function
|
||||
of arity one more than the number of `inds`. Returns the first
|
||||
falsey result encountered. Note that `pred` is only called as
|
||||
many times as the length of the shortest of `ind` and each of
|
||||
`inds`. If `ind` or any of `inds` are empty, returns true.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res true)
|
||||
(map-template :all res pred ind inds)
|
||||
res)
|
||||
|
||||
(defn some
|
||||
``Returns nil if `(pred item)` is false or nil for every item in `ind`.
|
||||
Otherwise, returns the first truthy result encountered.``
|
||||
```
|
||||
Returns nil if applying `pred` to every value in a data
|
||||
structure `ind` results in only falsey values, but only if no
|
||||
`inds` are provided. Multiple data structures can be handled
|
||||
if each `inds` is a data structure and `pred` is a function
|
||||
of arity one more than the number of `inds`. Returns the first
|
||||
truthy result encountered. Note that `pred` is only called as
|
||||
many times as the length of the shortest of `ind` and each of
|
||||
`inds`. If `ind` or any of `inds` are empty, returns nil.
|
||||
```
|
||||
[pred ind & inds]
|
||||
(var res nil)
|
||||
(map-template :some res pred ind inds)
|
||||
@@ -2313,7 +2360,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
|
||||
@@ -2534,7 +2581,7 @@
|
||||
* `:env` -- the environment to compile against - default is the current env
|
||||
|
||||
* `:source` -- source path for better errors (use keywords for non-paths) - default
|
||||
is :<anonymous>
|
||||
is `:<anonymous>`
|
||||
|
||||
* `:on-compile-error` -- callback when compilation fails - default is bad-compile
|
||||
|
||||
@@ -3140,12 +3187,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
|
||||
@@ -3208,12 +3260,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 @{})
|
||||
@@ -3344,7 +3394,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)
|
||||
@@ -3876,8 +3925,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))))
|
||||
@@ -3896,7 +3951,7 @@
|
||||
[& forms]
|
||||
(def state (gensym))
|
||||
(def loaded (gensym))
|
||||
~((fn []
|
||||
~((fn :delay []
|
||||
(var ,state nil)
|
||||
(var ,loaded nil)
|
||||
(fn []
|
||||
@@ -3928,7 +3983,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."
|
||||
@@ -3939,6 +3994,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
|
||||
@@ -3954,7 +4011,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)))
|
||||
@@ -3965,6 +4022,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.`
|
||||
@@ -3980,59 +4082,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))
|
||||
|
||||
###
|
||||
###
|
||||
@@ -4125,7 +4197,7 @@
|
||||
(spit manifest-name b))
|
||||
|
||||
(defn bundle/manifest
|
||||
"Get the manifest for a give installed bundle"
|
||||
"Get the manifest for a given installed bundle."
|
||||
[bundle-name]
|
||||
(def name (get-manifest-filename bundle-name))
|
||||
(assertf (fexists name) "no bundle %v found" bundle-name)
|
||||
@@ -4150,7 +4222,9 @@
|
||||
(put new-env *syspath* fixed-syspath)
|
||||
(with-env new-env
|
||||
(put new-env :bundle-dir (bundle-dir bundle-name)) # get the syspath right
|
||||
(require (string "@syspath/bundle/" bundle-name)))))
|
||||
(try
|
||||
(require (string "@syspath/bundle/" bundle-name))
|
||||
([_] (error "bundle must contain bundle.janet or bundle/init.janet"))))))
|
||||
|
||||
(defn- do-hook
|
||||
[module bundle-name hook & args]
|
||||
@@ -4187,7 +4261,9 @@
|
||||
nil)
|
||||
|
||||
(defn bundle/uninstall
|
||||
"Remove a bundle from the current syspath"
|
||||
``Remove a bundle from the current syspath. There is 1 hook called during
|
||||
uninstallation (uninstall). A user can register a hook by defining a
|
||||
function with the same name in the bundle script.``
|
||||
[bundle-name]
|
||||
(def breakage @{})
|
||||
(each b (bundle/list)
|
||||
@@ -4223,8 +4299,8 @@
|
||||
order)
|
||||
|
||||
(defn bundle/prune
|
||||
"Remove all orphaned bundles from the syspath. An orphaned bundle is a bundle that is
|
||||
marked for :auto-remove and is not depended on by any other bundle."
|
||||
``Remove all orphaned bundles from the current syspath. An orphaned bundle is a
|
||||
bundle that is marked for :auto-remove and is not depended on by any other bundle.``
|
||||
[]
|
||||
(def topo (bundle/topolist))
|
||||
(def rtopo (reverse topo))
|
||||
@@ -4253,58 +4329,67 @@
|
||||
(not (not (os/stat (bundle-dir bundle-name) :mode))))
|
||||
|
||||
(defn bundle/install
|
||||
"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`."
|
||||
``Install a bundle from the local filesystem. The name of the bundle is
|
||||
the value mapped to :name in either `config` or the info file. There are
|
||||
5 hooks called during installation (postdeps, clean, build, install and
|
||||
check). A user can register a hook by defining a function with the same name
|
||||
in the bundle script.``
|
||||
[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))
|
||||
(assertf bundle-name "unable to infer bundle name for %v, use :name argument" path)
|
||||
# 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 or add :name to info file" path)
|
||||
(assertf (not (string/check-set "\\/" bundle-name))
|
||||
"bundle name %v cannot contain path separators" bundle-name)
|
||||
(assert (next bundle-name) "cannot use empty bundle-name")
|
||||
(assertf (not (fexists (get-manifest-filename bundle-name)))
|
||||
"bundle %v is already installed" bundle-name)
|
||||
# Check bscript
|
||||
(def bscript-src1 (string path s "bundle" s "init.janet"))
|
||||
(def bscript-src2 (string path s "bundle.janet"))
|
||||
(def bscript-src (cond
|
||||
(fexists bscript-src1) bscript-src1
|
||||
(fexists bscript-src2) bscript-src2))
|
||||
# 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 aliased infofile
|
||||
(when (fexists infofile-src2)
|
||||
(copyfile infofile-src2 (bundle-file bundle-name "info.jdn")))
|
||||
# Copy aliased bscript
|
||||
(when (fexists bscript-src2)
|
||||
(copyfile bscript-src2 (bundle-file bundle-name "init.janet")))
|
||||
# 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 info
|
||||
(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 module (get-bundle-module bundle-name))
|
||||
(def clean (get config :clean))
|
||||
(def check (get config :check))
|
||||
(def all-hooks (seq [[k v] :pairs module :when (symbol? k) :unless (get v :private)] (keyword k)))
|
||||
(put man :hooks all-hooks)
|
||||
(do-hook module bundle-name :dependencies man)
|
||||
(do-hook module bundle-name :dependencies man) # deprecated, use :postdeps
|
||||
(do-hook module bundle-name :postdeps man)
|
||||
(when clean
|
||||
(do-hook module bundle-name :clean man))
|
||||
(do-hook module bundle-name :build man)
|
||||
@@ -4314,12 +4399,21 @@
|
||||
(when check
|
||||
(do-hook module bundle-name :check man)))
|
||||
(print "installed " bundle-name)
|
||||
(when (or (get man :has-exe)
|
||||
# remove eventually
|
||||
(get man :has-bin-script))
|
||||
(def binpath (string (dyn *syspath*) s "bin"))
|
||||
(eprintf "executable files have been installed to %s" binpath))
|
||||
(when (get man :has-man)
|
||||
(def manpath (string (dyn *syspath*) s "man"))
|
||||
(eprintf "man pages have been installed to %s" manpath))
|
||||
bundle-name)
|
||||
|
||||
(defn- bundle/pack
|
||||
"Take an installed bundle and create a bundle source directory that can be used to
|
||||
reinstall the bundle on a compatible system. This is used to create backups for installed
|
||||
bundles without rebuilding, or make a prebuilt bundle for other systems."
|
||||
``Take an installed bundle and create a bundle source directory that can be
|
||||
used to reinstall the bundle on a compatible system. This is used to create
|
||||
backups for installed bundles without rebuilding, or make a prebuilt bundle
|
||||
for other systems.``
|
||||
[bundle-name dest-dir &opt is-backup]
|
||||
(var i 0)
|
||||
(def man (bundle/manifest bundle-name))
|
||||
@@ -4349,9 +4443,9 @@
|
||||
dest-dir)
|
||||
|
||||
(defn bundle/replace
|
||||
"Reinstall an existing bundle from a new directory. Similar to bundle/reinstall,
|
||||
but installs the replacement bundle from any directory. This is necesarry to replace a package without
|
||||
breaking any dependencies."
|
||||
``Reinstall an existing bundle from a new directory. Similar to
|
||||
bundle/reinstall, but installs the replacement bundle from any directory.
|
||||
This is necessary to replace a package without breaking any dependencies.``
|
||||
[bundle-name path &keys new-config]
|
||||
(def manifest (bundle/manifest bundle-name))
|
||||
(def config (get manifest :config @{}))
|
||||
@@ -4378,7 +4472,7 @@
|
||||
bundle-name)
|
||||
|
||||
(defn bundle/add-directory
|
||||
"Add a directory during the install process relative to `(dyn *syspath*)`"
|
||||
"Add a directory during an install relative to `(dyn *syspath*)`."
|
||||
[manifest dest &opt chmod-mode]
|
||||
(def files (get-files manifest))
|
||||
(def s (sep))
|
||||
@@ -4406,7 +4500,7 @@
|
||||
ret)
|
||||
|
||||
(defn bundle/add-file
|
||||
"Add files during an install relative to `(dyn *syspath*)`"
|
||||
"Add a file during an install relative to `(dyn *syspath*)`."
|
||||
[manifest src &opt dest chmod-mode]
|
||||
(default dest src)
|
||||
(def files (get-files manifest))
|
||||
@@ -4423,9 +4517,9 @@
|
||||
absdest)
|
||||
|
||||
(defn bundle/add
|
||||
"Add files and directories during a bundle install relative to `(dyn *syspath*)`.
|
||||
Added paths will be recorded in the bundle manifest such that they are properly tracked
|
||||
and removed during an upgrade or uninstall."
|
||||
``Add a file or directory during an install relative to `(dyn *syspath*)`.
|
||||
Added files and directories will be recorded in the bundle manifest such
|
||||
that they are properly tracked and removed during an upgrade or uninstall.``
|
||||
[manifest src &opt dest chmod-mode]
|
||||
(default dest src)
|
||||
(def s (sep))
|
||||
@@ -4440,17 +4534,31 @@
|
||||
(errorf "bad path %s - file is a %s" src mode)))
|
||||
|
||||
(defn bundle/add-bin
|
||||
`Shorthand for adding scripts during an install. Scripts will be installed to
|
||||
(string (dyn *syspath*) "/bin") by default and will be set to be executable.`
|
||||
[manifest src &opt dest chmod-mode]
|
||||
``Add a file to the "bin" subdirectory of the current syspath. By default,
|
||||
files will be set to be executable.``
|
||||
[manifest src &opt filename chmod-mode]
|
||||
(def s (sep))
|
||||
(default dest (last (string/split s src)))
|
||||
(default filename (last (string/split s src)))
|
||||
(default chmod-mode 8r755)
|
||||
(os/mkdir (string (dyn *syspath*) s "bin"))
|
||||
(bundle/add-file manifest src (string "bin" s dest) chmod-mode))
|
||||
(put manifest :has-exe true)
|
||||
(put manifest :has-bin-script true) # remove eventually
|
||||
(bundle/add-file manifest src (string "bin" s filename) chmod-mode))
|
||||
|
||||
(defn bundle/add-manpage
|
||||
``Add a file to the man subdirectory of the current syspath. Files are
|
||||
copied inside a directory `mansec`. By default, `mansec` is "man1".``
|
||||
[manifest src &opt mansec]
|
||||
(def s (sep))
|
||||
(default mansec "man1")
|
||||
(def filename (last (string/split s src)))
|
||||
(os/mkdir (string (dyn *syspath*) s "man"))
|
||||
(os/mkdir (string (dyn *syspath*) s "man" s mansec))
|
||||
(put manifest :has-man true)
|
||||
(bundle/add-file manifest src (string "man" s mansec s filename)))
|
||||
|
||||
(defn bundle/update-all
|
||||
"Reinstall all bundles"
|
||||
"Reinstall all bundles."
|
||||
[&keys configs]
|
||||
(each bundle (bundle/topolist)
|
||||
(bundle/reinstall bundle ;(kvs configs)))))
|
||||
@@ -4462,7 +4570,10 @@
|
||||
###
|
||||
|
||||
# conditional compilation for reduced os
|
||||
(def- getenv-alias (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&])))
|
||||
(def- getenv-raw (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&])))
|
||||
(defn- getenv-alias [env-var &opt dflt]
|
||||
(def x (getenv-raw env-var dflt))
|
||||
(if (= x "") nil x)) # empty string is coerced to nil
|
||||
|
||||
(defn- run-main
|
||||
[env subargs arg]
|
||||
@@ -4585,7 +4696,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
|
||||
```)
|
||||
@@ -4837,14 +4948,15 @@
|
||||
"src/core/wrap.c"])
|
||||
|
||||
# Print janet.c to stdout
|
||||
(print "/* Amalgamated build - DO NOT EDIT */")
|
||||
(def image-only (has-value? boot/args "image-only"))
|
||||
(print "/* " (if image-only "Image-only" "Amalgamated") " build - DO NOT EDIT */")
|
||||
(print "/* Generated from janet version " janet/version "-" janet/build " */")
|
||||
(print "#define JANET_BUILD \"" janet/build "\"")
|
||||
(print ```#define JANET_AMALG```)
|
||||
|
||||
(defn do-one-file
|
||||
[fname]
|
||||
(unless (has-value? boot/args "image-only")
|
||||
(unless image-only
|
||||
(print "\n/* " fname " */")
|
||||
(print "#line 0 \"" fname "\"\n")
|
||||
(def source (slurp fname))
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
#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" */
|
||||
|
||||
/* These settings all affect linking, so use cautiously. */
|
||||
/* #define JANET_SINGLE_THREADED */
|
||||
/* #define JANET_THREAD_LOCAL _Thread_local */
|
||||
/* #define JANET_NO_DYNAMIC_MODULES */
|
||||
/* #define JANET_NO_NANBOX */
|
||||
/* #define JANET_API __attribute__((visibility ("default"))) */
|
||||
|
||||
@@ -589,6 +589,16 @@ JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_relaxed);
|
||||
#else
|
||||
return __atomic_load_n(x, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
245
src/core/ev.c
245
src/core/ev.c
@@ -112,6 +112,16 @@ typedef struct {
|
||||
JanetHandle write_pipe;
|
||||
} JanetEVThreadInit;
|
||||
|
||||
/* Structure used to initialize threads that run timeouts */
|
||||
typedef struct {
|
||||
double sec;
|
||||
JanetVM *vm;
|
||||
JanetFiber *fiber;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE cancel_event;
|
||||
#endif
|
||||
} JanetThreadedTimeout;
|
||||
|
||||
#define JANET_MAX_Q_CAPACITY 0x7FFFFFF
|
||||
|
||||
static void janet_q_init(JanetQueue *q) {
|
||||
@@ -346,21 +356,22 @@ JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod
|
||||
|
||||
static void janet_stream_close_impl(JanetStream *stream) {
|
||||
stream->flags |= JANET_STREAM_CLOSED;
|
||||
int canclose = !(stream->flags & JANET_STREAM_NOT_CLOSEABLE);
|
||||
#ifdef JANET_WINDOWS
|
||||
if (stream->handle != INVALID_HANDLE_VALUE) {
|
||||
#ifdef JANET_NET
|
||||
if (stream->flags & JANET_STREAM_SOCKET) {
|
||||
closesocket((SOCKET) stream->handle);
|
||||
if (canclose) closesocket((SOCKET) stream->handle);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
CloseHandle(stream->handle);
|
||||
if (canclose) CloseHandle(stream->handle);
|
||||
}
|
||||
stream->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
#else
|
||||
if (stream->handle != -1) {
|
||||
close(stream->handle);
|
||||
if (canclose) close(stream->handle);
|
||||
stream->handle = -1;
|
||||
#ifdef JANET_EV_POLL
|
||||
uint32_t i = stream->index;
|
||||
@@ -596,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);
|
||||
@@ -623,6 +669,7 @@ void janet_addtimeout(double sec) {
|
||||
to.curr_fiber = NULL;
|
||||
to.sched_id = fiber->sched_id;
|
||||
to.is_error = 1;
|
||||
to.has_worker = 0;
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
@@ -635,9 +682,61 @@ void janet_addtimeout_nil(double sec) {
|
||||
to.curr_fiber = NULL;
|
||||
to.sched_id = fiber->sched_id;
|
||||
to.is_error = 0;
|
||||
to.has_worker = 0;
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
static void janet_timeout_cb(JanetEVGenericMessage msg) {
|
||||
(void) msg;
|
||||
janet_interpreter_interrupt_handled(&janet_vm);
|
||||
}
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
|
||||
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
|
||||
janet_free(ptr);
|
||||
JanetTimestamp wait_begin = ts_now();
|
||||
DWORD duration = (DWORD)round(tto.sec * 1000);
|
||||
DWORD res = WAIT_TIMEOUT;
|
||||
JanetTimestamp wait_end = ts_now();
|
||||
for (DWORD 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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static void *janet_timeout_body(void *ptr) {
|
||||
#ifdef JANET_ANDROID
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = &janet_timeout_stop;
|
||||
sigaction(SIGUSR1, &action, NULL);
|
||||
#endif
|
||||
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
|
||||
janet_free(ptr);
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_t) tto.sec;
|
||||
ts.tv_nsec = (tto.sec <= UINT32_MAX)
|
||||
? (long)((tto.sec - ((uint32_t)tto.sec)) * 1000000000)
|
||||
: 0;
|
||||
nanosleep(&ts, &ts);
|
||||
janet_interpreter_interrupt(tto.vm);
|
||||
JanetEVGenericMessage msg = {0};
|
||||
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void janet_ev_inc_refcount(void) {
|
||||
janet_atomic_inc(&janet_vm.listener_count);
|
||||
}
|
||||
@@ -752,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) {
|
||||
@@ -834,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;
|
||||
@@ -843,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;
|
||||
@@ -855,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,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));
|
||||
@@ -973,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));
|
||||
@@ -1237,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) {
|
||||
@@ -1258,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) {
|
||||
@@ -1351,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 */
|
||||
@@ -1384,12 +1526,13 @@ JanetFiber *janet_loop1(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
handle_timeout_worker(to, 0);
|
||||
}
|
||||
|
||||
/* Run scheduled fibers unless interrupts need to be handled. */
|
||||
while (janet_vm.spawn.head != janet_vm.spawn.tail) {
|
||||
/* Don't run until all interrupts have been marked as handled by calling janet_interpreter_interrupt_handled */
|
||||
if (janet_vm.auto_suspend) break;
|
||||
if (janet_atomic_load_relaxed(&janet_vm.auto_suspend)) break;
|
||||
JanetTask task = {NULL, janet_wrap_nil(), JANET_SIGNAL_OK, 0};
|
||||
janet_q_pop(&janet_vm.spawn, &task, sizeof(task));
|
||||
if (task.fiber->gc.flags & JANET_FIBER_EV_FLAG_SUSPENDED) janet_ev_dec_refcount();
|
||||
@@ -1431,12 +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)) {
|
||||
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;
|
||||
@@ -1601,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;
|
||||
@@ -1759,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;
|
||||
@@ -1913,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;
|
||||
@@ -2085,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 {
|
||||
@@ -2932,7 +3077,8 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
|
||||
uint32_t count1;
|
||||
memcpy(&count1, nextbytes, sizeof(count1));
|
||||
size_t count = (size_t) count1;
|
||||
if (count > (endbytes - nextbytes) * sizeof(JanetCFunRegistry)) {
|
||||
/* Use division to avoid overflowing size_t */
|
||||
if (count > (endbytes - nextbytes - sizeof(count1)) / sizeof(JanetCFunRegistry)) {
|
||||
janet_panic("thread message invalid");
|
||||
}
|
||||
janet_vm.registry_count = count;
|
||||
@@ -3090,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();
|
||||
}
|
||||
@@ -3103,26 +3250,66 @@ JANET_CORE_FN(cfun_ev_sleep,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_ev_deadline,
|
||||
"(ev/deadline sec &opt tocancel tocheck)",
|
||||
"Schedules the event loop to try to cancel the `tocancel` "
|
||||
"task as with `ev/cancel`. After `sec` seconds, the event "
|
||||
"loop will attempt cancellation of `tocancel` if the "
|
||||
"`tocheck` fiber is resumable. `sec` is a number that can "
|
||||
"have a fractional part. `tocancel` defaults to "
|
||||
"`(fiber/root)`, but if specified, must be a task (root "
|
||||
"fiber). `tocheck` defaults to `(fiber/current)`, but if "
|
||||
"specified, should be a fiber. Returns `tocancel` "
|
||||
"immediately.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
"(ev/deadline sec &opt tocancel tocheck intr?)",
|
||||
"Schedules the event loop to try to cancel the `tocancel` task as with `ev/cancel`. "
|
||||
"After `sec` seconds, the event loop will attempt cancellation of `tocancel` if the "
|
||||
"`tocheck` fiber is resumable. `sec` is a number that can have a fractional part. "
|
||||
"`tocancel` defaults to `(fiber/root)`, but if specified, must be a task (root "
|
||||
"fiber). `tocheck` defaults to `(fiber/current)`, but if specified, must be a fiber. "
|
||||
"Returns `tocancel` immediately. If `interrupt?` is set to true, will create a "
|
||||
"background thread to try to interrupt the VM if the timeout expires.") {
|
||||
janet_arity(argc, 1, 4);
|
||||
double sec = janet_getnumber(argv, 0);
|
||||
sec = (sec < 0) ? 0 : sec;
|
||||
JanetFiber *tocancel = janet_optfiber(argv, argc, 1, janet_vm.root_fiber);
|
||||
JanetFiber *tocheck = janet_optfiber(argv, argc, 2, janet_vm.fiber);
|
||||
int use_interrupt = janet_optboolean(argv, argc, 3, 0);
|
||||
JanetTimeout to;
|
||||
to.when = ts_delta(ts_now(), sec);
|
||||
to.fiber = tocancel;
|
||||
to.curr_fiber = tocheck;
|
||||
to.is_error = 0;
|
||||
to.sched_id = to.fiber->sched_id;
|
||||
if (use_interrupt) {
|
||||
#ifdef JANET_ANDROID
|
||||
janet_sandbox_assert(JANET_SANDBOX_SIGNAL);
|
||||
#endif
|
||||
JanetThreadedTimeout *tto = janet_malloc(sizeof(JanetThreadedTimeout));
|
||||
if (NULL == tto) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
tto->sec = sec;
|
||||
tto->vm = &janet_vm;
|
||||
tto->fiber = tocheck;
|
||||
#ifdef JANET_WINDOWS
|
||||
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");
|
||||
}
|
||||
#else
|
||||
pthread_t worker;
|
||||
int err = pthread_create(&worker, NULL, janet_timeout_body, tto);
|
||||
if (err) {
|
||||
janet_free(tto);
|
||||
janet_panicf("%s", janet_strerror(err));
|
||||
}
|
||||
#endif
|
||||
to.has_worker = 1;
|
||||
to.worker = worker;
|
||||
#ifdef JANET_WINDOWS
|
||||
to.worker_event = cancel_event;
|
||||
ResumeThread(worker);
|
||||
#endif
|
||||
} else {
|
||||
to.has_worker = 0;
|
||||
}
|
||||
add_timeout(to);
|
||||
return janet_wrap_fiber(tocancel);
|
||||
}
|
||||
@@ -3414,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
|
||||
|
||||
@@ -1686,7 +1686,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
|
||||
JanetFFIType type = decode_ffi_type(argv[0]);
|
||||
uint32_t el_size = (uint32_t) type_size(type);
|
||||
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
|
||||
int32_t index = janet_optnat(argv, argc, 3, 0);
|
||||
int32_t index = janet_optnat(argv, argc, 3, buffer->count);
|
||||
int32_t old_count = buffer->count;
|
||||
if (index > old_count) janet_panic("index out of bounds");
|
||||
buffer->count = index;
|
||||
|
||||
@@ -599,33 +599,33 @@ JANET_CORE_FN(cfun_filewatch_make,
|
||||
JANET_CORE_FN(cfun_filewatch_add,
|
||||
"(filewatch/add watcher path &opt flags)",
|
||||
"Add a path to the watcher. Available flags depend on the current OS, and are as follows:\n\n"
|
||||
"Windows/MINGW (flags correspond to FILE_NOTIFY_CHANGE_* flags in win32 documentation):\n\n"
|
||||
"Windows/MINGW (flags correspond to `FILE_NOTIFY_CHANGE_*` flags in win32 documentation):\n\n"
|
||||
"* `:all` - trigger an event for all of the below triggers.\n\n"
|
||||
"* `:attributes` - FILE_NOTIFY_CHANGE_ATTRIBUTES\n\n"
|
||||
"* `:creation` - FILE_NOTIFY_CHANGE_CREATION\n\n"
|
||||
"* `:dir-name` - FILE_NOTIFY_CHANGE_DIR_NAME\n\n"
|
||||
"* `:last-access` - FILE_NOTIFY_CHANGE_LAST_ACCESS\n\n"
|
||||
"* `:last-write` - FILE_NOTIFY_CHANGE_LAST_WRITE\n\n"
|
||||
"* `:security` - FILE_NOTIFY_CHANGE_SECURITY\n\n"
|
||||
"* `:size` - FILE_NOTIFY_CHANGE_SIZE\n\n"
|
||||
"* `:attributes` - `FILE_NOTIFY_CHANGE_ATTRIBUTES`\n\n"
|
||||
"* `:creation` - `FILE_NOTIFY_CHANGE_CREATION`\n\n"
|
||||
"* `:dir-name` - `FILE_NOTIFY_CHANGE_DIR_NAME`\n\n"
|
||||
"* `:last-access` - `FILE_NOTIFY_CHANGE_LAST_ACCESS`\n\n"
|
||||
"* `:last-write` - `FILE_NOTIFY_CHANGE_LAST_WRITE`\n\n"
|
||||
"* `:security` - `FILE_NOTIFY_CHANGE_SECURITY`\n\n"
|
||||
"* `:size` - `FILE_NOTIFY_CHANGE_SIZE`\n\n"
|
||||
"* `:recursive` - watch subdirectories recursively\n\n"
|
||||
"Linux (flags correspond to IN_* flags from <sys/inotify.h>):\n\n"
|
||||
"* `:access` - IN_ACCESS\n\n"
|
||||
"* `:all` - IN_ALL_EVENTS\n\n"
|
||||
"* `:attrib` - IN_ATTRIB\n\n"
|
||||
"* `:close-nowrite` - IN_CLOSE_NOWRITE\n\n"
|
||||
"* `:close-write` - IN_CLOSE_WRITE\n\n"
|
||||
"* `:create` - IN_CREATE\n\n"
|
||||
"* `:delete` - IN_DELETE\n\n"
|
||||
"* `:delete-self` - IN_DELETE_SELF\n\n"
|
||||
"* `:ignored` - IN_IGNORED\n\n"
|
||||
"* `:modify` - IN_MODIFY\n\n"
|
||||
"* `:move-self` - IN_MOVE_SELF\n\n"
|
||||
"* `:moved-from` - IN_MOVED_FROM\n\n"
|
||||
"* `:moved-to` - IN_MOVED_TO\n\n"
|
||||
"* `:open` - IN_OPEN\n\n"
|
||||
"* `:q-overflow` - IN_Q_OVERFLOW\n\n"
|
||||
"* `:unmount` - IN_UNMOUNT\n\n\n"
|
||||
"Linux (flags correspond to `IN_*` flags from <sys/inotify.h>):\n\n"
|
||||
"* `:access` - `IN_ACCESS`\n\n"
|
||||
"* `:all` - `IN_ALL_EVENTS`\n\n"
|
||||
"* `:attrib` - `IN_ATTRIB`\n\n"
|
||||
"* `:close-nowrite` - `IN_CLOSE_NOWRITE`\n\n"
|
||||
"* `:close-write` - `IN_CLOSE_WRITE`\n\n"
|
||||
"* `:create` - `IN_CREATE`\n\n"
|
||||
"* `:delete` - `IN_DELETE`\n\n"
|
||||
"* `:delete-self` - `IN_DELETE_SELF`\n\n"
|
||||
"* `:ignored` - `IN_IGNORED`\n\n"
|
||||
"* `:modify` - `IN_MODIFY`\n\n"
|
||||
"* `:move-self` - `IN_MOVE_SELF`\n\n"
|
||||
"* `:moved-from` - `IN_MOVED_FROM`\n\n"
|
||||
"* `:moved-to` - `IN_MOVED_TO`\n\n"
|
||||
"* `:open` - `IN_OPEN`\n\n"
|
||||
"* `:q-overflow` - `IN_Q_OVERFLOW`\n\n"
|
||||
"* `:unmount` - `IN_UNMOUNT`\n\n\n"
|
||||
"On Windows, events will have the following possible types:\n\n"
|
||||
"* `:unknown`\n\n"
|
||||
"* `:added`\n\n"
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -657,14 +713,15 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
"The type parameter specifies the type of network connection, either "
|
||||
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
|
||||
":stream. The host and port arguments are the same as in net/address. The last boolean parameter `no-reuse` will "
|
||||
"disable the use of SO_REUSEADDR and SO_REUSEPORT when creating a server on some operating systems.") {
|
||||
"disable the use of `SO_REUSEADDR` and `SO_REUSEPORT` when creating a server on some operating systems.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
|
||||
janet_arity(argc, 2, 4);
|
||||
|
||||
/* 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) {
|
||||
@@ -1078,6 +1135,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),
|
||||
|
||||
120
src/core/os.c
120
src/core/os.c
@@ -55,6 +55,7 @@
|
||||
#include <sys/utime.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#else
|
||||
#include <spawn.h>
|
||||
#include <utime.h>
|
||||
@@ -65,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
|
||||
@@ -73,6 +76,26 @@ extern char **environ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Detect availability of posix_spawn_file_actions_addchdir_np. Since
|
||||
* this doesn't seem to follow any standard, just a common extension, we
|
||||
* must enumerate supported systems for availability. Define JANET_SPAWN_NO_CHDIR
|
||||
* to disable this. */
|
||||
#ifndef JANET_SPAWN_NO_CHDIR
|
||||
#ifdef __GLIBC__
|
||||
#define JANET_SPAWN_CHDIR
|
||||
#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
|
||||
#endif
|
||||
|
||||
/* Not POSIX, but all Unixes but Solaris have this function. */
|
||||
#if defined(JANET_POSIX) && !defined(__sun)
|
||||
time_t timegm(struct tm *tm);
|
||||
@@ -158,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
|
||||
@@ -297,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
|
||||
@@ -813,6 +845,19 @@ JANET_CORE_FN(os_proc_close,
|
||||
#endif
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_proc_getpid,
|
||||
"(os/getpid)",
|
||||
"Get the process ID of the current process.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
#ifdef JANET_WINDOWS
|
||||
return janet_wrap_number((double) _getpid());
|
||||
#else
|
||||
return janet_wrap_number((double) getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void swap_handles(JanetHandle *handles) {
|
||||
JanetHandle temp = handles[0];
|
||||
handles[0] = handles[1];
|
||||
@@ -1137,6 +1182,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
JanetAbstract orig_in = NULL, orig_out = NULL, orig_err = NULL;
|
||||
JanetHandle new_in = JANET_HANDLE_NONE, new_out = JANET_HANDLE_NONE, new_err = JANET_HANDLE_NONE;
|
||||
JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE;
|
||||
int stderr_is_stdout = 0;
|
||||
int pipe_errflag = 0; /* Track errors setting up pipes */
|
||||
int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0;
|
||||
|
||||
@@ -1161,11 +1207,28 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
if (is_spawn && janet_keyeq(maybe_stderr, "pipe")) {
|
||||
new_err = make_pipes(&pipe_err, 0, &pipe_errflag);
|
||||
pipe_owner_flags |= JANET_PROC_OWNS_STDERR;
|
||||
} else if (is_spawn && janet_keyeq(maybe_stderr, "out")) {
|
||||
stderr_is_stdout = 1;
|
||||
} else if (!janet_checktype(maybe_stderr, JANET_NIL)) {
|
||||
new_err = janet_getjstream(&maybe_stderr, 0, &orig_err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional working directory. Available for both os/execute and os/spawn. */
|
||||
const char *chdir_path = NULL;
|
||||
if (argc > 2) {
|
||||
JanetDictView tab = janet_getdictionary(argv, 2);
|
||||
Janet workdir = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("cd"));
|
||||
if (janet_checktype(workdir, JANET_STRING)) {
|
||||
chdir_path = (const char *) janet_unwrap_string(workdir);
|
||||
#ifndef JANET_SPAWN_CHDIR
|
||||
janet_panicf(":cd argument not supported on this system - %s", chdir_path);
|
||||
#endif
|
||||
} else if (!janet_checktype(workdir, JANET_NIL)) {
|
||||
janet_panicf("expected string for :cd argumnet, got %v", workdir);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up if any of the pipes have any issues */
|
||||
if (pipe_errflag) {
|
||||
if (pipe_in != JANET_HANDLE_NONE) close_handle(pipe_in);
|
||||
@@ -1180,6 +1243,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
PROCESS_INFORMATION processInfo;
|
||||
STARTUPINFO startupInfo;
|
||||
LPCSTR lpCurrentDirectory = NULL;
|
||||
memset(&saAttr, 0, sizeof(saAttr));
|
||||
memset(&processInfo, 0, sizeof(processInfo));
|
||||
memset(&startupInfo, 0, sizeof(startupInfo));
|
||||
@@ -1196,6 +1260,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
}
|
||||
const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
|
||||
|
||||
if (chdir_path != NULL) {
|
||||
lpCurrentDirectory = chdir_path;
|
||||
}
|
||||
|
||||
/* Do IO redirection */
|
||||
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
@@ -1203,7 +1271,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_in != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdInput = new_in;
|
||||
} else {
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(0);
|
||||
startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(stdin));
|
||||
}
|
||||
|
||||
if (pipe_out != JANET_HANDLE_NONE) {
|
||||
@@ -1211,15 +1279,17 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_out != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdOutput = new_out;
|
||||
} else {
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(1);
|
||||
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(stdout));
|
||||
}
|
||||
|
||||
if (pipe_err != JANET_HANDLE_NONE) {
|
||||
startupInfo.hStdError = pipe_err;
|
||||
} else if (new_err != NULL) {
|
||||
startupInfo.hStdError = new_err;
|
||||
} else if (stderr_is_stdout) {
|
||||
startupInfo.hStdError = startupInfo.hStdOutput;
|
||||
} else {
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(2);
|
||||
startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(stderr));
|
||||
}
|
||||
|
||||
int cp_failed = 0;
|
||||
@@ -1230,7 +1300,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
TRUE, /* handle inheritance */
|
||||
0, /* flags */
|
||||
use_environ ? NULL : envp, /* pass in environment */
|
||||
NULL, /* use parents starting directory */
|
||||
lpCurrentDirectory,
|
||||
&startupInfo,
|
||||
&processInfo)) {
|
||||
cp_failed = 1;
|
||||
@@ -1287,6 +1357,15 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
/* Posix spawn setup */
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawn_file_actions_init(&actions);
|
||||
#ifdef JANET_SPAWN_CHDIR
|
||||
if (chdir_path != NULL) {
|
||||
#ifdef JANET_SPAWN_CHDIR_NO_NP
|
||||
posix_spawn_file_actions_addchdir(&actions, chdir_path);
|
||||
#else
|
||||
posix_spawn_file_actions_addchdir_np(&actions, chdir_path);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (pipe_in != JANET_HANDLE_NONE) {
|
||||
posix_spawn_file_actions_adddup2(&actions, pipe_in, 0);
|
||||
posix_spawn_file_actions_addclose(&actions, pipe_in);
|
||||
@@ -1309,6 +1388,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
} else if (new_err != JANET_HANDLE_NONE && new_err != 2) {
|
||||
posix_spawn_file_actions_adddup2(&actions, new_err, 2);
|
||||
posix_spawn_file_actions_addclose(&actions, new_err);
|
||||
} else if (stderr_is_stdout) {
|
||||
posix_spawn_file_actions_adddup2(&actions, 1, 2);
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
@@ -1414,7 +1495,8 @@ JANET_CORE_FN(os_spawn,
|
||||
"`:pipe` may fail if there are too many active file descriptors. The caller is "
|
||||
"responsible for closing pipes created by `:pipe` (either individually or using "
|
||||
"`os/proc-close`). Similar to `os/execute`, the caller is responsible for ensuring "
|
||||
"pipes do not cause the program to block and deadlock.") {
|
||||
"pipes do not cause the program to block and deadlock. As a special case, the stream passed to `:err` "
|
||||
"can be the keyword `:out` to redirect stderr to stdout in the subprocess.") {
|
||||
return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN);
|
||||
}
|
||||
|
||||
@@ -1460,6 +1542,28 @@ 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
|
||||
(void) argv;
|
||||
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) {
|
||||
@@ -2768,6 +2872,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
|
||||
@@ -2797,6 +2902,10 @@ void janet_lib_os(JanetTable *env) {
|
||||
JANET_CORE_REG("os/proc-wait", os_proc_wait),
|
||||
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 */
|
||||
@@ -2805,7 +2914,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,11 +56,10 @@ 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 {
|
||||
ret = janet_wrap_string(cres.error);
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
if ((cres.error_mapping.line > 0) &&
|
||||
@@ -67,15 +67,19 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
line = cres.error_mapping.line;
|
||||
col = cres.error_mapping.column;
|
||||
}
|
||||
JanetString ctx = janet_formatc("%s:%d:%d: compile error",
|
||||
sourcePath, line, col);
|
||||
JanetString errstr = janet_formatc("%s: %s",
|
||||
(const char *)ctx,
|
||||
(const char *)cres.error);
|
||||
ret = janet_wrap_string(errstr);
|
||||
if (cres.macrofiber) {
|
||||
janet_eprintf("%s:%d:%d: compile error", sourcePath,
|
||||
line, col);
|
||||
janet_eprintf("%s", (const char *)ctx);
|
||||
janet_stacktrace_ext(cres.macrofiber, ret, "");
|
||||
} else {
|
||||
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
|
||||
line, col, (const char *)cres.error);
|
||||
janet_eprintf("%s\n", (const char *)errstr);
|
||||
}
|
||||
errflags |= 0x02;
|
||||
errflags |= JANET_DO_ERROR_COMPILE;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
@@ -88,12 +92,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
done = 1;
|
||||
break;
|
||||
case JANET_PARSE_ERROR: {
|
||||
const char *e = janet_parser_error(parser);
|
||||
errflags |= 0x04;
|
||||
ret = janet_cstringv(e);
|
||||
errflags |= JANET_DO_ERROR_PARSE;
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
|
||||
JanetString errstr = janet_formatc("%s:%d:%d: parse error: %s",
|
||||
sourcePath, line, col,
|
||||
janet_parser_error(parser));
|
||||
ret = janet_wrap_string(errstr);
|
||||
janet_eprintf("%s\n", (const char *)errstr);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
@@ -121,7 +127,8 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
janet_loop();
|
||||
if (fiber) {
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
ret = fiber->last_value;
|
||||
if (!errflags)
|
||||
ret = fiber->last_value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -23,11 +23,16 @@
|
||||
#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
|
||||
#ifndef JANET_WINDOWS
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
@@ -53,13 +58,22 @@ typedef struct {
|
||||
void *data;
|
||||
} JanetQueue;
|
||||
|
||||
#ifdef JANET_EV
|
||||
typedef struct {
|
||||
JanetTimestamp when;
|
||||
JanetFiber *fiber;
|
||||
JanetFiber *curr_fiber;
|
||||
uint32_t sched_id;
|
||||
int is_error;
|
||||
int has_worker;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE worker;
|
||||
HANDLE worker_event;
|
||||
#else
|
||||
pthread_t worker;
|
||||
#endif
|
||||
} JanetTimeout;
|
||||
#endif
|
||||
|
||||
/* Registry table for C functions - contains metadata that can
|
||||
* be looked up by cfunction pointer. All strings here are pointing to
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
#define vm_maybe_auto_suspend(COND)
|
||||
#else
|
||||
#define vm_maybe_auto_suspend(COND) do { \
|
||||
if ((COND) && janet_vm.auto_suspend) { \
|
||||
if ((COND) && janet_atomic_load_relaxed(&janet_vm.auto_suspend)) { \
|
||||
fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
|
||||
vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
|
||||
} \
|
||||
|
||||
@@ -67,11 +67,21 @@ extern "C" {
|
||||
#define JANET_LINUX 1
|
||||
#endif
|
||||
|
||||
/* Check for Android */
|
||||
#ifdef __ANDROID__
|
||||
#define JANET_ANDROID 1
|
||||
#endif
|
||||
|
||||
/* Check for Cygwin */
|
||||
#if defined(__CYGWIN__)
|
||||
#define JANET_CYGWIN 1
|
||||
#endif
|
||||
|
||||
/* Check for Illumos */
|
||||
#if defined(__illumos__)
|
||||
#define JANET_ILLUMOS 1
|
||||
#endif
|
||||
|
||||
/* Check Unix */
|
||||
#if defined(_AIX) \
|
||||
|| defined(__APPLE__) /* Darwin */ \
|
||||
@@ -137,6 +147,7 @@ extern "C" {
|
||||
|| defined(__s390x__) /* S390 64-bit */ \
|
||||
|| defined(__s390__) /* S390 32-bit */ \
|
||||
|| defined(__ARMEB__) /* ARM big endian */ \
|
||||
|| defined(__AARCH64EB__) /* ARM64 big endian */ \
|
||||
|| ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \
|
||||
&& defined(__BIG_ENDIAN))
|
||||
#define JANET_BIG_ENDIAN 1
|
||||
@@ -157,7 +168,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Check sun */
|
||||
#ifdef __sun
|
||||
#if defined(__sun) && !defined(JANET_ILLUMOS)
|
||||
#define JANET_NO_UTC_MKTIME
|
||||
#endif
|
||||
|
||||
@@ -165,14 +176,12 @@ extern "C" {
|
||||
/* Also enable the thread library only if not single-threaded */
|
||||
#ifdef JANET_SINGLE_THREADED
|
||||
#define JANET_THREAD_LOCAL
|
||||
#undef JANET_THREADS
|
||||
#elif defined(__GNUC__)
|
||||
#elif !(defined(JANET_THREAD_LOCAL)) && defined(__GNUC__)
|
||||
#define JANET_THREAD_LOCAL __thread
|
||||
#elif defined(_MSC_BUILD)
|
||||
#elif !(defined(JANET_THREAD_LOCAL)) && defined(_MSC_BUILD)
|
||||
#define JANET_THREAD_LOCAL __declspec(thread)
|
||||
#else
|
||||
#elif !(defined(JANET_THREAD_LOCAL))
|
||||
#define JANET_THREAD_LOCAL
|
||||
#undef JANET_THREADS
|
||||
#endif
|
||||
|
||||
/* Enable or disable dynamic module loading. Enabled by default. */
|
||||
@@ -591,6 +600,7 @@ typedef void *JanetAbstract;
|
||||
#define JANET_STREAM_WRITABLE 0x400
|
||||
#define JANET_STREAM_ACCEPTABLE 0x800
|
||||
#define JANET_STREAM_UDPSERVER 0x1000
|
||||
#define JANET_STREAM_NOT_CLOSEABLE 0x2000
|
||||
#define JANET_STREAM_TOCLOSE 0x10000
|
||||
|
||||
typedef enum {
|
||||
@@ -663,6 +673,7 @@ typedef int32_t JanetAtomicInt;
|
||||
JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x);
|
||||
JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x);
|
||||
JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x);
|
||||
JANET_API JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x);
|
||||
|
||||
/* We provide three possible implementations of Janets. The preferred
|
||||
* nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
|
||||
@@ -1178,6 +1189,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
|
||||
@@ -1197,7 +1209,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;
|
||||
@@ -1455,10 +1468,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);
|
||||
|
||||
@@ -1606,6 +1619,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);
|
||||
|
||||
@@ -1884,6 +1900,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)
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
(def errsym (keyword (gensym)))
|
||||
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
|
||||
(defmacro assert-error-value
|
||||
[msg errval & forms]
|
||||
(def e (gensym))
|
||||
~(assert (= ,errval (try (do ,;forms) ([,e] ,e))) ,msg))
|
||||
|
||||
(defn check-compile-error
|
||||
[form]
|
||||
(def result (compile form))
|
||||
|
||||
@@ -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 '[])))
|
||||
@@ -1016,4 +1023,11 @@
|
||||
(assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"}
|
||||
@{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys")
|
||||
|
||||
# different try overloads
|
||||
(assert (= (try (error :error) ([] :caught)) :caught))
|
||||
(assert (= (try (error :error) ([e] e)) :error))
|
||||
(assert (= (try (error :error) ([e fib] [e (fiber? fib)])) [:error true]))
|
||||
# regression test for #1659
|
||||
(assert (= (try (error :error) ([_ _] :caught)) :caught))
|
||||
|
||||
(end-suite)
|
||||
|
||||
@@ -117,8 +117,17 @@
|
||||
(assert (= 0 (length (bundle/list))) "bundles are listed correctly 7")
|
||||
(assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8")
|
||||
|
||||
# Try installing a bundle that is missing bundle script
|
||||
(assert-error-value "bundle missing bundle script"
|
||||
"bundle must contain bundle.janet or bundle/init.janet"
|
||||
(bundle/install "./examples/sample-bad-bundle1"))
|
||||
(assert (= 0 (length (bundle/list))) "check failure 0")
|
||||
(assert (= 0 (length (bundle/topolist))) "check failure 1")
|
||||
|
||||
# Try installing a bundle that fails check
|
||||
(assert-error "bad test" (bundle/install "./examples/sample-bad-bundle" :check true))
|
||||
(assert-error-value "bundle check hook fails"
|
||||
"Check failed!"
|
||||
(bundle/install "./examples/sample-bad-bundle2" :check true))
|
||||
(assert (= 0 (length (bundle/list))) "check failure 0")
|
||||
(assert (= 0 (length (bundle/topolist))) "check failure 1")
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -430,13 +436,7 @@
|
||||
# Now do our telnet chat
|
||||
(def bob (assert (net/connect test-host test-port :stream)))
|
||||
(expect-read bob "Whats your name?\n")
|
||||
(if (= :mingw (os/which))
|
||||
(net/write bob "bob")
|
||||
(do
|
||||
(def fbob (ev/to-file bob))
|
||||
(file/write fbob "bob")
|
||||
(file/flush fbob)
|
||||
(:close fbob)))
|
||||
(net/write bob "bob")
|
||||
(expect-read bob "Welcome bob\n")
|
||||
(def alice (assert (net/connect test-host test-port)))
|
||||
(expect-read alice "Whats your name?\n")
|
||||
@@ -501,8 +501,12 @@
|
||||
# soreuseport on unix domain sockets
|
||||
(compwhen (or (= :macos (os/which)) (= :linux (os/which)))
|
||||
(assert-no-error "unix-domain socket reuseaddr"
|
||||
(let [s (net/listen :unix "./unix-domain-socket" :stream)]
|
||||
(:close s))))
|
||||
(let [uds-path "./unix-domain-socket"]
|
||||
(defer (os/rm uds-path)
|
||||
(let [s (net/listen :unix uds-path :stream)]
|
||||
(:close s))))))
|
||||
|
||||
# (print "accept loop testing")
|
||||
|
||||
# net/accept-loop level triggering
|
||||
(gccollect)
|
||||
@@ -526,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)
|
||||
@@ -550,4 +556,51 @@
|
||||
(ev/sleep 0.15)
|
||||
(assert (not terminated-normally) "early termination failure 3"))
|
||||
|
||||
# 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)))
|
||||
(defn ev/slurp
|
||||
[f &opt buf]
|
||||
(default buf @"")
|
||||
(if (ev/read f 0x10000 buf)
|
||||
(ev/slurp f buf)
|
||||
buf))
|
||||
(def p (os/spawn [;run janet "-e" (string/format "%j" subproc-code)] :px {:out :pipe :err :out}))
|
||||
(def [exit-code data]
|
||||
(ev/gather
|
||||
(os/proc-wait p)
|
||||
(ev/slurp (p :out))))
|
||||
(def data (string/replace-all "\r" "" data))
|
||||
(assert (zero? exit-code) "subprocess ran")
|
||||
(assert (= data "hi\nthere\n") "output is correct")
|
||||
|
||||
(end-suite)
|
||||
|
||||
58
test/suite-ev2.janet
Normal file
58
test/suite-ev2.janet
Normal 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)
|
||||
@@ -55,4 +55,11 @@
|
||||
(compwhen has-ffi
|
||||
(assert-error "bad struct issue #1512" (ffi/struct :void)))
|
||||
|
||||
(compwhen has-ffi
|
||||
(def buf @"")
|
||||
(ffi/write :u8 10 buf)
|
||||
(assert (= 1 (length buf)))
|
||||
(ffi/write :u8 10 buf)
|
||||
(assert (= 2 (length buf))))
|
||||
|
||||
(end-suite)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)"
|
||||
|
||||
Reference in New Issue
Block a user