mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-29 06:37:41 +00:00 
			
		
		
		
	Compare commits
	
		
			220 Commits
		
	
	
		
			ev-epoll-f
			...
			bundle-too
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | dbc5d688e2 | ||
|   | e2a8951f68 | ||
|   | f0f03ad519 | ||
|   | e37575e763 | ||
|   | f4fd481415 | ||
|   | 8fca6b7af4 | ||
|   | 600e822933 | ||
|   | 2028ac8a20 | ||
|   | 7bae7d9efd | ||
|   | cb54fb02c1 | ||
|   | 7529abb542 | ||
|   | 16ac681ed9 | ||
|   | 74560ff805 | ||
|   | fe348187cc | ||
|   | fd5315793c | ||
|   | 87db463f4e | ||
|   | 1225cd31c8 | ||
|   | 6998865d7b | ||
|   | b8aec50763 | ||
|   | 7efb39d608 | ||
|   | f7c90bc1ff | ||
|   | aee077c1bd | ||
|   | 6968275ddf | ||
|   | 074ae4fc0d | ||
|   | 6cd35ed9c8 | ||
|   | 7911e74222 | ||
|   | 2fafe2b5d1 | ||
|   | de977819ce | ||
|   | 1844beecc3 | ||
|   | cb529bbd63 | ||
|   | 25990867e2 | ||
|   | 4fbc71c70d | ||
|   | eb21d4fff4 | ||
|   | 6d5fc1d743 | ||
|   | e88042b2fa | ||
|   | 750b448f75 | ||
|   | 14d1dc8749 | ||
|   | 8e0340252b | ||
|   | 641a16c133 | ||
|   | 533d78bffe | ||
|   | ae2c5820a1 | ||
|   | 8334504f4e | ||
|   | 2260a593bd | ||
|   | 7d8af2f99a | ||
|   | 46bdcece4d | ||
|   | 7387a1d91e | ||
|   | ae4b8078df | ||
|   | 60e0c8ea92 | ||
|   | 7d3acc0ed6 | ||
|   | 2637b33957 | ||
|   | 58ccb66659 | ||
|   | 634429cf61 | ||
|   | 6ac65e603d | ||
|   | 03166a745a | ||
|   | 4d61ba20ce | ||
|   | 751ff677fe | ||
|   | ace60e1898 | ||
|   | 876b7f106f | ||
|   | 809b6589a1 | ||
|   | 02f53ca014 | ||
|   | 0b03ddb21b | ||
|   | ea5d4fd3af | ||
|   | e6b73f8cd1 | ||
|   | af232ef729 | ||
|   | 2e2f8abfc0 | ||
|   | 91a583db27 | ||
|   | dc5cc630ff | ||
|   | c1647a74c5 | ||
|   | 721f280966 | ||
|   | 258ebb9145 | ||
|   | e914eaf055 | ||
|   | fe54013679 | ||
|   | fdaf2e1594 | ||
|   | f0092ef69b | ||
|   | a88ae7e1d9 | ||
|   | 9946f3bdf4 | ||
|   | c747e8d16c | ||
|   | 3e402d397e | ||
|   | 0350834cd3 | ||
|   | 980981c9ee | ||
|   | 3c8346f24e | ||
|   | 42bd27c24b | ||
|   | 4a0f67f3bd | ||
|   | 09b6fc4670 | ||
|   | 4d9bcd6bcc | ||
|   | cd34b89977 | ||
|   | 3151fa3988 | ||
|   | 5e58110e19 | ||
|   | e1cdd0f8cc | ||
|   | 1f39a0f180 | ||
|   | 367c4b14f5 | ||
|   | 9c437796d3 | ||
|   | 60e22d9703 | ||
|   | ee7362e847 | ||
|   | 369f96b80e | ||
|   | 7c5ed04ab1 | ||
|   | 4779a445e0 | ||
|   | f0f1b7ce9e | ||
|   | 7c9157a0ed | ||
|   | 522a6cb435 | ||
|   | d0d551d739 | ||
|   | 71a123fef7 | ||
|   | 3f40c8d7fb | ||
|   | 983c2e5499 | ||
|   | eebb4c3ade | ||
|   | 50425eac72 | ||
|   | 382ff77bbe | ||
|   | bf680fb5d3 | ||
|   | 4ed7db4f91 | ||
|   | bf19920d65 | ||
|   | 174b5f6686 | ||
|   | 4173645b81 | ||
|   | af511f1f55 | ||
|   | 83c6080380 | ||
|   | 2f0c789ea1 | ||
|   | a9b8f8e8a9 | ||
|   | f92f3eb6fa | ||
|   | 89e74dca3e | ||
|   | f2e86d2f8d | ||
|   | 623da131e5 | ||
|   | e89ec31ae5 | ||
|   | 68a6ed208e | ||
|   | c01b32c4f3 | ||
|   | ee11ff9da9 | ||
|   | ed56d5d6ff | ||
|   | b317ab755c | ||
|   | 9819994999 | ||
|   | e9dbaa81d2 | ||
|   | 9f9146ffae | ||
|   | 9d9732af97 | ||
|   | ebb8fa9787 | ||
|   | 9e6abbf4d4 | ||
|   | 6032a6d658 | ||
|   | c29ab22e6d | ||
|   | 592ac4904c | ||
|   | 03ae2ec153 | ||
|   | 3bc42d0d37 | ||
|   | 12630d3e54 | ||
|   | c9897f99c3 | ||
|   | e66dc14b3a | ||
|   | 7a2868c147 | ||
|   | 9e0daaee09 | ||
|   | c293c7de93 | ||
|   | 49eb5f8563 | ||
|   | 674b375b2c | ||
|   | 7e94c091eb | ||
|   | 5885ccba61 | ||
|   | 431ecd3d1a | ||
|   | f6df8ff935 | ||
|   | 3fd70f0951 | ||
|   | bebb635d4f | ||
|   | 354896bc4b | ||
|   | 5ddefff27e | ||
|   | 91827eef4f | ||
|   | 9c14c09962 | ||
|   | e85a84171f | ||
|   | 3a4f86c3d7 | ||
|   | 5e75963312 | ||
|   | 184d9289b5 | ||
|   | b7ff9577c0 | ||
|   | 942a1aaac6 | ||
|   | 69f0fe004d | ||
|   | 2a04347a42 | ||
|   | 1394f1a5c0 | ||
|   | cf4d19a8ea | ||
|   | 23b0fe9f8e | ||
|   | 1ba718b15e | ||
|   | df5f79ff35 | ||
|   | 6d7e8528ea | ||
|   | 197bb73a62 | ||
|   | f91e599451 | ||
|   | 5b9aa9237c | ||
|   | 61f38fab37 | ||
|   | 9142f38cbc | ||
|   | e8ed961572 | ||
|   | be11a2a1ad | ||
|   | ea75086300 | ||
|   | 9eeefbd79a | ||
|   | c573a98363 | ||
|   | 11d7af3f95 | ||
|   | a10b4f61d8 | ||
|   | a0cb7514f1 | ||
|   | b066edc116 | ||
|   | 938f5a689e | ||
|   | 772f4c26e8 | ||
|   | 6b5d151beb | ||
|   | a9176a77e6 | ||
|   | 16f409c6a9 | ||
|   | 9593c930de | ||
|   | 56f33f514b | ||
|   | 1ccd544b94 | ||
|   | 93c83a2ee2 | ||
|   | f459e32ada | ||
|   | 9b640c8e9c | ||
|   | a3228f4997 | ||
|   | 715eb69d92 | ||
|   | df2d5cb3d3 | ||
|   | 3b189eab64 | ||
|   | 609b629c22 | ||
|   | e74365fe38 | ||
|   | 46b34833c2 | ||
|   | 045c80869d | ||
|   | 2ea2e72ddd | ||
|   | 1b17e12fd6 | ||
|   | cc5beda0d2 | ||
|   | a363fd926d | ||
|   | 21ebede529 | ||
|   | 15d67e9191 | ||
|   | b5996f5f02 | ||
|   | 83204dc293 | ||
|   | e3f4142d2a | ||
|   | f18ad36b1b | ||
|   | cb25a2ecd6 | ||
|   | 741a5036e8 | ||
|   | 549ee95f3d | ||
|   | 6ae81058aa | ||
|   | 267c603824 | ||
|   | a8f583a372 | ||
|   | 2b5d90f73a | ||
|   | 4139e426fe | 
| @@ -1,4 +1,4 @@ | ||||
| image: freebsd/12.x | ||||
| image: freebsd/14.x | ||||
| sources: | ||||
| - https://git.sr.ht/~bakpakin/janet | ||||
| packages: | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| image: openbsd/latest | ||||
| image: openbsd/7.4 | ||||
| sources: | ||||
| - https://git.sr.ht/~bakpakin/janet | ||||
| packages: | ||||
|   | ||||
							
								
								
									
										38
									
								
								.github/cosmo/build
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/cosmo/build
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| #!/bin/sh | ||||
| set -eux | ||||
|  | ||||
| COSMO_DIR="/sc/cosmocc" | ||||
|  | ||||
| # build x86_64 | ||||
| X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc" | ||||
| X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar" | ||||
| mkdir -p /sc/cosmocc/x86_64 | ||||
| make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1 | ||||
| cp build/janet /sc/cosmocc/x86_64/janet | ||||
| make clean | ||||
|  | ||||
| # build aarch64 | ||||
| AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc" | ||||
| AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar" | ||||
| mkdir -p /sc/cosmocc/aarch64 | ||||
| make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1 | ||||
| cp build/janet /sc/cosmocc/aarch64/janet | ||||
| make clean | ||||
|  | ||||
| # fat binary | ||||
| apefat () { | ||||
|     OUTPUT="$1" | ||||
|     OLDNAME_X86_64="$(basename -- "$2")" | ||||
|     OLDNAME_AARCH64="$(basename -- "$3")" | ||||
|     TARG_FOLD="$(dirname "$OUTPUT")" | ||||
|     "$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \ | ||||
|         -l "$COSMO_DIR/bin/ape-aarch64.elf" \ | ||||
|         -M "$COSMO_DIR/bin/ape-m1.c" \ | ||||
|         -o "$OUTPUT" \ | ||||
|         "$2" \ | ||||
|         "$3" | ||||
|     cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64" | ||||
|     cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64" | ||||
| } | ||||
|  | ||||
| apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet | ||||
							
								
								
									
										21
									
								
								.github/cosmo/setup
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.github/cosmo/setup
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #!/bin/sh | ||||
| set -e | ||||
|  | ||||
| sudo apt update | ||||
| sudo apt-get install -y ca-certificates libssl-dev\ | ||||
|     qemu qemu-utils qemu-user-static\ | ||||
|     texinfo groff\ | ||||
|     cmake ninja-build bison zip\ | ||||
|     pkg-config build-essential autoconf re2c | ||||
|  | ||||
| # download cosmocc | ||||
| cd /sc | ||||
| wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip | ||||
| mkdir -p cosmocc | ||||
| cd cosmocc | ||||
| unzip ../cosmocc-3.3.3.zip | ||||
|  | ||||
| # register | ||||
| cd /sc/cosmocc | ||||
| sudo cp ./bin/ape-x86_64.elf /usr/bin/ape | ||||
| sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" | ||||
							
								
								
									
										27
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -60,3 +60,30 @@ jobs: | ||||
|             ./dist/*.zip | ||||
|             ./*.zip | ||||
|             ./*.msi | ||||
|  | ||||
|   release-cosmo: | ||||
|     permissions: | ||||
|       contents: write  # for softprops/action-gh-release to create GitHub release | ||||
|     name: Build release binaries for Cosmo | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@master | ||||
|       - name: create build folder | ||||
|         run: | | ||||
|           sudo mkdir -p /sc | ||||
|           sudo chmod -R 0777 /sc | ||||
|       - name: setup Cosmopolitan Libc | ||||
|         run: bash ./.github/cosmo/setup | ||||
|       - name: Set the version | ||||
|         run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV | ||||
|       - name: Set the platform | ||||
|         run: echo "platform=cosmo" >> $GITHUB_ENV | ||||
|       - name: build Janet APE binary | ||||
|         run: bash ./.github/cosmo/build | ||||
|       - name: push binary to github | ||||
|         uses: softprops/action-gh-release@v1 | ||||
|         with: | ||||
|           draft: true | ||||
|           files: | | ||||
|             /sc/cosmocc/janet.com | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -56,7 +56,7 @@ jobs: | ||||
|             gcc | ||||
|       - name: Build the project | ||||
|         shell: cmd | ||||
|         run: make -j CC=gcc | ||||
|         run: make -j4 CC=gcc JANET_NO_AMALG=1 | ||||
|  | ||||
|   test-mingw-linux: | ||||
|     name: Build and test with Mingw on Linux + Wine | ||||
| @@ -73,7 +73,7 @@ jobs: | ||||
|       - name: Compile the project | ||||
|         run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine | ||||
|       - name: Test the project | ||||
|         run: make test UNAME=MINGW RUN=wine | ||||
|         run: make test UNAME=MINGW RUN=wine VERBOSE=1 | ||||
|  | ||||
|   test-arm-linux: | ||||
|     name: Build and test ARM32 cross compilation | ||||
| @@ -88,4 +88,4 @@ jobs: | ||||
|       - name: Compile the project | ||||
|         run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc  | ||||
|       - name: Test the project | ||||
|         run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test | ||||
|         run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1 | ||||
|   | ||||
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -34,8 +34,11 @@ local | ||||
|  | ||||
| # Common test files I use. | ||||
| temp.janet | ||||
| temp*.janet | ||||
| temp.c | ||||
| temp*janet | ||||
| temp*.c | ||||
| scratch.janet | ||||
| scratch.c | ||||
|  | ||||
| # Emscripten | ||||
| *.bc | ||||
| @@ -45,6 +48,7 @@ janet.wasm | ||||
| # Generated files | ||||
| *.gen.h | ||||
| *.gen.c | ||||
| *.tmp | ||||
|  | ||||
| # Generate test files | ||||
| *.out | ||||
| @@ -123,6 +127,9 @@ vgcore.* | ||||
| *.idb | ||||
| *.pdb | ||||
|  | ||||
| # GGov | ||||
| *.gcov | ||||
|  | ||||
| # Kernel Module Compile Results | ||||
| *.mod* | ||||
| *.cmd | ||||
| @@ -131,6 +138,9 @@ Module.symvers | ||||
| Mkfile.old | ||||
| dkms.conf | ||||
|  | ||||
| # Coverage files | ||||
| *.cov | ||||
|  | ||||
| # End of https://www.gitignore.io/api/c | ||||
|  | ||||
| # Created by https://www.gitignore.io/api/cmake | ||||
|   | ||||
							
								
								
									
										53
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -2,6 +2,59 @@ | ||||
| All notable changes to this project will be documented in this file. | ||||
|  | ||||
| ## Unreleased - ??? | ||||
| - Add extra optional `env` argument to `eval` and `eval-string`. | ||||
| - Allow naming function literals with a keyword. This allows better stacktraces for macros without | ||||
|   accidentally adding new bindings. | ||||
| - Add `bundle/` module for managing packages within Janet. This should replace the jpm packaging | ||||
|   format eventually and is much simpler and amenable to more complicated builds. | ||||
| - Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks. | ||||
| - Add `with-env` | ||||
| - Add *module-make-env* dynamic binding | ||||
| - Add buffer/format-at | ||||
| - Add long form command line options for readable CLI usage | ||||
| - Fix bug with `net/accept-loop` that would sometimes miss connections. | ||||
|  | ||||
| ## 1.34.0 - 2024-03-22 | ||||
| - Add a new (split) PEG special by @ianthehenry | ||||
| - Add buffer/push-* sized int and float by @pnelson | ||||
| - Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu. | ||||
| - Expose _exit to skip certain cleanup with os/exit. | ||||
| - Swap set / body order for each by @sogaiu. | ||||
| - Abort on assert failure instead of exit. | ||||
| - Fix: os/proc-wait by @llmII. | ||||
| - Fix macex1 to keep syntax location for all tuples. | ||||
| - Restore if-let tail calls. | ||||
| - Don't try and resume fibers that can't be resumed. | ||||
| - Register stream on unmarshal. | ||||
| - Fix asm roundtrip issue. | ||||
|  | ||||
| ## 1.33.0 - 2024-01-07 | ||||
| - Add more + and * keywords to default-peg-grammar by @sogaiu. | ||||
| - Use libc strlen in janet_buffer_push_cstring by @williewillus. | ||||
| - Be a bit safer with reference counting. | ||||
| - Add support for atomic loads in Janet's atomic abstraction. | ||||
| - Fix poll event loop CPU usage issue. | ||||
| - Add ipv6, shared, and cryptorand options to meson. | ||||
| - Add more ipv6 feature detection. | ||||
| - Fix loop for forever loop. | ||||
| - Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv. | ||||
| - Fix warnings w/ MSVC and format. | ||||
| - Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE. | ||||
| - Fix `(default)`. | ||||
| - Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative. | ||||
| - Fix typo for SIGALARM in os/proc-kill. | ||||
| - Prevent bytecode optimization from remove mk* instructions. | ||||
| - Fix arity typo in peg.c by @pepe. | ||||
| - Update Makefile for MinGW. | ||||
| - Fix canceling waiting fiber. | ||||
| - Add a new (sub) PEG special by @ianthehenry. | ||||
| - Fix if net/server's handler has incorrect arity. | ||||
| - Fix macex raising on (). | ||||
|  | ||||
| ## 1.32.1 - 2023-10-15 | ||||
| - Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop. | ||||
| - Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`. | ||||
| - Rework event loop to make fewer system calls on kqueue and epoll. | ||||
| - Expose atomic refcount abstraction in janet.h | ||||
| - Add `array/weak` for weak references in arrays | ||||
| - Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`. | ||||
|   | ||||
							
								
								
									
										25
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								Makefile
									
									
									
									
									
								
							| @@ -33,6 +33,7 @@ CLIBS=-lm -lpthread | ||||
| JANET_TARGET=build/janet | ||||
| JANET_BOOT=build/janet_boot | ||||
| JANET_IMPORT_LIB=build/janet.lib | ||||
| JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib | ||||
| JANET_LIBRARY=build/libjanet.so | ||||
| JANET_STATIC_LIBRARY=build/libjanet.a | ||||
| JANET_PATH?=$(LIBDIR)/janet | ||||
| @@ -42,6 +43,7 @@ JANET_DIST_DIR?=janet-dist | ||||
| JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)' | ||||
| JANET_TARGET_OBJECTS=build/janet.o build/shell.o | ||||
| JPM_TAG?=master | ||||
| HAS_SHARED?=1 | ||||
| DEBUGGER=gdb | ||||
| SONAME_SETTER=-Wl,-soname, | ||||
|  | ||||
| @@ -51,6 +53,7 @@ HOSTAR?=$(AR) | ||||
| # Symbols are (optionally) removed later, keep -g as default! | ||||
| CFLAGS?=-O2 -g | ||||
| LDFLAGS?=-rdynamic | ||||
| LIBJANET_LDFLAGS?=$(LD_FLAGS) | ||||
| RUN:=$(RUN) | ||||
|  | ||||
| COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC | ||||
| @@ -93,12 +96,17 @@ endif | ||||
| ifeq ($(findstring MINGW,$(UNAME)), MINGW) | ||||
| 	CLIBS:=-lws2_32 -lpsapi -lwsock32 | ||||
| 	LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB) | ||||
| 	LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB) | ||||
| 	JANET_TARGET:=$(JANET_TARGET).exe | ||||
| 	JANET_BOOT:=$(JANET_BOOT).exe | ||||
| endif | ||||
|  | ||||
|  | ||||
| $(shell mkdir -p build/core build/c build/boot build/mainclient) | ||||
| all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h | ||||
| all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h | ||||
| ifeq ($(HAS_SHARED), 1) | ||||
| all: $(JANET_LIBRARY) | ||||
| endif | ||||
|  | ||||
| ###################### | ||||
| ##### Name Files ##### | ||||
| @@ -196,9 +204,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile | ||||
| ######################## | ||||
|  | ||||
| ifeq ($(UNAME), Darwin) | ||||
| SONAME=libjanet.1.31.dylib | ||||
| SONAME=libjanet.1.34.dylib | ||||
| else | ||||
| SONAME=libjanet.so.1.31 | ||||
| SONAME=libjanet.so.1.34 | ||||
| endif | ||||
|  | ||||
| build/c/shell.c: src/mainclient/shell.c | ||||
| @@ -220,7 +228,7 @@ $(JANET_TARGET): $(JANET_TARGET_OBJECTS) | ||||
| 	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS) | ||||
|  | ||||
| $(JANET_LIBRARY): $(JANET_TARGET_OBJECTS) | ||||
| 	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) | ||||
| 	$(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) | ||||
|  | ||||
| $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS) | ||||
| 	$(HOSTAR) rcs $@ $^ | ||||
| @@ -263,7 +271,7 @@ dist: build/janet-dist.tar.gz | ||||
|  | ||||
| build/janet-%.tar.gz: $(JANET_TARGET) \ | ||||
| 	build/janet.h \ | ||||
| 	janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \ | ||||
| 	janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \ | ||||
| 	README.md build/c/janet.c build/c/shell.c | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/bin | ||||
| 	cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/ | ||||
| @@ -271,13 +279,17 @@ build/janet-%.tar.gz: $(JANET_TARGET) \ | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/include | ||||
| 	cp build/janet.h build/$(JANET_DIST_DIR)/include/ | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/lib/ | ||||
| 	cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/ | ||||
| 	cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/ | ||||
| 	cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/man/man1/ | ||||
| 	cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1 | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/src/ | ||||
| 	cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/ | ||||
| 	cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/ | ||||
| 	cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR) | ||||
| ifeq ($(HAS_SHARED), 1) | ||||
| build/janet-%.tar.gz: $(JANET_LIBRARY) | ||||
| endif | ||||
|  | ||||
| ######################### | ||||
| ##### Documentation ##### | ||||
| @@ -331,6 +343,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc | ||||
| 	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)' | ||||
| 	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc' | ||||
| 	cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)' | ||||
| 	cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)' | ||||
| 	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs" | ||||
|  | ||||
| install-jpm-git: $(JANET_TARGET) | ||||
|   | ||||
| @@ -315,8 +315,7 @@ See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the w | ||||
|  | ||||
| ## Discussion | ||||
|  | ||||
| Feel free to ask questions and join the discussion on the [Janet Gitter channel](https://gitter.im/janet-language/community). | ||||
| Gitter provides Matrix and IRC bridges as well. | ||||
| Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/) | ||||
|  | ||||
| ## FAQ | ||||
|  | ||||
|   | ||||
| @@ -91,7 +91,9 @@ exit /b 0 | ||||
| :CLEAN | ||||
| del *.exe *.lib *.exp | ||||
| rd /s /q build | ||||
| rd /s /q dist | ||||
| if exist dist ( | ||||
|     rd /s /q dist | ||||
| ) | ||||
| exit /b 0 | ||||
|  | ||||
| @rem Run tests | ||||
|   | ||||
| @@ -55,6 +55,7 @@ | ||||
| (ffi/defbind sixints-fn six-ints []) | ||||
| (ffi/defbind sixints-fn-2 :int [x :int s six-ints]) | ||||
| (ffi/defbind sixints-fn-3 :int [s six-ints x :int]) | ||||
| (ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int]) | ||||
|  | ||||
| # | ||||
| # Struct reading and writing | ||||
| @@ -119,6 +120,7 @@ | ||||
| (tracev (return-struct 42)) | ||||
| (tracev (double-lots 1 2 3 4 5 6 700 800 9 10)) | ||||
| (tracev (struct-big 11 99.5)) | ||||
| (tracev (int-fn-aliased 10 20)) | ||||
|  | ||||
| (assert (= [10 10 12 12] (split-ret-fn 10 12))) | ||||
| (assert (= [12 12 10 10] (split-flip-ret-fn 10 12))) | ||||
|   | ||||
							
								
								
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| # Switch to python | ||||
|  | ||||
| (print "running in Janet") | ||||
| (os/posix-exec ["python"] :p) | ||||
| (print "will not print") | ||||
							
								
								
									
										4
									
								
								examples/sample-bundle/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/sample-bundle/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| @{ | ||||
|   :name "sample-bundle" | ||||
|   :dependencies ["sample-dep1" "sample-dep2"] | ||||
| } | ||||
							
								
								
									
										3
									
								
								examples/sample-bundle/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-bundle/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| (defn install | ||||
|   [manifest &] | ||||
|   (bundle/add-file manifest "mymod.janet")) | ||||
							
								
								
									
										7
									
								
								examples/sample-bundle/mymod.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/sample-bundle/mymod.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| (import dep1) | ||||
| (import dep2) | ||||
|  | ||||
| (defn myfn | ||||
|   [x] | ||||
|   (def y (dep2/function x)) | ||||
|   (dep1/function y)) | ||||
							
								
								
									
										4
									
								
								examples/sample-dep1/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/sample-dep1/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| @{ | ||||
|   :name "sample-dep1" | ||||
|   :dependencies ["sample-dep2"] | ||||
| } | ||||
							
								
								
									
										3
									
								
								examples/sample-dep1/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep1/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| (defn install | ||||
|   [manifest &] | ||||
|   (bundle/add-file manifest "dep1.janet")) | ||||
							
								
								
									
										3
									
								
								examples/sample-dep1/dep1.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep1/dep1.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| (defn function | ||||
|   [x] | ||||
|   (+ x x)) | ||||
							
								
								
									
										3
									
								
								examples/sample-dep2/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| @{ | ||||
|   :name "sample-dep2" | ||||
| } | ||||
							
								
								
									
										3
									
								
								examples/sample-dep2/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| (defn install | ||||
|   [manifest &] | ||||
|   (bundle/add-file manifest "dep2.janet")) | ||||
							
								
								
									
										3
									
								
								examples/sample-dep2/dep2.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/dep2.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| (defn function | ||||
|   [x] | ||||
|   (* x x)) | ||||
							
								
								
									
										63
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								meson.build
									
									
									
									
									
								
							| @@ -20,7 +20,7 @@ | ||||
|  | ||||
| project('janet', 'c', | ||||
|   default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'], | ||||
|   version : '1.31.0') | ||||
|   version : '1.34.0') | ||||
|  | ||||
| # Global settings | ||||
| janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet') | ||||
| @@ -61,6 +61,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps')) | ||||
| conf.set('JANET_NO_ASSEMBLER', not get_option('assembler')) | ||||
| conf.set('JANET_NO_PEG', not get_option('peg')) | ||||
| conf.set('JANET_NO_NET', not get_option('net')) | ||||
| conf.set('JANET_NO_IPV6', not get_option('ipv6')) | ||||
| conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded')) | ||||
| conf.set('JANET_REDUCED_OS', get_option('reduced_os')) | ||||
| conf.set('JANET_NO_INT_TYPES', not get_option('int_types')) | ||||
| @@ -78,6 +79,7 @@ conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue')) | ||||
| conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt')) | ||||
| conf.set('JANET_NO_FFI', not get_option('ffi')) | ||||
| conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit')) | ||||
| conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand')) | ||||
| if get_option('os_name') != '' | ||||
|   conf.set('JANET_OS_NAME', get_option('os_name')) | ||||
| endif | ||||
| @@ -182,32 +184,41 @@ if not get_option('single_threaded') | ||||
|   janet_dependencies += thread_dep | ||||
| endif | ||||
|  | ||||
| # Allow building with no shared library | ||||
| if cc.has_argument('-fvisibility=hidden') | ||||
|   lib_cflags = ['-fvisibility=hidden'] | ||||
| else | ||||
|   lib_cflags = [] | ||||
| endif | ||||
| libjanet = library('janet', janetc, | ||||
|   include_directories : incdir, | ||||
|   dependencies : janet_dependencies, | ||||
|   version: meson.project_version(), | ||||
|   soversion: version_parts[0] + '.' + version_parts[1], | ||||
|   c_args : lib_cflags, | ||||
|   install : true) | ||||
|  | ||||
| if get_option('shared') | ||||
|   libjanet = library('janet', janetc, | ||||
|     include_directories : incdir, | ||||
|     dependencies : janet_dependencies, | ||||
|     version: meson.project_version(), | ||||
|     soversion: version_parts[0] + '.' + version_parts[1], | ||||
|     c_args : lib_cflags, | ||||
|     install : true) | ||||
| # Extra c flags - adding -fvisibility=hidden matches the Makefile and | ||||
| # shaves off about 10k on linux x64, likely similar on other platforms. | ||||
| if cc.has_argument('-fvisibility=hidden') | ||||
|   extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT'] | ||||
|   if cc.has_argument('-fvisibility=hidden') | ||||
|     extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT'] | ||||
|   else | ||||
|     extra_cflags = ['-DJANET_DLL_IMPORT'] | ||||
|   endif | ||||
|   janet_mainclient = executable('janet', mainclient_src, | ||||
|     include_directories : incdir, | ||||
|     dependencies : janet_dependencies, | ||||
|     link_with: [libjanet], | ||||
|     c_args : extra_cflags, | ||||
|     install : true) | ||||
| else | ||||
|   extra_cflags = ['-DJANET_DLL_IMPORT'] | ||||
|   # No shared library | ||||
|   janet_mainclient = executable('janet', mainclient_src, janetc, | ||||
|     include_directories : incdir, | ||||
|     dependencies : janet_dependencies, | ||||
|     c_args : lib_cflags, | ||||
|     install : true) | ||||
| endif | ||||
| janet_mainclient = executable('janet', mainclient_src, | ||||
|   include_directories : incdir, | ||||
|   dependencies : janet_dependencies, | ||||
|   link_with: [libjanet], | ||||
|   c_args : extra_cflags, | ||||
|   install : true) | ||||
|  | ||||
| if meson.is_cross_build() | ||||
|   native_cc = meson.get_compiler('c', native: true) | ||||
| @@ -238,6 +249,7 @@ test_files = [ | ||||
|   'test/suite-asm.janet', | ||||
|   'test/suite-boot.janet', | ||||
|   'test/suite-buffer.janet', | ||||
|   'test/suite-bundle.janet', | ||||
|   'test/suite-capi.janet', | ||||
|   'test/suite-cfuns.janet', | ||||
|   'test/suite-compile.janet', | ||||
| @@ -271,14 +283,15 @@ endforeach | ||||
| run_target('repl', command : [janet_nativeclient]) | ||||
|  | ||||
| # For use as meson subproject (wrap) | ||||
| janet_dep = declare_dependency(include_directories : incdir, | ||||
|   link_with : libjanet) | ||||
|  | ||||
| if get_option('shared') | ||||
|   janet_dep = declare_dependency(include_directories : incdir, | ||||
|     link_with : libjanet) | ||||
| # pkgconfig | ||||
| pkg = import('pkgconfig') | ||||
| pkg.generate(libjanet, | ||||
|   subdirs: 'janet', | ||||
|   description: 'Library for the Janet programming language.') | ||||
|   pkg = import('pkgconfig') | ||||
|   pkg.generate(libjanet, | ||||
|     subdirs: 'janet', | ||||
|     description: 'Library for the Janet programming language.') | ||||
| endif | ||||
|  | ||||
| # Installation | ||||
| install_man('janet.1') | ||||
|   | ||||
| @@ -11,13 +11,14 @@ option('peg', type : 'boolean', value : true) | ||||
| option('int_types', type : 'boolean', value : true) | ||||
| option('prf', type : 'boolean', value : false) | ||||
| option('net', type : 'boolean', value : true) | ||||
| option('ipv6', type : 'boolean', value : true) | ||||
| option('ev', type : 'boolean', value : true) | ||||
| option('processes', type : 'boolean', value : true) | ||||
| option('umask', type : 'boolean', value : true) | ||||
| option('realpath', type : 'boolean', value : true) | ||||
| option('simple_getline', type : 'boolean', value : false) | ||||
| option('epoll', type : 'boolean', value : false) | ||||
| option('kqueue', type : 'boolean', value : false) | ||||
| option('epoll', type : 'boolean', value : true) | ||||
| option('kqueue', type : 'boolean', value : true) | ||||
| option('interpreter_interrupt', type : 'boolean', value : true) | ||||
| option('ffi', type : 'boolean', value : true) | ||||
| option('ffi_jit', type : 'boolean', value : true) | ||||
| @@ -29,3 +30,5 @@ option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7f | ||||
|  | ||||
| option('arch_name', type : 'string', value: '') | ||||
| option('os_name', type : 'string', value: '') | ||||
| option('shared', type : 'boolean', value: true) | ||||
| option('cryptorand', type : 'boolean', value: true) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -4,10 +4,10 @@ | ||||
| #define JANETCONF_H | ||||
|  | ||||
| #define JANET_VERSION_MAJOR 1 | ||||
| #define JANET_VERSION_MINOR 31 | ||||
| #define JANET_VERSION_MINOR 34 | ||||
| #define JANET_VERSION_PATCH 0 | ||||
| #define JANET_VERSION_EXTRA "" | ||||
| #define JANET_VERSION "1.31.0" | ||||
| #define JANET_VERSION "1.34.0" | ||||
|  | ||||
| /* #define JANET_BUILD "local" */ | ||||
|  | ||||
| @@ -52,6 +52,9 @@ | ||||
| /* #define JANET_EV_NO_EPOLL */ | ||||
| /* #define JANET_EV_NO_KQUEUE */ | ||||
| /* #define JANET_NO_INTERPRETER_INTERRUPT */ | ||||
| /* #define JANET_NO_IPV6 */ | ||||
| /* #define JANET_NO_CRYPTORAND */ | ||||
| /* #define JANET_USE_STDATOMIC */ | ||||
|  | ||||
| /* Custom vm allocator support */ | ||||
| /* #include <mimalloc.h> */ | ||||
|   | ||||
| @@ -31,8 +31,6 @@ | ||||
| #ifdef JANET_EV | ||||
| #ifdef JANET_WINDOWS | ||||
| #include <windows.h> | ||||
| #else | ||||
| #include <stdatomic.h> | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -560,6 +560,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int | ||||
|     x = janet_get1(s, janet_ckeywordv("vararg")); | ||||
|     if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG; | ||||
|  | ||||
|     /* Initialize slotcount */ | ||||
|     def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity; | ||||
|  | ||||
|     /* Check structarg */ | ||||
|     x = janet_get1(s, janet_ckeywordv("structarg")); | ||||
|     if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; | ||||
| @@ -784,8 +787,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int | ||||
|     } | ||||
|  | ||||
|     /* Verify the func def */ | ||||
|     if (janet_verify(def)) { | ||||
|         janet_asm_error(&a, "invalid assembly"); | ||||
|     int verify_status = janet_verify(def); | ||||
|     if (verify_status) { | ||||
|         janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status)); | ||||
|     } | ||||
|  | ||||
|     /* Add final flags */ | ||||
|   | ||||
| @@ -135,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) { | ||||
|  | ||||
| /* Push a cstring to buffer */ | ||||
| void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) { | ||||
|     int32_t len = 0; | ||||
|     while (cstring[len]) ++len; | ||||
|     int32_t len = (int32_t) strlen(cstring); | ||||
|     janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len); | ||||
| } | ||||
|  | ||||
| @@ -321,6 +320,143 @@ JANET_CORE_FN(cfun_buffer_chars, | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| static int should_reverse_bytes(const Janet *argv, int32_t argc) { | ||||
|     JanetKeyword order_kw = janet_getkeyword(argv, argc); | ||||
|     if (!janet_cstrcmp(order_kw, "le")) { | ||||
| #if JANET_BIG_ENDIAN | ||||
|         return 1; | ||||
| #endif | ||||
|     } else if (!janet_cstrcmp(order_kw, "be")) { | ||||
| #if JANET_LITTLE_ENDIAN | ||||
|         return 1; | ||||
| #endif | ||||
|     } else if (!janet_cstrcmp(order_kw, "native")) { | ||||
|         return 0; | ||||
|     } else { | ||||
|         janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void reverse_u32(uint8_t bytes[4]) { | ||||
|     uint8_t temp; | ||||
|     temp = bytes[3]; | ||||
|     bytes[3] = bytes[0]; | ||||
|     bytes[0] = temp; | ||||
|     temp = bytes[2]; | ||||
|     bytes[2] = bytes[1]; | ||||
|     bytes[1] = temp; | ||||
| } | ||||
|  | ||||
| static void reverse_u64(uint8_t bytes[8]) { | ||||
|     uint8_t temp; | ||||
|     temp = bytes[7]; | ||||
|     bytes[7] = bytes[0]; | ||||
|     bytes[0] = temp; | ||||
|     temp = bytes[6]; | ||||
|     bytes[6] = bytes[1]; | ||||
|     bytes[1] = temp; | ||||
|     temp = bytes[5]; | ||||
|     bytes[5] = bytes[2]; | ||||
|     bytes[2] = temp; | ||||
|     temp = bytes[4]; | ||||
|     bytes[4] = bytes[3]; | ||||
|     bytes[3] = temp; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_uint16, | ||||
|               "(buffer/push-uint16 buffer order data)", | ||||
|               "Push a 16 bit unsigned integer data onto the end of the buffer. " | ||||
|               "Returns the modified buffer.") { | ||||
|     janet_fixarity(argc, 3); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int reverse = should_reverse_bytes(argv, 1); | ||||
|     union { | ||||
|         uint16_t data; | ||||
|         uint8_t bytes[2]; | ||||
|     } u; | ||||
|     u.data = (uint16_t) janet_getinteger(argv, 2); | ||||
|     if (reverse) { | ||||
|         uint8_t temp = u.bytes[1]; | ||||
|         u.bytes[1] = u.bytes[0]; | ||||
|         u.bytes[0] = temp; | ||||
|     } | ||||
|     janet_buffer_push_u16(buffer, *(uint16_t *) u.bytes); | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_uint32, | ||||
|               "(buffer/push-uint32 buffer order data)", | ||||
|               "Push a 32 bit unsigned integer data onto the end of the buffer. " | ||||
|               "Returns the modified buffer.") { | ||||
|     janet_fixarity(argc, 3); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int reverse = should_reverse_bytes(argv, 1); | ||||
|     union { | ||||
|         uint32_t data; | ||||
|         uint8_t bytes[4]; | ||||
|     } u; | ||||
|     u.data = (uint32_t) janet_getinteger(argv, 2); | ||||
|     if (reverse) | ||||
|         reverse_u32(u.bytes); | ||||
|     janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes); | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_uint64, | ||||
|               "(buffer/push-uint64 buffer order data)", | ||||
|               "Push a 64 bit unsigned integer data onto the end of the buffer. " | ||||
|               "Returns the modified buffer.") { | ||||
|     janet_fixarity(argc, 3); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int reverse = should_reverse_bytes(argv, 1); | ||||
|     union { | ||||
|         uint64_t data; | ||||
|         uint8_t bytes[8]; | ||||
|     } u; | ||||
|     u.data = (uint64_t) janet_getuinteger64(argv, 2); | ||||
|     if (reverse) | ||||
|         reverse_u64(u.bytes); | ||||
|     janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes); | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_float32, | ||||
|               "(buffer/push-float32 buffer order data)", | ||||
|               "Push the underlying bytes of a 32 bit float data onto the end of the buffer. " | ||||
|               "Returns the modified buffer.") { | ||||
|     janet_fixarity(argc, 3); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int reverse = should_reverse_bytes(argv, 1); | ||||
|     union { | ||||
|         float data; | ||||
|         uint8_t bytes[4]; | ||||
|     } u; | ||||
|     u.data = (float) janet_getnumber(argv, 2); | ||||
|     if (reverse) | ||||
|         reverse_u32(u.bytes); | ||||
|     janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes); | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_float64, | ||||
|               "(buffer/push-float64 buffer order data)", | ||||
|               "Push the underlying bytes of a 64 bit float data onto the end of the buffer. " | ||||
|               "Returns the modified buffer.") { | ||||
|     janet_fixarity(argc, 3); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int reverse = should_reverse_bytes(argv, 1); | ||||
|     union { | ||||
|         double data; | ||||
|         uint8_t bytes[8]; | ||||
|     } u; | ||||
|     u.data = janet_getnumber(argv, 2); | ||||
|     if (reverse) | ||||
|         reverse_u64(u.bytes); | ||||
|     janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes); | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) { | ||||
|     for (int32_t i = argc_offset; i < argc; i++) { | ||||
|         if (janet_checktype(argv[i], JANET_NUMBER)) { | ||||
| @@ -519,6 +655,27 @@ JANET_CORE_FN(cfun_buffer_format, | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_format_at, | ||||
|               "(buffer/format-at buffer at format & args)", | ||||
|               "Snprintf like functionality for printing values into a buffer. Returns " | ||||
|               "the modified buffer.") { | ||||
|     janet_arity(argc, 2, -1); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int32_t at = janet_getinteger(argv, 1); | ||||
|     if (at < 0) { | ||||
|         at += buffer->count + 1; | ||||
|     } | ||||
|     if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at); | ||||
|     int32_t oldcount = buffer->count; | ||||
|     buffer->count = at; | ||||
|     const char *strfrmt = (const char *) janet_getstring(argv, 2); | ||||
|     janet_buffer_format(buffer, strfrmt, 2, argc, argv); | ||||
|     if (buffer->count < oldcount) { | ||||
|         buffer->count = oldcount; | ||||
|     } | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
| void janet_lib_buffer(JanetTable *env) { | ||||
|     JanetRegExt buffer_cfuns[] = { | ||||
|         JANET_CORE_REG("buffer/new", cfun_buffer_new), | ||||
| @@ -529,6 +686,11 @@ void janet_lib_buffer(JanetTable *env) { | ||||
|         JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8), | ||||
|         JANET_CORE_REG("buffer/push-word", cfun_buffer_word), | ||||
|         JANET_CORE_REG("buffer/push-string", cfun_buffer_chars), | ||||
|         JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16), | ||||
|         JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32), | ||||
|         JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64), | ||||
|         JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32), | ||||
|         JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64), | ||||
|         JANET_CORE_REG("buffer/push", cfun_buffer_push), | ||||
|         JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at), | ||||
|         JANET_CORE_REG("buffer/popn", cfun_buffer_popn), | ||||
| @@ -540,6 +702,7 @@ void janet_lib_buffer(JanetTable *env) { | ||||
|         JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle), | ||||
|         JANET_CORE_REG("buffer/blit", cfun_buffer_blit), | ||||
|         JANET_CORE_REG("buffer/format", cfun_buffer_format), | ||||
|         JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at), | ||||
|         JANET_REG_END | ||||
|     }; | ||||
|     janet_core_cfuns_ext(env, NULL, buffer_cfuns); | ||||
|   | ||||
| @@ -226,6 +226,7 @@ void janet_bytecode_movopt(JanetFuncDef *def) { | ||||
|                 case JOP_LOAD_TRUE: | ||||
|                 case JOP_LOAD_FALSE: | ||||
|                 case JOP_LOAD_SELF: | ||||
|                     break; | ||||
|                 case JOP_MAKE_ARRAY: | ||||
|                 case JOP_MAKE_BUFFER: | ||||
|                 case JOP_MAKE_STRING: | ||||
| @@ -233,6 +234,8 @@ void janet_bytecode_movopt(JanetFuncDef *def) { | ||||
|                 case JOP_MAKE_TABLE: | ||||
|                 case JOP_MAKE_TUPLE: | ||||
|                 case JOP_MAKE_BRACKET_TUPLE: | ||||
|                     /* Reads from the stack, don't remove */ | ||||
|                     janetc_regalloc_touch(&ra, DD); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read A */ | ||||
|   | ||||
| @@ -35,6 +35,13 @@ | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #ifdef JANET_USE_STDATOMIC | ||||
| #include <stdatomic.h> | ||||
| /* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations. | ||||
|  * Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of). | ||||
|  * For TCC and similar compilers, one would need -std=c11 or similar then to get access. */ | ||||
| #endif | ||||
|  | ||||
| JANET_NO_RETURN static void janet_top_level_signal(const char *msg) { | ||||
| #ifdef JANET_TOP_LEVEL_SIGNAL | ||||
|     JANET_TOP_LEVEL_SIGNAL(msg); | ||||
| @@ -338,7 +345,7 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c | ||||
|     int32_t not_raw = raw; | ||||
|     if (not_raw < 0) not_raw += length + 1; | ||||
|     if (not_raw < 0 || not_raw > length) | ||||
|         janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length); | ||||
|         janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length); | ||||
|     return not_raw; | ||||
| } | ||||
|  | ||||
| @@ -361,7 +368,7 @@ int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const ch | ||||
|     int32_t not_raw = raw; | ||||
|     if (not_raw < 0) not_raw += length; | ||||
|     if (not_raw < 0 || not_raw > length) | ||||
|         janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length); | ||||
|         janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length); | ||||
|     return not_raw; | ||||
| } | ||||
|  | ||||
| @@ -496,6 +503,8 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA | ||||
| JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     return InterlockedIncrement(x); | ||||
| #elif defined(JANET_USE_STDATOMIC) | ||||
|     return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1; | ||||
| #else | ||||
|     return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED); | ||||
| #endif | ||||
| @@ -504,8 +513,20 @@ JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) { | ||||
| JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     return InterlockedDecrement(x); | ||||
| #elif defined(JANET_USE_STDATOMIC) | ||||
|     return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1; | ||||
| #else | ||||
|     return __atomic_add_fetch(x, -1, __ATOMIC_RELAXED); | ||||
|     return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     return InterlockedOr(x, 0); | ||||
| #elif defined(JANET_USE_STDATOMIC) | ||||
|     return atomic_load_explicit(x, memory_order_acquire); | ||||
| #else | ||||
|     return __atomic_load_n(x, __ATOMIC_ACQUIRE); | ||||
| #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -934,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) { | ||||
|         int32_t slotchunks = (def->slotcount + 31) >> 5; | ||||
|         /* numchunks is min of slotchunks and scope->ua.count */ | ||||
|         int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks; | ||||
|         uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks); | ||||
|         uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t)); | ||||
|         if (NULL == chunks) { | ||||
|             JANET_OUT_OF_MEMORY; | ||||
|         } | ||||
| @@ -1056,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source, | ||||
|  | ||||
|     if (c.result.status == JANET_COMPILE_OK) { | ||||
|         JanetFuncDef *def = janetc_pop_funcdef(&c); | ||||
|         def->name = janet_cstring("_thunk"); | ||||
|         def->name = janet_cstring("thunk"); | ||||
|         janet_def_addflags(def); | ||||
|         c.result.funcdef = def; | ||||
|     } else { | ||||
|   | ||||
| @@ -69,15 +69,15 @@ JanetModule janet_native(const char *name, const uint8_t **error) { | ||||
|             host.minor < modconf.minor || | ||||
|             host.bits != modconf.bits) { | ||||
|         char errbuf[128]; | ||||
|         sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)", | ||||
|                 host.major, | ||||
|                 host.minor, | ||||
|                 host.patch, | ||||
|                 host.bits, | ||||
|                 modconf.major, | ||||
|                 modconf.minor, | ||||
|                 modconf.patch, | ||||
|                 modconf.bits); | ||||
|         snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)", | ||||
|                  host.major, | ||||
|                  host.minor, | ||||
|                  host.patch, | ||||
|                  host.bits, | ||||
|                  modconf.major, | ||||
|                  modconf.minor, | ||||
|                  modconf.patch, | ||||
|                  modconf.bits); | ||||
|         *error = janet_cstring(errbuf); | ||||
|         return NULL; | ||||
|     } | ||||
| @@ -110,14 +110,14 @@ JANET_CORE_FN(janet_core_expand_path, | ||||
|               "(module/expand-path path template)", | ||||
|               "Expands a path template as found in `module/paths` for `module/find`. " | ||||
|               "This takes in a path (the argument to require) and a template string, " | ||||
|               "to expand the path to a path that can be " | ||||
|               "used for importing files. The replacements are as follows:\n\n" | ||||
|               "to expand the path to a path that can be used for importing files. " | ||||
|               "The replacements are as follows:\n\n" | ||||
|               "* :all: -- the value of path verbatim.\n\n" | ||||
|               "* :@all: -- Same as :all:, but if `path` starts with the @ character,\n" | ||||
|               "           the first path segment is replaced with a dynamic binding\n" | ||||
|               "           `(dyn <first path segment as keyword>)`.\n\n" | ||||
|               "* :cur: -- the current file, or (dyn :current-file)\n\n" | ||||
|               "* :dir: -- the directory containing the current file\n\n" | ||||
|               "* :@all: -- Same as :all:, but if `path` starts with the @ character, " | ||||
|               "the first path segment is replaced with a dynamic binding " | ||||
|               "`(dyn <first path segment as keyword>)`.\n\n" | ||||
|               "* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n" | ||||
|               "* :dir: -- the directory portion, if any, of the path argument\n\n" | ||||
|               "* :name: -- the name component of path, with extension if given\n\n" | ||||
|               "* :native: -- the extension used to load natives, .so or .dll\n\n" | ||||
|               "* :sys: -- the system path, or (dyn :syspath)") { | ||||
| @@ -1144,17 +1144,20 @@ JanetTable *janet_core_env(JanetTable *replacements) { | ||||
|                     JDOC("(next ds &opt key)\n\n" | ||||
|                          "Gets the next key in a data structure. Can be used to iterate through " | ||||
|                          "the keys of a data structure in an unspecified order. Keys are guaranteed " | ||||
|                          "to be seen only once per iteration if they data structure is not mutated " | ||||
|                          "to be seen only once per iteration if the data structure is not mutated " | ||||
|                          "during iteration. If key is nil, next returns the first key. If next " | ||||
|                          "returns nil, there are no more keys to iterate through.")); | ||||
|     janet_quick_asm(env, JANET_FUN_PROP, | ||||
|                     "propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm), | ||||
|                     JDOC("(propagate x fiber)\n\n" | ||||
|                          "Propagate a signal from a fiber to the current fiber. The resulting " | ||||
|                          "stack trace from the current fiber will include frames from fiber. If " | ||||
|                          "fiber is in a state that can be resumed, resuming the current fiber will " | ||||
|                          "first resume fiber. This function can be used to re-raise an error without " | ||||
|                          "losing the original stack trace.")); | ||||
|                          "Propagate a signal from a fiber to the current fiber and " | ||||
|                          "set the last value of the current fiber to `x`.  The signal " | ||||
|                          "value is then available as the status of the current fiber. " | ||||
|                          "The resulting stack trace from the current fiber will include " | ||||
|                          "frames from fiber. If fiber is in a state that can be resumed, " | ||||
|                          "resuming the current fiber will first resume `fiber`. " | ||||
|                          "This function can be used to re-raise an error without losing " | ||||
|                          "the original stack trace.")); | ||||
|     janet_quick_asm(env, JANET_FUN_DEBUG, | ||||
|                     "debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm), | ||||
|                     JDOC("(debug &opt x)\n\n" | ||||
|   | ||||
| @@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) { | ||||
|                 } | ||||
|             } | ||||
|             if (frame->flags & JANET_STACKFRAME_TAILCALL) | ||||
|                 janet_eprintf(" (tailcall)"); | ||||
|                 janet_eprintf(" (tail call)"); | ||||
|             if (frame->func && frame->pc) { | ||||
|                 int32_t off = (int32_t)(frame->pc - def->bytecode); | ||||
|                 if (def->sourcemap) { | ||||
| @@ -180,6 +180,11 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) { | ||||
|                 } | ||||
|             } | ||||
|             janet_eprintf("\n"); | ||||
|             /* Print fiber points optionally. Clutters traces but provides info | ||||
|             if (i <= 0 && fi > 0) { | ||||
|                 janet_eprintf("  in parent fiber\n"); | ||||
|             } | ||||
|             */ | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -388,8 +393,8 @@ JANET_CORE_FN(cfun_debug_stack, | ||||
| JANET_CORE_FN(cfun_debug_stacktrace, | ||||
|               "(debug/stacktrace fiber &opt err prefix)", | ||||
|               "Prints a nice looking stacktrace for a fiber. Can optionally provide " | ||||
|               "an error value to print the stack trace with. If `err` is nil or not " | ||||
|               "provided, and no prefix is given, will skip the error line. Returns the fiber.") { | ||||
|               "an error value to print the stack trace with. If `prefix` is nil or not " | ||||
|               "provided, will skip the error line. Returns the fiber.") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|     JanetFiber *fiber = janet_getfiber(argv, 0); | ||||
|     Janet x = argc == 1 ? janet_wrap_nil() : argv[1]; | ||||
|   | ||||
							
								
								
									
										281
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								src/core/ev.c
									
									
									
									
									
								
							| @@ -255,34 +255,43 @@ static void add_timeout(JanetTimeout to) { | ||||
|  | ||||
| void janet_async_end(JanetFiber *fiber) { | ||||
|     if (fiber->ev_callback) { | ||||
|         fiber->ev_callback(fiber, JANET_ASYNC_EVENT_DEINIT); | ||||
|         janet_gcunroot(janet_wrap_abstract(fiber->ev_stream)); | ||||
|         fiber->ev_callback = NULL; | ||||
|         if (fiber->ev_state) { | ||||
|             if (!(fiber->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) { | ||||
|         if (!(fiber->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) { | ||||
|             if (fiber->ev_state) { | ||||
|                 janet_free(fiber->ev_state); | ||||
|                 janet_ev_dec_refcount(); | ||||
|                 fiber->ev_state = NULL; | ||||
|             } | ||||
|             fiber->ev_state = NULL; | ||||
|             janet_ev_dec_refcount(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void *janet_async_start(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, size_t data_size) { | ||||
| void janet_async_in_flight(JanetFiber *fiber) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
| #else | ||||
|     (void) fiber; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state) { | ||||
|     JanetFiber *fiber = janet_vm.root_fiber; | ||||
|     janet_assert(!fiber->ev_callback, "double async on fiber"); | ||||
|     if (mode & JANET_ASYNC_LISTEN_READ) stream->read_fiber = fiber; | ||||
|     if (mode & JANET_ASYNC_LISTEN_WRITE) stream->write_fiber = fiber; | ||||
|     if (mode & JANET_ASYNC_LISTEN_READ) { | ||||
|         stream->read_fiber = fiber; | ||||
|     } | ||||
|     if (mode & JANET_ASYNC_LISTEN_WRITE) { | ||||
|         stream->write_fiber = fiber; | ||||
|     } | ||||
|     fiber->ev_callback = callback; | ||||
|     fiber->ev_stream = stream; | ||||
|     janet_ev_inc_refcount(); | ||||
|     janet_gcroot(janet_wrap_abstract(stream)); | ||||
|     if (data_size) { | ||||
|         void *data = janet_malloc(data_size); | ||||
|         fiber->ev_state = data; | ||||
|         return data; | ||||
|     } else { | ||||
|         fiber->ev_state = NULL; | ||||
|         return NULL; | ||||
|     } | ||||
|     fiber->ev_state = state; | ||||
|     callback(fiber, JANET_ASYNC_EVENT_INIT); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| void janet_fiber_did_resume(JanetFiber *fiber) { | ||||
| @@ -445,6 +454,9 @@ static void *janet_stream_unmarshal(JanetMarshalContext *ctx) { | ||||
|     p->handle = (JanetHandle) janet_unmarshal_int64(ctx); | ||||
| #else | ||||
|     p->handle = (JanetHandle) janet_unmarshal_int(ctx); | ||||
| #endif | ||||
| #ifdef JANET_EV_POLL | ||||
|     janet_register_stream(p); | ||||
| #endif | ||||
|     return p; | ||||
| } | ||||
| @@ -454,6 +466,12 @@ static Janet janet_stream_next(void *p, Janet key) { | ||||
|     return janet_nextmethod(stream->methods, key); | ||||
| } | ||||
|  | ||||
| static void janet_stream_tostring(void *p, JanetBuffer *buffer) { | ||||
|     JanetStream *stream = p; | ||||
|     /* Let user print the file descriptor for debugging */ | ||||
|     janet_formatb(buffer, "<core/stream handle=%d>", stream->handle); | ||||
| } | ||||
|  | ||||
| const JanetAbstractType janet_stream_type = { | ||||
|     "core/stream", | ||||
|     janet_stream_gc, | ||||
| @@ -462,7 +480,7 @@ const JanetAbstractType janet_stream_type = { | ||||
|     NULL, | ||||
|     janet_stream_marshal, | ||||
|     janet_stream_unmarshal, | ||||
|     NULL, | ||||
|     janet_stream_tostring, | ||||
|     NULL, | ||||
|     NULL, | ||||
|     janet_stream_next, | ||||
| @@ -1161,10 +1179,12 @@ JANET_CORE_FN(cfun_channel_close, | ||||
|                 msg.argj = janet_wrap_nil(); | ||||
|                 janet_ev_post_event(vm, janet_thread_chan_cb, msg); | ||||
|             } else { | ||||
|                 if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) { | ||||
|                     janet_schedule(writer.fiber, make_close_result(channel)); | ||||
|                 } else { | ||||
|                     janet_schedule(writer.fiber, janet_wrap_nil()); | ||||
|                 if (janet_fiber_can_resume(writer.fiber)) { | ||||
|                     if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) { | ||||
|                         janet_schedule(writer.fiber, make_close_result(channel)); | ||||
|                     } else { | ||||
|                         janet_schedule(writer.fiber, janet_wrap_nil()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -1180,10 +1200,12 @@ JANET_CORE_FN(cfun_channel_close, | ||||
|                 msg.argj = janet_wrap_nil(); | ||||
|                 janet_ev_post_event(vm, janet_thread_chan_cb, msg); | ||||
|             } else { | ||||
|                 if (reader.mode == JANET_CP_MODE_CHOICE_READ) { | ||||
|                     janet_schedule(reader.fiber, make_close_result(channel)); | ||||
|                 } else { | ||||
|                     janet_schedule(reader.fiber, janet_wrap_nil()); | ||||
|                 if (janet_fiber_can_resume(reader.fiber)) { | ||||
|                     if (reader.mode == JANET_CP_MODE_CHOICE_READ) { | ||||
|                         janet_schedule(reader.fiber, make_close_result(channel)); | ||||
|                     } else { | ||||
|                         janet_schedule(reader.fiber, janet_wrap_nil()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -1279,7 +1301,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout); | ||||
| int janet_loop_done(void) { | ||||
|     return !((janet_vm.spawn.head != janet_vm.spawn.tail) || | ||||
|              janet_vm.tq_count || | ||||
|              janet_vm.listener_count); | ||||
|              janet_atomic_load(&janet_vm.listener_count)); | ||||
| } | ||||
|  | ||||
| JanetFiber *janet_loop1(void) { | ||||
| @@ -1341,7 +1363,7 @@ JanetFiber *janet_loop1(void) { | ||||
|     } | ||||
|  | ||||
|     /* Poll for events */ | ||||
|     if (janet_vm.tq_count || janet_vm.listener_count) { | ||||
|     if (janet_vm.tq_count || janet_atomic_load(&janet_vm.listener_count)) { | ||||
|         JanetTimeout to; | ||||
|         memset(&to, 0, sizeof(to)); | ||||
|         int has_timeout; | ||||
| @@ -1360,7 +1382,7 @@ JanetFiber *janet_loop1(void) { | ||||
|             break; | ||||
|         } | ||||
|         /* Run polling implementation only if pending timeouts or pending events */ | ||||
|         if (janet_vm.tq_count || janet_vm.listener_count) { | ||||
|         if (janet_vm.tq_count || janet_atomic_load(&janet_vm.listener_count)) { | ||||
|             janet_loop1_impl(has_timeout, to.when); | ||||
|         } | ||||
|     } | ||||
| @@ -1447,7 +1469,7 @@ void janet_ev_deinit(void) { | ||||
|     CloseHandle(janet_vm.iocp); | ||||
| } | ||||
|  | ||||
| void janet_register_stream(JanetStream *stream) { | ||||
| static void janet_register_stream(JanetStream *stream) { | ||||
|     if (NULL == CreateIoCompletionPort(stream->handle, janet_vm.iocp, (ULONG_PTR) stream, 0)) { | ||||
|         janet_panicf("failed to listen for events: %V", janet_ev_lasterr()); | ||||
|     } | ||||
| @@ -1504,6 +1526,14 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void janet_stream_edge_triggered(JanetStream *stream) { | ||||
|     (void) stream; | ||||
| } | ||||
|  | ||||
| void janet_stream_level_triggered(JanetStream *stream) { | ||||
|     (void) stream; | ||||
| } | ||||
|  | ||||
| #elif defined(JANET_EV_EPOLL) | ||||
|  | ||||
| static JanetTimestamp ts_now(void) { | ||||
| @@ -1515,15 +1545,15 @@ static JanetTimestamp ts_now(void) { | ||||
| } | ||||
|  | ||||
| /* Wait for the next event */ | ||||
| static void janet_register_stream(JanetStream *stream) { | ||||
| static void janet_register_stream_impl(JanetStream *stream, int mod, int edge_trigger) { | ||||
|     struct epoll_event ev; | ||||
|     ev.events = EPOLLET; | ||||
|     ev.events = edge_trigger ? EPOLLET : 0; | ||||
|     if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) ev.events |= EPOLLIN; | ||||
|     if (stream->flags & JANET_STREAM_WRITABLE) ev.events |= EPOLLOUT; | ||||
|     ev.data.ptr = stream; | ||||
|     int status; | ||||
|     do { | ||||
|         status = epoll_ctl(janet_vm.epoll, EPOLL_CTL_ADD, stream->handle, &ev); | ||||
|         status = epoll_ctl(janet_vm.epoll, mod ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, stream->handle, &ev); | ||||
|     } while (status == -1 && errno == EINTR); | ||||
|     if (status == -1) { | ||||
|         if (errno == EPERM) { | ||||
| @@ -1537,6 +1567,18 @@ static void janet_register_stream(JanetStream *stream) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void janet_register_stream(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 0, 1); | ||||
| } | ||||
|  | ||||
| void janet_stream_edge_triggered(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 1, 1); | ||||
| } | ||||
|  | ||||
| void janet_stream_level_triggered(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 1, 0); | ||||
| } | ||||
|  | ||||
| #define JANET_EPOLL_MAX_EVENTS 64 | ||||
| void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { | ||||
|     struct itimerspec its; | ||||
| @@ -1666,14 +1708,15 @@ static void timestamp2timespec(struct timespec *t, JanetTimestamp ts) { | ||||
|     t->tv_nsec = ts == 0 ? 0 : (ts % 1000) * 1000000; | ||||
| } | ||||
|  | ||||
| void janet_register_stream(JanetStream *stream) { | ||||
| void janet_register_stream_impl(JanetStream *stream, int edge_trigger) { | ||||
|     struct kevent kevs[2]; | ||||
|     int length = 0; | ||||
|     int clear = edge_trigger ? EV_CLEAR : 0; | ||||
|     if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) { | ||||
|         EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, stream); | ||||
|         EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE | clear, 0, 0, stream); | ||||
|     } | ||||
|     if (stream->flags & JANET_STREAM_WRITABLE) { | ||||
|         EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, stream); | ||||
|         EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_ADD | EV_ENABLE | clear, 0, 0, stream); | ||||
|     } | ||||
|     int status; | ||||
|     do { | ||||
| @@ -1684,6 +1727,18 @@ void janet_register_stream(JanetStream *stream) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void janet_register_stream(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 1); | ||||
| } | ||||
|  | ||||
| void janet_stream_edge_triggered(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 1); | ||||
| } | ||||
|  | ||||
| void janet_stream_level_triggered(JanetStream *stream) { | ||||
|     janet_register_stream_impl(stream, 0); | ||||
| } | ||||
|  | ||||
| #define JANET_KQUEUE_MAX_EVENTS 64 | ||||
|  | ||||
| void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { | ||||
| @@ -1806,15 +1861,30 @@ void janet_register_stream(JanetStream *stream) { | ||||
|     janet_vm.stream_count = new_count; | ||||
| } | ||||
|  | ||||
| void janet_stream_edge_triggered(JanetStream *stream) { | ||||
|     (void) stream; | ||||
| } | ||||
|  | ||||
| void janet_stream_level_triggered(JanetStream *stream) { | ||||
|     (void) stream; | ||||
| } | ||||
|  | ||||
| void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { | ||||
|  | ||||
|     /* set event flags */ | ||||
|     for (size_t i = 0; i < janet_vm.stream_count; i++) { | ||||
|         JanetStream *stream = janet_vm.streams[i]; | ||||
|         janet_vm.fds[i + 1].events = 0; | ||||
|         janet_vm.fds[i + 1].revents = 0; | ||||
|         if (stream->read_fiber) janet_vm.fds[i + 1].events |= POLLIN; | ||||
|         if (stream->write_fiber) janet_vm.fds[i + 1].events |= POLLOUT; | ||||
|         struct pollfd *pfd = janet_vm.fds + i + 1; | ||||
|         pfd->events = 0; | ||||
|         pfd->revents = 0; | ||||
|         JanetFiber *rf = stream->read_fiber; | ||||
|         JanetFiber *wf = stream->write_fiber; | ||||
|         if (rf && rf->ev_callback) pfd->events |= POLLIN; | ||||
|         if (wf && wf->ev_callback) pfd->events |= POLLOUT; | ||||
|         /* Hack to ignore a file descriptor - make file descriptor negative if we want to ignore */ | ||||
|         if (!pfd->events) { | ||||
|             pfd->fd = -pfd->fd; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Poll for events */ | ||||
| @@ -1831,6 +1901,14 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { | ||||
|         JANET_EXIT("failed to poll events"); | ||||
|     } | ||||
|  | ||||
|     /* Undo negative hack */ | ||||
|     for (size_t i = 0; i < janet_vm.stream_count; i++) { | ||||
|         struct pollfd *pfd = janet_vm.fds + i + 1; | ||||
|         if (pfd->fd < 0) { | ||||
|             pfd->fd = -pfd->fd; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Check selfpipe */ | ||||
|     if (janet_vm.fds[0].revents & POLLIN) { | ||||
|         janet_vm.fds[0].revents = 0; | ||||
| @@ -2013,7 +2091,7 @@ void janet_ev_threaded_call(JanetThreadedSubroutine fp, JanetEVGenericMessage ar | ||||
|     int err = pthread_create(&waiter_thread, &janet_vm.new_thread_attr, janet_thread_body, init); | ||||
|     if (err) { | ||||
|         janet_free(init); | ||||
|         janet_panicf("%s", strerror(err)); | ||||
|         janet_panicf("%s", janet_strerror(err)); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @@ -2026,33 +2104,35 @@ void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value) { | ||||
|     if (return_value.fiber == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     switch (return_value.tag) { | ||||
|         default: | ||||
|         case JANET_EV_TCTAG_NIL: | ||||
|             janet_schedule(return_value.fiber, janet_wrap_nil()); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_INTEGER: | ||||
|             janet_schedule(return_value.fiber, janet_wrap_integer(return_value.argi)); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_STRING: | ||||
|         case JANET_EV_TCTAG_STRINGF: | ||||
|             janet_schedule(return_value.fiber, janet_cstringv((const char *) return_value.argp)); | ||||
|             if (return_value.tag == JANET_EV_TCTAG_STRINGF) janet_free(return_value.argp); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_KEYWORD: | ||||
|             janet_schedule(return_value.fiber, janet_ckeywordv((const char *) return_value.argp)); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_ERR_STRING: | ||||
|         case JANET_EV_TCTAG_ERR_STRINGF: | ||||
|             janet_cancel(return_value.fiber, janet_cstringv((const char *) return_value.argp)); | ||||
|             if (return_value.tag == JANET_EV_TCTAG_STRINGF) janet_free(return_value.argp); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_ERR_KEYWORD: | ||||
|             janet_cancel(return_value.fiber, janet_ckeywordv((const char *) return_value.argp)); | ||||
|             break; | ||||
|         case JANET_EV_TCTAG_BOOLEAN: | ||||
|             janet_schedule(return_value.fiber, janet_wrap_boolean(return_value.argi)); | ||||
|             break; | ||||
|     if (janet_fiber_can_resume(return_value.fiber)) { | ||||
|         switch (return_value.tag) { | ||||
|             default: | ||||
|             case JANET_EV_TCTAG_NIL: | ||||
|                 janet_schedule(return_value.fiber, janet_wrap_nil()); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_INTEGER: | ||||
|                 janet_schedule(return_value.fiber, janet_wrap_integer(return_value.argi)); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_STRING: | ||||
|             case JANET_EV_TCTAG_STRINGF: | ||||
|                 janet_schedule(return_value.fiber, janet_cstringv((const char *) return_value.argp)); | ||||
|                 if (return_value.tag == JANET_EV_TCTAG_STRINGF) janet_free(return_value.argp); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_KEYWORD: | ||||
|                 janet_schedule(return_value.fiber, janet_ckeywordv((const char *) return_value.argp)); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_ERR_STRING: | ||||
|             case JANET_EV_TCTAG_ERR_STRINGF: | ||||
|                 janet_cancel(return_value.fiber, janet_cstringv((const char *) return_value.argp)); | ||||
|                 if (return_value.tag == JANET_EV_TCTAG_STRINGF) janet_free(return_value.argp); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_ERR_KEYWORD: | ||||
|                 janet_cancel(return_value.fiber, janet_ckeywordv((const char *) return_value.argp)); | ||||
|                 break; | ||||
|             case JANET_EV_TCTAG_BOOLEAN: | ||||
|                 janet_schedule(return_value.fiber, janet_wrap_boolean(return_value.argi)); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|     janet_gcunroot(janet_wrap_fiber(return_value.fiber)); | ||||
| } | ||||
| @@ -2120,7 +2200,7 @@ Janet janet_ev_lasterr(void) { | ||||
| } | ||||
| #else | ||||
| Janet janet_ev_lasterr(void) { | ||||
|     return janet_cstringv(strerror(errno)); | ||||
|     return janet_cstringv(janet_strerror(errno)); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -2199,7 +2279,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|         } | ||||
|  | ||||
|         /* fallthrough */ | ||||
|         case JANET_ASYNC_EVENT_USER: { | ||||
|         case JANET_ASYNC_EVENT_INIT: { | ||||
|             int32_t chunk_size = state->bytes_left > JANET_EV_CHUNKSIZE ? JANET_EV_CHUNKSIZE : state->bytes_left; | ||||
|             memset(&(state->overlapped), 0, sizeof(OVERLAPPED)); | ||||
|             int status; | ||||
| @@ -2237,7 +2317,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
|             janet_async_in_flight(fiber); | ||||
|         } | ||||
|         break; | ||||
| #else | ||||
| @@ -2253,7 +2333,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|  | ||||
|     read_more: | ||||
|         case JANET_ASYNC_EVENT_HUP: | ||||
|         case JANET_ASYNC_EVENT_USER: | ||||
|         case JANET_ASYNC_EVENT_INIT: | ||||
|         case JANET_ASYNC_EVENT_READ: { | ||||
|             JanetBuffer *buffer = state->buf; | ||||
|             int32_t bytes_left = state->bytes_left; | ||||
| @@ -2332,9 +2412,8 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) { | ||||
|     JanetFiber *f = janet_vm.root_fiber; | ||||
|     StateRead *state = (StateRead *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, ev_callback_read, sizeof(StateRead)); | ||||
| static JANET_NO_RETURN void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) { | ||||
|     StateRead *state = janet_malloc(sizeof(StateRead)); | ||||
|     state->is_chunk = is_chunked; | ||||
|     state->buf = buf; | ||||
|     state->bytes_left = nbytes; | ||||
| @@ -2345,23 +2424,23 @@ static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t | ||||
| #else | ||||
|     state->flags = flags; | ||||
| #endif | ||||
|     ev_callback_read(f, JANET_ASYNC_EVENT_USER); | ||||
|     janet_async_start(stream, JANET_ASYNC_LISTEN_READ, ev_callback_read, state); | ||||
| } | ||||
|  | ||||
| void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) { | ||||
| JANET_NO_RETURN void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) { | ||||
|     janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_READ, 0); | ||||
| } | ||||
| void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) { | ||||
| JANET_NO_RETURN void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) { | ||||
|     janet_ev_read_generic(stream, buf, nbytes, 1, JANET_ASYNC_READMODE_READ, 0); | ||||
| } | ||||
| #ifdef JANET_NET | ||||
| void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
|     janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_RECV, flags); | ||||
| } | ||||
| void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
|     janet_ev_read_generic(stream, buf, nbytes, 1, JANET_ASYNC_READMODE_RECV, flags); | ||||
| } | ||||
| void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) { | ||||
|     janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_RECVFROM, flags); | ||||
| } | ||||
| #endif | ||||
| @@ -2431,7 +2510,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             return; | ||||
|         } | ||||
|         break; | ||||
|         case JANET_ASYNC_EVENT_USER: { | ||||
|         case JANET_ASYNC_EVENT_INIT: { | ||||
|             /* Begin write */ | ||||
|             int32_t len; | ||||
|             const uint8_t *bytes; | ||||
| @@ -2461,7 +2540,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|                 status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped, NULL); | ||||
|                 if (status) { | ||||
|                     if (WSA_IO_PENDING == WSAGetLastError()) { | ||||
|                         fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
|                         janet_async_in_flight(fiber); | ||||
|                     } else { | ||||
|                         janet_cancel(fiber, janet_ev_lasterr()); | ||||
|                         janet_async_end(fiber); | ||||
| @@ -2486,7 +2565,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|                 status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped); | ||||
|                 if (!status) { | ||||
|                     if (ERROR_IO_PENDING == GetLastError()) { | ||||
|                         fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
|                         janet_async_in_flight(fiber); | ||||
|                     } else { | ||||
|                         janet_cancel(fiber, janet_ev_lasterr()); | ||||
|                         janet_async_end(fiber); | ||||
| @@ -2505,7 +2584,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             janet_cancel(fiber, janet_cstringv("stream hup")); | ||||
|             janet_async_end(fiber); | ||||
|             break; | ||||
|         case JANET_ASYNC_EVENT_USER: | ||||
|         case JANET_ASYNC_EVENT_INIT: | ||||
|         case JANET_ASYNC_EVENT_WRITE: { | ||||
|             int32_t start, len; | ||||
|             const uint8_t *bytes; | ||||
| @@ -2570,10 +2649,8 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) { | ||||
|     JanetFiber *f = janet_vm.root_fiber; | ||||
|     StateWrite *state = (StateWrite *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE, | ||||
|                         ev_callback_write, sizeof(StateWrite)); | ||||
| static JANET_NO_RETURN void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) { | ||||
|     StateWrite *state = janet_malloc(sizeof(StateWrite)); | ||||
|     state->is_buffer = is_buffer; | ||||
|     state->src.buf = buf; | ||||
|     state->dest_abst = dest_abst; | ||||
| @@ -2584,31 +2661,31 @@ static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_ab | ||||
|     state->flags = flags; | ||||
|     state->start = 0; | ||||
| #endif | ||||
|     ev_callback_write(f, JANET_ASYNC_EVENT_USER); | ||||
|     janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, ev_callback_write, state); | ||||
| } | ||||
|  | ||||
| void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) { | ||||
| JANET_NO_RETURN void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) { | ||||
|     janet_ev_write_generic(stream, buf, NULL, JANET_ASYNC_WRITEMODE_WRITE, 1, 0); | ||||
| } | ||||
|  | ||||
| void janet_ev_write_string(JanetStream *stream, JanetString str) { | ||||
| JANET_NO_RETURN void janet_ev_write_string(JanetStream *stream, JanetString str) { | ||||
|     janet_ev_write_generic(stream, (void *) str, NULL, JANET_ASYNC_WRITEMODE_WRITE, 0, 0); | ||||
| } | ||||
|  | ||||
| #ifdef JANET_NET | ||||
| void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags) { | ||||
|     janet_ev_write_generic(stream, buf, NULL, JANET_ASYNC_WRITEMODE_SEND, 1, flags); | ||||
| } | ||||
|  | ||||
| void janet_ev_send_string(JanetStream *stream, JanetString str, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_send_string(JanetStream *stream, JanetString str, int flags) { | ||||
|     janet_ev_write_generic(stream, (void *) str, NULL, JANET_ASYNC_WRITEMODE_SEND, 0, flags); | ||||
| } | ||||
|  | ||||
| void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags) { | ||||
|     janet_ev_write_generic(stream, buf, dest, JANET_ASYNC_WRITEMODE_SENDTO, 1, flags); | ||||
| } | ||||
|  | ||||
| void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) { | ||||
| JANET_NO_RETURN void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) { | ||||
|     janet_ev_write_generic(stream, (void *) str, dest, JANET_ASYNC_WRITEMODE_SENDTO, 0, flags); | ||||
| } | ||||
| #endif | ||||
| @@ -2941,10 +3018,15 @@ JANET_CORE_FN(cfun_ev_sleep, | ||||
|  | ||||
| JANET_CORE_FN(cfun_ev_deadline, | ||||
|               "(ev/deadline sec &opt tocancel tocheck)", | ||||
|               "Set a deadline for a fiber `tocheck`. If `tocheck` is not finished after `sec` seconds, " | ||||
|               "`tocancel` will be canceled as with `ev/cancel`. " | ||||
|               "If `tocancel` and `tocheck` are not given, they default to `(fiber/root)` and " | ||||
|               "`(fiber/current)` respectively. Returns `tocancel`.") { | ||||
|               "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); | ||||
|     double sec = janet_getnumber(argv, 0); | ||||
|     JanetFiber *tocancel = janet_optfiber(argv, argc, 1, janet_vm.root_fiber); | ||||
| @@ -2999,7 +3081,6 @@ JANET_CORE_FN(janet_cfun_stream_read, | ||||
|         if (to != INFINITY) janet_addtimeout(to); | ||||
|         janet_ev_read(stream, buffer, n); | ||||
|     } | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_cfun_stream_chunk, | ||||
| @@ -3014,7 +3095,6 @@ JANET_CORE_FN(janet_cfun_stream_chunk, | ||||
|     double to = janet_optnumber(argv, argc, 3, INFINITY); | ||||
|     if (to != INFINITY) janet_addtimeout(to); | ||||
|     janet_ev_readchunk(stream, buffer, n); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_cfun_stream_write, | ||||
| @@ -3034,7 +3114,6 @@ JANET_CORE_FN(janet_cfun_stream_write, | ||||
|         if (to != INFINITY) janet_addtimeout(to); | ||||
|         janet_ev_write_string(stream, bytes.bytes); | ||||
|     } | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| static int mutexgc(void *p, size_t size) { | ||||
|   | ||||
| @@ -76,4 +76,6 @@ | ||||
| #define __BSD_VISIBLE 1 | ||||
| #endif | ||||
|  | ||||
| #define _FILE_OFFSET_BITS 64 | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -239,8 +239,8 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) { | ||||
|                                          fiber->data + tuplehead, | ||||
|                                          oldtop - tuplehead) | ||||
|                                      : janet_wrap_tuple(janet_tuple_n( | ||||
|                                                  fiber->data + tuplehead, | ||||
|                                                  oldtop - tuplehead)); | ||||
|                                              fiber->data + tuplehead, | ||||
|                                              oldtop - tuplehead)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -370,8 +370,8 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) { | ||||
|                                          fiber->data + tuplehead, | ||||
|                                          fiber->stacktop - tuplehead) | ||||
|                                      : janet_wrap_tuple(janet_tuple_n( | ||||
|                                                  fiber->data + tuplehead, | ||||
|                                                  fiber->stacktop - tuplehead)); | ||||
|                                              fiber->data + tuplehead, | ||||
|                                              fiber->stacktop - tuplehead)); | ||||
|         } | ||||
|         stacksize = tuplehead - fiber->stackstart + 1; | ||||
|     } else { | ||||
| @@ -662,7 +662,7 @@ JANET_CORE_FN(cfun_fiber_can_resume, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_fiber_last_value, | ||||
|               "(fiber/last-value)", | ||||
|               "(fiber/last-value fiber)", | ||||
|               "Get the last value returned or signaled from the fiber.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     JanetFiber *fiber = janet_getfiber(argv, 0); | ||||
|   | ||||
| @@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) { | ||||
|  | ||||
| static void it_s64_tostring(void *p, JanetBuffer *buffer) { | ||||
|     char str[32]; | ||||
|     sprintf(str, "%" PRId64, *((int64_t *)p)); | ||||
|     snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p)); | ||||
|     janet_buffer_push_cstring(buffer, str); | ||||
| } | ||||
|  | ||||
| static void it_u64_tostring(void *p, JanetBuffer *buffer) { | ||||
|     char str[32]; | ||||
|     sprintf(str, "%" PRIu64, *((uint64_t *)p)); | ||||
|     snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p)); | ||||
|     janet_buffer_push_cstring(buffer, str); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -41,6 +41,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx); | ||||
| static void *io_file_unmarshal(JanetMarshalContext *ctx); | ||||
| static Janet io_file_next(void *p, Janet key); | ||||
|  | ||||
| #ifdef JANET_WINDOWS | ||||
| #define ftell _ftelli64 | ||||
| #define fseek _fseeki64 | ||||
| #endif | ||||
|  | ||||
| const JanetAbstractType janet_file_type = { | ||||
|     "core/file", | ||||
|     cfun_io_gc, | ||||
| @@ -126,7 +131,7 @@ JANET_CORE_FN(cfun_io_temp, | ||||
|     // XXX use mkostemp when we can to avoid CLOEXEC race. | ||||
|     FILE *tmp = tmpfile(); | ||||
|     if (!tmp) | ||||
|         janet_panicf("unable to create temporary file - %s", strerror(errno)); | ||||
|         janet_panicf("unable to create temporary file - %s", janet_strerror(errno)); | ||||
|     return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY); | ||||
| } | ||||
|  | ||||
| @@ -168,7 +173,7 @@ JANET_CORE_FN(cfun_io_fopen, | ||||
|         } | ||||
|     } | ||||
|     return f ? janet_makefile(f, flags) | ||||
|            : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil()) | ||||
|            : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil()) | ||||
|            : janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -337,7 +342,7 @@ JANET_CORE_FN(cfun_io_fseek, | ||||
|     JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); | ||||
|     if (iof->flags & JANET_FILE_CLOSED) | ||||
|         janet_panic("file is closed"); | ||||
|     long int offset = 0; | ||||
|     int64_t offset = 0; | ||||
|     int whence = SEEK_CUR; | ||||
|     if (argc >= 2) { | ||||
|         const uint8_t *whence_sym = janet_getkeyword(argv, 1); | ||||
| @@ -351,7 +356,7 @@ JANET_CORE_FN(cfun_io_fseek, | ||||
|             janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]); | ||||
|         } | ||||
|         if (argc == 3) { | ||||
|             offset = (long) janet_getinteger64(argv, 2); | ||||
|             offset = (int64_t) janet_getinteger64(argv, 2); | ||||
|         } | ||||
|     } | ||||
|     if (fseek(iof->file, offset, whence)) janet_panic("error seeking file"); | ||||
| @@ -365,7 +370,7 @@ JANET_CORE_FN(cfun_io_ftell, | ||||
|     JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type); | ||||
|     if (iof->flags & JANET_FILE_CLOSED) | ||||
|         janet_panic("file is closed"); | ||||
|     long pos = ftell(iof->file); | ||||
|     int64_t pos = ftell(iof->file); | ||||
|     if (pos == -1) janet_panic("error getting position in file"); | ||||
|     return janet_wrap_number((double)pos); | ||||
| } | ||||
|   | ||||
| @@ -185,6 +185,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags); | ||||
| /* Prevent stack overflows */ | ||||
| #define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow") | ||||
|  | ||||
| /* Quick check if a fiber cannot be marshalled. This is will | ||||
|  * have no false positives, but may have false negatives. */ | ||||
| static int fiber_cannot_be_marshalled(JanetFiber *fiber) { | ||||
|     if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1; | ||||
|     int32_t i = fiber->frame; | ||||
|     while (i > 0) { | ||||
|         JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE); | ||||
|         if (!frame->func) return 1; /* has cfunction on stack */ | ||||
|         i = frame->prevframe; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Marshal a function env */ | ||||
| static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) { | ||||
|     MARSH_STACKCHECK; | ||||
| @@ -197,7 +210,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) { | ||||
|     } | ||||
|     janet_env_valid(env); | ||||
|     janet_v_push(st->seen_envs, env); | ||||
|     if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) { | ||||
|  | ||||
|     /* Special case for early detachment */ | ||||
|     if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) { | ||||
|         pushint(st, 0); | ||||
|         pushint(st, env->length); | ||||
|         Janet *values = env->as.fiber->data + env->offset; | ||||
| @@ -328,7 +343,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) { | ||||
|     while (i > 0) { | ||||
|         JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE); | ||||
|         if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV; | ||||
|         if (!frame->func) janet_panic("cannot marshal fiber with c stackframe"); | ||||
|         if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc)); | ||||
|         pushint(st, frame->flags); | ||||
|         pushint(st, frame->prevframe); | ||||
|         int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode); | ||||
|   | ||||
| @@ -349,6 +349,26 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)", | ||||
|     return janet_wrap_number(janet_lcm(x, y)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)", | ||||
|               "Returns a tuple of (mantissa, exponent) from number.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     double x = janet_getnumber(argv, 0); | ||||
|     int exp; | ||||
|     x = frexp(x, &exp); | ||||
|     Janet *result = janet_tuple_begin(2); | ||||
|     result[0] = janet_wrap_number(x); | ||||
|     result[1] = janet_wrap_number((double) exp); | ||||
|     return janet_wrap_tuple(janet_tuple_end(result)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)", | ||||
|               "Creates a new number from a mantissa and an exponent.") { | ||||
|     janet_fixarity(argc, 2); | ||||
|     double x = janet_getnumber(argv, 0); | ||||
|     int32_t y = janet_getinteger(argv, 1); | ||||
|     return janet_wrap_number(ldexp(x, y)); | ||||
| } | ||||
|  | ||||
| /* Module entry point */ | ||||
| void janet_lib_math(JanetTable *env) { | ||||
|     JanetRegExt math_cfuns[] = { | ||||
| @@ -395,6 +415,8 @@ void janet_lib_math(JanetTable *env) { | ||||
|         JANET_CORE_REG("math/next", janet_nextafter), | ||||
|         JANET_CORE_REG("math/gcd", janet_cfun_gcd), | ||||
|         JANET_CORE_REG("math/lcm", janet_cfun_lcm), | ||||
|         JANET_CORE_REG("math/frexp", janet_cfun_frexp), | ||||
|         JANET_CORE_REG("math/ldexp", janet_cfun_ldexp), | ||||
|         JANET_REG_END | ||||
|     }; | ||||
|     janet_core_cfuns_ext(env, NULL, math_cfuns); | ||||
|   | ||||
| @@ -79,12 +79,20 @@ const JanetAbstractType janet_address_type = { | ||||
|  | ||||
| /* maximum number of bytes in a socket address host (post name resolution) */ | ||||
| #ifdef JANET_WINDOWS | ||||
| #ifdef JANET_NO_IPV6 | ||||
| #define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1) | ||||
| #else | ||||
| #define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1) | ||||
| #endif | ||||
| typedef unsigned short in_port_t; | ||||
| #else | ||||
| #define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b)) | ||||
| #ifdef JANET_NO_IPV6 | ||||
| #define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1) | ||||
| #else | ||||
| #define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1) | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| static JanetStream *make_stream(JSock handle, uint32_t flags); | ||||
|  | ||||
| @@ -114,16 +122,18 @@ static void janet_net_socknoblock(JSock s) { | ||||
|  | ||||
| /* State machine for async connect */ | ||||
|  | ||||
| typedef struct { | ||||
|     int did_connect; | ||||
| } NetStateConnect; | ||||
|  | ||||
| void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     JanetStream *stream = fiber->ev_stream; | ||||
|     NetStateConnect *state = (NetStateConnect *)fiber->ev_state; | ||||
|     switch (event) { | ||||
|         default: | ||||
|             break; | ||||
| #ifndef JANET_WINDOWS | ||||
|         /* Wait until we have an actual event before checking. | ||||
|          * Windows doesn't support async connect with this, just try immediately.*/ | ||||
|         case JANET_ASYNC_EVENT_INIT: | ||||
| #endif | ||||
|         case JANET_ASYNC_EVENT_DEINIT: | ||||
|             return; | ||||
|         case JANET_ASYNC_EVENT_CLOSE: | ||||
|             janet_cancel(fiber, janet_cstringv("stream closed")); | ||||
|             janet_async_end(fiber); | ||||
| @@ -140,10 +150,9 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
| #endif | ||||
|     if (r == 0) { | ||||
|         if (res == 0) { | ||||
|             state->did_connect = 1; | ||||
|             janet_schedule(fiber, janet_wrap_abstract(stream)); | ||||
|         } else { | ||||
|             janet_cancel(fiber, janet_cstringv(strerror(res))); | ||||
|             janet_cancel(fiber, janet_cstringv(janet_strerror(res))); | ||||
|             stream->flags |= JANET_STREAM_TOCLOSE; | ||||
|         } | ||||
|     } else { | ||||
| @@ -153,13 +162,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     janet_async_end(fiber); | ||||
| } | ||||
|  | ||||
| static void net_sched_connect(JanetStream *stream) { | ||||
|     JanetFiber *f = janet_vm.root_fiber; | ||||
|     NetStateConnect *state = (NetStateConnect *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, sizeof(NetStateConnect)); | ||||
|     state->did_connect = 0; | ||||
| #ifdef JANET_WINDOWS | ||||
|     net_callback_connect(f, JANET_ASYNC_EVENT_USER); | ||||
| #endif | ||||
| static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) { | ||||
|     janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL); | ||||
| } | ||||
|  | ||||
| /* State machine for accepting connections. */ | ||||
| @@ -229,14 +233,16 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|  | ||||
| JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) { | ||||
|     Janet err; | ||||
|     JanetFiber *f = janet_vm.root_fiber; | ||||
|     NetStateAccept *state = (NetStateAccept *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, sizeof(NetStateAccept)); | ||||
|     NetStateAccept *state = janet_malloc(sizeof(NetStateAccept)); | ||||
|     memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED)); | ||||
|     memset(&state->buf, 0, 1024); | ||||
|     state->function = fun; | ||||
|     state->lstream = stream; | ||||
|     if (net_sched_accept_impl(state, f, &err)) janet_panicv(err); | ||||
|     janet_await(); | ||||
|     if (net_sched_accept_impl(state, janet_root_fiber(), &err)) { | ||||
|         janet_free(state); | ||||
|         janet_panicv(err); | ||||
|     } | ||||
|     janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state); | ||||
| } | ||||
|  | ||||
| static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) { | ||||
| @@ -253,7 +259,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet | ||||
|         int code = WSAGetLastError(); | ||||
|         if (code == WSA_IO_PENDING) { | ||||
|             /* indicates io is happening async */ | ||||
|             fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
|             janet_async_in_flight(fiber); | ||||
|             return 0; | ||||
|         } | ||||
|         *err = janet_ev_lasterr(); | ||||
| @@ -282,7 +288,7 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             janet_schedule(fiber, janet_wrap_nil()); | ||||
|             janet_async_end(fiber); | ||||
|             return; | ||||
|         case JANET_ASYNC_EVENT_USER: | ||||
|         case JANET_ASYNC_EVENT_INIT: | ||||
|         case JANET_ASYNC_EVENT_READ: { | ||||
| #if defined(JANET_LINUX) | ||||
|             JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC); | ||||
| @@ -310,11 +316,11 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
| } | ||||
|  | ||||
| JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) { | ||||
|     JanetFiber *f = janet_vm.root_fiber; | ||||
|     NetStateAccept *state = (NetStateAccept *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, sizeof(NetStateAccept)); | ||||
|     NetStateAccept *state = janet_malloc(sizeof(NetStateAccept)); | ||||
|     memset(state, 0, sizeof(NetStateAccept)); | ||||
|     state->function = fun; | ||||
|     net_callback_accept(f, JANET_ASYNC_EVENT_USER); | ||||
|     janet_await(); | ||||
|     if (fun) janet_stream_level_triggered(stream); | ||||
|     janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state); | ||||
| } | ||||
|  | ||||
| #endif | ||||
| @@ -570,7 +576,6 @@ JANET_CORE_FN(cfun_net_connect, | ||||
|     } | ||||
|  | ||||
|     net_sched_connect(stream); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| static const char *serverify_socket(JSock sfd) { | ||||
| @@ -741,6 +746,7 @@ static Janet janet_so_getname(const void *sa_any) { | ||||
|             Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))}; | ||||
|             return janet_wrap_tuple(janet_tuple_n(pair, 2)); | ||||
|         } | ||||
| #ifndef JANET_NO_IPV6 | ||||
|         case AF_INET6: { | ||||
|             const struct sockaddr_in6 *sai6 = sa_any; | ||||
|             if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) { | ||||
| @@ -749,6 +755,7 @@ static Janet janet_so_getname(const void *sa_any) { | ||||
|             Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))}; | ||||
|             return janet_wrap_tuple(janet_tuple_n(pair, 2)); | ||||
|         } | ||||
| #endif | ||||
| #ifndef JANET_WINDOWS | ||||
|         case AF_UNIX: { | ||||
|             const struct sockaddr_un *sun = sa_any; | ||||
| @@ -815,6 +822,7 @@ JANET_CORE_FN(cfun_stream_accept_loop, | ||||
|     JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type); | ||||
|     janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET); | ||||
|     JanetFunction *fun = janet_getfunction(argv, 1); | ||||
|     if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument"); | ||||
|     janet_sched_accept(stream, fun); | ||||
| } | ||||
|  | ||||
| @@ -851,7 +859,6 @@ JANET_CORE_FN(cfun_stream_read, | ||||
|         if (to != INFINITY) janet_addtimeout(to); | ||||
|         janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL); | ||||
|     } | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_stream_chunk, | ||||
| @@ -866,7 +873,6 @@ JANET_CORE_FN(cfun_stream_chunk, | ||||
|     double to = janet_optnumber(argv, argc, 3, INFINITY); | ||||
|     if (to != INFINITY) janet_addtimeout(to); | ||||
|     janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_stream_recv_from, | ||||
| @@ -881,7 +887,6 @@ JANET_CORE_FN(cfun_stream_recv_from, | ||||
|     double to = janet_optnumber(argv, argc, 3, INFINITY); | ||||
|     if (to != INFINITY) janet_addtimeout(to); | ||||
|     janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_stream_write, | ||||
| @@ -901,7 +906,6 @@ JANET_CORE_FN(cfun_stream_write, | ||||
|         if (to != INFINITY) janet_addtimeout(to); | ||||
|         janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL); | ||||
|     } | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_stream_send_to, | ||||
| @@ -922,7 +926,6 @@ JANET_CORE_FN(cfun_stream_send_to, | ||||
|         if (to != INFINITY) janet_addtimeout(to); | ||||
|         janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL); | ||||
|     } | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_stream_flush, | ||||
| @@ -956,8 +959,10 @@ static const struct sockopt_type sockopt_type_list[] = { | ||||
|     { "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER }, | ||||
|     { "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER }, | ||||
|     { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER }, | ||||
| #ifndef JANET_NO_IPV6 | ||||
|     { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER }, | ||||
|     { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER }, | ||||
| #endif | ||||
|     { NULL, 0, 0, JANET_POINTER } | ||||
| }; | ||||
|  | ||||
| @@ -994,7 +999,9 @@ JANET_CORE_FN(cfun_net_setsockopt, | ||||
|     union { | ||||
|         int v_int; | ||||
|         struct ip_mreq v_mreq; | ||||
| #ifndef JANET_NO_IPV6 | ||||
|         struct ipv6_mreq v_mreq6; | ||||
| #endif | ||||
|     } val; | ||||
|  | ||||
|     void *optval = (void *)&val; | ||||
| @@ -1012,12 +1019,14 @@ JANET_CORE_FN(cfun_net_setsockopt, | ||||
|         val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY); | ||||
|         inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr); | ||||
|         optlen = sizeof(val.v_mreq); | ||||
| #ifndef JANET_NO_IPV6 | ||||
|     } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) { | ||||
|         const char *addr = janet_getcstring(argv, 2); | ||||
|         memset(&val.v_mreq6, 0, sizeof val.v_mreq6); | ||||
|         val.v_mreq6.ipv6mr_interface = 0; | ||||
|         inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr); | ||||
|         optlen = sizeof(val.v_mreq6); | ||||
| #endif | ||||
|     } else { | ||||
|         janet_panicf("invalid socket option type"); | ||||
|     } | ||||
| @@ -1026,7 +1035,7 @@ JANET_CORE_FN(cfun_net_setsockopt, | ||||
|  | ||||
|     int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen); | ||||
|     if (r == -1) { | ||||
|         janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno)); | ||||
|         janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno)); | ||||
|     } | ||||
|  | ||||
|     return janet_wrap_nil(); | ||||
|   | ||||
							
								
								
									
										312
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										312
									
								
								src/core/os.c
									
									
									
									
									
								
							| @@ -38,6 +38,7 @@ | ||||
| #include <string.h> | ||||
| #include <sys/stat.h> | ||||
| #include <signal.h> | ||||
| #include <locale.h> | ||||
|  | ||||
| #ifdef JANET_BSD | ||||
| #include <sys/sysctl.h> | ||||
| @@ -229,10 +230,11 @@ JANET_CORE_FN(os_compiler, | ||||
| #undef janet_stringify | ||||
|  | ||||
| JANET_CORE_FN(os_exit, | ||||
|               "(os/exit &opt x)", | ||||
|               "(os/exit &opt x force)", | ||||
|               "Exit from janet with an exit code equal to x. If x is not an integer, " | ||||
|               "the exit with status equal the hash of x.") { | ||||
|     janet_arity(argc, 0, 1); | ||||
|               "the exit with status equal the hash of x. If `force` is truthy will exit immediately and " | ||||
|               "skip cleanup code.") { | ||||
|     janet_arity(argc, 0, 2); | ||||
|     int status; | ||||
|     if (argc == 0) { | ||||
|         status = EXIT_SUCCESS; | ||||
| @@ -242,7 +244,11 @@ JANET_CORE_FN(os_exit, | ||||
|         status = EXIT_FAILURE; | ||||
|     } | ||||
|     janet_deinit(); | ||||
|     exit(status); | ||||
|     if (argc >= 2 && janet_truthy(argv[1])) { | ||||
|         _exit(status); | ||||
|     } else { | ||||
|         exit(status); | ||||
|     } | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -500,8 +506,11 @@ static int proc_get_status(JanetProc *proc) { | ||||
|         status = WEXITSTATUS(status); | ||||
|     } else if (WIFSTOPPED(status)) { | ||||
|         status = WSTOPSIG(status) + 128; | ||||
|     } else { | ||||
|     } else if (WIFSIGNALED(status)) { | ||||
|         status = WTERMSIG(status) + 128; | ||||
|     } else { | ||||
|         /* Could possibly return -1 but for now, just panic */ | ||||
|         janet_panicf("Undefined status code for process termination, %d.", status); | ||||
|     } | ||||
|     return status; | ||||
| } | ||||
| @@ -529,7 +538,9 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) { | ||||
|             JanetString s = janet_formatc("command failed with non-zero exit code %d", status); | ||||
|             janet_cancel(args.fiber, janet_wrap_string(s)); | ||||
|         } else { | ||||
|             janet_schedule(args.fiber, janet_wrap_integer(status)); | ||||
|             if (janet_fiber_can_resume(args.fiber)) { | ||||
|                 janet_schedule(args.fiber, janet_wrap_integer(status)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -611,7 +622,11 @@ os_proc_wait_impl(JanetProc *proc) { | ||||
|  | ||||
| JANET_CORE_FN(os_proc_wait, | ||||
|               "(os/proc-wait proc)", | ||||
|               "Block until the subprocess completes. Returns the subprocess return code.") { | ||||
|               "Suspend the current fiber until the subprocess completes. Returns the subprocess return code. " | ||||
|               "os/proc-wait cannot be called twice on the same process. If `ev/with-deadline` cancels `os/proc-wait` " | ||||
|               "with an error or os/proc-wait is cancelled with any error caused by anything else, os/proc-wait still " | ||||
|               "finishes in the background. Only after os/proc-wait finishes, a process is cleaned up by the operating " | ||||
|               "system. Thus, a process becomes a zombie process if os/proc-wait is not called.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); | ||||
| #ifdef JANET_EV | ||||
| @@ -640,7 +655,7 @@ static const struct keyword_signal signal_keywords[] = { | ||||
| #ifdef SIGTERM | ||||
|     {"term", SIGTERM}, | ||||
| #endif | ||||
| #ifdef SIGARLM | ||||
| #ifdef SIGALRM | ||||
|     {"alrm", SIGALRM}, | ||||
| #endif | ||||
| #ifdef SIGHUP | ||||
| @@ -722,10 +737,11 @@ static int get_signal_kw(const Janet *argv, int32_t n) { | ||||
| JANET_CORE_FN(os_proc_kill, | ||||
|               "(os/proc-kill proc &opt wait signal)", | ||||
|               "Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process " | ||||
|               "handle on windows. If `wait` is truthy, will wait for the process to finish and " | ||||
|               "returns the exit code. Otherwise, returns `proc`. If signal is specified send it instead." | ||||
|               "Signal keywords are named after their C counterparts but in lowercase with the leading " | ||||
|               "`SIG` stripped. Signals are ignored on windows.") { | ||||
|               "handle on windows. If os/proc-wait already finished for proc, os/proc-kill raises an error. After " | ||||
|               "sending signal to proc, if `wait` is truthy, will wait for the process to finish and return the exit " | ||||
|               "code by calling os/proc-wait. Otherwise, returns `proc`. If signal is specified, send it instead. " | ||||
|               "Signal keywords are named after their C counterparts but in lowercase with the leading `SIG` stripped. " | ||||
|               "Signals are ignored on windows.") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|     JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); | ||||
|     if (proc->flags & JANET_PROC_WAITED) { | ||||
| @@ -746,7 +762,7 @@ JANET_CORE_FN(os_proc_kill, | ||||
|     } | ||||
|     int status = kill(proc->pid, signal == -1 ? SIGKILL : signal); | ||||
|     if (status) { | ||||
|         janet_panic(strerror(errno)); | ||||
|         janet_panic(janet_strerror(errno)); | ||||
|     } | ||||
| #endif | ||||
|     /* After killing process we wait on it. */ | ||||
| @@ -764,8 +780,9 @@ JANET_CORE_FN(os_proc_kill, | ||||
|  | ||||
| JANET_CORE_FN(os_proc_close, | ||||
|               "(os/proc-close proc)", | ||||
|               "Wait on a process if it has not been waited on, and close pipes created by `os/spawn` " | ||||
|               "if they have not been closed. Returns nil.") { | ||||
|               "Close pipes created by `os/spawn` if they have not been closed. Then, if os/proc-wait was not already " | ||||
|               "called on proc, os/proc-wait is called on it, and it returns the exit code returned by os/proc-wait. " | ||||
|               "Otherwise, returns nil.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); | ||||
| #ifdef JANET_EV | ||||
| @@ -875,8 +892,9 @@ JANET_CORE_FN(os_sigaction, | ||||
|     } | ||||
|     struct sigaction action; | ||||
|     sigset_t mask; | ||||
|     sigfillset(&mask); | ||||
|     sigaddset(&mask, sig); | ||||
|     memset(&action, 0, sizeof(action)); | ||||
|     action.sa_flags |= SA_RESTART; | ||||
|     if (can_interrupt) { | ||||
| #ifdef JANET_NO_INTERPRETER_INTERRUPT | ||||
|         janet_panic("interpreter interrupt not enabled"); | ||||
| @@ -1081,11 +1099,18 @@ static JanetFile *get_stdio_for_handle(JanetHandle handle, void *orig, int iswri | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
| typedef enum { | ||||
|     JANET_EXECUTE_EXECUTE, | ||||
|     JANET_EXECUTE_SPAWN, | ||||
|     JANET_EXECUTE_EXEC | ||||
| } JanetExecuteMode; | ||||
|  | ||||
| static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); | ||||
|     janet_arity(argc, 1, 3); | ||||
|  | ||||
|     /* Get flags */ | ||||
|     int is_spawn = mode == JANET_EXECUTE_SPAWN; | ||||
|     uint64_t flags = 0; | ||||
|     if (argc > 1) { | ||||
|         flags = janet_getflags(argv, 1, "epxd"); | ||||
| @@ -1109,7 +1134,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
|     int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0; | ||||
|  | ||||
|     /* Get optional redirections */ | ||||
|     if (argc > 2) { | ||||
|     if (argc > 2 && (mode != JANET_EXECUTE_EXEC)) { | ||||
|         JanetDictView tab = janet_getdictionary(argv, 2); | ||||
|         Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in")); | ||||
|         Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out")); | ||||
| @@ -1230,12 +1255,32 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
|      * of posix_spawn would modify the argv array passed in. */ | ||||
|     char *const *cargv = (char *const *)child_argv; | ||||
|  | ||||
|     /* Use posix_spawn to spawn new process */ | ||||
|  | ||||
|     if (use_environ) { | ||||
|         janet_lock_environ(); | ||||
|     } | ||||
|  | ||||
|     /* exec mode */ | ||||
|     if (mode == JANET_EXECUTE_EXEC) { | ||||
| #ifdef JANET_WINDOWS | ||||
|         janet_panic("not supported on windows"); | ||||
| #else | ||||
|         int status; | ||||
|         if (!use_environ) { | ||||
|             environ = envp; | ||||
|         } | ||||
|         do { | ||||
|             if (janet_flag_at(flags, 1)) { | ||||
|                 status = execvp(cargv[0], cargv); | ||||
|             } else { | ||||
|                 status = execv(cargv[0], cargv); | ||||
|             } | ||||
|         } while (status == -1 && errno == EINTR); | ||||
|         janet_panicf("%p: %s", cargv[0], janet_strerror(errno ? errno : ENOENT)); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     /* Use posix_spawn to spawn new process */ | ||||
|  | ||||
|     /* Posix spawn setup */ | ||||
|     posix_spawn_file_actions_t actions; | ||||
|     posix_spawn_file_actions_init(&actions); | ||||
| @@ -1287,7 +1332,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
|     os_execute_cleanup(envp, child_argv); | ||||
|     if (status) { | ||||
|         /* correct for macos bug where errno is not set */ | ||||
|         janet_panicf("%p: %s", argv[0], strerror(errno ? errno : ENOENT)); | ||||
|         janet_panicf("%p: %s", argv[0], janet_strerror(errno ? errno : ENOENT)); | ||||
|     } | ||||
|  | ||||
| #endif | ||||
| @@ -1342,22 +1387,63 @@ JANET_CORE_FN(os_execute, | ||||
|               "* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n" | ||||
|               "`env` is a table or struct mapping environment variables to values. It can also " | ||||
|               "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. " | ||||
|               "These arguments should be core/file values. " | ||||
|               "Returns the exit status of the program.") { | ||||
|     return os_execute_impl(argc, argv, 0); | ||||
|               ":in, :out, and :err should be core/file values or core/stream values. core/file values and core/stream " | ||||
|               "values passed to :in, :out, and :err should be closed manually because os/execute doesn't close them. " | ||||
|               "Returns the exit code of the program.") { | ||||
|     return os_execute_impl(argc, argv, JANET_EXECUTE_EXECUTE); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_spawn, | ||||
|               "(os/spawn args &opt flags env)", | ||||
|               "Execute a program on the system and return a handle to the process. Otherwise, takes the " | ||||
|               "same arguments as `os/execute`. Does not wait for the process. " | ||||
|               "For each of the :in, :out, and :err keys to the `env` argument, one " | ||||
|               "can also pass in the keyword `:pipe` " | ||||
|               "to get streams for standard IO of the subprocess that can be read from and written to. " | ||||
|               "The returned value `proc` has the fields :in, :out, :err, :return-code, and " | ||||
|               "the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the " | ||||
|               "subprocess or `(os/proc-kill proc)`.") { | ||||
|     return os_execute_impl(argc, argv, 1); | ||||
|               "same arguments as `os/execute`. Does not wait for the process. For each of the :in, :out, and :err keys " | ||||
|               "of the `env` argument, one can also pass in the keyword `:pipe` to get streams for standard IO of the " | ||||
|               "subprocess that can be read from and written to. The returned value `proc` has the fields :in, :out, " | ||||
|               ":err, and the additional field :pid on unix-like platforms. `(os/proc-wait proc)` must be called to " | ||||
|               "rejoin the subprocess. After `(os/proc-wait proc)` finishes, proc gains a new field, :return-code. " | ||||
|               "If :x flag is passed to os/spawn, non-zero exit code will cause os/proc-wait to raise an error. " | ||||
|               "If pipe streams created with :pipe keyword are not closed in time, janet can run out of file " | ||||
|               "descriptors. They can be closed individually, or `os/proc-close` can close all pipe streams on proc. " | ||||
|               "If pipe streams aren't read before `os/proc-wait` finishes, then pipe buffers become full, and the " | ||||
|               "process cannot finish because the process cannot print more on pipe buffers which are already full. " | ||||
|               "If the process cannot finish, os/proc-wait cannot finish, either.") { | ||||
|     return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_posix_exec, | ||||
|               "(os/posix-exec args &opt flags env)", | ||||
|               "Use the execvpe or execve system calls to replace the current process with an interface similar to os/execute. " | ||||
|               "Hoever, instead of creating a subprocess, the current process is replaced. Is not supported on windows, and " | ||||
|               "does not allow redirection of stdio.") { | ||||
|     return os_execute_impl(argc, argv, JANET_EXECUTE_EXEC); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_posix_fork, | ||||
|               "(os/posix-fork)", | ||||
|               "Make a `fork` system call and create a new process. Return nil if in the new process, otherwise a core/process object (as returned by os/spawn). " | ||||
|               "Not supported on all systems (POSIX only).") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); | ||||
|     janet_fixarity(argc, 0); | ||||
|     (void) argv; | ||||
| #ifdef JANET_WINDOWS | ||||
|     janet_panic("not supported"); | ||||
| #else | ||||
|     pid_t result; | ||||
|     do { | ||||
|         result = fork(); | ||||
|     } while (result == -1 && errno == EINTR); | ||||
|     if (result == -1) { | ||||
|         janet_panic(janet_strerror(errno)); | ||||
|     } | ||||
|     if (result) { | ||||
|         JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc)); | ||||
|         memset(proc, 0, sizeof(JanetProc)); | ||||
|         proc->pid = result; | ||||
|         proc->flags = JANET_PROC_ALLOW_ZOMBIE; | ||||
|         return janet_wrap_abstract(proc); | ||||
|     } | ||||
|     return janet_wrap_nil(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifdef JANET_EV | ||||
| @@ -1479,34 +1565,51 @@ JANET_CORE_FN(os_time, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_clock, | ||||
|               "(os/clock &opt source)", | ||||
|               "Return the number of whole + fractional seconds of the requested clock source.\n\n" | ||||
|               "(os/clock &opt source format)", | ||||
|               "Return the current time of the requested clock source.\n\n" | ||||
|               "The `source` argument selects the clock source to use, when not specified the default " | ||||
|               "is `:realtime`:\n" | ||||
|               "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous " | ||||
|               "  jumps in the system time\n" | ||||
|               "- :monotonic: Return the number of whole + fractional seconds since some fixed point in " | ||||
|               "  time. The clock is guaranteed to be non-decreasing in real time.\n" | ||||
|               "- :cputime: Return the CPU time consumed by this process  (i.e. all threads in the process)\n") { | ||||
|               "- :cputime: Return the CPU time consumed by this process  (i.e. all threads in the process)\n" | ||||
|               "The `format` argument selects the type of output, when not specified the default is `:double`:\n" | ||||
|               "- :double: Return the number of seconds + fractional seconds as a double\n" | ||||
|               "- :int: Return the number of seconds as an integer\n" | ||||
|               "- :tuple: Return a 2 integer tuple [seconds, nanoseconds]\n") { | ||||
|     enum JanetTimeSource source; | ||||
|     janet_sandbox_assert(JANET_SANDBOX_HRTIME); | ||||
|     janet_arity(argc, 0, 1); | ||||
|     enum JanetTimeSource source = JANET_TIME_REALTIME; | ||||
|     if (argc == 1) { | ||||
|         JanetKeyword sourcestr = janet_getkeyword(argv, 0); | ||||
|         if (janet_cstrcmp(sourcestr, "realtime") == 0) { | ||||
|             source = JANET_TIME_REALTIME; | ||||
|         } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) { | ||||
|             source = JANET_TIME_MONOTONIC; | ||||
|         } else if (janet_cstrcmp(sourcestr, "cputime") == 0) { | ||||
|             source = JANET_TIME_CPUTIME; | ||||
|         } else { | ||||
|             janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]); | ||||
|         } | ||||
|     janet_arity(argc, 0, 2); | ||||
|  | ||||
|     JanetKeyword sourcestr = janet_optkeyword(argv, argc, 0, (const uint8_t *) "realtime"); | ||||
|     if (janet_cstrcmp(sourcestr, "realtime") == 0) { | ||||
|         source = JANET_TIME_REALTIME; | ||||
|     } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) { | ||||
|         source = JANET_TIME_MONOTONIC; | ||||
|     } else if (janet_cstrcmp(sourcestr, "cputime") == 0) { | ||||
|         source = JANET_TIME_CPUTIME; | ||||
|     } else { | ||||
|         janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]); | ||||
|     } | ||||
|  | ||||
|     struct timespec tv; | ||||
|     if (janet_gettime(&tv, source)) janet_panic("could not get time"); | ||||
|     double dtime = tv.tv_sec + (tv.tv_nsec / 1E9); | ||||
|     return janet_wrap_number(dtime); | ||||
|  | ||||
|     JanetKeyword formatstr = janet_optkeyword(argv, argc, 1, (const uint8_t *) "double"); | ||||
|     if (janet_cstrcmp(formatstr, "double") == 0) { | ||||
|         double dtime = (double)(tv.tv_sec + (tv.tv_nsec / 1E9)); | ||||
|         return janet_wrap_number(dtime); | ||||
|     } else if (janet_cstrcmp(formatstr, "int") == 0) { | ||||
|         return janet_wrap_number((double)(tv.tv_sec)); | ||||
|     } else if (janet_cstrcmp(formatstr, "tuple") == 0) { | ||||
|         Janet tup[2] = {janet_wrap_number((double)tv.tv_sec), | ||||
|                         janet_wrap_number((double)tv.tv_nsec) | ||||
|                        }; | ||||
|         return janet_wrap_tuple(janet_tuple_n(tup, 2)); | ||||
|     } else { | ||||
|         janet_panicf("expected :double, :int, or :tuple, got %v", argv[1]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_sleep, | ||||
| @@ -1542,7 +1645,7 @@ JANET_CORE_FN(os_isatty, | ||||
|     return janet_wrap_boolean(_isatty(fd)); | ||||
| #else | ||||
|     int fd = fileno(f); | ||||
|     if (fd == -1) janet_panic(strerror(errno)); | ||||
|     if (fd == -1) janet_panic(janet_strerror(errno)); | ||||
|     return janet_wrap_boolean(isatty(fd)); | ||||
| #endif | ||||
| } | ||||
| @@ -1777,7 +1880,7 @@ JANET_CORE_FN(os_mktime, | ||||
|     } | ||||
|  | ||||
|     if (t == (time_t) -1) { | ||||
|         janet_panicf("%s", strerror(errno)); | ||||
|         janet_panicf("%s", janet_strerror(errno)); | ||||
|     } | ||||
|  | ||||
|     return janet_wrap_number((double)t); | ||||
| @@ -1789,6 +1892,43 @@ JANET_CORE_FN(os_mktime, | ||||
| #define j_symlink symlink | ||||
| #endif | ||||
|  | ||||
| JANET_CORE_FN(os_setlocale, | ||||
|               "(os/setlocale &opt locale category)", | ||||
|               "Set the system locale, which affects how dates and numbers are formatted. " | ||||
|               "Passing nil to locale will return the current locale. Category can be one of:\n\n" | ||||
|               " * :all (default)\n" | ||||
|               " * :collate\n" | ||||
|               " * :ctype\n" | ||||
|               " * :monetary\n" | ||||
|               " * :numeric\n" | ||||
|               " * :time\n\n" | ||||
|               "Returns the new locale if set successfully, otherwise nil. Note that this will affect " | ||||
|               "other functions such as `os/strftime` and even `printf`.") { | ||||
|     janet_arity(argc, 0, 2); | ||||
|     const char *locale_name = janet_optcstring(argv, argc, 0, NULL); | ||||
|     int category_int = LC_ALL; | ||||
|     if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) { | ||||
|         if (janet_keyeq(argv[1], "all")) { | ||||
|             category_int = LC_ALL; | ||||
|         } else if (janet_keyeq(argv[1], "collate")) { | ||||
|             category_int = LC_COLLATE; | ||||
|         } else if (janet_keyeq(argv[1], "ctype")) { | ||||
|             category_int = LC_CTYPE; | ||||
|         } else if (janet_keyeq(argv[1], "monetary")) { | ||||
|             category_int = LC_MONETARY; | ||||
|         } else if (janet_keyeq(argv[1], "numeric")) { | ||||
|             category_int = LC_NUMERIC; | ||||
|         } else if (janet_keyeq(argv[1], "time")) { | ||||
|             category_int = LC_TIME; | ||||
|         } else { | ||||
|             janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[1]); | ||||
|         } | ||||
|     } | ||||
|     const char *old = setlocale(category_int, locale_name); | ||||
|     if (old == NULL) return janet_wrap_nil(); | ||||
|     return janet_cstringv(old); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_link, | ||||
|               "(os/link oldpath newpath &opt symlink)", | ||||
|               "Create a link at newpath that points to oldpath and returns nil. " | ||||
| @@ -1806,7 +1946,7 @@ JANET_CORE_FN(os_link, | ||||
|     const char *oldpath = janet_getcstring(argv, 0); | ||||
|     const char *newpath = janet_getcstring(argv, 1); | ||||
|     int res = ((argc == 3 && janet_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath); | ||||
|     if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); | ||||
|     if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath); | ||||
|     return janet_wrap_nil(); | ||||
| #endif | ||||
| } | ||||
| @@ -1825,7 +1965,7 @@ JANET_CORE_FN(os_symlink, | ||||
|     const char *oldpath = janet_getcstring(argv, 0); | ||||
|     const char *newpath = janet_getcstring(argv, 1); | ||||
|     int res = j_symlink(oldpath, newpath); | ||||
|     if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath); | ||||
|     if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath); | ||||
|     return janet_wrap_nil(); | ||||
| #endif | ||||
| } | ||||
| @@ -1847,7 +1987,7 @@ JANET_CORE_FN(os_mkdir, | ||||
| #endif | ||||
|     if (res == 0) return janet_wrap_true(); | ||||
|     if (errno == EEXIST) return janet_wrap_false(); | ||||
|     janet_panicf("%s: %s", strerror(errno), path); | ||||
|     janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_rmdir, | ||||
| @@ -1861,7 +2001,7 @@ JANET_CORE_FN(os_rmdir, | ||||
| #else | ||||
|     int res = rmdir(path); | ||||
| #endif | ||||
|     if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); | ||||
|     if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -1876,7 +2016,7 @@ JANET_CORE_FN(os_cd, | ||||
| #else | ||||
|     int res = chdir(path); | ||||
| #endif | ||||
|     if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); | ||||
|     if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -1900,7 +2040,7 @@ JANET_CORE_FN(os_touch, | ||||
|         bufp = NULL; | ||||
|     } | ||||
|     int res = utime(path, bufp); | ||||
|     if (-1 == res) janet_panic(strerror(errno)); | ||||
|     if (-1 == res) janet_panic(janet_strerror(errno)); | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -1910,7 +2050,7 @@ JANET_CORE_FN(os_remove, | ||||
|     janet_fixarity(argc, 1); | ||||
|     const char *path = janet_getcstring(argv, 0); | ||||
|     int status = remove(path); | ||||
|     if (-1 == status) janet_panicf("%s: %s", strerror(errno), path); | ||||
|     if (-1 == status) janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -1929,7 +2069,7 @@ JANET_CORE_FN(os_readlink, | ||||
|     const char *path = janet_getcstring(argv, 0); | ||||
|     ssize_t len = readlink(path, buffer, sizeof buffer); | ||||
|     if (len < 0 || (size_t)len >= sizeof buffer) | ||||
|         janet_panicf("%s: %s", strerror(errno), path); | ||||
|         janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
|     return janet_stringv((const uint8_t *)buffer, len); | ||||
| #endif | ||||
| } | ||||
| @@ -2224,7 +2364,7 @@ JANET_CORE_FN(os_chmod, | ||||
| #else | ||||
|     int res = chmod(path, os_getmode(argv, 1)); | ||||
| #endif | ||||
|     if (-1 == res) janet_panicf("%s: %s", strerror(errno), path); | ||||
|     if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path); | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| @@ -2260,7 +2400,7 @@ JANET_CORE_FN(os_dir, | ||||
|         janet_panicf("path too long: %s", dir); | ||||
|     sprintf(pattern, "%s/*", dir); | ||||
|     intptr_t res = _findfirst(pattern, &afile); | ||||
|     if (-1 == res) janet_panicv(janet_cstringv(strerror(errno))); | ||||
|     if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno))); | ||||
|     do { | ||||
|         if (strcmp(".", afile.name) && strcmp("..", afile.name)) { | ||||
|             janet_array_push(paths, janet_cstringv(afile.name)); | ||||
| @@ -2271,8 +2411,18 @@ JANET_CORE_FN(os_dir, | ||||
|     /* Read directory items with opendir / readdir / closedir */ | ||||
|     struct dirent *dp; | ||||
|     DIR *dfd = opendir(dir); | ||||
|     if (dfd == NULL) janet_panicf("cannot open directory %s", dir); | ||||
|     while ((dp = readdir(dfd)) != NULL) { | ||||
|     if (dfd == NULL) janet_panicf("cannot open directory %s: %s", dir, janet_strerror(errno)); | ||||
|     for (;;) { | ||||
|         errno = 0; | ||||
|         dp = readdir(dfd); | ||||
|         if (dp == NULL) { | ||||
|             if (errno) { | ||||
|                 int olderr = errno; | ||||
|                 closedir(dfd); | ||||
|                 janet_panicf("failed to read directory %s: %s", dir, janet_strerror(olderr)); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { | ||||
|             continue; | ||||
|         } | ||||
| @@ -2292,7 +2442,7 @@ JANET_CORE_FN(os_rename, | ||||
|     const char *dest = janet_getcstring(argv, 1); | ||||
|     int status = rename(src, dest); | ||||
|     if (status) { | ||||
|         janet_panic(strerror(errno)); | ||||
|         janet_panic(janet_strerror(errno)); | ||||
|     } | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
| @@ -2312,7 +2462,7 @@ JANET_CORE_FN(os_realpath, | ||||
| #else | ||||
|     char *dest = realpath(src, NULL); | ||||
| #endif | ||||
|     if (NULL == dest) janet_panicf("%s: %s", strerror(errno), src); | ||||
|     if (NULL == dest) janet_panicf("%s: %s", janet_strerror(errno), src); | ||||
|     Janet ret = janet_cstringv(dest); | ||||
|     janet_free(dest); | ||||
|     return ret; | ||||
| @@ -2336,34 +2486,6 @@ JANET_CORE_FN(os_permission_int, | ||||
|     return janet_wrap_integer(os_get_unix_mode(argv, 0)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_posix_fork, | ||||
|               "(os/posix-fork)", | ||||
|               "Make a `fork` system call and create a new process. Return nil if in the new process, otherwise a core/process object (as returned by os/spawn). " | ||||
|               "Not supported on all systems (POSIX only).") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS); | ||||
|     janet_fixarity(argc, 0); | ||||
|     (void) argv; | ||||
| #ifdef JANET_WINDOWS | ||||
|     janet_panic("not supported"); | ||||
| #else | ||||
|     pid_t result; | ||||
|     do { | ||||
|         result = fork(); | ||||
|     } while (result == -1 && errno == EINTR); | ||||
|     if (result == -1) { | ||||
|         janet_panic(strerror(errno)); | ||||
|     } | ||||
|     if (result) { | ||||
|         JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc)); | ||||
|         memset(proc, 0, sizeof(JanetProc)); | ||||
|         proc->pid = result; | ||||
|         proc->flags = JANET_PROC_ALLOW_ZOMBIE; | ||||
|         return janet_wrap_abstract(proc); | ||||
|     } | ||||
|     return janet_wrap_nil(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifdef JANET_EV | ||||
|  | ||||
| /* | ||||
| @@ -2614,6 +2736,7 @@ void janet_lib_os(JanetTable *env) { | ||||
|         JANET_CORE_REG("os/strftime", os_strftime), | ||||
|         JANET_CORE_REG("os/sleep", os_sleep), | ||||
|         JANET_CORE_REG("os/isatty", os_isatty), | ||||
|         JANET_CORE_REG("os/setlocale", os_setlocale), | ||||
|  | ||||
|         /* env functions */ | ||||
|         JANET_CORE_REG("os/environ", os_environ), | ||||
| @@ -2651,6 +2774,7 @@ void janet_lib_os(JanetTable *env) { | ||||
|         JANET_CORE_REG("os/spawn", os_spawn), | ||||
|         JANET_CORE_REG("os/shell", os_shell), | ||||
|         JANET_CORE_REG("os/posix-fork", os_posix_fork), | ||||
|         JANET_CORE_REG("os/posix-exec", os_posix_exec), | ||||
|         /* no need to sandbox process management if you can't create processes | ||||
|          * (allows for limited functionality if use exposes C-functions to create specific processes) */ | ||||
|         JANET_CORE_REG("os/proc-wait", os_proc_wait), | ||||
|   | ||||
							
								
								
									
										106
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/core/peg.c
									
									
									
									
									
								
							| @@ -39,6 +39,10 @@ | ||||
| typedef struct { | ||||
|     const uint8_t *text_start; | ||||
|     const uint8_t *text_end; | ||||
|     /* text_end can be restricted by some rules, but | ||||
|        outer_text_end will always contain the real end of | ||||
|        input, which we need to generate a line mapping */ | ||||
|     const uint8_t *outer_text_end; | ||||
|     const uint32_t *bytecode; | ||||
|     const Janet *constants; | ||||
|     JanetArray *captures; | ||||
| @@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) { | ||||
|     /* Generate if not made yet */ | ||||
|     if (s->linemaplen < 0) { | ||||
|         int32_t newline_count = 0; | ||||
|         for (const uint8_t *c = s->text_start; c < s->text_end; c++) { | ||||
|         for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) { | ||||
|             if (*c == '\n') newline_count++; | ||||
|         } | ||||
|         int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count); | ||||
|         size_t index = 0; | ||||
|         for (const uint8_t *c = s->text_start; c < s->text_end; c++) { | ||||
|         for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) { | ||||
|             if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start); | ||||
|         } | ||||
|         s->linemaplen = newline_count; | ||||
| @@ -179,7 +183,7 @@ static const uint8_t *peg_rule( | ||||
|     const uint32_t *rule, | ||||
|     const uint8_t *text) { | ||||
| tail: | ||||
|     switch (*rule & 0x1F) { | ||||
|     switch (*rule) { | ||||
|         default: | ||||
|             janet_panic("unexpected opcode"); | ||||
|             return NULL; | ||||
| @@ -482,6 +486,68 @@ tail: | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         case RULE_SUB: { | ||||
|             const uint8_t *text_start = text; | ||||
|             const uint32_t *rule_window = s->bytecode + rule[1]; | ||||
|             const uint32_t *rule_subpattern = s->bytecode + rule[2]; | ||||
|             down1(s); | ||||
|             const uint8_t *window_end = peg_rule(s, rule_window, text); | ||||
|             up1(s); | ||||
|             if (!window_end) { | ||||
|                 return NULL; | ||||
|             } | ||||
|             const uint8_t *saved_end = s->text_end; | ||||
|             s->text_end = window_end; | ||||
|             down1(s); | ||||
|             const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start); | ||||
|             up1(s); | ||||
|             s->text_end = saved_end; | ||||
|  | ||||
|             if (!next_text) { | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             return window_end; | ||||
|         } | ||||
|  | ||||
|         case RULE_SPLIT: { | ||||
|             const uint8_t *saved_end = s->text_end; | ||||
|             const uint32_t *rule_separator = s->bytecode + rule[1]; | ||||
|             const uint32_t *rule_subpattern = s->bytecode + rule[2]; | ||||
|  | ||||
|             const uint8_t *separator_end = NULL; | ||||
|             do { | ||||
|                 const uint8_t *text_start = text; | ||||
|                 CapState cs = cap_save(s); | ||||
|                 down1(s); | ||||
|                 while (text <= s->text_end) { | ||||
|                     separator_end = peg_rule(s, rule_separator, text); | ||||
|                     cap_load(s, cs); | ||||
|                     if (separator_end) { | ||||
|                         break; | ||||
|                     } | ||||
|                     text++; | ||||
|                 } | ||||
|                 up1(s); | ||||
|  | ||||
|                 if (separator_end) { | ||||
|                     s->text_end = text; | ||||
|                     text = separator_end; | ||||
|                 } | ||||
|  | ||||
|                 down1(s); | ||||
|                 const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start); | ||||
|                 up1(s); | ||||
|                 s->text_end = saved_end; | ||||
|  | ||||
|                 if (!subpattern_end) { | ||||
|                     return NULL; | ||||
|                 } | ||||
|             } while (separator_end); | ||||
|  | ||||
|             return s->text_end; | ||||
|         } | ||||
|  | ||||
|         case RULE_REPLACE: | ||||
|         case RULE_MATCHTIME: { | ||||
|             uint32_t tag = rule[3]; | ||||
| @@ -1107,6 +1173,22 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     emit_3(r, RULE_MATCHTIME, subrule, cindex, tag); | ||||
| } | ||||
|  | ||||
| static void spec_sub(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     peg_fixarity(b, argc, 2); | ||||
|     Reserve r = reserve(b, 3); | ||||
|     uint32_t subrule1 = peg_compile1(b, argv[0]); | ||||
|     uint32_t subrule2 = peg_compile1(b, argv[1]); | ||||
|     emit_2(r, RULE_SUB, subrule1, subrule2); | ||||
| } | ||||
|  | ||||
| static void spec_split(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     peg_fixarity(b, argc, 2); | ||||
|     Reserve r = reserve(b, 3); | ||||
|     uint32_t subrule1 = peg_compile1(b, argv[0]); | ||||
|     uint32_t subrule2 = peg_compile1(b, argv[1]); | ||||
|     emit_2(r, RULE_SPLIT, subrule1, subrule2); | ||||
| } | ||||
|  | ||||
| #ifdef JANET_INT_TYPES | ||||
| #define JANET_MAX_READINT_WIDTH 8 | ||||
| #else | ||||
| @@ -1190,6 +1272,8 @@ static const SpecialPair peg_specials[] = { | ||||
|     {"sequence", spec_sequence}, | ||||
|     {"set", spec_set}, | ||||
|     {"some", spec_some}, | ||||
|     {"split", spec_split}, | ||||
|     {"sub", spec_sub}, | ||||
|     {"thru", spec_thru}, | ||||
|     {"to", spec_to}, | ||||
|     {"uint", spec_uint_le}, | ||||
| @@ -1431,7 +1515,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) { | ||||
|         uint32_t instr = bytecode[i]; | ||||
|         uint32_t *rule = bytecode + i; | ||||
|         op_flags[i] |= 0x02; | ||||
|         switch (instr & 0x1F) { | ||||
|         switch (instr) { | ||||
|             case RULE_LITERAL: | ||||
|                 i += 2 + ((rule[1] + 3) >> 2); | ||||
|                 break; | ||||
| @@ -1524,6 +1608,15 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) { | ||||
|                 op_flags[rule[1]] |= 0x01; | ||||
|                 i += 4; | ||||
|                 break; | ||||
|             case RULE_SUB: | ||||
|             case RULE_SPLIT: | ||||
|                 /* [rule, rule] */ | ||||
|                 if (rule[1] >= blen) goto bad; | ||||
|                 if (rule[2] >= blen) goto bad; | ||||
|                 op_flags[rule[1]] |= 0x01; | ||||
|                 op_flags[rule[2]] |= 0x01; | ||||
|                 i += 3; | ||||
|                 break; | ||||
|             case RULE_ERROR: | ||||
|             case RULE_DROP: | ||||
|             case RULE_NOT: | ||||
| @@ -1652,7 +1745,7 @@ typedef struct { | ||||
| static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) { | ||||
|     PegCall ret; | ||||
|     int32_t min = get_replace ? 3 : 2; | ||||
|     janet_arity(argc, get_replace, -1); | ||||
|     janet_arity(argc, min, -1); | ||||
|     if (janet_checktype(argv[0], JANET_ABSTRACT) && | ||||
|             janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) { | ||||
|         ret.peg = janet_unwrap_abstract(argv[0]); | ||||
| @@ -1677,6 +1770,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) { | ||||
|     ret.s.mode = PEG_MODE_NORMAL; | ||||
|     ret.s.text_start = ret.bytes.bytes; | ||||
|     ret.s.text_end = ret.bytes.bytes + ret.bytes.len; | ||||
|     ret.s.outer_text_end = ret.s.text_end; | ||||
|     ret.s.depth = JANET_RECURSION_GUARD; | ||||
|     ret.s.captures = janet_array(0); | ||||
|     ret.s.tagged_captures = janet_array(0); | ||||
| @@ -1771,7 +1865,7 @@ JANET_CORE_FN(cfun_peg_replace_all, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_peg_replace, | ||||
|               "(peg/replace peg repl text &opt start & args)", | ||||
|               "(peg/replace peg subst text &opt start & args)", | ||||
|               "Replace first match of `peg` in `text` with `subst`, returning a new buffer. " | ||||
|               "The peg does not need to make captures to do replacement. " | ||||
|               "If `subst` is a function, it will be called with the " | ||||
|   | ||||
| @@ -31,6 +31,7 @@ | ||||
| #include <string.h> | ||||
| #include <ctype.h> | ||||
| #include <inttypes.h> | ||||
| #include <float.h> | ||||
|  | ||||
| /* Implements a pretty printer for Janet. The pretty printer | ||||
|  * is simple and not that flexible, but fast. */ | ||||
| @@ -38,11 +39,15 @@ | ||||
| /* Temporary buffer size */ | ||||
| #define BUFSIZE 64 | ||||
|  | ||||
| /* Preprocessor hacks */ | ||||
| #define STR_HELPER(x) #x | ||||
| #define STR(x) STR_HELPER(x) | ||||
|  | ||||
| static void number_to_string_b(JanetBuffer *buffer, double x) { | ||||
|     janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2); | ||||
|     const char *fmt = (x == floor(x) && | ||||
|                        x <= JANET_INTMAX_DOUBLE && | ||||
|                        x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g"; | ||||
|                        x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g"); | ||||
|     int count; | ||||
|     if (x == 0.0) { | ||||
|         /* Prevent printing of '-0' */ | ||||
| @@ -374,8 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) { | ||||
|             break; | ||||
|         case JANET_NUMBER: | ||||
|             janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2); | ||||
|             int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x)); | ||||
|             S->buffer->count += count; | ||||
|             double num = janet_unwrap_number(x); | ||||
|             if (isnan(num)) return 1; | ||||
|             if (isinf(num)) return 1; | ||||
|             janet_buffer_dtostr(S->buffer, num); | ||||
|             break; | ||||
|         case JANET_SYMBOL: | ||||
|         case JANET_KEYWORD: | ||||
| @@ -772,6 +779,8 @@ struct FmtMapping { | ||||
| /* Janet uses fixed width integer types for most things, so map | ||||
|  * format specifiers to these fixed sizes */ | ||||
| static const struct FmtMapping format_mappings[] = { | ||||
|     {'D', PRId64}, | ||||
|     {'I', PRIi64}, | ||||
|     {'d', PRId64}, | ||||
|     {'i', PRIi64}, | ||||
|     {'o', PRIo64}, | ||||
| @@ -823,7 +832,7 @@ static const char *scanformat( | ||||
|         if (loc != NULL && *loc != '\0') { | ||||
|             const char *mapping = get_fmt_mapping(*p2++); | ||||
|             size_t len = strlen(mapping); | ||||
|             strcpy(form, mapping); | ||||
|             memcpy(form, mapping, len); | ||||
|             form += len; | ||||
|         } else { | ||||
|             *(form++) = *(p2++); | ||||
| @@ -850,13 +859,19 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) { | ||||
|             c = scanformat(c, form, width, precision); | ||||
|             switch (*c++) { | ||||
|                 case 'c': { | ||||
|                     int n = va_arg(args, long); | ||||
|                     int n = va_arg(args, int); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
|                 case 'd': | ||||
|                 case 'i': { | ||||
|                     int64_t n = va_arg(args, int); | ||||
|                     int64_t n = (int64_t) va_arg(args, int32_t); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
|                 case 'D': | ||||
|                 case 'I': { | ||||
|                     int64_t n = va_arg(args, int64_t); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
| @@ -864,7 +879,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) { | ||||
|                 case 'X': | ||||
|                 case 'o': | ||||
|                 case 'u': { | ||||
|                     uint64_t n = va_arg(args, unsigned int); | ||||
|                     uint64_t n = va_arg(args, uint64_t); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
| @@ -908,7 +923,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) { | ||||
|                     janet_buffer_push_cstring(b, typestr(va_arg(args, Janet))); | ||||
|                     break; | ||||
|                 case 'T': { | ||||
|                     int types = va_arg(args, long); | ||||
|                     int types = va_arg(args, int); | ||||
|                     pushtypes(b, types); | ||||
|                     break; | ||||
|                 } | ||||
| @@ -1017,6 +1032,8 @@ void janet_buffer_format( | ||||
|                                   janet_getinteger(argv, arg)); | ||||
|                     break; | ||||
|                 } | ||||
|                 case 'D': | ||||
|                 case 'I': | ||||
|                 case 'd': | ||||
|                 case 'i': { | ||||
|                     int64_t n = janet_getinteger64(argv, arg); | ||||
|   | ||||
| @@ -32,6 +32,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char | ||||
|     int errflags = 0, done = 0; | ||||
|     int32_t index = 0; | ||||
|     Janet ret = janet_wrap_nil(); | ||||
|     JanetFiber *fiber = NULL; | ||||
|     const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL; | ||||
|  | ||||
|     if (where) janet_gcroot(janet_wrap_string(where)); | ||||
| @@ -47,7 +48,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char | ||||
|             JanetCompileResult cres = janet_compile(form, env, where); | ||||
|             if (cres.status == JANET_COMPILE_OK) { | ||||
|                 JanetFunction *f = janet_thunk(cres.funcdef); | ||||
|                 JanetFiber *fiber = janet_fiber(f, 64, 0, NULL); | ||||
|                 fiber = janet_fiber(f, 64, 0, NULL); | ||||
|                 fiber->env = env; | ||||
|                 JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret); | ||||
|                 if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) { | ||||
| @@ -112,9 +113,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char | ||||
| #ifdef JANET_EV | ||||
|     /* Enter the event loop if we are not already in it */ | ||||
|     if (janet_vm.stackn == 0) { | ||||
|         janet_gcroot(ret); | ||||
|         if (fiber) { | ||||
|             janet_gcroot(janet_wrap_fiber(fiber)); | ||||
|         } | ||||
|         janet_loop(); | ||||
|         janet_gcunroot(ret); | ||||
|         if (fiber) { | ||||
|             janet_gcunroot(janet_wrap_fiber(fiber)); | ||||
|             ret = fiber->last_value; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|     if (out) *out = ret; | ||||
|   | ||||
| @@ -149,7 +149,7 @@ static int destructure(JanetCompiler *c, | ||||
|                        JanetTable *attr) { | ||||
|     switch (janet_type(left)) { | ||||
|         default: | ||||
|             janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left)); | ||||
|             janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left)); | ||||
|             return 1; | ||||
|         case JANET_SYMBOL: | ||||
|             /* Leaf, assign right to left */ | ||||
| @@ -531,17 +531,11 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
| } | ||||
|  | ||||
| /* Check if a form matches the pattern (= nil _) or (not= nil _) */ | ||||
| static int janetc_check_nil_form(JanetFopts opts, Janet x, Janet *capture, uint32_t fun_tag) { | ||||
| static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) { | ||||
|     if (!janet_checktype(x, JANET_TUPLE)) return 0; | ||||
|     JanetTuple tup = janet_unwrap_tuple(x); | ||||
|     if (3 != janet_tuple_length(tup)) return 0; | ||||
|     Janet op1 = tup[0]; | ||||
|     if (janet_checktype(op1, JANET_SYMBOL)) { | ||||
|         Janet entry = janet_table_get(opts.compiler->env, op1); | ||||
|         if (janet_checktype(entry, JANET_TABLE)) { | ||||
|             op1 = janet_table_get(janet_unwrap_table(entry), janet_ckeywordv("value")); | ||||
|         } | ||||
|     } | ||||
|     if (!janet_checktype(op1, JANET_FUNCTION)) return 0; | ||||
|     JanetFunction *fun = janet_unwrap_function(op1); | ||||
|     uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG; | ||||
| @@ -601,10 +595,9 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     janetc_scope(&condscope, c, 0, "if"); | ||||
|  | ||||
|     Janet condform = argv[0]; | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_EQ)) { | ||||
|     if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) { | ||||
|         ifnjmp = JOP_JUMP_IF_NOT_NIL; | ||||
|     } | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_NEQ)) { | ||||
|     } else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) { | ||||
|         ifnjmp = JOP_JUMP_IF_NIL; | ||||
|     } | ||||
|  | ||||
| @@ -613,7 +606,11 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     /* Check constant condition. */ | ||||
|     /* TODO: Use type info for more short circuits */ | ||||
|     if (cond.flags & JANET_SLOT_CONSTANT) { | ||||
|         if (!janet_truthy(cond.constant)) { | ||||
|         int swap_condition = 0; | ||||
|         if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1; | ||||
|         if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1; | ||||
|         if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1; | ||||
|         if (swap_condition) { | ||||
|             /* Swap the true and false bodies */ | ||||
|             Janet temp = falsebody; | ||||
|             falsebody = truebody; | ||||
| @@ -808,12 +805,12 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|      * jmpnl or jmpnn instructions. This let's us implement `(each ...)` | ||||
|      * more efficiently. */ | ||||
|     Janet condform = argv[0]; | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_EQ)) { | ||||
|     if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) { | ||||
|         is_nil_form = 1; | ||||
|         ifjmp = JOP_JUMP_IF_NIL; | ||||
|         ifnjmp = JOP_JUMP_IF_NOT_NIL; | ||||
|     } | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_NEQ)) { | ||||
|     if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) { | ||||
|         is_notnil_form = 1; | ||||
|         ifjmp = JOP_JUMP_IF_NOT_NIL; | ||||
|         ifnjmp = JOP_JUMP_IF_NIL; | ||||
| @@ -928,6 +925,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     int structarg = 0; | ||||
|     int allow_extra = 0; | ||||
|     int selfref = 0; | ||||
|     int hasname = 0; | ||||
|     int seenamp = 0; | ||||
|     int seenopt = 0; | ||||
|     int namedargs = 0; | ||||
| @@ -946,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     head = argv[0]; | ||||
|     if (janet_checktype(head, JANET_SYMBOL)) { | ||||
|         selfref = 1; | ||||
|         hasname = 1; | ||||
|         parami = 1; | ||||
|     } else if (janet_checktype(head, JANET_KEYWORD)) { | ||||
|         hasname = 1; | ||||
|         parami = 1; | ||||
|     } | ||||
|     if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) { | ||||
| @@ -1106,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG; | ||||
|     if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; | ||||
|  | ||||
|     if (selfref) def->name = janet_unwrap_symbol(head); | ||||
|     if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */ | ||||
|     janet_def_addflags(def); | ||||
|     defindex = janetc_addfuncdef(c, def); | ||||
|  | ||||
|   | ||||
| @@ -149,6 +149,11 @@ struct JanetVM { | ||||
|     JanetTraversalNode *traversal_top; | ||||
|     JanetTraversalNode *traversal_base; | ||||
|  | ||||
|     /* Thread safe strerror error buffer - for janet_strerror */ | ||||
| #ifndef JANET_WINDOWS | ||||
|     char strerror_buf[256]; | ||||
| #endif | ||||
|  | ||||
|     /* Event loop and scheduler globals */ | ||||
| #ifdef JANET_EV | ||||
|     size_t tq_count; | ||||
|   | ||||
| @@ -549,8 +549,8 @@ JANET_CORE_FN(cfun_string_format, | ||||
|               "- `a`, `A`: floating point number, formatted as a hexadecimal number.\n" | ||||
|               "- `s`: formatted as a string, precision indicates padding and maximum length.\n" | ||||
|               "- `t`: emit the type of the given value.\n" | ||||
|               "- `v`: format with (describe x)" | ||||
|               "- `V`: format with (string x)" | ||||
|               "- `v`: format with (describe x)\n" | ||||
|               "- `V`: format with (string x)\n" | ||||
|               "- `j`: format to jdn (Janet data notation).\n" | ||||
|               "\n" | ||||
|               "The following conversion specifiers are used for \"pretty-printing\", where the upper-case " | ||||
|   | ||||
| @@ -490,3 +490,18 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) { | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| void janet_buffer_dtostr(JanetBuffer *buffer, double x) { | ||||
| #define BUFSIZE 32 | ||||
|     janet_buffer_extra(buffer, BUFSIZE); | ||||
|     int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x); | ||||
| #undef BUFSIZE | ||||
|     /* fix locale issues with commas */ | ||||
|     for (int i = 0; i < count; i++) { | ||||
|         char c = buffer->data[buffer->count + i]; | ||||
|         if (c == ',') { | ||||
|             buffer->data[buffer->count + i] = '.'; | ||||
|         } | ||||
|     } | ||||
|     buffer->count += count; | ||||
| } | ||||
|   | ||||
| @@ -234,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) { | ||||
|     head->hash = hash; | ||||
|     sym = (uint8_t *)(head->data); | ||||
|     memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter)); | ||||
|     sym[head->length] = 0; | ||||
|     janet_symcache_put((const uint8_t *)sym, bucket); | ||||
|     return (const uint8_t *)sym; | ||||
| } | ||||
|   | ||||
| @@ -319,13 +319,6 @@ JANET_CORE_FN(cfun_table_new, | ||||
|     int32_t cap = janet_getnat(argv, 0); | ||||
|     return janet_wrap_table(janet_table(cap)); | ||||
| } | ||||
| /* | ||||
|     uint32_t flags = janet_getflags(argv, 1, "kv"); | ||||
|     if (flags == 0) return janet_wrap_table(janet_table(cap)); | ||||
|     if (flags == 1) return janet_wrap_table(janet_table_weakk(cap)); | ||||
|     if (flags == 2) return janet_wrap_table(janet_table_weakv(cap)); | ||||
|     return janet_wrap_table(janet_table_weakkv(cap)); | ||||
|     */ | ||||
|  | ||||
| JANET_CORE_FN(cfun_table_weak, | ||||
|               "(table/weak capacity)", | ||||
|   | ||||
| @@ -953,6 +953,20 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* Better strerror (thread-safe if available) */ | ||||
| const char *janet_strerror(int e) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     /* Microsoft strerror seems sane here and is thread safe by default */ | ||||
|     return strerror(e); | ||||
| #elif defined(_GNU_SOURCE) | ||||
|     /* See https://linux.die.net/man/3/strerror_r */ | ||||
|     return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf)); | ||||
| #else | ||||
|     strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf)); | ||||
|     return janet_vm.strerror_buf; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* Setting C99 standard makes this not available, but it should | ||||
|  * work/link properly if we detect a BSD */ | ||||
| #if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) | ||||
| @@ -960,6 +974,7 @@ void arc4random_buf(void *buf, size_t nbytes); | ||||
| #endif | ||||
|  | ||||
| int janet_cryptorand(uint8_t *out, size_t n) { | ||||
| #ifndef JANET_NO_CRYPTORAND | ||||
| #ifdef JANET_WINDOWS | ||||
|     for (size_t i = 0; i < n; i += sizeof(unsigned int)) { | ||||
|         unsigned int v; | ||||
| @@ -971,7 +986,10 @@ int janet_cryptorand(uint8_t *out, size_t n) { | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| #elif defined(JANET_LINUX) || defined(JANET_CYGWIN) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) ) | ||||
| #elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) | ||||
|     arc4random_buf(out, n); | ||||
|     return 0; | ||||
| #else | ||||
|     /* We should be able to call getrandom on linux, but it doesn't seem | ||||
|        to be uniformly supported on linux distros. | ||||
|        On Mac, arc4random_buf wasn't available on until 10.7. | ||||
| @@ -993,12 +1011,10 @@ int janet_cryptorand(uint8_t *out, size_t n) { | ||||
|     } | ||||
|     RETRY_EINTR(rc, close(randfd)); | ||||
|     return 0; | ||||
| #elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7) | ||||
|     arc4random_buf(out, n); | ||||
|     return 0; | ||||
| #endif | ||||
| #else | ||||
|     (void) n; | ||||
|     (void) out; | ||||
|     (void) n; | ||||
|     return -1; | ||||
| #endif | ||||
| } | ||||
|   | ||||
| @@ -49,11 +49,11 @@ | ||||
| #ifndef JANET_EXIT | ||||
| #include <stdio.h> | ||||
| #define JANET_EXIT(m) do { \ | ||||
|     fprintf(stderr, "janet interpreter runtime error at line %d in file %s: %s\n",\ | ||||
|     fprintf(stderr, "janet internal error at line %d in file %s: %s\n",\ | ||||
|         __LINE__,\ | ||||
|         __FILE__,\ | ||||
|         (m));\ | ||||
|     exit(1);\ | ||||
|     abort();\ | ||||
| } while (0) | ||||
| #endif | ||||
|  | ||||
| @@ -80,6 +80,8 @@ void janet_memempty(JanetKV *mem, int32_t count); | ||||
| void *janet_memalloc_empty(int32_t count); | ||||
| JanetTable *janet_get_core_table(const char *name); | ||||
| void janet_def_addflags(JanetFuncDef *def); | ||||
| void janet_buffer_dtostr(JanetBuffer *buffer, double x); | ||||
| const char *janet_strerror(int e); | ||||
| const void *janet_strbinsearch( | ||||
|     const void *tab, | ||||
|     size_t tabcount, | ||||
|   | ||||
| @@ -318,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh | ||||
|         Janet lr = janet_method_lookup(rhs, rmethod); | ||||
|         Janet argv[2] = { rhs, lhs }; | ||||
|         if (janet_checktype(lr, JANET_NIL)) { | ||||
|             janet_panicf("could not find method :%s for %v, or :%s for %v", | ||||
|             janet_panicf("could not find method :%s for %v or :%s for %v", | ||||
|                          lmethod, lhs, | ||||
|                          rmethod, rhs); | ||||
|         } | ||||
|   | ||||
| @@ -112,7 +112,8 @@ extern "C" { | ||||
|     || defined(__s390x__) /* S390 64-bit (BE) */ \ | ||||
|     || (defined(__ppc64__) || defined(__PPC64__)) \ | ||||
|     || defined(__aarch64__) /* ARM 64-bit */ \ | ||||
|     || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ | ||||
|     || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \ | ||||
|     || defined(__loongarch64) /* LoongArch64 64-bit */ | ||||
| #define JANET_64 1 | ||||
| #else | ||||
| #define JANET_32 1 | ||||
| @@ -596,8 +597,7 @@ typedef enum { | ||||
|     JANET_ASYNC_EVENT_READ = 6, | ||||
|     JANET_ASYNC_EVENT_WRITE = 7, | ||||
|     JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */ | ||||
|     JANET_ASYNC_EVENT_FAILED = 9, /* Used on windows for IOCP */ | ||||
|     JANET_ASYNC_EVENT_USER = 10 | ||||
|     JANET_ASYNC_EVENT_FAILED = 9 /* Used on windows for IOCP */ | ||||
| } JanetAsyncEvent; | ||||
|  | ||||
| typedef enum { | ||||
| @@ -606,9 +606,7 @@ typedef enum { | ||||
|     JANET_ASYNC_LISTEN_BOTH | ||||
| } JanetAsyncMode; | ||||
|  | ||||
| /* Typedefs */ | ||||
| typedef struct JanetStream JanetStream; | ||||
| typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event); | ||||
|  | ||||
| /* Wrapper around file descriptors and HANDLEs that can be polled. */ | ||||
| struct JanetStream { | ||||
| @@ -620,9 +618,30 @@ struct JanetStream { | ||||
|     const void *methods; /* Methods for this stream */ | ||||
| }; | ||||
|  | ||||
| typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event); | ||||
|  | ||||
| /* Start listening for events from a stream on the current root fiber. After | ||||
|  * calling this, users should call janet_await() before returning from the | ||||
|  * current C Function. This also will call janet_await. | ||||
|  * mode is which events to listen for, and callback is the function pointer to | ||||
|  * call when ever an event is sent from the event loop. state is an optional (can be NULL) | ||||
|  * pointer to data allocated with janet_malloc. This pointer will be passed to callback as | ||||
|  * fiber->ev_state. It will also be freed for you by the runtime when the event loop determines | ||||
|  * it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct. */ | ||||
| JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state); | ||||
|  | ||||
| /* Do not send any more events to the given callback. Call this after scheduling fiber to be resume | ||||
|  * or canceled. */ | ||||
| JANET_API void janet_async_end(JanetFiber *fiber); | ||||
| JANET_API void *janet_async_start(JanetFiber *fiber, JanetStream *stream, | ||||
|                                   JanetAsyncMode mode, JanetEVCallback callback, size_t data_size); | ||||
|  | ||||
| /* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */ | ||||
| JANET_API void janet_async_in_flight(JanetFiber *fiber); | ||||
|  | ||||
| /* On some platforms, it is important to be able to control if a stream is edge-trigger or level triggered. | ||||
|  * For example, a server that is accepting connections might want to be level triggered or edge-triggered | ||||
|  * depending on expected service. */ | ||||
| JANET_API void janet_stream_edge_triggered(JanetStream *stream); | ||||
| JANET_API void janet_stream_level_triggered(JanetStream *stream); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| @@ -635,6 +654,7 @@ typedef int32_t JanetAtomicInt; | ||||
| #endif | ||||
| JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x); | ||||
| JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x); | ||||
| JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x); | ||||
|  | ||||
| /* We provide three possible implementations of Janets. The preferred | ||||
|  * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the | ||||
| @@ -1488,22 +1508,22 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric | ||||
| JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value); | ||||
|  | ||||
| /* Read async from a stream */ | ||||
| JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); | ||||
| JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes); | ||||
| #ifdef JANET_NET | ||||
| JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags); | ||||
| #endif | ||||
|  | ||||
| /* Write async to a stream */ | ||||
| JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf); | ||||
| JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str); | ||||
| #ifdef JANET_NET | ||||
| JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags); | ||||
| JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags); | ||||
| JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags); | ||||
| JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags); | ||||
| JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags); | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
| @@ -2127,7 +2147,9 @@ typedef enum { | ||||
|     RULE_LINE,         /* [tag] */ | ||||
|     RULE_COLUMN,       /* [tag] */ | ||||
|     RULE_UNREF,        /* [rule, tag] */ | ||||
|     RULE_CAPTURE_NUM   /* [rule, tag] */ | ||||
|     RULE_CAPTURE_NUM,  /* [rule, tag] */ | ||||
|     RULE_SUB,          /* [rule, rule] */ | ||||
|     RULE_SPLIT         /* [rule, rule] */ | ||||
| } JanetPegOpcod; | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
| @@ -502,10 +502,10 @@ static void kright(void) { | ||||
| } | ||||
|  | ||||
| static void krightw(void) { | ||||
|     while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) { | ||||
|     while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) { | ||||
|         gbl_pos++; | ||||
|     } | ||||
|     while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) { | ||||
|     while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) { | ||||
|         gbl_pos++; | ||||
|     } | ||||
|     refresh(); | ||||
|   | ||||
| @@ -51,5 +51,13 @@ | ||||
| (def f (asm (disasm (fn [x] (fn [y] (+ x y)))))) | ||||
| (assert (= ((f 10) 37) 47) "asm environment tables") | ||||
|  | ||||
| # issue #1424 | ||||
| (assert-no-error "arity > used slots (issue #1424)" | ||||
|                  (asm | ||||
|                    (disasm | ||||
|                      (fn [] | ||||
|                        (def foo (fn [one two] one)) | ||||
|                        (foo 100 200))))) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
|   | ||||
| @@ -241,6 +241,16 @@ | ||||
|   (assert (pos? (% x 4)) "generate in loop")) | ||||
| (assert (= gencount 75) "generate loop count") | ||||
|  | ||||
| # more loop checks | ||||
| (assert (deep= (seq [i :range [0 10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 1") | ||||
| (assert (deep= (seq [i :range [0 10 2]] i) @[0 2 4 6 8]) "seq 2") | ||||
| (assert (deep= (seq [i :range [10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 3") | ||||
| (assert (deep= (seq [i :range-to [10]] i) @[0 1 2 3 4 5 6 7 8 9 10]) "seq 4") | ||||
| (def gen (generate [x :range-to [0 nil 2]] x)) | ||||
| (assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit") | ||||
| (def gen (generate [x :range [0 nil 2]] x)) | ||||
| (assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit 2") | ||||
|  | ||||
| # Even and odd | ||||
| # ff163a5ae | ||||
| (assert (odd? 9) "odd? 1") | ||||
| @@ -354,7 +364,7 @@ | ||||
|         "sort 5") | ||||
| (assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6") | ||||
|  | ||||
| # #1283   | ||||
| # #1283 | ||||
| (assert (deep= | ||||
|           (partition 2 (generate [ i :in [:a :b :c :d :e]] i)) | ||||
|           '@[(:a :b) (:c :d) (:e)])) | ||||
| @@ -945,10 +955,28 @@ | ||||
| (defn case-4 [&] | ||||
|   (def x (break (break (break))))) | ||||
| (bytecode-roundtrip case-4) | ||||
| (defn case-5 [] | ||||
|   (def foo (fn [one two] one)) | ||||
|   (foo 100 200)) | ||||
| (bytecode-roundtrip case-5) | ||||
|  | ||||
| # Debug bytecode of these functions | ||||
| # (pp (disasm case-1)) | ||||
| # (pp (disasm case-2)) | ||||
| # (pp (disasm case-3)) | ||||
|  | ||||
| # Regression #1330 | ||||
| (defn regress-1330 [&] | ||||
|   (def a [1 2 3]) | ||||
|   (def b [;a]) | ||||
|   (identity a)) | ||||
| (assert (= [1 2 3] (regress-1330)) "regression 1330") | ||||
|  | ||||
| # Issue 1341 | ||||
| (assert (= () '() (macex '())) "macex ()") | ||||
| (assert (= '[] (macex '[])) "macex []") | ||||
|  | ||||
| (assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn") | ||||
| (assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2)))) | ||||
|  | ||||
| (end-suite) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # Copyright (c) 2024 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| @@ -77,6 +77,46 @@ | ||||
| (buffer/push-string b5 "456" @"789") | ||||
| (assert (= "123456789" (string b5)) "buffer/push-buffer 2") | ||||
|  | ||||
| (def buffer-uint16-be @"") | ||||
| (buffer/push-uint16 buffer-uint16-be :be 0x0102) | ||||
| (assert (= "\x01\x02" (string buffer-uint16-be)) "buffer/push-uint16 big endian") | ||||
|  | ||||
| (def buffer-uint16-le @"") | ||||
| (buffer/push-uint16 buffer-uint16-le :le 0x0102) | ||||
| (assert (= "\x02\x01" (string buffer-uint16-le)) "buffer/push-uint16 little endian") | ||||
|  | ||||
| (def buffer-uint16-negative @"") | ||||
| (buffer/push-uint16 buffer-uint16-negative :be -1) | ||||
| (assert (= "\xff\xff" (string buffer-uint16-negative)) "buffer/push-uint16 negative") | ||||
|  | ||||
| (def buffer-uint32-be @"") | ||||
| (buffer/push-uint32 buffer-uint32-be :be 0x01020304) | ||||
| (assert (= "\x01\x02\x03\x04" (string buffer-uint32-be)) "buffer/push-uint32 big endian") | ||||
|  | ||||
| (def buffer-uint32-le @"") | ||||
| (buffer/push-uint32 buffer-uint32-le :le 0x01020304) | ||||
| (assert (= "\x04\x03\x02\x01" (string buffer-uint32-le)) "buffer/push-uint32 little endian") | ||||
|  | ||||
| (def buffer-uint32-negative @"") | ||||
| (buffer/push-uint32 buffer-uint32-negative :be -1) | ||||
| (assert (= "\xff\xff\xff\xff" (string buffer-uint32-negative)) "buffer/push-uint32 negative") | ||||
|  | ||||
| (def buffer-float32-be @"") | ||||
| (buffer/push-float32 buffer-float32-be :be 1.234) | ||||
| (assert (= "\x3f\x9d\xf3\xb6" (string buffer-float32-be)) "buffer/push-float32 big endian") | ||||
|  | ||||
| (def buffer-float32-le @"") | ||||
| (buffer/push-float32 buffer-float32-le :le 1.234) | ||||
| (assert (= "\xb6\xf3\x9d\x3f" (string buffer-float32-le)) "buffer/push-float32 little endian") | ||||
|  | ||||
| (def buffer-float64-be @"") | ||||
| (buffer/push-float64 buffer-float64-be :be 1.234) | ||||
| (assert (= "\x3f\xf3\xbe\x76\xc8\xb4\x39\x58" (string buffer-float64-be)) "buffer/push-float64 big endian") | ||||
|  | ||||
| (def buffer-float64-le @"") | ||||
| (buffer/push-float64 buffer-float64-le :le 1.234) | ||||
| (assert (= "\x58\x39\xb4\xc8\x76\xbe\xf3\x3f" (string buffer-float64-le)) "buffer/push-float64 little endian") | ||||
|  | ||||
| # Buffer from bytes | ||||
| (assert (deep= @"" (buffer/from-bytes)) "buffer/from-bytes 1") | ||||
| (assert (deep= @"ABC" (buffer/from-bytes 65 66 67)) "buffer/from-bytes 2") | ||||
| @@ -122,5 +162,20 @@ | ||||
| (assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4")) | ||||
|         "buffer/push-at 3") | ||||
|  | ||||
| # buffer/format-at | ||||
| (def start-buf (buffer/new-filled 100 (chr "x"))) | ||||
| (buffer/format-at start-buf 50 "aa%dbb" 32) | ||||
| (assert (= (string start-buf) (string (string/repeat "x" 50) "aa32bb"  (string/repeat "x" 44))) | ||||
|         "buffer/format-at 1") | ||||
| (assert | ||||
|   (deep= | ||||
|     (buffer/format @"" "%j" [1 2 3 :a :b :c]) | ||||
|     (buffer/format-at @"" 0 "%j" [1 2 3 :a :b :c])) | ||||
|   "buffer/format-at empty buffer") | ||||
| (def buf @"xxxyyy") | ||||
| (buffer/format-at buf -4 "xxx") | ||||
| (assert (= (string buf) "xxxxxx") "buffer/format-at negative index") | ||||
| (assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc")) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
|   | ||||
							
								
								
									
										125
									
								
								test/suite-bundle.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								test/suite-bundle.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| # Copyright (c) 2024 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| (assert true) # smoke test | ||||
|  | ||||
| # Copy since not exposed in boot.janet | ||||
| (defn- bundle-rpath | ||||
|   [path] | ||||
|   (string/replace-all "\\" "/" (os/realpath path))) | ||||
|  | ||||
| (defn- rmrf | ||||
|   "rm -rf in janet" | ||||
|   [x] | ||||
|   (case (os/lstat x :mode) | ||||
|     nil nil | ||||
|     :directory (do | ||||
|                  (each y (os/dir x) | ||||
|                    (rmrf (string x "/" y))) | ||||
|                  (os/rmdir x)) | ||||
|     (os/rm x)) | ||||
|   nil) | ||||
|  | ||||
| # Test mkdir -> rmdir | ||||
| (assert (os/mkdir "tempdir123")) | ||||
| (rmrf "tempdir123") | ||||
|  | ||||
| # Setup a temporary syspath for manipultation | ||||
| (math/seedrandom (os/cryptorand 16)) | ||||
| (def syspath (string (math/random) "_jpm_tree.tmp")) | ||||
| (rmrf syspath) | ||||
| (assert (os/mkdir syspath)) | ||||
| (put root-env *syspath* (bundle-rpath syspath)) | ||||
| (unless (os/getenv "VERBOSE") | ||||
|   (setdyn *out* @"")) | ||||
| (assert (empty? (bundle/list)) "initial bundle/list") | ||||
| (assert (empty? (bundle/topolist)) "initial bundle/topolist") | ||||
|  | ||||
| # Try (and fail) to install sample-bundle (missing deps) | ||||
| (assert-error "missing dependencies sample-dep1, sample-dep2" | ||||
|               (bundle/install "./examples/sample-bundle")) | ||||
| (assert (empty? (bundle/list))) | ||||
|  | ||||
| # Install deps (dep1 as :auto-remove) | ||||
| (assert-no-error "sample-dep2" | ||||
|                  (bundle/install "./examples/sample-dep2")) | ||||
| (assert (= 1 (length (bundle/list)))) | ||||
| (assert-no-error "sample-dep1" (bundle/install "./examples/sample-dep1")) | ||||
| (assert (= 2 (length (bundle/list)))) | ||||
|  | ||||
| (assert-no-error "sample-dep2 reinstall" (bundle/reinstall "sample-dep2")) | ||||
| (assert-no-error "sample-dep1 reinstall" (bundle/reinstall "sample-dep1" :auto-remove true)) | ||||
|  | ||||
| (assert (= 2 (length (bundle/list))) "bundles are listed correctly 1") | ||||
| (assert (= 2 (length (bundle/topolist))) "bundles are listed correctly 2") | ||||
|  | ||||
| # Now install sample-bundle | ||||
| (assert-no-error "sample-bundle install" (bundle/install "./examples/sample-bundle")) | ||||
|  | ||||
| (assert-error "" (bundle/install "./examples/sample-dep11111")) | ||||
|  | ||||
| (assert (= 3 (length (bundle/list))) "bundles are listed correctly 3") | ||||
| (assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4") | ||||
|  | ||||
| # Check topolist has not bad order | ||||
| (def tlist (bundle/topolist)) | ||||
| (assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep2" tlist)) "topolist 1") | ||||
| (assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep1" tlist)) "topolist 2") | ||||
| (assert (> (index-of "sample-dep1" tlist) (index-of "sample-dep2" tlist)) "topolist 3") | ||||
|  | ||||
| # Prune should do nothing | ||||
| (assert-no-error "first prune" (bundle/prune)) | ||||
| (assert (= 3 (length (bundle/list))) "bundles are listed correctly 3") | ||||
| (assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4") | ||||
|  | ||||
| # Check that we can import the main dependency | ||||
| (import mymod) | ||||
| (assert (= 288 (mymod/myfn 12)) "using sample-bundle") | ||||
|  | ||||
| # Manual uninstall of dep1 and dep2 shouldn't work either since that would break dependencies | ||||
| (assert-error "cannot uninstall sample-dep1, breaks dependent bundles @[\"sample-bundle\"]" | ||||
|               (bundle/uninstall "sample-dep1")) | ||||
|  | ||||
| # Now re-install sample-bundle as auto-remove | ||||
| (assert-no-error "sample-bundle install" (bundle/reinstall "sample-bundle" :auto-remove true)) | ||||
|  | ||||
| # Reinstallation should also work without being concerned about breaking dependencies | ||||
| (assert-no-error "reinstall dep" (bundle/reinstall "sample-dep2")) | ||||
|  | ||||
| # Now prune should get rid of everything except sample-dep2 | ||||
| (assert-no-error "second prune" (bundle/prune)) | ||||
|  | ||||
| # Now check that we exactly one package left, which is dep2 | ||||
| (assert (= 1 (length (bundle/list))) "bundles are listed correctly 5") | ||||
| (assert (= 1 (length (bundle/topolist))) "bundles are listed correctly 6") | ||||
|  | ||||
| # Which we can uninstall manually | ||||
| (assert-no-error "uninstall dep2" (bundle/uninstall "sample-dep2")) | ||||
|  | ||||
| # Now check bundle listing is again empty | ||||
| (assert (= 0 (length (bundle/list))) "bundles are listed correctly 7") | ||||
| (assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8") | ||||
|  | ||||
| (rmrf syspath) | ||||
|  | ||||
| (end-suite) | ||||
| @@ -21,6 +21,9 @@ | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| (def test-port (os/getenv "JANET_TEST_PORT" "8761")) | ||||
| (def test-host (os/getenv "JANET_TEST_HOST" "127.0.0.1")) | ||||
|  | ||||
| # Subprocess | ||||
| # 5e1a8c86f | ||||
| (def janet (dyn *executable*)) | ||||
| @@ -192,11 +195,11 @@ | ||||
|       (net/write stream b) | ||||
|       (buffer/clear b))) | ||||
|  | ||||
|   (def s (net/server "127.0.0.1" "8000" handler)) | ||||
|   (def s (net/server test-host test-port handler)) | ||||
|   (assert s "made server 1") | ||||
|  | ||||
|   (defn test-echo [msg] | ||||
|     (with [conn (net/connect "127.0.0.1" "8000")] | ||||
|     (with [conn (net/connect test-host test-port)] | ||||
|       (net/write conn msg) | ||||
|       (def res (net/read conn 1024)) | ||||
|       (assert (= (string res) msg) (string "echo " msg)))) | ||||
| @@ -216,18 +219,18 @@ | ||||
|     # prevent immediate close | ||||
|     (ev/read stream 1) | ||||
|     (def [host port] (net/localname stream)) | ||||
|     (assert (= host "127.0.0.1") "localname host server") | ||||
|     (assert (= port 8000) "localname port server"))) | ||||
|     (assert (= host test-host) "localname host server") | ||||
|     (assert (= port (scan-number test-port)) "localname port server"))) | ||||
|  | ||||
| # Test localname and peername | ||||
| # 077bf5eba | ||||
| (repeat 10 | ||||
|   (with [s (net/server "127.0.0.1" "8000" names-handler)] | ||||
|   (with [s (net/server test-host test-port names-handler)] | ||||
|     (repeat 10 | ||||
|       (with [conn (net/connect "127.0.0.1" "8000")] | ||||
|       (with [conn (net/connect test-host test-port)] | ||||
|         (def [host port] (net/peername conn)) | ||||
|         (assert (= host "127.0.0.1") "peername host client ") | ||||
|         (assert (= port 8000) "peername port client") | ||||
|         (assert (= host test-host) "peername host client ") | ||||
|         (assert (= port (scan-number test-port)) "peername port client") | ||||
|         # let server close | ||||
|         (ev/write conn " ")))) | ||||
|   (gccollect)) | ||||
| @@ -366,4 +369,10 @@ | ||||
|                (exec-slurp ;run janet "-e" "(print :hi)"))) | ||||
|           "exec-slurp 1")) | ||||
|  | ||||
| # valgrind-able check for #1337 | ||||
| (def superv (ev/chan 10)) | ||||
| (def f (ev/go |(ev/sleep 1e9) nil superv)) | ||||
| (ev/cancel f (gensym)) | ||||
| (ev/take superv) | ||||
|  | ||||
| (end-suite) | ||||
|   | ||||
| @@ -96,11 +96,23 @@ | ||||
|   (assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer") | ||||
|   (assert (= (length buf) 2) "cryptorand appends to buffer")) | ||||
|  | ||||
| (assert-no-error "realtime clock" (os/clock)) | ||||
| (assert-no-error "realtime clock" (os/clock nil)) | ||||
| (assert-no-error "realtime clock" (os/clock nil nil)) | ||||
|  | ||||
| # 80db68210 | ||||
| (assert-no-error "realtime clock" (os/clock :realtime)) | ||||
| (assert-no-error "cputime clock" (os/clock :cputime)) | ||||
| (assert-no-error "monotonic clock" (os/clock :monotonic)) | ||||
|  | ||||
| (assert-no-error "realtime clock double output" (os/clock nil :double)) | ||||
| (assert-no-error "realtime clock int output" (os/clock nil :int)) | ||||
| (assert-no-error "realtime clock tuple output" (os/clock nil :tuple)) | ||||
|  | ||||
| (assert-error "invalid clock" (os/clock :a)) | ||||
| (assert-error "invalid output" (os/clock :realtime :b)) | ||||
| (assert-error "invalid clock and output" (os/clock :a :b)) | ||||
|  | ||||
| (def before (os/clock :monotonic)) | ||||
| (def after (os/clock :monotonic)) | ||||
| (assert (>= after before) "monotonic clock is monotonic") | ||||
| @@ -148,4 +160,3 @@ | ||||
|                                {:out dn :err dn}))) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
|   | ||||
| @@ -263,6 +263,9 @@ | ||||
| (marshpeg '(if-not "abcdf" 123)) | ||||
| (marshpeg ~(cmt "abcdf" ,identity)) | ||||
| (marshpeg '(group "abc")) | ||||
| (marshpeg '(sub "abcdf" "abc")) | ||||
| (marshpeg '(* (sub 1 1))) | ||||
| (marshpeg '(split "," (+ "a" "b" "c"))) | ||||
|  | ||||
| # Peg swallowing errors | ||||
| # 159651117 | ||||
| @@ -660,5 +663,98 @@ | ||||
|   (peg/match '(if (not (* (constant 7) "a")) "hello") "hello") | ||||
|   @[]) "peg if not") | ||||
|  | ||||
| (defn test [name peg input expected] | ||||
|   (assert (deep= (peg/match peg input) expected) name)) | ||||
|  | ||||
| (test "sub: matches the same input twice" | ||||
|   ~(sub "abcd" "abc") | ||||
|   "abcdef" | ||||
|   @[]) | ||||
|  | ||||
| (test "sub: second pattern cannot match more than the first pattern" | ||||
|   ~(sub "abcd" "abcde") | ||||
|   "abcdef" | ||||
|   nil) | ||||
|  | ||||
| (test "sub: fails if first pattern fails" | ||||
|   ~(sub "x" "abc") | ||||
|   "abcdef" | ||||
|   nil) | ||||
|  | ||||
| (test "sub: fails if second pattern fails" | ||||
|   ~(sub "abc" "x") | ||||
|   "abcdef" | ||||
|   nil) | ||||
|  | ||||
| (test "sub: keeps captures from both patterns" | ||||
|   ~(sub '"abcd" '"abc") | ||||
|   "abcdef" | ||||
|   @["abcd" "abc"]) | ||||
|  | ||||
| (test "sub: second pattern can reference captures from first" | ||||
|   ~(* (constant 5 :tag) (sub (capture "abc" :tag) (backref :tag))) | ||||
|   "abcdef" | ||||
|   @[5 "abc" "abc"]) | ||||
|  | ||||
| (test "sub: second pattern can't see past what the first pattern matches" | ||||
|   ~(sub "abc" (* "abc" -1)) | ||||
|   "abcdef" | ||||
|   @[]) | ||||
|  | ||||
| (test "sub: positions inside second match are still relative to the entire input" | ||||
|   ~(* "one\ntw" (sub "o" (* ($) (line) (column)))) | ||||
|   "one\ntwo\nthree\n" | ||||
|   @[6 2 3]) | ||||
|  | ||||
| (test "sub: advances to the end of the first pattern's match" | ||||
|   ~(* (sub "abc" "ab") "d") | ||||
|   "abcdef" | ||||
|   @[]) | ||||
|  | ||||
| (test "split: basic functionality" | ||||
|   ~(split "," '1) | ||||
|   "a,b,c" | ||||
|   @["a" "b" "c"]) | ||||
|  | ||||
| (test "split: drops captures from separator pattern" | ||||
|   ~(split '"," '1) | ||||
|   "a,b,c" | ||||
|   @["a" "b" "c"]) | ||||
|  | ||||
| (test "split: can match empty subpatterns" | ||||
|   ~(split "," ':w*) | ||||
|   ",a,,bar,,,c,," | ||||
|   @["" "a" "" "bar" "" "" "c" "" ""]) | ||||
|  | ||||
| (test "split: subpattern is limited to only text before the separator" | ||||
|   ~(split "," '(to -1)) | ||||
|   "a,,bar,c" | ||||
|   @["a" "" "bar" "c"]) | ||||
|  | ||||
| (test "split: fails if any subpattern fails" | ||||
|   ~(split "," '"a") | ||||
|   "a,a,b" | ||||
|   nil) | ||||
|  | ||||
| (test "split: separator does not have to match anything" | ||||
|   ~(split "x" '(to -1)) | ||||
|   "a,a,b" | ||||
|   @["a,a,b"]) | ||||
|  | ||||
| (test "split: always consumes entire input" | ||||
|   ~(split 1 '"") | ||||
|   "abc" | ||||
|   @["" "" "" ""]) | ||||
|  | ||||
| (test "split: separator can be an arbitrary PEG" | ||||
|   ~(split :s+ '(to -1)) | ||||
|   "a   b      c" | ||||
|   @["a" "b" "c"]) | ||||
|  | ||||
| (test "split: does not advance past the end of the input" | ||||
|   ~(* (split "," ':w+) 0) | ||||
|   "a,b,c" | ||||
|   @["a" "b" "c"]) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
|   | ||||
| @@ -198,5 +198,9 @@ | ||||
|  | ||||
| (assert (= (test) '(1 ())) "issue #919") | ||||
|  | ||||
| (end-suite) | ||||
| # Regression #1327 | ||||
| (def x "A") | ||||
| (def x (if (= nil x) "B" x)) | ||||
| (assert (= x "A")) | ||||
|  | ||||
| (end-suite) | ||||
|   | ||||
| @@ -42,7 +42,7 @@ | ||||
|  | ||||
| (defn buffer-factory | ||||
|   [] | ||||
|   @"im am a buffer") | ||||
|   @"i am a buffer") | ||||
|  | ||||
| (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation") | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user